Merge "[MIPS] Update fast TLB handler for new kernels"
diff --git a/target-mips/helper.c b/target-mips/helper.c
index 838ccbb..c69a8bd 100644
--- a/target-mips/helper.c
+++ b/target-mips/helper.c
@@ -262,42 +262,82 @@
/*
* 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) */
+ /* 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 = ((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);
- }
+ 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 @@
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 @@
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;