Changeset 25eec4e in mainline for kernel/arch/mips32/src/mm/tlb.c
- Timestamp:
- 2013-04-19T18:38:18Z (12 years ago)
- Branches:
- lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
- Children:
- 6d717a4
- Parents:
- a1e2df13 (diff), 289cb7dd (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)
links above to see all the changes relative to each parent. - File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
kernel/arch/mips32/src/mm/tlb.c
ra1e2df13 r25eec4e 48 48 #include <symtab.h> 49 49 50 static void tlb_refill_fail(istate_t *); 51 static void tlb_invalid_fail(istate_t *); 52 static void tlb_modified_fail(istate_t *); 53 54 static pte_t *find_mapping_and_check(uintptr_t, int, istate_t *, int *); 50 #define PFN_SHIFT 12 51 #define VPN_SHIFT 12 52 #define ADDR2VPN(a) ((a) >> VPN_SHIFT) 53 #define ADDR2VPN2(a) (ADDR2VPN((a)) >> 1) 54 #define VPN2ADDR(vpn) ((vpn) << VPN_SHIFT) 55 #define VPN22ADDR(vpn2) (VPN2ADDR(vpn2) << 1) 56 #define PFN2ADDR(pfn) ((pfn) << PFN_SHIFT) 57 58 #define BANK_SELECT_BIT(a) (((a) >> PAGE_WIDTH) & 1) 59 55 60 56 61 /** Initialize TLB. … … 88 93 { 89 94 entry_lo_t lo; 90 entry_hi_t hi;91 asid_t asid;92 95 uintptr_t badvaddr; 93 96 pte_t *pte; 94 int pfrc;95 97 96 98 badvaddr = cp0_badvaddr_read(); 97 asid = AS->asid; 98 99 pte = find_mapping_and_check(badvaddr, PF_ACCESS_READ, istate, &pfrc); 100 if (!pte) { 101 switch (pfrc) { 102 case AS_PF_FAULT: 103 goto fail; 104 break; 105 case AS_PF_DEFER: 106 /* 107 * The page fault came during copy_from_uspace() 108 * or copy_to_uspace(). 109 */ 110 return; 111 default: 112 panic("Unexpected pfrc (%d).", pfrc); 99 100 pte = page_mapping_find(AS, badvaddr, true); 101 if (pte && pte->p) { 102 /* 103 * Record access to PTE. 104 */ 105 pte->a = 1; 106 107 tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->d, 108 pte->cacheable, pte->pfn); 109 110 /* 111 * New entry is to be inserted into TLB 112 */ 113 if (BANK_SELECT_BIT(badvaddr) == 0) { 114 cp0_entry_lo0_write(lo.value); 115 cp0_entry_lo1_write(0); 116 } else { 117 cp0_entry_lo0_write(0); 118 cp0_entry_lo1_write(lo.value); 113 119 } 114 } 115 116 /* 117 * Record access to PTE. 118 */ 119 pte->a = 1; 120 121 tlb_prepare_entry_hi(&hi, asid, badvaddr); 122 tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->d, pte->cacheable, 123 pte->pfn); 124 125 /* 126 * New entry is to be inserted into TLB 127 */ 128 cp0_entry_hi_write(hi.value); 129 if ((badvaddr / PAGE_SIZE) % 2 == 0) { 130 cp0_entry_lo0_write(lo.value); 131 cp0_entry_lo1_write(0); 132 } 133 else { 134 cp0_entry_lo0_write(0); 135 cp0_entry_lo1_write(lo.value); 136 } 137 cp0_pagemask_write(TLB_PAGE_MASK_16K); 138 tlbwr(); 139 140 return; 141 142 fail: 143 tlb_refill_fail(istate); 120 cp0_pagemask_write(TLB_PAGE_MASK_16K); 121 tlbwr(); 122 return; 123 } 124 125 (void) as_page_fault(badvaddr, PF_ACCESS_READ, istate); 144 126 } 145 127 … … 150 132 void tlb_invalid(istate_t *istate) 151 133 { 134 entry_lo_t lo; 152 135 tlb_index_t index; 153 136 uintptr_t badvaddr; 154 entry_lo_t lo;155 entry_hi_t hi;156 137 pte_t *pte; 157 int pfrc;158 159 badvaddr = cp0_badvaddr_read();160 138 161 139 /* 162 140 * Locate the faulting entry in TLB. 163 141 */ 164 hi.value = cp0_entry_hi_read();165 tlb_prepare_entry_hi(&hi, hi.asid, badvaddr);166 cp0_entry_hi_write(hi.value);167 142 tlbp(); 168 143 index.value = cp0_index_read(); 169 144 145 #if defined(PROCESSOR_4Kc) 170 146 /* 171 * Fail if the entry is not in TLB. 147 * This can happen on a 4Kc when Status.EXL is 1 and there is a TLB miss. 148 * EXL is 1 when interrupts are disabled. The combination of a TLB miss 149 * and disabled interrupts is possible in copy_to/from_uspace(). 172 150 */ 173 151 if (index.p) { 174 printf("TLB entry not found.\n"); 175 goto fail; 176 } 177 178 pte = find_mapping_and_check(badvaddr, PF_ACCESS_READ, istate, &pfrc); 179 if (!pte) { 180 switch (pfrc) { 181 case AS_PF_FAULT: 182 goto fail; 183 break; 184 case AS_PF_DEFER: 185 /* 186 * The page fault came during copy_from_uspace() 187 * or copy_to_uspace(). 188 */ 189 return; 190 default: 191 panic("Unexpected pfrc (%d).", pfrc); 192 } 193 } 194 195 /* 196 * Read the faulting TLB entry. 197 */ 198 tlbr(); 199 200 /* 201 * Record access to PTE. 202 */ 203 pte->a = 1; 204 205 tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->d, pte->cacheable, 206 pte->pfn); 207 208 /* 209 * The entry is to be updated in TLB. 210 */ 211 if ((badvaddr / PAGE_SIZE) % 2 == 0) 212 cp0_entry_lo0_write(lo.value); 213 else 214 cp0_entry_lo1_write(lo.value); 215 cp0_pagemask_write(TLB_PAGE_MASK_16K); 216 tlbwi(); 217 218 return; 219 220 fail: 221 tlb_invalid_fail(istate); 152 tlb_refill(istate); 153 return; 154 } 155 #endif 156 157 ASSERT(!index.p); 158 159 badvaddr = cp0_badvaddr_read(); 160 161 pte = page_mapping_find(AS, badvaddr, true); 162 if (pte && pte->p) { 163 /* 164 * Read the faulting TLB entry. 165 */ 166 tlbr(); 167 168 /* 169 * Record access to PTE. 170 */ 171 pte->a = 1; 172 173 tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->d, 174 pte->cacheable, pte->pfn); 175 176 /* 177 * The entry is to be updated in TLB. 178 */ 179 if (BANK_SELECT_BIT(badvaddr) == 0) 180 cp0_entry_lo0_write(lo.value); 181 else 182 cp0_entry_lo1_write(lo.value); 183 tlbwi(); 184 return; 185 } 186 187 (void) as_page_fault(badvaddr, PF_ACCESS_READ, istate); 222 188 } 223 189 … … 228 194 void tlb_modified(istate_t *istate) 229 195 { 196 entry_lo_t lo; 230 197 tlb_index_t index; 231 198 uintptr_t badvaddr; 232 entry_lo_t lo;233 entry_hi_t hi;234 199 pte_t *pte; 235 int pfrc;236 200 237 201 badvaddr = cp0_badvaddr_read(); … … 240 204 * Locate the faulting entry in TLB. 241 205 */ 242 hi.value = cp0_entry_hi_read();243 tlb_prepare_entry_hi(&hi, hi.asid, badvaddr);244 cp0_entry_hi_write(hi.value);245 206 tlbp(); 246 207 index.value = cp0_index_read(); 247 208 248 209 /* 249 * Fail if the entry is not in TLB. 210 * Emit warning if the entry is not in TLB. 211 * 212 * We do not assert on this because this could be a manifestation of 213 * an emulator bug, such as QEMU Bug #1128935: 214 * https://bugs.launchpad.net/qemu/+bug/1128935 250 215 */ 251 216 if (index.p) { 252 printf("TLB entry not found.\n"); 253 goto fail; 254 } 255 256 pte = find_mapping_and_check(badvaddr, PF_ACCESS_WRITE, istate, &pfrc); 257 if (!pte) { 258 switch (pfrc) { 259 case AS_PF_FAULT: 260 goto fail; 261 break; 262 case AS_PF_DEFER: 263 /* 264 * The page fault came during copy_from_uspace() 265 * or copy_to_uspace(). 266 */ 267 return; 268 default: 269 panic("Unexpected pfrc (%d).", pfrc); 270 } 271 } 272 273 /* 274 * Read the faulting TLB entry. 275 */ 276 tlbr(); 277 278 /* 279 * Record access and write to PTE. 280 */ 281 pte->a = 1; 282 pte->d = 1; 283 284 tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->w, pte->cacheable, 285 pte->pfn); 286 287 /* 288 * The entry is to be updated in TLB. 289 */ 290 if ((badvaddr / PAGE_SIZE) % 2 == 0) 291 cp0_entry_lo0_write(lo.value); 292 else 293 cp0_entry_lo1_write(lo.value); 294 cp0_pagemask_write(TLB_PAGE_MASK_16K); 295 tlbwi(); 296 297 return; 298 299 fail: 300 tlb_modified_fail(istate); 301 } 302 303 void tlb_refill_fail(istate_t *istate) 304 { 305 uintptr_t va = cp0_badvaddr_read(); 306 307 fault_if_from_uspace(istate, "TLB Refill Exception on %p.", 308 (void *) va); 309 panic_memtrap(istate, PF_ACCESS_UNKNOWN, va, "TLB Refill Exception."); 310 } 311 312 313 void tlb_invalid_fail(istate_t *istate) 314 { 315 uintptr_t va = cp0_badvaddr_read(); 316 317 fault_if_from_uspace(istate, "TLB Invalid Exception on %p.", 318 (void *) va); 319 panic_memtrap(istate, PF_ACCESS_UNKNOWN, va, "TLB Invalid Exception."); 320 } 321 322 void tlb_modified_fail(istate_t *istate) 323 { 324 uintptr_t va = cp0_badvaddr_read(); 325 326 fault_if_from_uspace(istate, "TLB Modified Exception on %p.", 327 (void *) va); 328 panic_memtrap(istate, PF_ACCESS_WRITE, va, "TLB Modified Exception."); 329 } 330 331 /** Try to find PTE for faulting address. 332 * 333 * @param badvaddr Faulting virtual address. 334 * @param access Access mode that caused the fault. 335 * @param istate Pointer to interrupted state. 336 * @param pfrc Pointer to variable where as_page_fault() return code 337 * will be stored. 338 * 339 * @return PTE on success, NULL otherwise. 340 */ 341 pte_t * 342 find_mapping_and_check(uintptr_t badvaddr, int access, istate_t *istate, 343 int *pfrc) 344 { 345 entry_hi_t hi; 346 pte_t *pte; 347 348 hi.value = cp0_entry_hi_read(); 349 350 /* 351 * Handler cannot succeed if the ASIDs don't match. 352 */ 353 if (hi.asid != AS->asid) { 354 printf("EntryHi.asid=%d, AS->asid=%d\n", hi.asid, AS->asid); 355 return NULL; 356 } 357 358 /* 359 * Check if the mapping exists in page tables. 360 */ 217 printf("%s: TLBP failed in exception handler (badvaddr=%#" 218 PRIxn ", ASID=%d).\n", __func__, badvaddr, 219 AS ? AS->asid : -1); 220 return; 221 } 222 361 223 pte = page_mapping_find(AS, badvaddr, true); 362 if (pte && pte->p && (pte->w || access != PF_ACCESS_WRITE)) { 363 /* 364 * Mapping found in page tables. 365 * Immediately succeed. 366 */ 367 return pte; 368 } else { 369 int rc; 370 371 /* 372 * Mapping not found in page tables. 373 * Resort to higher-level page fault handler. 374 */ 375 switch (rc = as_page_fault(badvaddr, access, istate)) { 376 case AS_PF_OK: 377 /* 378 * The higher-level page fault handler succeeded, 379 * The mapping ought to be in place. 380 */ 381 pte = page_mapping_find(AS, badvaddr, true); 382 ASSERT(pte && pte->p); 383 ASSERT(pte->w || access != PF_ACCESS_WRITE); 384 return pte; 385 case AS_PF_DEFER: 386 *pfrc = AS_PF_DEFER; 387 return NULL; 388 case AS_PF_FAULT: 389 *pfrc = AS_PF_FAULT; 390 return NULL; 391 default: 392 panic("Unexpected rc (%d).", rc); 393 } 394 395 } 224 if (pte && pte->p && pte->w) { 225 /* 226 * Read the faulting TLB entry. 227 */ 228 tlbr(); 229 230 /* 231 * Record access and write to PTE. 232 */ 233 pte->a = 1; 234 pte->d = 1; 235 236 tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->w, 237 pte->cacheable, pte->pfn); 238 239 /* 240 * The entry is to be updated in TLB. 241 */ 242 if (BANK_SELECT_BIT(badvaddr) == 0) 243 cp0_entry_lo0_write(lo.value); 244 else 245 cp0_entry_lo1_write(lo.value); 246 tlbwi(); 247 return; 248 } 249 250 (void) as_page_fault(badvaddr, PF_ACCESS_WRITE, istate); 396 251 } 397 252 … … 410 265 void tlb_prepare_entry_hi(entry_hi_t *hi, asid_t asid, uintptr_t addr) 411 266 { 412 hi->value = ALIGN_DOWN(addr, PAGE_SIZE * 2); 267 hi->value = 0; 268 hi->vpn2 = ADDR2VPN2(ALIGN_DOWN(addr, PAGE_SIZE)); 413 269 hi->asid = asid; 414 270 } … … 417 273 void tlb_print(void) 418 274 { 419 page_mask_t mask ;420 entry_lo_t lo0, lo 1;275 page_mask_t mask, mask_save; 276 entry_lo_t lo0, lo0_save, lo1, lo1_save; 421 277 entry_hi_t hi, hi_save; 422 278 unsigned int i; 423 279 424 280 hi_save.value = cp0_entry_hi_read(); 425 426 printf("[nr] [asid] [vpn2] [mask] [gvdc] [pfn ]\n"); 281 lo0_save.value = cp0_entry_lo0_read(); 282 lo1_save.value = cp0_entry_lo1_read(); 283 mask_save.value = cp0_pagemask_read(); 284 285 printf("[nr] [asid] [vpn2 ] [mask] [gvdc] [pfn ]\n"); 427 286 428 287 for (i = 0; i < TLB_ENTRY_COUNT; i++) { … … 435 294 lo1.value = cp0_entry_lo1_read(); 436 295 437 printf("%-4u %-6u % #6x %#6x %1u%1u%1u%1u %#6x\n",438 i, hi.asid, hi.vpn2, mask.mask,439 lo0.g, lo0.v, lo0.d, lo0.c, lo0.pfn);440 printf(" %1u%1u%1u%1u %#6x\n",441 lo1.g, lo1.v, lo1.d, lo1.c, lo1.pfn);296 printf("%-4u %-6u %0#10x %-#6x %1u%1u%1u%1u %0#10x\n", 297 i, hi.asid, VPN22ADDR(hi.vpn2), mask.mask, 298 lo0.g, lo0.v, lo0.d, lo0.c, PFN2ADDR(lo0.pfn)); 299 printf(" %1u%1u%1u%1u %0#10x\n", 300 lo1.g, lo1.v, lo1.d, lo1.c, PFN2ADDR(lo1.pfn)); 442 301 } 443 302 444 303 cp0_entry_hi_write(hi_save.value); 304 cp0_entry_lo0_write(lo0_save.value); 305 cp0_entry_lo1_write(lo1_save.value); 306 cp0_pagemask_write(mask_save.value); 445 307 } 446 308 … … 448 310 void tlb_invalidate_all(void) 449 311 { 450 ipl_t ipl;451 312 entry_lo_t lo0, lo1; 452 313 entry_hi_t hi_save; 453 314 int i; 454 315 316 ASSERT(interrupts_disabled()); 317 455 318 hi_save.value = cp0_entry_hi_read(); 456 ipl = interrupts_disable();457 319 458 320 for (i = TLB_WIRED; i < TLB_ENTRY_COUNT; i++) { … … 472 334 } 473 335 474 interrupts_restore(ipl);475 336 cp0_entry_hi_write(hi_save.value); 476 337 } … … 482 343 void tlb_invalidate_asid(asid_t asid) 483 344 { 484 ipl_t ipl;485 345 entry_lo_t lo0, lo1; 486 346 entry_hi_t hi, hi_save; 487 347 int i; 488 348 349 ASSERT(interrupts_disabled()); 489 350 ASSERT(asid != ASID_INVALID); 490 351 491 352 hi_save.value = cp0_entry_hi_read(); 492 ipl = interrupts_disable();493 353 494 354 for (i = 0; i < TLB_ENTRY_COUNT; i++) { … … 512 372 } 513 373 514 interrupts_restore(ipl);515 374 cp0_entry_hi_write(hi_save.value); 516 375 } … … 526 385 { 527 386 unsigned int i; 528 ipl_t ipl;529 387 entry_lo_t lo0, lo1; 530 388 entry_hi_t hi, hi_save; 531 389 tlb_index_t index; 390 391 ASSERT(interrupts_disabled()); 532 392 533 393 if (asid == ASID_INVALID) … … 535 395 536 396 hi_save.value = cp0_entry_hi_read(); 537 ipl = interrupts_disable();538 397 539 398 for (i = 0; i < cnt + 1; i += 2) { 540 hi.value = 0;541 399 tlb_prepare_entry_hi(&hi, asid, page + i * PAGE_SIZE); 542 400 cp0_entry_hi_write(hi.value); … … 565 423 } 566 424 567 interrupts_restore(ipl);568 425 cp0_entry_hi_write(hi_save.value); 569 426 }
Note:
See TracChangeset
for help on using the changeset viewer.