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); | ... | ... |