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>
diff --git a/linux-user/mips/target_cpu.h b/linux-user/mips/target_cpu.h
index ba8e9eb..19b8855 100644
--- a/linux-user/mips/target_cpu.h
+++ b/linux-user/mips/target_cpu.h
@@ -30,7 +30,7 @@
static inline void cpu_set_tls(CPUMIPSState *env, target_ulong newtls)
{
- env->tls_value = newtls;
+ env->active_tc.CP0_UserLocal = newtls;
}
#endif
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index c134c32..7d74079 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -8702,7 +8702,7 @@
#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)
diff --git a/target-mips/cpu.h b/target-mips/cpu.h
index a9b2c7a..8b9a92e 100644
--- a/target-mips/cpu.h
+++ b/target-mips/cpu.h
@@ -168,6 +168,7 @@
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 @@
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 @@
/* 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 @@
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 @@
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 @@
{
*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)
diff --git a/target-mips/machine.c b/target-mips/machine.c
index 0a07db8..0496faa 100644
--- a/target-mips/machine.c
+++ b/target-mips/machine.c
@@ -25,6 +25,7 @@
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 @@
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 @@
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 @@
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 @@
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]);
diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c
index 4704216..27651a4 100644
--- a/target-mips/op_helper.c
+++ b/target-mips/op_helper.c
@@ -1297,7 +1297,19 @@
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)
diff --git a/target-mips/translate.c b/target-mips/translate.c
index 76deb7b..2b3befb 100644
--- a/target-mips/translate.c
+++ b/target-mips/translate.c
@@ -1072,6 +1072,7 @@
uint32_t hflags, saved_hflags;
int bstate;
target_ulong btarget;
+ bool ulri;
} DisasContext;
enum {
@@ -4215,7 +4216,18 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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;