Changes in kernel/arch/mips32/src/mm/tlb.c [59fb782:aaa6af2] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
kernel/arch/mips32/src/mm/tlb.c
r59fb782 raaa6af2 48 48 #include <symtab.h> 49 49 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 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 *); 60 55 61 56 /** Initialize TLB. … … 93 88 { 94 89 entry_lo_t lo; 90 entry_hi_t hi; 91 asid_t asid; 95 92 uintptr_t badvaddr; 96 93 pte_t *pte; 94 int pfrc; 97 95 98 96 badvaddr = cp0_badvaddr_read(); 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); 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); 119 113 } 120 cp0_pagemask_write(TLB_PAGE_MASK_16K); 121 tlbwr(); 122 return; 123 } 124 125 (void) as_page_fault(badvaddr, PF_ACCESS_READ, istate); 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); 126 144 } 127 145 … … 132 150 void tlb_invalid(istate_t *istate) 133 151 { 134 entry_lo_t lo;135 152 tlb_index_t index; 136 153 uintptr_t badvaddr; 154 entry_lo_t lo; 155 entry_hi_t hi; 137 156 pte_t *pte; 157 int pfrc; 158 159 badvaddr = cp0_badvaddr_read(); 138 160 139 161 /* 140 162 * Locate the faulting entry in TLB. 141 163 */ 164 hi.value = cp0_entry_hi_read(); 165 tlb_prepare_entry_hi(&hi, hi.asid, badvaddr); 166 cp0_entry_hi_write(hi.value); 142 167 tlbp(); 143 168 index.value = cp0_index_read(); 144 169 145 #if defined(PROCESSOR_4Kc) 146 /* 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(). 170 /* 171 * Fail if the entry is not in TLB. 150 172 */ 151 173 if (index.p) { 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); 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); 188 222 } 189 223 … … 194 228 void tlb_modified(istate_t *istate) 195 229 { 196 entry_lo_t lo;197 230 tlb_index_t index; 198 231 uintptr_t badvaddr; 232 entry_lo_t lo; 233 entry_hi_t hi; 199 234 pte_t *pte; 235 int pfrc; 200 236 201 237 badvaddr = cp0_badvaddr_read(); … … 204 240 * Locate the faulting entry in TLB. 205 241 */ 242 hi.value = cp0_entry_hi_read(); 243 tlb_prepare_entry_hi(&hi, hi.asid, badvaddr); 244 cp0_entry_hi_write(hi.value); 206 245 tlbp(); 207 246 index.value = cp0_index_read(); 208 247 209 248 /* 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 249 * Fail if the entry is not in TLB. 215 250 */ 216 251 if (index.p) { 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 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 */ 223 361 pte = page_mapping_find(AS, badvaddr, true); 224 if (pte && pte->p && pte->w) {362 if (pte && pte->p && (pte->w || access != PF_ACCESS_WRITE)) { 225 363 /* 226 * Read the faulting TLB entry. 364 * Mapping found in page tables. 365 * Immediately succeed. 227 366 */ 228 tlbr(); 229 367 return pte; 368 } else { 369 int rc; 370 230 371 /* 231 * Record access and write to PTE. 372 * Mapping not found in page tables. 373 * Resort to higher-level page fault handler. 232 374 */ 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); 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 } 251 396 } 252 397 … … 265 410 void tlb_prepare_entry_hi(entry_hi_t *hi, asid_t asid, uintptr_t addr) 266 411 { 267 hi->value = 0; 268 hi->vpn2 = ADDR2VPN2(ALIGN_DOWN(addr, PAGE_SIZE)); 412 hi->value = ALIGN_DOWN(addr, PAGE_SIZE * 2); 269 413 hi->asid = asid; 270 414 } … … 273 417 void tlb_print(void) 274 418 { 275 page_mask_t mask , mask_save;276 entry_lo_t lo0, lo 0_save, lo1, lo1_save;419 page_mask_t mask; 420 entry_lo_t lo0, lo1; 277 421 entry_hi_t hi, hi_save; 278 422 unsigned int i; 279 423 280 424 hi_save.value = cp0_entry_hi_read(); 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"); 425 426 printf("[nr] [asid] [vpn2] [mask] [gvdc] [pfn ]\n"); 286 427 287 428 for (i = 0; i < TLB_ENTRY_COUNT; i++) { … … 294 435 lo1.value = cp0_entry_lo1_read(); 295 436 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));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); 301 442 } 302 443 303 444 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);307 445 } 308 446 … … 310 448 void tlb_invalidate_all(void) 311 449 { 450 ipl_t ipl; 312 451 entry_lo_t lo0, lo1; 313 452 entry_hi_t hi_save; 314 453 int i; 315 454 316 ASSERT(interrupts_disabled());317 318 455 hi_save.value = cp0_entry_hi_read(); 456 ipl = interrupts_disable(); 319 457 320 458 for (i = TLB_WIRED; i < TLB_ENTRY_COUNT; i++) { … … 334 472 } 335 473 474 interrupts_restore(ipl); 336 475 cp0_entry_hi_write(hi_save.value); 337 476 } … … 343 482 void tlb_invalidate_asid(asid_t asid) 344 483 { 484 ipl_t ipl; 345 485 entry_lo_t lo0, lo1; 346 486 entry_hi_t hi, hi_save; 347 487 int i; 348 488 349 ASSERT(interrupts_disabled());350 489 ASSERT(asid != ASID_INVALID); 351 490 352 491 hi_save.value = cp0_entry_hi_read(); 492 ipl = interrupts_disable(); 353 493 354 494 for (i = 0; i < TLB_ENTRY_COUNT; i++) { … … 372 512 } 373 513 514 interrupts_restore(ipl); 374 515 cp0_entry_hi_write(hi_save.value); 375 516 } … … 385 526 { 386 527 unsigned int i; 528 ipl_t ipl; 387 529 entry_lo_t lo0, lo1; 388 530 entry_hi_t hi, hi_save; 389 531 tlb_index_t index; 390 391 ASSERT(interrupts_disabled());392 532 393 533 if (asid == ASID_INVALID) … … 395 535 396 536 hi_save.value = cp0_entry_hi_read(); 537 ipl = interrupts_disable(); 397 538 398 539 for (i = 0; i < cnt + 1; i += 2) { 540 hi.value = 0; 399 541 tlb_prepare_entry_hi(&hi, asid, page + i * PAGE_SIZE); 400 542 cp0_entry_hi_write(hi.value); … … 423 565 } 424 566 567 interrupts_restore(ipl); 425 568 cp0_entry_hi_write(hi_save.value); 426 569 }
Note:
See TracChangeset
for help on using the changeset viewer.