Commit 06afe2c8840ec39c3b23db0eb830a5f49244b947
1 parent
29e179bc
[sh4] MMU bug fix
Some bugs on SH4 MMU are fixed. - When a TLB entry is overwritten or invalidated, tlb_flush_page() should be invoked to invalidate old entry. - When a ASID is changed, tlb_flush() should be invoke to invalidate entries which have old ASID. - The check for shared bit in TLB entry causes multiple TLB hit exception. As SH3's MMU, shared bit is ignored. - ASID is used when MMUCR's SV bit or SR's MD bit is zero. No need to check both bits are zero. (Shin-ichiro KAWASAKI) git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@5068 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
2 changed files
with
24 additions
and
3 deletions
hw/sh7750.c
| ... | ... | @@ -30,6 +30,7 @@ |
| 30 | 30 | #include "sh7750_regs.h" |
| 31 | 31 | #include "sh7750_regnames.h" |
| 32 | 32 | #include "sh_intc.h" |
| 33 | +#include "exec-all.h" | |
| 33 | 34 | #include "cpu.h" |
| 34 | 35 | |
| 35 | 36 | #define NB_DEVICES 4 |
| ... | ... | @@ -356,6 +357,9 @@ static void sh7750_mem_writel(void *opaque, target_phys_addr_t addr, |
| 356 | 357 | s->cpu->mmucr = mem_value; |
| 357 | 358 | return; |
| 358 | 359 | case SH7750_PTEH_A7: |
| 360 | + /* If asid changes, clear all registered tlb entries. */ | |
| 361 | + if ((s->cpu->pteh & 0xff) != (mem_value & 0xff)) | |
| 362 | + tlb_flush(s->cpu, 1); | |
| 359 | 363 | s->cpu->pteh = mem_value; |
| 360 | 364 | return; |
| 361 | 365 | case SH7750_PTEL_A7: | ... | ... |
target-sh4/helper.c
| ... | ... | @@ -251,7 +251,7 @@ static int find_tlb_entry(CPUState * env, target_ulong address, |
| 251 | 251 | for (i = 0; i < nbtlb; i++) { |
| 252 | 252 | if (!entries[i].v) |
| 253 | 253 | continue; /* Invalid entry */ |
| 254 | - if (use_asid && entries[i].asid != asid && !entries[i].sh) | |
| 254 | + if (use_asid && entries[i].asid != asid) | |
| 255 | 255 | continue; /* Bad ASID */ |
| 256 | 256 | #if 0 |
| 257 | 257 | switch (entries[i].sz) { |
| ... | ... | @@ -320,8 +320,14 @@ int find_itlb_entry(CPUState * env, target_ulong address, |
| 320 | 320 | else if (e == MMU_DTLB_MISS && update) { |
| 321 | 321 | e = find_tlb_entry(env, address, env->utlb, UTLB_SIZE, use_asid); |
| 322 | 322 | if (e >= 0) { |
| 323 | + tlb_t * ientry; | |
| 323 | 324 | n = itlb_replacement(env); |
| 324 | - env->itlb[n] = env->utlb[e]; | |
| 325 | + ientry = &env->itlb[n]; | |
| 326 | + if (ientry->v) { | |
| 327 | + if (!same_tlb_entry_exists(env->utlb, UTLB_SIZE, ientry)) | |
| 328 | + tlb_flush_page(env, ientry->vpn << 10); | |
| 329 | + } | |
| 330 | + *ientry = env->utlb[e]; | |
| 325 | 331 | e = n; |
| 326 | 332 | } else if (e == MMU_DTLB_MISS) |
| 327 | 333 | e = MMU_ITLB_MISS; |
| ... | ... | @@ -356,7 +362,7 @@ static int get_mmu_address(CPUState * env, target_ulong * physical, |
| 356 | 362 | int use_asid, is_code, n; |
| 357 | 363 | tlb_t *matching = NULL; |
| 358 | 364 | |
| 359 | - use_asid = (env->mmucr & MMUCR_SV) == 0 && (env->sr & SR_MD) == 0; | |
| 365 | + use_asid = (env->mmucr & MMUCR_SV) == 0 || (env->sr & SR_MD) == 0; | |
| 360 | 366 | is_code = env->pc == address; /* Hack */ |
| 361 | 367 | |
| 362 | 368 | /* Use a hack to find if this is an instruction or data access */ |
| ... | ... | @@ -540,6 +546,17 @@ void cpu_load_tlb(CPUState * env) |
| 540 | 546 | int n = cpu_mmucr_urc(env->mmucr); |
| 541 | 547 | tlb_t * entry = &env->utlb[n]; |
| 542 | 548 | |
| 549 | + if (entry->v) { | |
| 550 | + /* Overwriting valid entry in utlb. */ | |
| 551 | + target_ulong address = entry->vpn << 10; | |
| 552 | + if (!same_tlb_entry_exists(env->itlb, ITLB_SIZE, entry)) { | |
| 553 | + tlb_flush_page(env, address); | |
| 554 | + } | |
| 555 | + } | |
| 556 | + | |
| 557 | + /* per utlb access cannot implemented. */ | |
| 558 | + increment_urc(env); | |
| 559 | + | |
| 543 | 560 | /* Take values into cpu status from registers. */ |
| 544 | 561 | entry->asid = (uint8_t)cpu_pteh_asid(env->pteh); |
| 545 | 562 | entry->vpn = cpu_pteh_vpn(env->pteh); | ... | ... |