PowerPC 601 need specific callbacks for its BATs setup.
authorj_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
Sun, 4 Nov 2007 02:55:33 +0000 (02:55 +0000)
committerj_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
Sun, 4 Nov 2007 02:55:33 +0000 (02:55 +0000)
Implement PowerPC 601 HID0 register, needed for little-endian mode support.
As a consequence, we need to merge hflags coming from MSR with other ones.
Use little-endian mode from hflags instead of MSR during code translation.

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3524 c046a42c-6fe2-441c-8c8c-71466251a162

target-ppc/cpu.h
target-ppc/helper.c
target-ppc/helper_regs.h
target-ppc/op.c
target-ppc/op_helper.c
target-ppc/op_helper.h
target-ppc/translate.c
target-ppc/translate_init.c

index 4beeab2..939dbcd 100644 (file)
@@ -651,7 +651,8 @@ struct CPUPPCState {
     /* Those resources are used only in Qemu core */
     jmp_buf jmp_env;
     int user_mode_only; /* user mode only simulation */
-    target_ulong hflags; /* hflags is a MSR & HFLAGS_MASK */
+    target_ulong hflags;      /* hflags is a MSR & HFLAGS_MASK         */
+    target_ulong hflags_nmsr; /* specific hflags, not comming from MSR */
     int mmu_idx;         /* precomputed MMU index to speed up mem accesses */
 
     /* Power management */
@@ -698,6 +699,8 @@ target_ulong do_load_dbatu (CPUPPCState *env, int nr);
 target_ulong do_load_dbatl (CPUPPCState *env, int nr);
 void do_store_dbatu (CPUPPCState *env, int nr, target_ulong value);
 void do_store_dbatl (CPUPPCState *env, int nr, target_ulong value);
+void do_store_ibatu_601 (CPUPPCState *env, int nr, target_ulong value);
+void do_store_ibatl_601 (CPUPPCState *env, int nr, target_ulong value);
 target_ulong do_load_sdr1 (CPUPPCState *env);
 void do_store_sdr1 (CPUPPCState *env, target_ulong value);
 #if defined(TARGET_PPC64)
index 9d6f490..9fd9721 100644 (file)
@@ -482,10 +482,12 @@ static always_inline void bat_601_size_prot (CPUState *env,target_ulong *blp,
     int key, pp, valid, prot;
 
     bl = (*BATl & 0x0000003F) << 17;
+#if defined (DEBUG_BATS)
     if (loglevel != 0) {
         fprintf(logfile, "b %02x ==> bl %08x msk %08x\n",
                 *BATl & 0x0000003F, bl, ~bl);
     }
+#endif
     prot = 0;
     valid = (*BATl >> 6) & 1;
     if (valid) {
@@ -1836,6 +1838,76 @@ void do_store_dbatl (CPUPPCState *env, int nr, target_ulong value)
     env->DBAT[1][nr] = value;
 }
 
+void do_store_ibatu_601 (CPUPPCState *env, int nr, target_ulong value)
+{
+    target_ulong mask;
+    int do_inval;
+
+    dump_store_bat(env, 'I', 0, nr, value);
+    if (env->IBAT[0][nr] != value) {
+        do_inval = 0;
+        mask = (env->IBAT[1][nr] << 17) & 0x0FFE0000UL;
+        if (env->IBAT[1][nr] & 0x40) {
+            /* Invalidate BAT only if it is valid */
+#if !defined(FLUSH_ALL_TLBS)
+            do_invalidate_BAT(env, env->IBAT[0][nr], mask);
+#else
+            do_inval = 1;
+#endif
+        }
+        /* When storing valid upper BAT, mask BEPI and BRPN
+         * and invalidate all TLBs covered by this BAT
+         */
+        env->IBAT[0][nr] = (value & 0x00001FFFUL) |
+            (value & ~0x0001FFFFUL & ~mask);
+        env->DBAT[0][nr] = env->IBAT[0][nr];
+        if (env->IBAT[1][nr] & 0x40) {
+#if !defined(FLUSH_ALL_TLBS)
+            do_invalidate_BAT(env, env->IBAT[0][nr], mask);
+#else
+            do_inval = 1;
+#endif
+        }
+#if defined(FLUSH_ALL_TLBS)
+        if (do_inval)
+            tlb_flush(env, 1);
+#endif
+    }
+}
+
+void do_store_ibatl_601 (CPUPPCState *env, int nr, target_ulong value)
+{
+    target_ulong mask;
+    int do_inval;
+
+    dump_store_bat(env, 'I', 1, nr, value);
+    if (env->IBAT[1][nr] != value) {
+        do_inval = 0;
+        if (env->IBAT[1][nr] & 0x40) {
+#if !defined(FLUSH_ALL_TLBS)
+            mask = (env->IBAT[1][nr] << 17) & 0x0FFE0000UL;
+            do_invalidate_BAT(env, env->IBAT[0][nr], mask);
+#else
+            do_inval = 1;
+#endif
+        }
+        if (value & 0x40) {
+#if !defined(FLUSH_ALL_TLBS)
+            mask = (value << 17) & 0x0FFE0000UL;
+            do_invalidate_BAT(env, env->IBAT[0][nr], mask);
+#else
+            do_inval = 1;
+#endif
+        }
+        env->IBAT[1][nr] = value;
+        env->DBAT[1][nr] = value;
+#if defined(FLUSH_ALL_TLBS)
+        if (do_inval)
+            tlb_flush(env, 1);
+#endif
+    }
+}
+
 /*****************************************************************************/
 /* TLB management */
 void ppc_tlb_invalidate_all (CPUPPCState *env)
@@ -2684,6 +2756,7 @@ static always_inline void powerpc_excp (CPUState *env,
      *      any special case that could occur. Just store MSR and update hflags
      */
     env->msr = new_msr;
+    env->hflags_nmsr = 0x00000000;
     hreg_compute_hflags(env);
     env->nip = vector;
     /* Reset exception state */
index 2a5de2e..c52ae9e 100644 (file)
@@ -58,6 +58,17 @@ static always_inline void hreg_swap_gpr_tgpr (CPUPPCState *env)
     env->tgpr[3] = tmp;
 }
 
+static always_inline void hreg_compute_mem_idx (CPUPPCState *env)
+{
+#if defined (TARGET_PPC64H)
+    /* Precompute MMU index */
+    if (msr_pr == 0 && msr_hv != 0)
+        env->mmu_idx = 2;
+    else
+#endif
+        env->mmu_idx = 1 - msr_pr;
+}
+
 static always_inline void hreg_compute_hflags (CPUPPCState *env)
 {
     target_ulong hflags_mask;
@@ -70,14 +81,12 @@ static always_inline void hreg_compute_hflags (CPUPPCState *env)
     hflags_mask |= (1ULL << MSR_CM) | (1ULL << MSR_SF);
 #if defined (TARGET_PPC64H)
     hflags_mask |= 1ULL << MSR_HV;
-    /* Precompute MMU index */
-    if (msr_pr == 0 && msr_hv != 0)
-        env->mmu_idx = 2;
-    else
 #endif
 #endif
-        env->mmu_idx = 1 - msr_pr;
+    hreg_compute_mem_idx(env);
     env->hflags = env->msr & hflags_mask;
+    /* Merge with hflags coming from other registers */
+    env->hflags |= env->hflags_nmsr;
 }
 
 static always_inline int hreg_store_msr (CPUPPCState *env, target_ulong value)
index da08ec5..730dc0e 100644 (file)
@@ -2190,30 +2190,27 @@ void OPPROTO op_store_601_rtcu (void)
     RETURN();
 }
 
+void OPPROTO op_store_hid0_601 (void)
+{
+    do_store_hid0_601();
+    RETURN();
+}
+
 void OPPROTO op_load_601_bat (void)
 {
     T0 = env->IBAT[PARAM1][PARAM2];
     RETURN();
 }
-#endif /* !defined(CONFIG_USER_ONLY) */
 
-/* 601 unified BATs store.
- * To avoid using specific MMU code for 601, we store BATs in
- * IBAT and DBAT simultaneously, then emulate unified BATs.
- */
-#if !defined(CONFIG_USER_ONLY)
 void OPPROTO op_store_601_batl (void)
 {
-    int nr = PARAM1;
-
-    env->IBAT[1][nr] = T0;
-    env->DBAT[1][nr] = T0;
+    do_store_ibatl_601(env, PARAM1, T0);
     RETURN();
 }
 
 void OPPROTO op_store_601_batu (void)
 {
-    do_store_601_batu(PARAM1);
+    do_store_ibatu_601(env, PARAM1, T0);
     RETURN();
 }
 #endif /* !defined(CONFIG_USER_ONLY) */
index f5d26ae..6ed9c95 100644 (file)
@@ -1701,12 +1701,23 @@ void do_POWER_rfsvc (void)
     __do_rfi(env->lr, env->ctr, 0x0000FFFF, 0);
 }
 
