aboutsummaryrefslogtreecommitdiff
path: root/target/nios2/translate.c
diff options
context:
space:
mode:
authorRichard Henderson <richard.henderson@linaro.org>2022-04-21 08:17:08 -0700
committerRichard Henderson <richard.henderson@linaro.org>2022-04-26 08:16:41 -0700
commit796945d596058516f3c66f509e1a4804e4ae198a (patch)
tree903974d1f27a1e14b5334497941b997c25d0be57 /target/nios2/translate.c
parentbe77e1d5fe1abaaf70d886719a4084ea9c0c5eec (diff)
target/nios2: Prevent writes to read-only or reserved control fields
Create an array of masks which detail the writable and readonly bits for each control register. Apply them when writing to control registers, including the write to status during eret. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-Id: <20220421151735.31996-38-richard.henderson@linaro.org>
Diffstat (limited to 'target/nios2/translate.c')
-rw-r--r--target/nios2/translate.c78
1 files changed, 63 insertions, 15 deletions
diff --git a/target/nios2/translate.c b/target/nios2/translate.c
index 97e531529f..b8d75207a4 100644
--- a/target/nios2/translate.c
+++ b/target/nios2/translate.c
@@ -102,6 +102,7 @@ typedef struct DisasContext {
TCGv_i32 zero;
target_ulong pc;
int mem_idx;
+ const ControlRegState *cr_state;
} DisasContext;
static TCGv cpu_R[NUM_GP_REGS];
@@ -471,14 +472,23 @@ static void callr(DisasContext *dc, uint32_t code, uint32_t flags)
/* rC <- ctlN */
static void rdctl(DisasContext *dc, uint32_t code, uint32_t flags)
{
+ if (!gen_check_supervisor(dc)) {
+ return;
+ }
+
+#ifdef CONFIG_USER_ONLY
+ g_assert_not_reached();
+#else
R_TYPE(instr, code);
TCGv t1, t2;
- if (!gen_check_supervisor(dc)) {
+ if (unlikely(instr.c == R_ZERO)) {
return;
}
- if (unlikely(instr.c == R_ZERO)) {
+ /* Reserved registers read as zero. */
+ if (nios2_cr_reserved(&dc->cr_state[instr.imm5])) {
+ tcg_gen_movi_tl(cpu_R[instr.c], 0);
return;
}
@@ -505,6 +515,7 @@ static void rdctl(DisasContext *dc, uint32_t code, uint32_t flags)
offsetof(CPUNios2State, ctrl[instr.imm5]));
break;
}
+#endif
}
/* ctlN <- rA */
@@ -519,6 +530,14 @@ static void wrctl(DisasContext *dc, uint32_t code, uint32_t flags)
#else
R_TYPE(instr, code);
TCGv v = load_gpr(dc, instr.a);
+ uint32_t ofs = offsetof(CPUNios2State, ctrl[instr.imm5]);
+ uint32_t wr = dc->cr_state[instr.imm5].writable;
+ uint32_t ro = dc->cr_state[instr.imm5].readonly;
+
+ /* Skip reserved or readonly registers. */
+ if (wr == 0) {
+ return;
+ }
switch (instr.imm5) {
case CR_PTEADDR:
@@ -530,17 +549,35 @@ static void wrctl(DisasContext *dc, uint32_t code, uint32_t flags)
case CR_TLBMISC:
gen_helper_mmu_write_tlbmisc(cpu_env, v);
break;
- case CR_IPENDING:
- /* ipending is read only, writes ignored. */
- break;
case CR_STATUS:
case CR_IENABLE:
/* If interrupts were enabled using WRCTL, trigger them. */
dc->base.is_jmp = DISAS_UPDATE;
/* fall through */
default:
- tcg_gen_st_tl(v, cpu_env,
- offsetof(CPUNios2State, ctrl[instr.imm5]));
+ if (wr == -1) {
+ /* The register is entirely writable. */
+ tcg_gen_st_tl(v, cpu_env, ofs);
+ } else {
+ /*
+ * The register is partially read-only or reserved:
+ * merge the value.
+ */
+ TCGv n = tcg_temp_new();
+
+ tcg_gen_andi_tl(n, v, wr);
+
+ if (ro != 0) {
+ TCGv o = tcg_temp_new();
+ tcg_gen_ld_tl(o, cpu_env, ofs);
+ tcg_gen_andi_tl(o, o, ro);
+ tcg_gen_or_tl(n, n, o);
+ tcg_temp_free(o);
+ }
+
+ tcg_gen_st_tl(n, cpu_env, ofs);
+ tcg_temp_free(n);
+ }
break;
}
#endif
@@ -818,9 +855,11 @@ static void nios2_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
{
DisasContext *dc = container_of(dcbase, DisasContext, base);
CPUNios2State *env = cs->env_ptr;
+ Nios2CPU *cpu = env_archcpu(env);
int page_insns;
dc->mem_idx = cpu_mmu_index(env, false);
+ dc->cr_state = cpu->cr_state;
/* Bound the number of insns to execute to those left on the page. */
page_insns = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4;
@@ -932,16 +971,25 @@ void nios2_cpu_dump_state(CPUState *cs, FILE *f, int flags)
}
#if !defined(CONFIG_USER_ONLY)
- for (i = 0; i < NUM_CR_REGS; i++) {
- qemu_fprintf(f, "%9s=%8.8x ", cr_regnames[i], env->ctrl[i]);
- if ((i + 1) % 4 == 0) {
- qemu_fprintf(f, "\n");
+ int j;
+
+ for (i = j = 0; i < NUM_CR_REGS; i++) {
+ if (!nios2_cr_reserved(&cpu->cr_state[i])) {
+ qemu_fprintf(f, "%9s=%8.8x ", cr_regnames[i], env->ctrl[i]);
+ if (++j % 4 == 0) {
+ qemu_fprintf(f, "\n");
+ }
}
}
- qemu_fprintf(f, " mmu write: VPN=%05X PID %02X TLBACC %08X\n",
- env->mmu.pteaddr_wr & R_CR_PTEADDR_VPN_MASK,
- FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID),
- env->mmu.tlbacc_wr);
+ if (j % 4 != 0) {
+ qemu_fprintf(f, "\n");
+ }
+ if (cpu->mmu_present) {
+ qemu_fprintf(f, " mmu write: VPN=%05X PID %02X TLBACC %08X\n",
+ env->mmu.pteaddr_wr & R_CR_PTEADDR_VPN_MASK,
+ FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID),
+ env->mmu.tlbacc_wr);
+ }
#endif
qemu_fprintf(f, "\n\n");
}