diff options
Diffstat (limited to 'target-mips/helper.c')
-rw-r--r-- | target-mips/helper.c | 221 |
1 files changed, 193 insertions, 28 deletions
diff --git a/target-mips/helper.c b/target-mips/helper.c index 12caf34..838ccbb 100644 --- a/target-mips/helper.c +++ b/target-mips/helper.c @@ -66,14 +66,20 @@ int r4k_map_address (CPUState *env, target_phys_addr_t *physical, int *prot, target_ulong address, int rw, int access_type) { uint8_t ASID = env->CP0_EntryHi & 0xFF; + r4k_tlb_t *tlb; + target_ulong mask; + target_ulong tag; + target_ulong VPN; + int n; int i; - for (i = 0; i < env->tlb->tlb_in_use; i++) { - r4k_tlb_t *tlb = &env->tlb->mmu.r4k.tlb[i]; + for (i = 0; i < env->tlb->nb_tlb; i++) { + tlb = &env->tlb->mmu.r4k.tlb[i]; /* 1k pages are not supported. */ - target_ulong mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1); - target_ulong tag = address & ~mask; - target_ulong VPN = tlb->VPN & ~mask; + mask = ~(TARGET_PAGE_MASK << 1); + tag = address & ~mask; + VPN = tlb->VPN & ~mask; + #if defined(TARGET_MIPS64) tag &= env->SEGMask; #endif @@ -81,7 +87,7 @@ int r4k_map_address (CPUState *env, target_phys_addr_t *physical, int *prot, /* Check ASID, virtual page number & size */ if ((tlb->G == 1 || tlb->ASID == ASID) && VPN == tag) { /* TLB match */ - int n = !!(address & mask & ~(mask >> 1)); + n = !!(address & mask & ~(mask >> 1)); /* Check access rights */ if (!(n ? tlb->V1 : tlb->V0)) return TLBRET_INVALID; @@ -120,7 +126,7 @@ static int get_physical_address (CPUState *env, target_phys_addr_t *physical, if (address <= (int32_t)0x7FFFFFFFUL) { /* useg */ - if (env->CP0_Status & (1 << CP0St_ERL)) { + if (unlikely(env->CP0_Status & (1 << CP0St_ERL))) { *physical = address & 0xFFFFFFFF; *prot = PAGE_READ | PAGE_WRITE; } else { @@ -253,18 +259,140 @@ static void raise_mmu_exception(CPUState *env, target_ulong address, env->error_code = error_code; } -target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) +/* + * Get the pgd_current from TLB exception handler + * The exception handler is generated by function build_r4000_tlb_refill_handler. + * 0x80000000:0x3c1b8033: lui k1,0x8033 + * 0x80000004:0x401a4000: mfc0 k0,c0_badvaddr + * 0x80000008:0x8f7bb000: lw k1,-20480(k1) + * + */ +static inline target_ulong cpu_mips_get_pgd(CPUState *env) { -#if defined(CONFIG_USER_ONLY) - return addr; -#else - target_phys_addr_t phys_addr; - int prot; + static target_ulong pgd_current_p = 0; + static target_ulong probed = 0; - if (get_physical_address(env, &phys_addr, &prot, addr, 0, ACCESS_INT) != 0) - return -1; - return phys_addr; -#endif + if (likely(pgd_current_p)) { + /* Get pgd_current */ + return ldl_phys(pgd_current_p); + } + else if (unlikely(!probed)) { + uint32_t ins1, ins2; + uint32_t address; + uint32_t ebase; + + probed = 1; + + ebase = env->CP0_EBase - 0x80000000; + + /* Get pgd_current pointer from TLB refill exception handler */ + ins1 = ldl_phys(ebase); /* lui k1,%hi(pgd_current_p) */ + ins2 = ldl_phys(ebase + 8); /* lw k1,%lo(pgd_current_p)(k1) */ + + address = ((ins1 & 0xffff)<<16); + address += (((int32_t)(ins2 & 0xffff))<<16)>>16; + /* assumes pgd_current_p != 0 */ + if (address > 0x80000000 && address < 0xa0000000) { + pgd_current_p = address -= 0x80000000; + return ldl_phys(pgd_current_p); + } + } + return 0; +} + +static inline int cpu_mips_tlb_refill(CPUState *env, target_ulong address, int rw , + int mmu_idx, int is_softmmu) +{ + int32_t saved_hflags; + target_ulong saved_badvaddr,saved_entryhi,saved_context; + + target_ulong pgd_addr,pt_addr,index; + target_ulong fault_addr,ptw_phys; + target_ulong elo_even,elo_odd; + uint32_t page_valid; + int ret; + + saved_badvaddr = env->CP0_BadVAddr; + saved_context = env->CP0_Context; + saved_entryhi = env->CP0_EntryHi; + saved_hflags = env->hflags; + + env->CP0_BadVAddr = address; + env->CP0_Context = (env->CP0_Context & ~0x007fffff) | + ((address >> 9) & 0x007ffff0); + env->CP0_EntryHi = + (env->CP0_EntryHi & 0xFF) | (address & (TARGET_PAGE_MASK << 1)); + + env->hflags = MIPS_HFLAG_KM; + + fault_addr = env->CP0_BadVAddr; + page_valid = 0; + + pgd_addr = cpu_mips_get_pgd(env); + if (unlikely(!pgd_addr)) + { + /*not valid pgd_addr,just return.*/ + //return TLBRET_NOMATCH; + ret = TLBRET_NOMATCH; + goto out; + } + + ptw_phys = pgd_addr - (int32_t)0x80000000UL; + index = (fault_addr>>22)<<2; + ptw_phys += index; + + pt_addr = ldl_phys(ptw_phys); + + ptw_phys = pt_addr - (int32_t)0x80000000UL; + index = (env->CP0_Context>>1)&0xff8; + ptw_phys += index; + + /*get the page table entry*/ + elo_even = ldl_phys(ptw_phys); + elo_odd = ldl_phys(ptw_phys+4); + elo_even = elo_even >> 6; + elo_odd = elo_odd >> 6; + env->CP0_EntryLo0 = elo_even; + env->CP0_EntryLo1 = elo_odd; + /*Done. refill the TLB */ + r4k_helper_ptw_tlbrefill(env); + + /* Since we know the value of TLB entry, we can + * return the TLB lookup value here. + */ + + env->hflags = saved_hflags; + + target_ulong mask = env->CP0_PageMask | ~(TARGET_PAGE_MASK << 1); + int n = !!(address & mask & ~(mask >> 1)); + /* Check access rights */ + if (!(n ? (elo_odd & 2) != 0 : (elo_even & 2) != 0)) + { + ret = TLBRET_INVALID; + goto out; + } + + if (rw == 0 || (n ? (elo_odd & 4) != 0 : (elo_even & 4) != 0)) { + target_ulong physical = (n?(elo_odd >> 6) << 12 : (elo_even >> 6) << 12); + physical |= (address & (mask >> 1)); + int prot = PAGE_READ; + if (n ? (elo_odd & 4) != 0 : (elo_even & 4) != 0) + prot |= PAGE_WRITE; + + tlb_set_page(env, address & TARGET_PAGE_MASK, + physical & TARGET_PAGE_MASK, prot, + mmu_idx, is_softmmu); + ret = TLBRET_MATCH; + goto out; + } + ret = TLBRET_DIRTY; + +out: + env->CP0_BadVAddr = saved_badvaddr; + env->CP0_Context = saved_context; + env->CP0_EntryHi = saved_entryhi; + env->hflags = saved_hflags; + return ret; } int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw, @@ -274,6 +402,7 @@ int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw, target_phys_addr_t physical; int prot; #endif + int exception = 0, error_code = 0; int access_type; int ret = 0; @@ -300,7 +429,10 @@ int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw, ret = tlb_set_page(env, address & TARGET_PAGE_MASK, physical & TARGET_PAGE_MASK, prot, mmu_idx, is_softmmu); - } else if (ret < 0) + } + else if (ret == TLBRET_NOMATCH) + ret = cpu_mips_tlb_refill(env,address,rw,mmu_idx,is_softmmu); + if (ret < 0) #endif { raise_mmu_exception(env, address, rw, ret); @@ -333,6 +465,48 @@ target_phys_addr_t cpu_mips_translate_address(CPUState *env, target_ulong addres } #endif +target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) +{ +#if defined(CONFIG_USER_ONLY) + return addr; +#else + target_phys_addr_t phys_addr; + int prot, ret; + + ret = get_physical_address(env, &phys_addr, &prot, addr, 0, ACCESS_INT); + if (ret != TLBRET_MATCH && ret != TLBRET_DIRTY) { + target_ulong pgd_addr = cpu_mips_get_pgd(env); + if (unlikely(!pgd_addr)) { + phys_addr = -1; + } + else { + target_ulong pgd_phys, pgd_index; + target_ulong pt_addr, pt_phys, pt_index; + target_ulong lo; + /* Mimic the steps taken for a TLB refill */ + pgd_phys = pgd_addr - (int32_t)0x80000000UL; + pgd_index = (addr >> 22) << 2; + pt_addr = ldl_phys(pgd_phys + pgd_index); + pt_phys = pt_addr - (int32_t)0x80000000UL; + pt_index = (((addr >> 9) & 0x007ffff0) >> 1) & 0xff8; + /* get the entrylo value */ + if (addr & 0x1000) + lo = ldl_phys(pt_phys + pt_index + 4); + else + lo = ldl_phys(pt_phys + pt_index); + /* convert software TLB entry to hardware value */ + lo >>= 6; + if (lo & 0x00000002) + /* It is valid */ + phys_addr = (lo >> 6) << 12; + else + phys_addr = -1; + } + } + return phys_addr; +#endif +} + static const char * const excp_names[EXCP_LAST + 1] = { [EXCP_RESET] = "reset", [EXCP_SRESET] = "soft reset", @@ -592,7 +766,7 @@ void do_interrupt (CPUState *env) env->exception_index = EXCP_NONE; } -void r4k_invalidate_tlb (CPUState *env, int idx, int use_extra) +void r4k_invalidate_tlb (CPUState *env, int idx) { r4k_tlb_t *tlb; target_ulong addr; @@ -607,15 +781,6 @@ void r4k_invalidate_tlb (CPUState *env, int idx, int use_extra) return; } - if (use_extra && env->tlb->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->mmu.r4k.tlb[env->tlb->tlb_in_use] = *tlb; - env->tlb->tlb_in_use++; - return; - } - /* 1k pages are not supported. */ mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1); if (tlb->V0) { |