diff options
author | j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162> | 2007-11-04 02:55:33 +0000 |
---|---|---|
committer | j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162> | 2007-11-04 02:55:33 +0000 |
commit | 056401eae60953822098ff1dc30860364c9681be (patch) | |
tree | d6ec477e5ebd870c5cd091c7543034ab9c0ac883 | |
parent | 7a51ad822f533472cab908d2622578d51eb97dc6 (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
-rw-r--r-- | target-ppc/cpu.h | 5 | ||||
-rw-r--r-- | target-ppc/helper.c | 73 | ||||
-rw-r--r-- | target-ppc/helper_regs.h | 19 | ||||
-rw-r--r-- | target-ppc/op.c | 19 | ||||
-rw-r--r-- | target-ppc/op_helper.c | 21 | ||||
-rw-r--r-- | target-ppc/op_helper.h | 2 | ||||
-rw-r--r-- | target-ppc/translate.c | 21 | ||||
-rw-r--r-- | target-ppc/translate_init.c | 11 |
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", |