-/* PowerPC 601 BAT management helper */
-void do_store_601_batu (int nr)
+void do_store_hid0_601 (void)
 {
-    do_store_ibatu(env, nr, (uint32_t)T0);
-    env->DBAT[0][nr] = env->IBAT[0][nr];
-    env->DBAT[1][nr] = env->IBAT[1][nr];
+    uint32_t hid0;
+
+    hid0 = env->spr[SPR_HID0];
+    if ((T0 ^ hid0) & 0x00000008) {
+        /* Change current endianness */
+        env->hflags &= ~(1 << MSR_LE);
+        env->hflags_nmsr &= ~(1 << MSR_LE);
+        env->hflags_nmsr |= (1 << MSR_LE) & (((T0 >> 3) & 1) << MSR_LE);
+        env->hflags |= env->hflags_nmsr;
+        if (loglevel != 0) {
+            fprintf(logfile, "%s: set endianness to %c => " ADDRX "\n",
+                    __func__, T0 & 0x8 ? 'l' : 'b', env->hflags);
+        }
+    }
+    env->spr[SPR_HID0] = T0;
 }
 #endif
 
index 915b32a..6575d3d 100644 (file)
@@ -155,7 +155,6 @@ void do_load_74xx_tlb (int is_code);
 #endif
 
 /* POWER / PowerPC 601 specific helpers */
