target-mips: implement UserLocal Register

From MIPS documentation (Volume III):

UserLocal Register (CP0 Register 4, Select 2)
Compliance Level: Recommended.

The UserLocal register is a read-write register that is not interpreted by
the hardware and conditionally readable via the RDHWR instruction.

This register only exists if the Config3-ULRI register field is set.

Privileged software may write this register with arbitrary information and
make it accessible to unprivileged software via register 29 (ULR) of the
RDHWR instruction. To do so, bit 29 of the HWREna register must be set to a
1 to enable unprivileged access to the register.

Signed-off-by: Petar Jovanovic <petar.jovanovic@imgtec.com>
Reviewed-by: Andreas Färber <afaerber@suse.de>
Reviewed-by: Aurelien Jarno <aurelien@aurel32.net>
Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
This commit is contained in:
Petar Jovanovic 2014-06-18 17:48:20 +02:00 committed by Aurelien Jarno
parent 739b7a9075
commit d279279e2b
6 changed files with 85 additions and 15 deletions

View file

@ -30,7 +30,7 @@ static inline void cpu_clone_regs(CPUMIPSState *env, target_ulong newsp)
static inline void cpu_set_tls(CPUMIPSState *env, target_ulong newtls)
{
env->tls_value = newtls;
env->active_tc.CP0_UserLocal = newtls;
}
#endif

View file

@ -8702,7 +8702,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
#ifdef TARGET_NR_set_thread_area
case TARGET_NR_set_thread_area:
#if defined(TARGET_MIPS)
((CPUMIPSState *) cpu_env)->tls_value = arg1;
((CPUMIPSState *) cpu_env)->active_tc.CP0_UserLocal = arg1;
ret = 0;
break;
#elif defined(TARGET_CRIS)

View file

