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,6 +30,7 @@ | ||
| 30 | #include "sh7750_regs.h" | 30 | #include "sh7750_regs.h" |
| 31 | #include "sh7750_regnames.h" | 31 | #include "sh7750_regnames.h" |
| 32 | #include "sh_intc.h" | 32 | #include "sh_intc.h" |
| 33 | +#include "exec-all.h" | ||
| 33 | #include "cpu.h" | 34 | #include "cpu.h" |
| 34 | 35 | ||
| 35 | #define NB_DEVICES 4 | 36 | #define NB_DEVICES 4 |
| @@ -356,6 +357,9 @@ static void sh7750_mem_writel(void *opaque, target_phys_addr_t addr, | @@ -356,6 +357,9 @@ static void sh7750_mem_writel(void *opaque, target_phys_addr_t addr, | ||
| 356 | s->cpu->mmucr = mem_value; | 357 | s->cpu->mmucr = mem_value; |
| 357 | return; | 358 | return; |
| 358 | case SH7750_PTEH_A7: | 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 | s->cpu->pteh = mem_value; | 363 | s->cpu->pteh = mem_value; |
| 360 | return; | 364 | return; |
| 361 | case SH7750_PTEL_A7: | 365 | case SH7750_PTEL_A7: |
target-sh4/helper.c
| @@ -251,7 +251,7 @@ static int find_tlb_entry(CPUState * env, target_ulong address, | @@ -251,7 +251,7 @@ static int find_tlb_entry(CPUState * env, target_ulong address, | ||
| 251 | for (i = 0; i < nbtlb; i++) { | 251 | for (i = 0; i < nbtlb; i++) { |
| 252 | if (!entries[i].v) | 252 | if (!entries[i].v) |
| 253 | continue; /* Invalid entry */ | 253 | continue; /* Invalid entry */ |
| 254 | - if (use_asid && entries[i].asid != asid && !entries[i].sh) | 254 | + if (use_asid && entries[i].asid != asid) |
| 255 | continue; /* Bad ASID */ | 255 | continue; /* Bad ASID */ |
| 256 | #if 0 | 256 | #if 0 |
| 257 | switch (entries[i].sz) { | 257 | switch (entries[i].sz) { |
| @@ -320,8 +320,14 @@ int find_itlb_entry(CPUState * env, target_ulong address, | @@ -320,8 +320,14 @@ int find_itlb_entry(CPUState * env, target_ulong address, | ||
| 320 | else if (e == MMU_DTLB_MISS && update) { | 320 | else if (e == MMU_DTLB_MISS && update) { |
| 321 | e = find_tlb_entry(env, address, env->utlb, UTLB_SIZE, use_asid); | 321 | e = find_tlb_entry(env, address, env->utlb, UTLB_SIZE, use_asid); |
| 322 | if (e >= 0) { | 322 | if (e >= 0) { |
| 323 | + tlb_t * ientry; | ||
| 323 | n = itlb_replacement(env); | 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 | e = n; | 331 | e = n; |
| 326 | } else if (e == MMU_DTLB_MISS) | 332 | } else if (e == MMU_DTLB_MISS) |
| 327 | e = MMU_ITLB_MISS; | 333 | e = MMU_ITLB_MISS; |
| @@ -356,7 +362,7 @@ static int get_mmu_address(CPUState * env, target_ulong * physical, | @@ -356,7 +362,7 @@ static int get_mmu_address(CPUState * env, target_ulong * physical, | ||
| 356 | int use_asid, is_code, n; | 362 | int use_asid, is_code, n; |
| 357 | tlb_t *matching = NULL; | 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 | is_code = env->pc == address; /* Hack */ | 366 | is_code = env->pc == address; /* Hack */ |
| 361 | 367 | ||
| 362 | /* Use a hack to find if this is an instruction or data access */ | 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,6 +546,17 @@ void cpu_load_tlb(CPUState * env) | ||
| 540 | int n = cpu_mmucr_urc(env->mmucr); | 546 | int n = cpu_mmucr_urc(env->mmucr); |
| 541 | tlb_t * entry = &env->utlb[n]; | 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 | /* Take values into cpu status from registers. */ | 560 | /* Take values into cpu status from registers. */ |
| 544 | entry->asid = (uint8_t)cpu_pteh_asid(env->pteh); | 561 | entry->asid = (uint8_t)cpu_pteh_asid(env->pteh); |
| 545 | entry->vpn = cpu_pteh_vpn(env->pteh); | 562 | entry->vpn = cpu_pteh_vpn(env->pteh); |