@@ -60,6 +60,11 @@ struct Nios2CPUClass {
#define NUM_GP_REGS 32
#define NUM_CR_REGS 32
+#ifndef CONFIG_USER_ONLY
+/* 63 shadow register sets; index 0 is the primary register set. */
+#define NUM_REG_SETS 64
+#endif
+
/* General purpose register aliases */
enum {
R_ZERO = 0,
@@ -178,7 +183,13 @@ FIELD(CR_TLBMISC, EE, 24, 1)
#define EXCP_MPUD 17
struct CPUArchState {
+#ifdef CONFIG_USER_ONLY
uint32_t regs[NUM_GP_REGS];
+#else
+ uint32_t shadow_regs[NUM_REG_SETS][NUM_GP_REGS];
+ /* Pointer into shadow_regs for the current register set. */
+ uint32_t *regs;
+#endif
uint32_t ctrl[NUM_CR_REGS];
uint32_t pc;
@@ -229,6 +240,14 @@ static inline bool nios2_cr_reserved(const ControlRegState *s)
return (s->writable | s->readonly) == 0;
}
+static inline void nios2_update_crs(CPUNios2State *env)
+{
+#ifndef CONFIG_USER_ONLY
+ unsigned crs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, CRS);
+ env->regs = env->shadow_regs[crs];
+#endif
+}
+
void nios2_tcg_init(void);
void nios2_cpu_do_interrupt(CPUState *cs);
void dump_mmu(CPUNios2State *env);
@@ -267,12 +286,20 @@ typedef Nios2CPU ArchCPU;
#include "exec/cpu-all.h"
+FIELD(TBFLAGS, CRS0, 0, 1) /* Set if CRS == 0. */
+FIELD(TBFLAGS, U, 1, 1) /* Overlaps CR_STATUS_U */
+FIELD(TBFLAGS, R0_0, 2, 1) /* Set if R0 == 0. */
+
static inline void cpu_get_tb_cpu_state(CPUNios2State *env, target_ulong *pc,
target_ulong *cs_base, uint32_t *flags)
{
+ unsigned crs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, CRS);
+
*pc = env->pc;
*cs_base = 0;
- *flags = env->ctrl[CR_STATUS] & CR_STATUS_U;
+ *flags = (env->ctrl[CR_STATUS] & CR_STATUS_U)
+ | (crs ? 0 : R_TBFLAGS_CRS0_MASK)
+ | (env->regs[0] ? 0 : R_TBFLAGS_R0_0_MASK);
}
#endif /* NIOS2_CPU_H */
@@ -48,15 +48,17 @@ static void nios2_cpu_reset(DeviceState *dev)
ncc->parent_reset(dev);
- memset(env->regs, 0, sizeof(env->regs));
memset(env->ctrl, 0, sizeof(env->ctrl));
env->pc = cpu->reset_addr;
#if defined(CONFIG_USER_ONLY)
/* Start in user mode with interrupts enabled. */
env->ctrl[CR_STATUS] = CR_STATUS_RSIE | CR_STATUS_U | CR_STATUS_PIE;
+ memset(env->regs, 0, sizeof(env->regs));
#else
env->ctrl[CR_STATUS] = CR_STATUS_RSIE;
+ nios2_update_crs(env);
+ memset(env->shadow_regs, 0, sizeof(env->shadow_regs));
#endif
}
@@ -127,12 +127,16 @@ typedef struct DisasContext {
DisasContextBase base;
target_ulong pc;
int mem_idx;
+ uint32_t tb_flags;
TCGv sink;
const ControlRegState *cr_state;
} DisasContext;
static TCGv cpu_R[NUM_GP_REGS];
static TCGv cpu_pc;
+#ifndef CONFIG_USER_ONLY
+static TCGv cpu_crs_R[NUM_GP_REGS];
+#endif
typedef struct Nios2Instruction {
void (*handler)(DisasContext *dc, uint32_t code, uint32_t flags);
@@ -154,22 +158,47 @@ static uint8_t get_opxcode(uint32_t code)
static TCGv load_gpr(DisasContext *dc, unsigned reg)
{
assert(reg < NUM_GP_REGS);
- if (unlikely(reg == R_ZERO)) {
+
+ /*
+ * With shadow register sets, register r0 does not necessarily contain 0,
+ * but it is overwhelmingly likely that it does -- software is supposed
+ * to have set r0 to 0 in every shadow register set before use.
+ */
+ if (unlikely(reg == R_ZERO) && FIELD_EX32(dc->tb_flags, TBFLAGS, R0_0)) {
return tcg_constant_tl(0);
}
- return cpu_R[reg];
+ if (FIELD_EX32(dc->tb_flags, TBFLAGS, CRS0)) {
+ return cpu_R[reg];
+ }
+#ifdef CONFIG_USER_ONLY
+ g_assert_not_reached();
+#else
+ return cpu_crs_R[reg];
+#endif
}
static TCGv dest_gpr(DisasContext *dc, unsigned reg)
{
assert(reg < NUM_GP_REGS);
+
+ /*
+ * The spec for shadow register sets isn't clear, but we assume that
+ * writes to r0 are discarded regardless of CRS.
+ */
if (unlikely(reg == R_ZERO)) {
if (dc->sink == NULL) {
dc->sink = tcg_temp_new();
}
return dc->sink;
}
- return cpu_R[reg];
+ if (FIELD_EX32(dc->tb_flags, TBFLAGS, CRS0)) {
+ return cpu_R[reg];
+ }
+#ifdef CONFIG_USER_ONLY
+ g_assert_not_reached();
+#else
+ return cpu_crs_R[reg];
+#endif
}
static void t_gen_helper_raise_exception(DisasContext *dc,
@@ -225,7 +254,7 @@ static void gen_excp(DisasContext *dc, uint32_t code, uint32_t flags)
static bool gen_check_supervisor(DisasContext *dc)
{
- if (dc->base.tb->flags & CR_STATUS_U) {
+ if (FIELD_EX32(dc->tb_flags, TBFLAGS, U)) {
/* CPU in user mode, privileged instruction called, stop. */
t_gen_helper_raise_exception(dc, EXCP_SUPERI);
return false;
@@ -335,7 +364,7 @@ static void do_i_math_logic(DisasContext *dc, uint32_t insn,
val = imm(&instr);
- if (instr.a == R_ZERO) {
+ if (instr.a == R_ZERO && FIELD_EX32(dc->tb_flags, TBFLAGS, R0_0)) {
/* This catches the canonical expansions of movi and movhi. */
tcg_gen_movi_tl(dest_gpr(dc, instr.b), x_op_0_eq_x ? val : 0);
} else {
@@ -865,6 +894,7 @@ static void nios2_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
dc->mem_idx = cpu_mmu_index(env, false);
dc->cr_state = cpu->cr_state;
+ dc->tb_flags = dc->base.tb->flags;
/* Bound the number of insns to execute to those left on the page. */
page_insns = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4;
@@ -999,13 +1029,26 @@ void nios2_cpu_dump_state(CPUState *cs, FILE *f, int flags)
void nios2_tcg_init(void)
{
- int i;
+#ifndef CONFIG_USER_ONLY
+ TCGv_ptr crs = tcg_global_mem_new_ptr(cpu_env,
+ offsetof(CPUNios2State, regs), "crs");
- for (i = 0; i < NUM_GP_REGS; i++) {
- cpu_R[i] = tcg_global_mem_new(cpu_env,
- offsetof(CPUNios2State, regs[i]),
+ for (int i = 0; i < NUM_GP_REGS; i++) {
+ cpu_crs_R[i] = tcg_global_mem_new(crs, 4 * i, gr_regnames[i]);
+ }
+
+#define offsetof_regs0(N) offsetof(CPUNios2State, shadow_regs[0][N])
+#else
+#define offsetof_regs0(N) offsetof(CPUNios2State, regs[N])
+#endif
+
+ for (int i = 0; i < NUM_GP_REGS; i++) {
+ cpu_R[i] = tcg_global_mem_new(cpu_env, offsetof_regs0(i),
gr_regnames[i]);
}
+
+#undef offsetof_regs0
+
cpu_pc = tcg_global_mem_new(cpu_env,
offsetof(CPUNios2State, pc), "pc");
}