@ -168,6 +168,7 @@ struct TCState {
target_ulong CP0_TCSchedule;
target_ulong CP0_TCScheFBack;
int32_t CP0_Debug_tcstatus;
target_ulong CP0_UserLocal;
};
typedef struct CPUMIPSState CPUMIPSState;
@ -362,6 +363,7 @@ struct CPUMIPSState {
int32_t CP0_Config3;
#define CP0C3_M 31
#define CP0C3_ISA_ON_EXC 16
#define CP0C3_ULRI 13
#define CP0C3_DSPP 10
#define CP0C3_LPA 7
#define CP0C3_VEIC 6
@ -470,6 +472,8 @@ struct CPUMIPSState {
/* MIPS DSP resources access. */
#define MIPS_HFLAG_DSP 0x40000 /* Enable access to MIPS DSP resources. */
#define MIPS_HFLAG_DSPR2 0x80000 /* Enable access to MIPS DSPR2 resources. */
/* Extra flag about HWREna register. */
#define MIPS_HFLAG_HWRENA_ULR 0x100000 /* ULR bit from HWREna is set. */
target_ulong btarget; /* Jump / branch target */
target_ulong bcond; /* Branch condition (if needed) */
@ -479,8 +483,6 @@ struct CPUMIPSState {
uint32_t CP0_TCStatus_rw_bitmask; /* Read/write bits in CP0_TCStatus */
int insn_flags; /* Supported instruction set */
target_ulong tls_value; /* For usermode emulation */
CPU_COMMON
/* Fields from here on are preserved across CPU reset. */
@ -523,7 +525,7 @@ void mips_cpu_list (FILE *f, fprintf_function cpu_fprintf);
extern void cpu_wrdsp(uint32_t rs, uint32_t mask_num, CPUMIPSState *env);
extern uint32_t cpu_rddsp(uint32_t mask_num, CPUMIPSState *env);
#define CPU_SAVE_VERSION 3
#define CPU_SAVE_VERSION 4
/* MMU modes definitions. We carefully match the indices with our
hflags layout. */
@ -682,7 +684,8 @@ static inline void cpu_get_tb_cpu_state(CPUMIPSState *env, target_ulong *pc,
{
*pc = env->active_tc.PC;
*cs_base = 0;
*flags = env->hflags & (MIPS_HFLAG_TMASK | MIPS_HFLAG_BMASK);
*flags = env->hflags & (MIPS_HFLAG_TMASK | MIPS_HFLAG_BMASK |
MIPS_HFLAG_HWRENA_ULR);
}
static inline int mips_vpe_active(CPUMIPSState *env)

View file

@ -25,6 +25,7 @@ static void save_tc(QEMUFile *f, TCState *tc)
qemu_put_betls(f, &tc->CP0_TCSchedule);
qemu_put_betls(f, &tc->CP0_TCScheFBack);
qemu_put_sbe32s(f, &tc->CP0_Debug_tcstatus);
qemu_put_betls(f, &tc->CP0_UserLocal);
}
static void save_fpu(QEMUFile *f, CPUMIPSFPUContext *fpu)
@ -151,7 +152,7 @@ void cpu_save(QEMUFile *f, void *opaque)
save_fpu(f, &env->fpus[i]);
}
static void load_tc(QEMUFile *f, TCState *tc)
static void load_tc(QEMUFile *f, TCState *tc, int version_id)
{
int i;
@ -173,6 +174,9 @@ static void load_tc(QEMUFile *f, TCState *tc)
qemu_get_betls(f, &tc->CP0_TCSchedule);
qemu_get_betls(f, &tc->CP0_TCScheFBack);
qemu_get_sbe32s(f, &tc->CP0_Debug_tcstatus);
if (version_id >= 4) {
qemu_get_betls(f, &tc->CP0_UserLocal);
}
}
static void load_fpu(QEMUFile *f, CPUMIPSFPUContext *fpu)
@ -194,11 +198,12 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
MIPSCPU *cpu = mips_env_get_cpu(env);
int i;
if (version_id != 3)
if (version_id < 3) {
return -EINVAL;
}
/* Load active TC */
load_tc(f, &env->active_tc);
load_tc(f, &env->active_tc, version_id);
/* Load active FPU */
load_fpu(f, &env->active_fpu);
@ -298,8 +303,9 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
qemu_get_sbe32s(f, &env->CP0_DESAVE);
/* Load inactive TC state */
for (i = 0; i < MIPS_SHADOW_SET_MAX; i++)
load_tc(f, &env->tcs[i]);
for (i = 0; i < MIPS_SHADOW_SET_MAX; i++) {
load_tc(f, &env->tcs[i], version_id);
}
for (i = 0; i < MIPS_FPU_MAX; i++)
load_fpu(f, &env->fpus[i]);

View file

@ -1297,7 +1297,19 @@ void helper_mtc0_srsconf4(CPUMIPSState *env, target_ulong arg1)
void helper_mtc0_hwrena(CPUMIPSState *env, target_ulong arg1)
{
env->CP0_HWREna = arg1 & 0x0000000F;
uint32_t mask = 0x0000000F;
if (env->CP0_Config3 & (1 << CP0C3_ULRI)) {
mask |= (1 << 29);
if (arg1 & (1 << 29)) {
env->hflags |= MIPS_HFLAG_HWRENA_ULR;
} else {
env->hflags &= ~MIPS_HFLAG_HWRENA_ULR;
}
}
env->CP0_HWREna = arg1 & mask;
}
void helper_mtc0_count(CPUMIPSState *env, target_ulong arg1)

View file

@ -1072,6 +1072,7 @@ typedef struct DisasContext {
uint32_t hflags, saved_hflags;
int bstate;
target_ulong btarget;
bool ulri;
} DisasContext;
enum {
@ -4215,7 +4216,18 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
case 1:
// gen_helper_mfc0_contextconfig(arg); /* SmartMIPS ASE */
rn = "ContextConfig";
goto die;
// break;
case 2:
if (ctx->ulri) {
tcg_gen_ld32s_tl(arg, cpu_env,
offsetof(CPUMIPSState,
active_tc.CP0_UserLocal));
rn = "UserLocal";
} else {
tcg_gen_movi_tl(arg, 0);
}
break;
default:
goto die;
}
@ -4802,7 +4814,15 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
case 1:
// gen_helper_mtc0_contextconfig(cpu_env, arg); /* SmartMIPS ASE */
rn = "ContextConfig";
goto die;
// break;
case 2:
if (ctx->ulri) {
tcg_gen_st_tl(arg, cpu_env,
offsetof(CPUMIPSState, active_tc.CP0_UserLocal));
rn = "UserLocal";
}
break;
default:
goto die;
}
@ -4862,6 +4882,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
case 0:
check_insn(ctx, ISA_MIPS32R2);
gen_helper_mtc0_hwrena(cpu_env, arg);
ctx->bstate = BS_STOP;
rn = "HWREna";
break;
default:
@ -5406,7 +5427,17 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
case 1:
// gen_helper_dmfc0_contextconfig(arg); /* SmartMIPS ASE */
rn = "ContextConfig";
goto die;
// break;
case 2:
if (ctx->ulri) {
tcg_gen_ld_tl(arg, cpu_env,
offsetof(CPUMIPSState, active_tc.CP0_UserLocal));
rn = "UserLocal";
} else {
tcg_gen_movi_tl(arg, 0);
}
break;
default:
goto die;
}
@ -5978,7 +6009,15 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
case 1:
// gen_helper_mtc0_contextconfig(cpu_env, arg); /* SmartMIPS ASE */
rn = "ContextConfig";
goto die;
// break;
case 2:
if (ctx->ulri) {
tcg_gen_st_tl(arg, cpu_env,
offsetof(CPUMIPSState, active_tc.CP0_UserLocal));
rn = "UserLocal";
}
break;
default:
goto die;
}
@ -6038,6 +6077,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
case 0:
check_insn(ctx, ISA_MIPS32R2);
gen_helper_mtc0_hwrena(cpu_env, arg);
ctx->bstate = BS_STOP;
rn = "HWREna";
break;
default:
@ -9060,12 +9100,20 @@ static void gen_rdhwr(DisasContext *ctx, int rt, int rd)
break;
case 29:
#if defined(CONFIG_USER_ONLY)
tcg_gen_ld_tl(t0, cpu_env, offsetof(CPUMIPSState, tls_value));
tcg_gen_ld_tl(t0, cpu_env,
offsetof(CPUMIPSState, active_tc.CP0_UserLocal));
gen_store_gpr(t0, rt);
break;
#else
/* XXX: Some CPUs implement this in hardware.
Not supported yet. */
if ((ctx->hflags & MIPS_HFLAG_CP0) ||
(ctx->hflags & MIPS_HFLAG_HWRENA_ULR)) {
tcg_gen_ld_tl(t0, cpu_env,
offsetof(CPUMIPSState, active_tc.CP0_UserLocal));
gen_store_gpr(t0, rt);
} else {
generate_exception(ctx, EXCP_RI);
}
break;
#endif
default: /* Invalid */
MIPS_INVAL("rdhwr");
@ -15609,6 +15657,7 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb,
ctx.bstate = BS_NONE;
/* Restore delay slot state from the tb context. */
ctx.hflags = (uint32_t)tb->flags; /* FIXME: maybe use 64 bits here? */
ctx.ulri = env->CP0_Config3 & (1 << CP0C3_ULRI);
restore_cpu_state(env, &ctx);
#ifdef CONFIG_USER_ONLY
ctx.mem_idx = MIPS_HFLAG_UM;