mirror of
https://gitlab.com/qemu-project/qemu
synced 2024-09-06 18:05:37 +00:00
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
This commit is contained in:
parent
7a51ad822f
commit
056401eae6
|
@ -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)
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) */
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in a new issue