aboutsummaryrefslogtreecommitdiff
path: root/target-ppc
diff options
context:
space:
mode:
authorj_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>2007-11-04 02:55:33 +0000
committerj_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>2007-11-04 02:55:33 +0000
commit056401eae60953822098ff1dc30860364c9681be (patch)
treed6ec477e5ebd870c5cd091c7543034ab9c0ac883 /target-ppc
parent7a51ad822f533472cab908d2622578d51eb97dc6 (diff)
PowerPC 601 need specific callbacks for its BATs setup.
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
Diffstat (limited to 'target-ppc')
-rw-r--r--target-ppc/cpu.h5
-rw-r--r--target-ppc/helper.c73
-rw-r--r--target-ppc/helper_regs.h19
-rw-r--r--target-ppc/op.c19
-rw-r--r--target-ppc/op_helper.c21
-rw-r--r--target-ppc/op_helper.h2
-rw-r--r--target-ppc/translate.c21
-rw-r--r--target-ppc/translate_init.c11
8 files changed, 136 insertions, 35 deletions
diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
index 4beeab25f1..939dbcd982 100644
--- a/target-ppc/cpu.h
+++ b/target-ppc/cpu.h
@@ -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)
diff --git a/target-ppc/helper.c b/target-ppc/helper.c
index 9d6f490b53..9fd9721cde 100644
--- a/target-ppc/helper.c
+++ b/target-ppc/helper.c
@@ -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 */
diff --git a/target-ppc/helper_regs.h b/target-ppc/helper_regs.h
index 2a5de2ed15..c52ae9ebc6 100644
--- a/target-ppc/helper_regs.h
+++ b/target-ppc/helper_regs.h
@@ -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)
diff --git a/target-ppc/op.c b/target-ppc/op.c
index da08ec5264..730dc0e90b 100644
--- a/target-ppc/op.c
+++ b/target-ppc/op.c
@@ -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) */
diff --git a/target-ppc/op_helper.c b/target-ppc/op_helper.c
index f5d26aeaa3..6ed9c95331 100644
--- a/target-ppc/op_helper.c
+++ b/target-ppc/op_helper.c
@@ -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
diff --git a/target-ppc/op_helper.h b/target-ppc/op_helper.h
index 915b32a28e..6575d3df6c 100644
--- a/target-ppc/op_helper.h
+++ b/target-ppc/op_helper.h
@@ -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 */
diff --git a/target-ppc/translate.c b/target-ppc/translate.c
index cd7a483465..1adff9fdf5 100644
--- a/target-ppc/translate.c
+++ b/target-ppc/translate.c
@@ -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");
diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c
index 0d648de0c1..eae228b26a 100644
--- a/target-ppc/translate_init.c
+++ b/target-ppc/translate_init.c
@@ -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",