OSDN Git Service

MIPS TLB style selection at runtime, by Herve Poussineau.
authorths <ths@c046a42c-6fe2-441c-8c8c-71466251a162>
Sun, 13 May 2007 13:49:44 +0000 (13:49 +0000)
committerths <ths@c046a42c-6fe2-441c-8c8c-71466251a162>
Sun, 13 May 2007 13:49:44 +0000 (13:49 +0000)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2809 c046a42c-6fe2-441c-8c8c-71466251a162

target-mips/cpu.h
target-mips/exec.h
target-mips/helper.c
target-mips/mips-defs.h
target-mips/op.c
target-mips/op_helper.c
target-mips/translate.c
target-mips/translate_init.c

index 33cb657..9e02919 100644 (file)
@@ -33,9 +33,8 @@ union fpr_t {
 #  define FP_ENDIAN_IDX 0
 #endif
 
-#if defined(MIPS_USES_R4K_TLB)
-typedef struct tlb_t tlb_t;
-struct tlb_t {
+typedef struct r4k_tlb_t r4k_tlb_t;
+struct r4k_tlb_t {
     target_ulong VPN;
     uint32_t PageMask;
     uint_fast8_t ASID;
@@ -48,7 +47,6 @@ struct tlb_t {
     uint_fast16_t D1:1;
     target_ulong PFN[2];
 };
-#endif
 
 typedef struct CPUMIPSState CPUMIPSState;
 struct CPUMIPSState {
@@ -100,11 +98,19 @@ struct CPUMIPSState {
 #define FP_INVALID        16
 #define FP_UNIMPLEMENTED  32
 
-#if defined(MIPS_USES_R4K_TLB)
-    tlb_t tlb[MIPS_TLB_MAX];
-    uint32_t tlb_in_use;
     uint32_t nb_tlb;
-#endif
+    uint32_t tlb_in_use;
+    int (*map_address) (CPUMIPSState *env, target_ulong *physical, int *prot, target_ulong address, int rw, int access_type);
+    void (*do_tlbwi) (void);
+    void (*do_tlbwr) (void);
+    void (*do_tlbp) (void);
+    void (*do_tlbr) (void);
+    union {
+        struct {
+            r4k_tlb_t tlb[MIPS_TLB_MAX];
+        } r4k;
+    } mmu;
+
     int32_t CP0_Index;
     int32_t CP0_Random;
     target_ulong CP0_EntryLo0;
@@ -289,6 +295,16 @@ struct CPUMIPSState {
     struct QEMUTimer *timer; /* Internal timer */
 };
 
+int no_mmu_map_address (CPUMIPSState *env, target_ulong *physical, int *prot,
+                        target_ulong address, int rw, int access_type);
+int fixed_mmu_map_address (CPUMIPSState *env, target_ulong *physical, int *prot,
+                           target_ulong address, int rw, int access_type);
+int r4k_map_address (CPUMIPSState *env, target_ulong *physical, int *prot,
+                     target_ulong address, int rw, int access_type);
+void r4k_do_tlbwi (void);
+void r4k_do_tlbwr (void);
+void r4k_do_tlbp (void);
+void r4k_do_tlbr (void);
 typedef struct mips_def_t mips_def_t;
 int mips_find_by_name (const unsigned char *name, mips_def_t **def);
 void mips_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...));
index d9160a1..44314a9 100644 (file)
@@ -105,10 +105,6 @@ void do_mfc0_count(void);
 void do_mtc0_entryhi(uint32_t in);
 void do_mtc0_status_debug(uint32_t old, uint32_t val);
 void do_mtc0_status_irqraise_debug(void);
-void do_tlbwi (void);
-void do_tlbwr (void);
-void do_tlbp (void);
-void do_tlbr (void);
 void dump_fpu(CPUState *env);
 void fpu_dump_state(CPUState *env, FILE *f, 
                     int (*fpu_fprintf)(FILE *f, const char *fmt, ...),
@@ -151,7 +147,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 r4k_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);
index 1b61a31..cb568f1 100644 (file)
@@ -36,16 +36,42 @@ enum {
     TLBRET_MATCH = 0
 };
 
-/* MIPS32 4K MMU emulation */
-#ifdef MIPS_USES_R4K_TLB
-static int map_address (CPUState *env, target_ulong *physical, int *prot,
+/* no MMU emulation */
+int no_mmu_map_address (CPUState *env, target_ulong *physical, int *prot,
                         target_ulong address, int rw, int access_type)
 {
+    *physical = address;
+    *prot = PAGE_READ | PAGE_WRITE;
+    return TLBRET_MATCH;
+}
+
+/* fixed mapping MMU emulation */
+int fixed_mmu_map_address (CPUState *env, target_ulong *physical, int *prot,
+                           target_ulong address, int rw, int access_type)
+{
+    if (address <= (int32_t)0x7FFFFFFFUL) {
+        if (!(env->CP0_Status & (1 << CP0St_ERL)))
+            *physical = address + 0x40000000UL;
+        else
+            *physical = address;
+    } else if (address <= (int32_t)0xBFFFFFFFUL)
+        *physical = address & 0x1FFFFFFF;
+    else
+        *physical = address;
+
+    *prot = PAGE_READ | PAGE_WRITE;
+    return TLBRET_MATCH;
+}
+
+/* MIPS32/MIPS64 R4000-style MMU emulation */
+int r4k_map_address (CPUState *env, target_ulong *physical, int *prot,
+                     target_ulong address, int rw, int access_type)
+{
     uint8_t ASID = env->CP0_EntryHi & 0xFF;
     int i;
 
     for (i = 0; i < env->tlb_in_use; i++) {
-        tlb_t *tlb = &env->tlb[i];
+        r4k_tlb_t *tlb = &env->mmu.r4k.tlb[i];
         /* 1k pages are not supported. */
         target_ulong mask = tlb->PageMask | 0x1FFF;
         target_ulong tag = address & ~mask;
@@ -71,7 +97,6 @@ static int map_address (CPUState *env, target_ulong *physical, int *prot,
     }
     return TLBRET_NOMATCH;
 }
-#endif
 
 static int get_physical_address (CPUState *env, target_ulong *physical,
                                 int *prot, target_ulong address,
@@ -104,14 +129,9 @@ static int get_physical_address (CPUState *env, target_ulong *physical,
     if (address <= (int32_t)0x7FFFFFFFUL) {
         /* useg */
         if (!(env->CP0_Status & (1 << CP0St_ERL) && user_mode)) {
-#ifdef MIPS_USES_R4K_TLB
-            ret = map_address(env, physical, prot, address, rw, access_type);
-#else
-            *physical = address + 0x40000000UL;
-            *prot = PAGE_READ | PAGE_WRITE;
-#endif
+            ret = env->map_address(env, physical, prot, address, rw, access_type);
         } else {
-            *physical = address;
+            *physical = address & 0xFFFFFFFF;
             *prot = PAGE_READ | PAGE_WRITE;
         }
 #ifdef TARGET_MIPS64
@@ -123,14 +143,14 @@ static int get_physical_address (CPUState *env, target_ulong *physical,
     } else if (address < 0x3FFFFFFFFFFFFFFFULL) {
         /* xuseg */
        if (UX && address < 0x000000FFFFFFFFFFULL) {
-            ret = map_address(env, physical, prot, address, rw, access_type);
+            ret = env->map_address(env, physical, prot, address, rw, access_type);
        } else {
            ret = TLBRET_BADADDR;
         }
     } else if (address < 0x7FFFFFFFFFFFFFFFULL) {
         /* xsseg */
        if (SX && address < 0x400000FFFFFFFFFFULL) {
-            ret = map_address(env, physical, prot, address, rw, access_type);
+            ret = env->map_address(env, physical, prot, address, rw, access_type);
        } else {
            ret = TLBRET_BADADDR;
         }
@@ -148,7 +168,7 @@ static int get_physical_address (CPUState *env, target_ulong *physical,
         /* xkseg */
         /* XXX: check supervisor mode */
        if (KX && address < 0xC00000FF7FFFFFFFULL) {
-            ret = map_address(env, physical, prot, address, rw, access_type);
+            ret = env->map_address(env, physical, prot, address, rw, access_type);
        } else {
            ret = TLBRET_BADADDR;
        }
@@ -165,22 +185,12 @@ static int get_physical_address (CPUState *env, target_ulong *physical,
         *prot = PAGE_READ | PAGE_WRITE;
     } else if (address < (int32_t)0xE0000000UL) {
         /* kseg2 */
-#ifdef MIPS_USES_R4K_TLB
-        ret = map_address(env, physical, prot, address, rw, access_type);
-#else
-        *physical = address & 0xFFFFFFFF;
-        *prot = PAGE_READ | PAGE_WRITE;
-#endif
+        ret = env->map_address(env, physical, prot, address, rw, access_type);
     } else {
         /* kseg3 */
         /* XXX: check supervisor mode */
         /* XXX: debug segment is not emulated */
-#ifdef MIPS_USES_R4K_TLB
-        ret = map_address(env, physical, prot, address, rw, access_type);
-#else
-        *physical = address & 0xFFFFFFFF;
-        *prot = PAGE_READ | PAGE_WRITE;
-#endif
+        ret = env->map_address(env, physical, prot, address, rw, access_type);
     }
 #if 0
     if (logfile) {
@@ -483,15 +493,15 @@ void do_interrupt (CPUState *env)
 }
 #endif /* !defined(CONFIG_USER_ONLY) */
 
-void invalidate_tlb (CPUState *env, int idx, int use_extra)
+void r4k_invalidate_tlb (CPUState *env, int idx, int use_extra)
 {
-    tlb_t *tlb;
+    r4k_tlb_t *tlb;
     target_ulong addr;
     target_ulong end;
     uint8_t ASID = env->CP0_EntryHi & 0xFF;
     target_ulong mask;
 
-    tlb = &env->tlb[idx];
+    tlb = &env->mmu.r4k.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) {
@@ -502,7 +512,7 @@ void invalidate_tlb (CPUState *env, int idx, int use_extra)
         /* 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->mmu.r4k.tlb[env->tlb_in_use] = *tlb;
         env->tlb_in_use++;
         return;
     }
index 414f476..6f012a9 100644 (file)
@@ -6,8 +6,6 @@
 
 /* real pages are variable size... */
 #define TARGET_PAGE_BITS 12
-/* Uses MIPS R4Kc TLB model */
-#define MIPS_USES_R4K_TLB
 #define MIPS_TLB_MAX 128
 
 #ifdef TARGET_MIPS64
index 748f997..a0611e0 100644 (file)
@@ -1411,12 +1411,7 @@ void op_mtc0_ebase (void)
 
 void op_mtc0_config0 (void)
 {
-#if defined(MIPS_USES_R4K_TLB)
-     /* Fixed mapping MMU not implemented */
-    env->CP0_Config0 = (env->CP0_Config0 & 0x8017FF88) | (T0 & 0x00000001);
-#else
-    env->CP0_Config0 = (env->CP0_Config0 & 0xFE17FF88) | (T0 & 0x00000001);
-#endif
+    env->CP0_Config0 = (env->CP0_Config0 & 0x81FFFFF8) | (T0 & 0x00000001);
     RETURN();
 }
 
@@ -2680,31 +2675,29 @@ void op_bc1tany4 (void)
     RETURN();
 }
 
-#if defined(MIPS_USES_R4K_TLB)
 void op_tlbwi (void)
 {
-    CALL_FROM_TB0(do_tlbwi);
+    CALL_FROM_TB0(env->do_tlbwi);
     RETURN();
 }
 
 void op_tlbwr (void)
 {
-    CALL_FROM_TB0(do_tlbwr);
+    CALL_FROM_TB0(env->do_tlbwr);
     RETURN();
 }
 
 void op_tlbp (void)
 {
-    CALL_FROM_TB0(do_tlbp);
+    CALL_FROM_TB0(env->do_tlbp);
     RETURN();
 }
 
 void op_tlbr (void)
 {
-    CALL_FROM_TB0(do_tlbr);
+    CALL_FROM_TB0(env->do_tlbr);
     RETURN();
 }
-#endif
 
 /* Specials */
 #if defined (CONFIG_USER_ONLY)
index 2ebcede..9d7a560 100644 (file)
@@ -298,26 +298,6 @@ void do_mtc0_status_irqraise_debug (void)
     cpu_abort(env, "mtc0 status irqraise debug\n");
 }
 
-void do_tlbwi (void)
-{
-    cpu_abort(env, "tlbwi\n");
-}
-
-void do_tlbwr (void)
-{
-    cpu_abort(env, "tlbwr\n");
-}
-
-void do_tlbp (void)
-{
-    cpu_abort(env, "tlbp\n");
-}
-
-void do_tlbr (void)
-{
-    cpu_abort(env, "tlbr\n");
-}
-
 void cpu_mips_tlb_flush (CPUState *env, int flush_global)
 {
     cpu_abort(env, "mips_tlb_flush\n");
@@ -389,7 +369,6 @@ void fpu_handle_exception(void)
 }
 
 /* TLB management */
-#if defined(MIPS_USES_R4K_TLB)
 void cpu_mips_tlb_flush (CPUState *env, int flush_global)
 {
     /* Flush qemu's TLB and discard all shadowed entries.  */
@@ -397,20 +376,20 @@ void cpu_mips_tlb_flush (CPUState *env, int flush_global)
     env->tlb_in_use = env->nb_tlb;
 }
 
-static void mips_tlb_flush_extra (CPUState *env, int first)
+static void r4k_mips_tlb_flush_extra (CPUState *env, int first)
 {
     /* Discard entries from env->tlb[first] onwards.  */
     while (env->tlb_in_use > first) {
-        invalidate_tlb(env, --env->tlb_in_use, 0);
+        r4k_invalidate_tlb(env, --env->tlb_in_use, 0);
     }
 }
 
-static void fill_tlb (int idx)
+static void r4k_fill_tlb (int idx)
 {
-    tlb_t *tlb;
+    r4k_tlb_t *tlb;
 
     /* XXX: detect conflicting TLBs and raise a MCHECK exception when needed */
-    tlb = &env->tlb[idx];
+    tlb = &env->mmu.r4k.tlb[idx];
     tlb->VPN = env->CP0_EntryHi & ~(target_ulong)0x1FFF;
     tlb->ASID = env->CP0_EntryHi & 0xFF;
     tlb->PageMask = env->CP0_PageMask;
@@ -425,28 +404,28 @@ static void fill_tlb (int idx)
     tlb->PFN[1] = (env->CP0_EntryLo1 >> 6) << 12;
 }
 
-void do_tlbwi (void)
+void r4k_do_tlbwi (void)
 {
     /* Discard cached TLB entries.  We could avoid doing this if the
        tlbwi is just upgrading access permissions on the current entry;
        that might be a further win.  */
-    mips_tlb_flush_extra (env, env->nb_tlb);
+    r4k_mips_tlb_flush_extra (env, env->nb_tlb);
 
-    invalidate_tlb(env, env->CP0_Index % env->nb_tlb, 0);
-    fill_tlb(env->CP0_Index % env->nb_tlb);
+    r4k_invalidate_tlb(env, env->CP0_Index % env->nb_tlb, 0);
+    r4k_fill_tlb(env->CP0_Index % env->nb_tlb);
 }
 
-void do_tlbwr (void)
+void r4k_do_tlbwr (void)
 {
     int r = cpu_mips_get_random(env);
 
-    invalidate_tlb(env, r, 1);
-    fill_tlb(r);
+    r4k_invalidate_tlb(env, r, 1);
+    r4k_fill_tlb(r);
 }
 
-void do_tlbp (void)
+void r4k_do_tlbp (void)
 {
-    tlb_t *tlb;
+    r4k_tlb_t *tlb;
     target_ulong tag;
     uint8_t ASID;
     int i;
@@ -454,7 +433,7 @@ void do_tlbp (void)
     tag = env->CP0_EntryHi & (int32_t)0xFFFFE000;
     ASID = env->CP0_EntryHi & 0xFF;
     for (i = 0; i < env->nb_tlb; i++) {
-        tlb = &env->tlb[i];
+        tlb = &env->mmu.r4k.tlb[i];
         /* Check ASID, virtual page number & size */
         if ((tlb->G == 1 || tlb->ASID == ASID) && tlb->VPN == tag) {
             /* TLB match */
@@ -465,11 +444,11 @@ void do_tlbp (void)
     if (i == env->nb_tlb) {
         /* No match.  Discard any shadow entries, if any of them match.  */
         for (i = env->nb_tlb; i < env->tlb_in_use; i++) {
-           tlb = &env->tlb[i];
+           tlb = &env->mmu.r4k.tlb[i];
 
            /* Check ASID, virtual page number & size */
            if ((tlb->G == 1 || tlb->ASID == ASID) && tlb->VPN == tag) {
-                mips_tlb_flush_extra (env, i);
+                r4k_mips_tlb_flush_extra (env, i);
                break;
            }
        }
@@ -478,19 +457,19 @@ void do_tlbp (void)
     }
 }
 
-void do_tlbr (void)
+void r4k_do_tlbr (void)
 {
-    tlb_t *tlb;
+    r4k_tlb_t *tlb;
     uint8_t ASID;
 
     ASID = env->CP0_EntryHi & 0xFF;
-    tlb = &env->tlb[env->CP0_Index % env->nb_tlb];
+    tlb = &env->mmu.r4k.tlb[env->CP0_Index % env->nb_tlb];
 
     /* If this will change the current ASID, flush qemu's TLB.  */
     if (ASID != tlb->ASID)
         cpu_mips_tlb_flush (env, 1);
 
-    mips_tlb_flush_extra(env, env->nb_tlb);
+    r4k_mips_tlb_flush_extra(env, env->nb_tlb);
 
     env->CP0_EntryHi = tlb->VPN | tlb->ASID;
     env->CP0_PageMask = tlb->PageMask;
@@ -499,7 +478,6 @@ void do_tlbr (void)
     env->CP0_EntryLo1 = tlb->G | (tlb->V1 << 1) | (tlb->D1 << 2) |
                         (tlb->C1 << 3) | (tlb->PFN[1] >> 6);
 }
-#endif
 
 #endif /* !CONFIG_USER_ONLY */
 
index 79a33c5..fb802f0 100644 (file)
@@ -4164,7 +4164,7 @@ die:
 }
 #endif /* TARGET_MIPS64 */
 
-static void gen_cp0 (DisasContext *ctx, uint32_t opc, int rt, int rd)
+static void gen_cp0 (CPUState *env, DisasContext *ctx, uint32_t opc, int rt, int rd)
 {
     const char *opn = "ldst";
 
@@ -4199,24 +4199,30 @@ static void gen_cp0 (DisasContext *ctx, uint32_t opc, int rt, int rd)
         opn = "dmtc0";
         break;
 #endif
-#if defined(MIPS_USES_R4K_TLB)
     case OPC_TLBWI:
-        gen_op_tlbwi();
         opn = "tlbwi";
+        if (!env->do_tlbwi)
+            goto die;
+        gen_op_tlbwi();
         break;
     case OPC_TLBWR:
-        gen_op_tlbwr();
         opn = "tlbwr";
+        if (!env->do_tlbwr)
+            goto die;
+        gen_op_tlbwr();
         break;
     case OPC_TLBP:
-        gen_op_tlbp();
         opn = "tlbp";
+        if (!env->do_tlbp)
+            goto die;
+        gen_op_tlbp();
         break;
     case OPC_TLBR:
-        gen_op_tlbr();
         opn = "tlbr";
+        if (!env->do_tlbr)
+            goto die;
+        gen_op_tlbr();
         break;
-#endif
     case OPC_ERET:
         opn = "eret";
         save_cpu_state(ctx, 0);
@@ -4244,6 +4250,7 @@ static void gen_cp0 (DisasContext *ctx, uint32_t opc, int rt, int rd)
         ctx->bstate = BS_EXCP;
         break;
     default:
+ die:
         MIPS_INVAL(opn);
         generate_exception(ctx, EXCP_RI);
         return;
@@ -5576,10 +5583,10 @@ static void decode_opc (CPUState *env, DisasContext *ctx)
         case OPC_DMFC0:
         case OPC_DMTC0:
 #endif
-            gen_cp0(ctx, op1, rt, rd);
+            gen_cp0(env, ctx, op1, rt, rd);
             break;
         case OPC_C0_FIRST ... OPC_C0_LAST:
-            gen_cp0(ctx, MASK_C0(ctx->opcode), rt, rd);
+            gen_cp0(env, ctx, MASK_C0(ctx->opcode), rt, rd);
             break;
         case OPC_MFMC0:
             op2 = MASK_MFMC0(ctx->opcode);
index e38ab28..7d60f19 100644 (file)
@@ -148,7 +148,7 @@ static mips_def_t mips_defs[] =
         .Status_rw_bitmask = 0x3678FFFF,
         .CP1_fcr0 = (1 << FCR0_F64) | (1 << FCR0_L) | (1 << FCR0_W) |
                     (1 << FCR0_D) | (1 << FCR0_S) |
-                    (0x4 << FCR0_PRID) | (0x0 << FCR0_REV),
+                    (0x5 << FCR0_PRID) | (0x0 << FCR0_REV),
     },
 #endif
 };
@@ -180,6 +180,30 @@ void mips_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...))
     }
 }
 
+#ifndef CONFIG_USER_ONLY
+static void no_mmu_init (CPUMIPSState *env, mips_def_t *def)
+{
+    env->nb_tlb = 1;
+    env->map_address = &no_mmu_map_address;
+}
+
+static void fixed_mmu_init (CPUMIPSState *env, mips_def_t *def)
+{
+    env->nb_tlb = 1;
+    env->map_address = &fixed_mmu_map_address;
+}
+
+static void r4k_mmu_init (CPUMIPSState *env, mips_def_t *def)
+{
+    env->nb_tlb = 1 + ((def->CP0_Config1 >> CP0C1_MMU) & 63);
+    env->map_address = &r4k_map_address;
+    env->do_tlbwi = r4k_do_tlbwi;
+    env->do_tlbwr = r4k_do_tlbwr;
+    env->do_tlbp = r4k_do_tlbp;
+    env->do_tlbr = r4k_do_tlbr;
+}
+#endif /* CONFIG_USER_ONLY */
+
 int cpu_mips_register (CPUMIPSState *env, mips_def_t *def)
 {
     if (!def)
@@ -199,10 +223,23 @@ int cpu_mips_register (CPUMIPSState *env, mips_def_t *def)
     env->CCRes = def->CCRes;
     env->Status_rw_bitmask = def->Status_rw_bitmask;
     env->fcr0 = def->CP1_fcr0;
-#if defined (MIPS_USES_R4K_TLB)
-    env->nb_tlb = 1 + ((def->CP0_Config1 >> CP0C1_MMU) & 63);
+#ifndef CONFIG_USER_ONLY
+    switch ((env->CP0_Config0 >> CP0C0_MT) & 3) {
+        case 0:
+            no_mmu_init(env, def);
+            break;
+        case 1:
+            r4k_mmu_init(env, def);
+            break;
+        case 3:
+            fixed_mmu_init(env, def);
+            break;
+        default:
+            /* Older CPUs like the R3000 may need nonstandard handling here. */
+            cpu_abort(env, "MMU type not supported\n");
+    }
     env->CP0_Random = env->nb_tlb - 1;
     env->tlb_in_use = env->nb_tlb;
-#endif
+#endif /* CONFIG_USER_ONLY */
     return 0;
 }