diff options
author | Keun-young Park <keunyoung@google.com> | 2013-03-28 01:25:32 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2013-03-28 01:25:32 +0000 |
commit | 4505a09ab18f99a19bdc696f67ab901ee50aa7f1 (patch) | |
tree | 19c24b5c5223b1334a3131c48bde6139774ad6ae | |
parent | 256022ed8ca37209ec266083be0d34a3be284091 (diff) | |
parent | 6e22b2095d718a417cee9bb2d5b3a2cf4b3d9809 (diff) | |
download | qemu-jb-mr2-release.tar.gz |
Merge "[MIPS] Update fast TLB handler for new kernels"android-4.3_r3.1android-4.3_r3android-4.3_r2.3android-4.3_r2.2android-4.3_r2.1android-4.3_r2android-4.3_r1.1android-4.3_r1android-4.3_r0.9.1android-4.3_r0.9android-4.3.1_r1jb-mr2.0.0-releasejb-mr2.0-releasejb-mr2-releasejb-mr2-dev
-rw-r--r-- | target-mips/helper.c | 104 |
1 files changed, 72 insertions, 32 deletions
diff --git a/target-mips/helper.c b/target-mips/helper.c index 838ccbb654..c69a8bd039 100644 --- a/target-mips/helper.c +++ b/target-mips/helper.c @@ -262,42 +262,82 @@ static void raise_mmu_exception(CPUState *env, target_ulong address, /* * 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 struct { + target_ulong pgd_current_p; + int softshift; +} linux_pte_info = {0}; + static inline target_ulong cpu_mips_get_pgd(CPUState *env) { - static target_ulong pgd_current_p = 0; - static target_ulong probed = 0; - - 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; + if (unlikely(linux_pte_info.pgd_current_p == 0)) { + int i; + uint32_t lui_ins, lw_ins, srl_ins; + uint32_t address; uint32_t ebase; - probed = 1; + /* + * The exact TLB refill code varies depeing on the kernel version + * and configuration. Examins the TLB handler to extract + * pgd_current_p and the shift required to convert in memory PTE + * to TLB format + */ + static struct { + struct { + uint32_t off; + uint32_t op; + uint32_t mask; + } lui, lw, srl; + } handlers[] = { + /* 2.6.29+ */ + { + {0x00, 0x3c1b0000, 0xffff0000}, /* 0x3c1b803f : lui k1,%hi(pgd_current_p) */ + {0x08, 0x8f7b0000, 0xffff0000}, /* 0x8f7b3000 : lw k1,%lo(k1) */ + {0x34, 0x001ad182, 0xffffffff} /* 0x001ad182 : srl k0,k0,0x6 */ + }, + /* 3.4+ */ + { + {0x00, 0x3c1b0000, 0xffff0000}, /* 0x3c1b803f : lui k1,%hi(pgd_current_p) */ + {0x08, 0x8f7b0000, 0xffff0000}, /* 0x8f7b3000 : lw k1,%lo(k1) */ + {0x34, 0x001ad142, 0xffffffff} /* 0x001ad182 : srl k0,k0,0x5 */ + } + }; 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); - } + /* Match the kernel TLB refill exception handler against known code */ + for (i = 0; i < sizeof(handlers)/sizeof(handlers[0]); i++) { + lui_ins = ldl_phys(ebase + handlers[i].lui.off); + lw_ins = ldl_phys(ebase + handlers[i].lw.off); + srl_ins = ldl_phys(ebase + handlers[i].srl.off); + if (((lui_ins & handlers[i].lui.mask) == handlers[i].lui.op) && + ((lw_ins & handlers[i].lw.mask) == handlers[i].lw.op) && + ((srl_ins & handlers[i].srl.mask) == handlers[i].srl.op)) + break; + } + if (i >= sizeof(handlers)/sizeof(handlers[0])) { + printf("TLBMiss handler dump:\n"); + for (i = 0; i < 0x80; i+= 4) + printf("0x%08x: 0x%08x\n", ebase + i, ldl_phys(ebase + i)); + cpu_abort(env, "TLBMiss handler signature not recognised\n"); + } + + address = (lui_ins & 0xffff) << 16; + address += (((int32_t)(lw_ins & 0xffff)) << 16) >> 16; + if (address >= 0x80000000 && address < 0xa0000000) + address -= 0x80000000; + else if (address >= 0xa0000000 && address <= 0xc0000000) + address -= 0xa0000000; + else + cpu_abort(env, "pgd_current_p not in KSEG0/KSEG1\n"); + + linux_pte_info.pgd_current_p = address; + linux_pte_info.softshift = (srl_ins >> 6) & 0x1f; } - return 0; + + /* Get pgd_current */ + return ldl_phys(linux_pte_info.pgd_current_p); } static inline int cpu_mips_tlb_refill(CPUState *env, target_ulong address, int rw , @@ -347,14 +387,14 @@ static inline int cpu_mips_tlb_refill(CPUState *env, target_ulong address, int r index = (env->CP0_Context>>1)&0xff8; ptw_phys += index; - /*get the page table entry*/ + /* 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; + elo_even = elo_even >> linux_pte_info.softshift; + elo_odd = elo_odd >> linux_pte_info.softshift; env->CP0_EntryLo0 = elo_even; env->CP0_EntryLo1 = elo_odd; - /*Done. refill the TLB */ + /* Done. refill the TLB */ r4k_helper_ptw_tlbrefill(env); /* Since we know the value of TLB entry, we can @@ -495,7 +535,7 @@ target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) else lo = ldl_phys(pt_phys + pt_index); /* convert software TLB entry to hardware value */ - lo >>= 6; + lo >>= linux_pte_info.softshift; if (lo & 0x00000002) /* It is valid */ phys_addr = (lo >> 6) << 12; |