filename | src/sh4/mmu.c |
changeset | 569:a1c49e1e8776 |
prev | 561:533f6b478071 |
next | 570:d2893980fbf5 |
author | nkeynes |
date | Fri Jan 04 11:54:17 2008 +0000 (14 years ago) |
branch | lxdream-mmu |
permissions | -rw-r--r-- |
last change | Bring icache partially into line with the mmu, a little less slow with AT off now. |
file | annotate | diff | log | raw |
1.1 --- a/src/sh4/mmu.c Tue Jan 01 05:08:38 2008 +00001.2 +++ b/src/sh4/mmu.c Fri Jan 04 11:54:17 2008 +00001.3 @@ -70,6 +70,7 @@1.4 static uint32_t mmu_urc;1.5 static uint32_t mmu_urb;1.6 static uint32_t mmu_lrui;1.7 +static uint32_t mmu_asid; // current asid1.9 static sh4ptr_t cache = NULL;1.11 @@ -101,6 +102,10 @@1.12 switch(reg) {1.13 case PTEH:1.14 val &= 0xFFFFFCFF;1.15 + if( (val & 0xFF) != mmu_asid ) {1.16 + mmu_asid = val&0xFF;1.17 + sh4_icache.page_vma = -1; // invalidate icache as asid has changed1.18 + }1.19 break;1.20 case PTEL:1.21 val &= 0x1FFFFDFF;1.22 @@ -234,17 +239,15 @@1.23 */1.25 /**1.26 - * Perform the actual utlb lookup.1.27 + * Perform the actual utlb lookup w/ asid matching.1.28 * Possible utcomes are:1.29 * 0..63 Single match - good, return entry found1.30 * -1 No match - raise a tlb data miss exception1.31 * -2 Multiple matches - raise a multi-hit exception (reset)1.32 * @param vpn virtual address to resolve1.33 - * @param asid Address space identifier1.34 - * @param use_asid whether to require an asid match on non-shared pages.1.35 * @return the resultant UTLB entry, or an error.1.36 */1.37 -static inline int mmu_utlb_lookup_vpn( uint32_t vpn, uint32_t asid, int use_asid )1.38 +static inline int mmu_utlb_lookup_vpn_asid( uint32_t vpn )1.39 {1.40 int result = -1;1.41 unsigned int i;1.42 @@ -254,32 +257,52 @@1.43 mmu_urc = 0;1.44 }1.46 - if( use_asid ) {1.47 - for( i = 0; i < UTLB_ENTRY_COUNT; i++ ) {1.48 - if( (mmu_utlb[i].flags & TLB_VALID) &&1.49 - ((mmu_utlb[i].flags & TLB_SHARE) || asid == mmu_utlb[i].asid) &&1.50 - ((mmu_utlb[i].vpn ^ vpn) & mmu_utlb[i].mask) == 0 ) {1.51 - if( result != -1 ) {1.52 - return -2;1.53 - }1.54 - result = i;1.55 + for( i = 0; i < UTLB_ENTRY_COUNT; i++ ) {1.56 + if( (mmu_utlb[i].flags & TLB_VALID) &&1.57 + ((mmu_utlb[i].flags & TLB_SHARE) || mmu_asid == mmu_utlb[i].asid) &&1.58 + ((mmu_utlb[i].vpn ^ vpn) & mmu_utlb[i].mask) == 0 ) {1.59 + if( result != -1 ) {1.60 + return -2;1.61 }1.62 - }1.63 - } else {1.64 - for( i = 0; i < UTLB_ENTRY_COUNT; i++ ) {1.65 - if( (mmu_utlb[i].flags & TLB_VALID) &&1.66 - ((mmu_utlb[i].vpn ^ vpn) & mmu_utlb[i].mask) == 0 ) {1.67 - if( result != -1 ) {1.68 - return -2;1.69 - }1.70 - result = i;1.71 - }1.72 + result = i;1.73 }1.74 }1.75 return result;1.76 }1.78 /**1.79 + * Perform the actual utlb lookup matching on vpn only1.80 + * Possible utcomes are:1.81 + * 0..63 Single match - good, return entry found1.82 + * -1 No match - raise a tlb data miss exception1.83 + * -2 Multiple matches - raise a multi-hit exception (reset)1.84 + * @param vpn virtual address to resolve1.85 + * @return the resultant UTLB entry, or an error.1.86 + */1.87 +static inline int mmu_utlb_lookup_vpn( uint32_t vpn )1.88 +{1.89 + int result = -1;1.90 + unsigned int i;1.91 +1.92 + mmu_urc++;1.93 + if( mmu_urc == mmu_urb || mmu_urc == 0x40 ) {1.94 + mmu_urc = 0;1.95 + }1.96 +1.97 + for( i = 0; i < UTLB_ENTRY_COUNT; i++ ) {1.98 + if( (mmu_utlb[i].flags & TLB_VALID) &&1.99 + ((mmu_utlb[i].vpn ^ vpn) & mmu_utlb[i].mask) == 0 ) {1.100 + if( result != -1 ) {1.101 + return -2;1.102 + }1.103 + result = i;1.104 + }1.105 + }1.106 +1.107 + return result;1.108 +}1.109 +1.110 +/**1.111 * Find a UTLB entry for the associative TLB write - same as the normal1.112 * lookup but ignores the valid bit.1.113 */1.114 @@ -300,53 +323,9 @@1.115 }1.117 /**1.118 - * Perform the actual itlb lookup.1.119 - * Possible utcomes are:1.120 - * 0..63 Single match - good, return entry found1.121 - * -1 No match - raise a tlb data miss exception1.122 - * -2 Multiple matches - raise a multi-hit exception (reset)1.123 - * @param vpn virtual address to resolve1.124 - * @param asid Address space identifier1.125 - * @param use_asid whether to require an asid match on non-shared pages.1.126 - * @return the resultant ITLB entry, or an error.1.127 + * Update the ITLB by replacing the LRU entry with the specified UTLB entry.1.128 + * @return the number (0-3) of the replaced entry.1.129 */1.130 -static inline int mmu_itlb_lookup_vpn( uint32_t vpn, uint32_t asid, int use_asid )1.131 -{1.132 - int result = -1;1.133 - unsigned int i;1.134 - if( use_asid ) {1.135 - for( i = 0; i < ITLB_ENTRY_COUNT; i++ ) {1.136 - if( (mmu_itlb[i].flags & TLB_VALID) &&1.137 - ((mmu_itlb[i].flags & TLB_SHARE) || asid == mmu_itlb[i].asid) &&1.138 - ((mmu_itlb[i].vpn ^ vpn) & mmu_itlb[i].mask) == 0 ) {1.139 - if( result != -1 ) {1.140 - return -2;1.141 - }1.142 - result = i;1.143 - }1.144 - }1.145 - } else {1.146 - for( i = 0; i < ITLB_ENTRY_COUNT; i++ ) {1.147 - if( (mmu_itlb[i].flags & TLB_VALID) &&1.148 - ((mmu_itlb[i].vpn ^ vpn) & mmu_itlb[i].mask) == 0 ) {1.149 - if( result != -1 ) {1.150 - return -2;1.151 - }1.152 - result = i;1.153 - }1.154 - }1.155 - }1.156 -1.157 - switch( result ) {1.158 - case 0: mmu_lrui = (mmu_lrui & 0x07); break;1.159 - case 1: mmu_lrui = (mmu_lrui & 0x19) | 0x20; break;1.160 - case 2: mmu_lrui = (mmu_lrui & 0x3E) | 0x14; break;1.161 - case 3: mmu_lrui = (mmu_lrui | 0x0B); break;1.162 - }1.163 -1.164 - return result;1.165 -}1.166 -1.167 static int inline mmu_itlb_update_from_utlb( int entryNo )1.168 {1.169 int replace;1.170 @@ -374,6 +353,93 @@1.171 }1.173 /**1.174 + * Perform the actual itlb lookup w/ asid protection1.175 + * Possible utcomes are:1.176 + * 0..63 Single match - good, return entry found1.177 + * -1 No match - raise a tlb data miss exception1.178 + * -2 Multiple matches - raise a multi-hit exception (reset)1.179 + * @param vpn virtual address to resolve1.180 + * @return the resultant ITLB entry, or an error.1.181 + */1.182 +static inline int mmu_itlb_lookup_vpn_asid( uint32_t vpn )1.183 +{1.184 + int result = -1;1.185 + unsigned int i;1.186 +1.187 + for( i = 0; i < ITLB_ENTRY_COUNT; i++ ) {1.188 + if( (mmu_itlb[i].flags & TLB_VALID) &&1.189 + ((mmu_itlb[i].flags & TLB_SHARE) || mmu_asid == mmu_itlb[i].asid) &&1.190 + ((mmu_itlb[i].vpn ^ vpn) & mmu_itlb[i].mask) == 0 ) {1.191 + if( result != -1 ) {1.192 + return -2;1.193 + }1.194 + result = i;1.195 + }1.196 + }1.197 +1.198 + if( result == -1 ) {1.199 + int utlbEntry = mmu_utlb_lookup_vpn( vpn );1.200 + if( utlbEntry == -1 ) {1.201 + return -1;1.202 + } else {1.203 + return mmu_itlb_update_from_utlb( utlbEntry );1.204 + }1.205 + }1.206 +1.207 + switch( result ) {1.208 + case 0: mmu_lrui = (mmu_lrui & 0x07); break;1.209 + case 1: mmu_lrui = (mmu_lrui & 0x19) | 0x20; break;1.210 + case 2: mmu_lrui = (mmu_lrui & 0x3E) | 0x14; break;1.211 + case 3: mmu_lrui = (mmu_lrui | 0x0B); break;1.212 + }1.213 +1.214 + return result;1.215 +}1.216 +1.217 +/**1.218 + * Perform the actual itlb lookup on vpn only1.219 + * Possible utcomes are:1.220 + * 0..63 Single match - good, return entry found1.221 + * -1 No match - raise a tlb data miss exception1.222 + * -2 Multiple matches - raise a multi-hit exception (reset)1.223 + * @param vpn virtual address to resolve1.224 + * @return the resultant ITLB entry, or an error.1.225 + */1.226 +static inline int mmu_itlb_lookup_vpn( uint32_t vpn )1.227 +{1.228 + int result = -1;1.229 + unsigned int i;1.230 +1.231 + for( i = 0; i < ITLB_ENTRY_COUNT; i++ ) {1.232 + if( (mmu_itlb[i].flags & TLB_VALID) &&1.233 + ((mmu_itlb[i].vpn ^ vpn) & mmu_itlb[i].mask) == 0 ) {1.234 + if( result != -1 ) {1.235 + return -2;1.236 + }1.237 + result = i;1.238 + }1.239 + }1.240 +1.241 + if( result == -1 ) {1.242 + int utlbEntry = mmu_utlb_lookup_vpn( vpn );1.243 + if( utlbEntry == -1 ) {1.244 + return -1;1.245 + } else {1.246 + return mmu_itlb_update_from_utlb( utlbEntry );1.247 + }1.248 + }1.249 +1.250 + switch( result ) {1.251 + case 0: mmu_lrui = (mmu_lrui & 0x07); break;1.252 + case 1: mmu_lrui = (mmu_lrui & 0x19) | 0x20; break;1.253 + case 2: mmu_lrui = (mmu_lrui & 0x3E) | 0x14; break;1.254 + case 3: mmu_lrui = (mmu_lrui | 0x0B); break;1.255 + }1.256 +1.257 + return result;1.258 +}1.259 +1.260 +/**1.261 * Find a ITLB entry for the associative TLB write - same as the normal1.262 * lookup but ignores the valid bit.1.263 */1.264 @@ -396,18 +462,15 @@1.265 #define RAISE_TLB_ERROR(code, vpn) \1.266 MMIO_WRITE(MMU, TEA, vpn); \1.267 MMIO_WRITE(MMU, PTEH, ((MMIO_READ(MMU, PTEH) & 0x000003FF) | (vpn&0xFFFFFC00))); \1.268 - sh4_raise_tlb_exception(code); \1.269 - return (((uint64_t)code)<<32)1.270 + sh4_raise_tlb_exception(code);1.272 #define RAISE_MEM_ERROR(code, vpn) \1.273 MMIO_WRITE(MMU, TEA, vpn); \1.274 MMIO_WRITE(MMU, PTEH, ((MMIO_READ(MMU, PTEH) & 0x000003FF) | (vpn&0xFFFFFC00))); \1.275 - sh4_raise_exception(code); \1.276 - return (((uint64_t)code)<<32)1.277 + sh4_raise_exception(code);1.279 #define RAISE_OTHER_ERROR(code) \1.280 - sh4_raise_exception(code); \1.281 - return (((uint64_t)EXV_EXCEPTION)<<32)1.282 + sh4_raise_exception(code);1.284 /**1.285 * Abort with a non-MMU address error. Caused by user-mode code attempting1.286 @@ -423,8 +486,7 @@1.287 #define MMU_TLB_WRITE_PROT_ERROR(vpn) RAISE_MEM_ERROR(EXC_TLB_PROT_WRITE, vpn)1.288 #define MMU_TLB_MULTI_HIT_ERROR(vpn) sh4_raise_reset(EXC_TLB_MULTI_HIT); \1.289 MMIO_WRITE(MMU, TEA, vpn); \1.290 - MMIO_WRITE(MMU, PTEH, ((MMIO_READ(MMU, PTEH) & 0x000003FF) | (vpn&0xFFFFFC00))); \1.291 - return (((uint64_t)EXC_TLB_MULTI_HIT)<<32)1.292 + MMIO_WRITE(MMU, PTEH, ((MMIO_READ(MMU, PTEH) & 0x000003FF) | (vpn&0xFFFFFC00)));1.294 uint64_t mmu_vma_to_phys_write( sh4addr_t addr )1.295 {1.296 @@ -442,6 +504,7 @@1.297 return (uint64_t)addr;1.298 }1.299 MMU_WRITE_ADDR_ERROR();1.300 + return 0x100000000LL;1.301 }1.302 }1.304 @@ -450,28 +513,31 @@1.305 }1.307 /* If we get this far, translation is required */1.308 -1.309 - int use_asid = ((mmucr & MMUCR_SV) == 0) || !IS_SH4_PRIVMODE();1.310 - uint32_t asid = MMIO_READ( MMU, PTEH ) & 0xFF;1.311 -1.312 - int entryNo = mmu_utlb_lookup_vpn( addr, asid, use_asid );1.313 + int entryNo;1.314 + if( ((mmucr & MMUCR_SV) == 0) || !IS_SH4_PRIVMODE() ) {1.315 + entryNo = mmu_utlb_lookup_vpn_asid( addr );1.316 + } else {1.317 + entryNo = mmu_utlb_lookup_vpn( addr );1.318 + }1.320 switch(entryNo) {1.321 case -1:1.322 MMU_TLB_WRITE_MISS_ERROR(addr);1.323 - break;1.324 + return 0x100000000LL;1.325 case -2:1.326 MMU_TLB_MULTI_HIT_ERROR(addr);1.327 - break;1.328 + return 0x100000000LL;1.329 default:1.330 if( IS_SH4_PRIVMODE() ? ((mmu_utlb[entryNo].flags & TLB_WRITABLE) == 0)1.331 : ((mmu_utlb[entryNo].flags & TLB_USERWRITABLE) != TLB_USERWRITABLE) ) {1.332 /* protection violation */1.333 MMU_TLB_WRITE_PROT_ERROR(addr);1.334 + return 0x100000000LL;1.335 }1.337 if( (mmu_utlb[entryNo].flags & TLB_DIRTY) == 0 ) {1.338 MMU_TLB_INITIAL_WRITE_ERROR(addr);1.339 + return 0x100000000LL;1.340 }1.342 /* finally generate the target address */1.343 @@ -482,64 +548,6 @@1.345 }1.347 -uint64_t mmu_vma_to_phys_exec( sh4addr_t addr )1.348 -{1.349 - uint32_t mmucr = MMIO_READ(MMU,MMUCR);1.350 - if( addr & 0x80000000 ) {1.351 - if( IS_SH4_PRIVMODE() ) {1.352 - if( addr < 0xC0000000 ) {1.353 - /* P1, P2 and P4 regions are pass-through (no translation) */1.354 - return (uint64_t)addr;1.355 - } else if( addr >= 0xE0000000 ) {1.356 - MMU_READ_ADDR_ERROR();1.357 - }1.358 - } else {1.359 - MMU_READ_ADDR_ERROR();1.360 - }1.361 - }1.362 -1.363 - if( (mmucr & MMUCR_AT) == 0 ) {1.364 - return (uint64_t)addr;1.365 - }1.366 -1.367 - /* If we get this far, translation is required */1.368 - int use_asid = ((mmucr & MMUCR_SV) == 0) || !IS_SH4_PRIVMODE();1.369 - uint32_t asid = MMIO_READ( MMU, PTEH ) & 0xFF;1.370 -1.371 - int entryNo = mmu_itlb_lookup_vpn( addr, asid, use_asid );1.372 - if( entryNo == -1 ) {1.373 - entryNo = mmu_utlb_lookup_vpn( addr, asid, use_asid );1.374 - if( entryNo >= 0 ) {1.375 - entryNo = mmu_itlb_update_from_utlb( entryNo );1.376 - }1.377 - }1.378 - switch(entryNo) {1.379 - case -1:1.380 - MMU_TLB_READ_MISS_ERROR(addr);1.381 - break;1.382 - case -2:1.383 - MMU_TLB_MULTI_HIT_ERROR(addr);1.384 - break;1.385 - default:1.386 - if( (mmu_itlb[entryNo].flags & TLB_USERMODE) == 0 &&1.387 - !IS_SH4_PRIVMODE() ) {1.388 - /* protection violation */1.389 - MMU_TLB_READ_PROT_ERROR(addr);1.390 - }1.391 -1.392 - /* finally generate the target address */1.393 - return (mmu_itlb[entryNo].ppn & mmu_itlb[entryNo].mask) |1.394 - (addr & (~mmu_itlb[entryNo].mask));1.395 - }1.396 - return -1;1.397 -}1.398 -1.399 -uint64_t mmu_vma_to_phys_read_noexc( sh4addr_t addr ) {1.400 -1.401 -1.402 -}1.403 -1.404 -1.405 uint64_t mmu_vma_to_phys_read( sh4addr_t addr )1.406 {1.407 uint32_t mmucr = MMIO_READ(MMU,MMUCR);1.408 @@ -556,6 +564,7 @@1.409 return (uint64_t)addr;1.410 }1.411 MMU_READ_ADDR_ERROR();1.412 + return 0x100000000LL;1.413 }1.414 }1.416 @@ -564,24 +573,26 @@1.417 }1.419 /* If we get this far, translation is required */1.420 -1.421 - int use_asid = ((mmucr & MMUCR_SV) == 0) || !IS_SH4_PRIVMODE();1.422 - uint32_t asid = MMIO_READ( MMU, PTEH ) & 0xFF;1.423 -1.424 - int entryNo = mmu_utlb_lookup_vpn( addr, asid, use_asid );1.425 + int entryNo;1.426 + if( ((mmucr & MMUCR_SV) == 0) || !IS_SH4_PRIVMODE() ) {1.427 + entryNo = mmu_utlb_lookup_vpn_asid( addr );1.428 + } else {1.429 + entryNo = mmu_utlb_lookup_vpn( addr );1.430 + }1.432 switch(entryNo) {1.433 case -1:1.434 MMU_TLB_READ_MISS_ERROR(addr);1.435 - break;1.436 + return 0x100000000LL;1.437 case -2:1.438 MMU_TLB_MULTI_HIT_ERROR(addr);1.439 - break;1.440 + return 0x100000000LL;1.441 default:1.442 if( (mmu_utlb[entryNo].flags & TLB_USERMODE) == 0 &&1.443 !IS_SH4_PRIVMODE() ) {1.444 /* protection violation */1.445 MMU_TLB_READ_PROT_ERROR(addr);1.446 + return 0x100000000LL;1.447 }1.449 /* finally generate the target address */1.450 @@ -720,3 +731,106 @@1.451 void mmu_ocache_data_write( sh4addr_t addr, uint32_t val )1.452 {1.453 }1.454 +1.455 +/**1.456 + * Update the icache for an untranslated address1.457 + */1.458 +void mmu_update_icache_phys( sh4addr_t addr )1.459 +{1.460 + if( (addr & 0x1C000000) == 0x0C000000 ) {1.461 + /* Main ram */1.462 + sh4_icache.page_vma = addr & 0xFF000000;1.463 + sh4_icache.page_ppa = 0x0C000000;1.464 + sh4_icache.mask = 0xFF000000;1.465 + sh4_icache.page = sh4_main_ram;1.466 + } else if( (addr & 0x1FE00000 == 0 ) ) {1.467 + /* BIOS ROM */1.468 + sh4_icache.page_vma = addr & 0xFFE00000;1.469 + sh4_icache.page_ppa = 0;1.470 + sh4_icache.mask = 0xFFE00000;1.471 + sh4_icache.page = mem_get_region(0);1.472 + } else {1.473 + /* not supported */1.474 + sh4_icache.page_vma = -1;1.475 + }1.476 +}1.477 +1.478 +/**1.479 + * Update the sh4_icache structure to describe the page(s) containing the1.480 + * given vma. If the address does not reference a RAM/ROM region, the icache1.481 + * will be invalidated instead.1.482 + * If AT is on, this method will raise TLB exceptions normally1.483 + * (hence this method should only be used immediately prior to execution of1.484 + * code), and otherwise will set the icache according to the matching TLB entry.1.485 + * If AT is off, this method will set the entire referenced RAM/ROM region in1.486 + * the icache.1.487 + * @return TRUE if the update completed (successfully or otherwise), FALSE1.488 + * if an exception was raised.1.489 + */1.490 +gboolean mmu_update_icache( sh4vma_t addr )1.491 +{1.492 + int entryNo;1.493 + if( IS_SH4_PRIVMODE() ) {1.494 + if( addr & 0x80000000 ) {1.495 + if( addr < 0xC0000000 ) {1.496 + /* P1, P2 and P4 regions are pass-through (no translation) */1.497 + mmu_update_icache_phys(addr);1.498 + return TRUE;1.499 + } else if( addr >= 0xE0000000 && addr < 0xFFFFFF00 ) {1.500 + MMU_READ_ADDR_ERROR();1.501 + return FALSE;1.502 + }1.503 + } else {1.504 + MMU_READ_ADDR_ERROR();1.505 + return FALSE;1.506 + }1.507 +1.508 + uint32_t mmucr = MMIO_READ(MMU,MMUCR);1.509 + if( (mmucr & MMUCR_AT) == 0 ) {1.510 + mmu_update_icache_phys(addr);1.511 + return TRUE;1.512 + }1.513 +1.514 + entryNo = mmu_itlb_lookup_vpn( addr );1.515 + } else {1.516 + if( addr & 0x80000000 ) {1.517 + MMU_READ_ADDR_ERROR();1.518 + return FALSE;1.519 + }1.520 +1.521 + uint32_t mmucr = MMIO_READ(MMU,MMUCR);1.522 + if( (mmucr & MMUCR_AT) == 0 ) {1.523 + mmu_update_icache_phys(addr);1.524 + return TRUE;1.525 + }1.526 +1.527 + if( mmucr & MMUCR_SV ) {1.528 + entryNo = mmu_itlb_lookup_vpn( addr );1.529 + } else {1.530 + entryNo = mmu_itlb_lookup_vpn_asid( addr );1.531 + }1.532 + if( entryNo != -1 && (mmu_itlb[entryNo].flags & TLB_USERMODE) == 0 ) {1.533 + MMU_TLB_READ_PROT_ERROR(addr);1.534 + return FALSE;1.535 + }1.536 + }1.537 +1.538 + switch(entryNo) {1.539 + case -1:1.540 + MMU_TLB_READ_MISS_ERROR(addr);1.541 + return FALSE;1.542 + case -2:1.543 + MMU_TLB_MULTI_HIT_ERROR(addr);1.544 + return FALSE;1.545 + default:1.546 + sh4_icache.page_ppa = mmu_itlb[entryNo].ppn & mmu_itlb[entryNo].mask;1.547 + sh4_icache.page = mem_get_region( sh4_icache.page_ppa );1.548 + if( sh4_icache.page == NULL ) {1.549 + sh4_icache.page_vma = -1;1.550 + } else {1.551 + sh4_icache.page_vma = mmu_itlb[entryNo].vpn & mmu_itlb[entryNo].mask;1.552 + sh4_icache.mask = mmu_itlb[entryNo].mask;1.553 + }1.554 + return TRUE;1.555 + }1.556 +}
.