-void do_store_601_batu (int nr);
 void do_POWER_abso (void);
 void do_POWER_clcs (void);
 void do_POWER_div (void);
@@ -168,6 +167,7 @@ void do_POWER_mulo (void);
 #if !defined(CONFIG_USER_ONLY)
 void do_POWER_rac (void);
 void do_POWER_rfsvc (void);
+void do_store_hid0_601 (void);
 #endif
 
 /* PowerPC 602 specific helper */
index cd7a483..1adff9f 100644 (file)
@@ -6801,7 +6801,7 @@ static always_inline int gen_intermediate_code_internal (CPUState *env,
     opc_handler_t **table, *handler;
     target_ulong pc_start;
     uint16_t *gen_opc_end;
-    int supervisor;
+    int supervisor, little_endian;
     int single_step, branch_step;
     int j, lj = -1;
 
@@ -6821,11 +6821,12 @@ static always_inline int gen_intermediate_code_internal (CPUState *env,
 #if !defined(CONFIG_USER_ONLY)
     ctx.supervisor = supervisor;
 #endif
+    little_endian = env->hflags & (1 << MSR_LE) ? 1 : 0;
 #if defined(TARGET_PPC64)
     ctx.sf_mode = msr_sf;
-    ctx.mem_idx = (supervisor << 2) | (msr_sf << 1) | msr_le;
+    ctx.mem_idx = (supervisor << 2) | (msr_sf << 1) | little_endian;
 #else
-    ctx.mem_idx = (supervisor << 1) | msr_le;
+    ctx.mem_idx = (supervisor << 1) | little_endian;
 #endif
     ctx.dcache_line_size = env->dcache_line_size;
     ctx.fpu_enabled = msr_fp;
@@ -6880,18 +6881,16 @@ static always_inline int gen_intermediate_code_internal (CPUState *env,
                     ctx.nip, supervisor, (int)msr_ir);
         }
 #endif
-        ctx.opcode = ldl_code(ctx.nip);
-        if (msr_le) {
-            ctx.opcode = ((ctx.opcode & 0xFF000000) >> 24) |
-                ((ctx.opcode & 0x00FF0000) >> 8) |
-                ((ctx.opcode & 0x0000FF00) << 8) |
-                ((ctx.opcode & 0x000000FF) << 24);
+        if (unlikely(little_endian)) {
+            ctx.opcode = bswap32(ldl_code(ctx.nip));
+        } else {
+            ctx.opcode = ldl_code(ctx.nip);
         }
 #if defined PPC_DEBUG_DISAS
         if (loglevel & CPU_LOG_TB_IN_ASM) {
             fprintf(logfile, "translate opcode %08x (%02x %02x %02x) (%s)\n",
                     ctx.opcode, opc1(ctx.opcode), opc2(ctx.opcode),
-                    opc3(ctx.opcode), msr_le ? "little" : "big");
+                    opc3(ctx.opcode), little_endian ? "little" : "big");
         }
 #endif
         ctx.nip += 4;
@@ -6986,7 +6985,7 @@ static always_inline int gen_intermediate_code_internal (CPUState *env,
     if (loglevel & CPU_LOG_TB_IN_ASM) {
         int flags;
         flags = env->bfd_mach;
-        flags |= msr_le << 16;
+        flags |= little_endian << 16;
         fprintf(logfile, "IN: %s\n", lookup_symbol(pc_start));
         target_disas(logfile, pc_start, ctx.nip - pc_start, flags);
         fprintf(logfile, "\n");
index 0d648de..eae228b 100644 (file)
@@ -314,6 +314,15 @@ static void spr_write_601_rtcl (void *opaque, int sprn)
 {
     gen_op_store_601_rtcl();
 }
+
+static void spr_write_hid0_601 (void *opaque, int sprn)
+{
+    DisasContext *ctx = opaque;
+
+    gen_op_store_hid0_601();
+    /* Must stop the translation as endianness may have changed */
+    GEN_STOP(ctx);
+}
 #endif
 
 /* Unified bats */
@@ -3259,7 +3268,7 @@ static void init_proc_601 (CPUPPCState *env)
     /* XXX : not implemented */
     spr_register(env, SPR_HID0, "HID0",
                  SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_generic,
+                 &spr_read_generic, &spr_write_hid0_601,
                  0x80010080);
     /* XXX : not implemented */
     spr_register(env, SPR_HID1, "HID1",