diff --git a/target-mips/exec.h b/target-mips/exec.h index e364d8a6fc..3d6bb7d609 100644 --- a/target-mips/exec.h +++ b/target-mips/exec.h @@ -149,6 +149,7 @@ void dump_sc (void); int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw, int is_user, int is_softmmu); void do_interrupt (CPUState *env); +void invalidate_tlb (CPUState *env, int idx, int use_extra); void cpu_loop_exit(void); void do_raise_exception_err (uint32_t exception, int error_code); diff --git a/target-mips/helper.c b/target-mips/helper.c index e70dc1a99e..43038c2d7e 100644 --- a/target-mips/helper.c +++ b/target-mips/helper.c @@ -416,3 +416,44 @@ void do_interrupt (CPUState *env) env->exception_index = EXCP_NONE; } #endif /* !defined(CONFIG_USER_ONLY) */ + +void invalidate_tlb (CPUState *env, int idx, int use_extra) +{ + tlb_t *tlb; + target_ulong addr; + uint8_t ASID; + + ASID = env->CP0_EntryHi & 0xFF; + + tlb = &env->tlb[idx]; + /* The qemu TLB is flushed then the ASID changes, so no need to + flush these entries again. */ + if (tlb->G == 0 && tlb->ASID != ASID) { + return; + } + + if (use_extra && env->tlb_in_use < MIPS_TLB_MAX) { + /* For tlbwr, we can shadow the discarded entry into + a new (fake) TLB entry, as long as the guest can not + tell that it's there. */ + env->tlb[env->tlb_in_use] = *tlb; + env->tlb_in_use++; + return; + } + + if (tlb->V0) { + addr = tlb->VPN; + while (addr < tlb->end) { + tlb_flush_page (env, addr); + addr += TARGET_PAGE_SIZE; + } + } + if (tlb->V1) { + addr = tlb->end; + while (addr < tlb->end2) { + tlb_flush_page (env, addr); + addr += TARGET_PAGE_SIZE; + } + } +} + diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c index b7defc9e5e..f4eb6e6a03 100644 --- a/target-mips/op_helper.c +++ b/target-mips/op_helper.c @@ -376,53 +376,11 @@ void cpu_mips_tlb_flush (CPUState *env, int flush_global) env->tlb_in_use = MIPS_TLB_NB; } -static void invalidate_tlb (int idx, int use_extra) -{ - tlb_t *tlb; - target_ulong addr; - uint8_t ASID; - - ASID = env->CP0_EntryHi & 0xFF; - - tlb = &env->tlb[idx]; - /* The qemu TLB is flushed then the ASID changes, so no need to - flush these entries again. */ - if (tlb->G == 0 && tlb->ASID != ASID) { - return; - } - - if (use_extra && env->tlb_in_use < MIPS_TLB_MAX) { - /* For tlbwr, we can shadow the discarded entry into - a new (fake) TLB entry, as long as the guest can not - tell that it's there. */ - env->tlb[env->tlb_in_use] = *tlb; - env->tlb_in_use++; - return; - } - - if (tlb->V0) { - tb_invalidate_page_range(tlb->PFN[0], tlb->end - tlb->VPN); - addr = tlb->VPN; - while (addr < tlb->end) { - tlb_flush_page (env, addr); - addr += TARGET_PAGE_SIZE; - } - } - if (tlb->V1) { - tb_invalidate_page_range(tlb->PFN[1], tlb->end2 - tlb->end); - addr = tlb->end; - while (addr < tlb->end2) { - tlb_flush_page (env, addr); - addr += TARGET_PAGE_SIZE; - } - } -} - static void mips_tlb_flush_extra (CPUState *env, int first) { /* Discard entries from env->tlb[first] onwards. */ while (env->tlb_in_use > first) { - invalidate_tlb(--env->tlb_in_use, 0); + invalidate_tlb(env, --env->tlb_in_use, 0); } } @@ -459,7 +417,7 @@ void do_tlbwi (void) /* Wildly undefined effects for CP0_index containing a too high value and MIPS_TLB_NB not being a power of two. But so does real silicon. */ - invalidate_tlb(env->CP0_index & (MIPS_TLB_NB - 1), 0); + invalidate_tlb(env, env->CP0_index & (MIPS_TLB_NB - 1), 0); fill_tlb(env->CP0_index & (MIPS_TLB_NB - 1)); } @@ -467,7 +425,7 @@ void do_tlbwr (void) { int r = cpu_mips_get_random(env); - invalidate_tlb(r, 1); + invalidate_tlb(env, r, 1); fill_tlb(r); }