@@ -61,6 +61,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,
@@ -176,7 +181,13 @@ FIELD(CR_TLBMISC, EE, 24, 1)
#define EXCP_MPUD 17
struct CPUNios2State {
+#ifdef CONFIG_USER_ONLY
uint32_t regs[NUM_GP_REGS];
+#else
+ uint32_t shadow_regs[NUM_REG_SETS][NUM_GP_REGS];
+ uint32_t *crs;
+#endif
+
union {
uint32_t ctrl[NUM_CR_REGS];
struct {
@@ -245,6 +256,23 @@ 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->status, CR_STATUS, CRS);
+ env->crs = env->shadow_regs[crs];
+#endif
+}
+
+static inline uint32_t *nios2_crs(CPUNios2State *env)
+{
+#ifdef CONFIG_USER_ONLY
+ return env->regs;
+#else
+ return env->crs;
+#endif
+}
+
void nios2_tcg_init(void);
void nios2_cpu_do_interrupt(CPUState *cs);
void dump_mmu(CPUNios2State *env);
@@ -286,12 +314,16 @@ typedef Nios2CPU ArchCPU;
#include "exec/cpu-all.h"
+FIELD(TBFLAGS, CRS0, 0, 1)
+FIELD(TBFLAGS, U, 1, 1) /* Overlaps CR_STATUS_U */
+
static inline void cpu_get_tb_cpu_state(CPUNios2State *env, target_ulong *pc,
target_ulong *cs_base, uint32_t *flags)
{
*pc = env->pc;
*cs_base = 0;
*flags = env->status & CR_STATUS_U;
+ *flags |= env->status & R_CR_STATUS_CRS_MASK ? 0 : R_TBFLAGS_CRS0_MASK;
}
#endif /* NIOS2_CPU_H */
@@ -62,10 +62,10 @@ static void main_cpu_reset(void *opaque)
cpu_reset(CPU(cpu));
- env->regs[R_ARG0] = NIOS2_MAGIC;
- env->regs[R_ARG1] = boot_info.initrd_start;
- env->regs[R_ARG2] = boot_info.fdt;
- env->regs[R_ARG3] = boot_info.cmdline;
+ nios2_crs(env)[R_ARG0] = NIOS2_MAGIC;
+ nios2_crs(env)[R_ARG1] = boot_info.initrd_start;
+ nios2_crs(env)[R_ARG2] = boot_info.fdt;
+ nios2_crs(env)[R_ARG3] = boot_info.cmdline;
cpu_set_pc(cs, boot_info.bootstrap_pc);
if (boot_info.machine_cpu_reset) {
@@ -53,7 +53,6 @@ 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;
@@ -63,6 +62,8 @@ static void nios2_cpu_reset(DeviceState *dev)
#else
env->status = CR_STATUS_RSIE;
#endif
+
+ nios2_update_crs(env);
}
#ifndef CONFIG_USER_ONLY
@@ -210,7 +211,7 @@ static int nios2_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n)
uint32_t val;
if (n < 32) { /* GP regs */
- val = env->regs[n];
+ val = nios2_crs(env)[n];
} else if (n == 32) { /* PC */
val = env->pc;
} else if (n < 49) { /* Status regs */
@@ -241,7 +242,7 @@ static int nios2_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
val = ldl_p(mem_buf);
if (n < 32) { /* GP regs */
- env->regs[n] = val;
+ nios2_crs(env)[n] = val;
} else if (n == 32) { /* PC */
env->pc = val;
} else if (n < 49) { /* Status regs */
@@ -64,7 +64,7 @@ void nios2_cpu_do_interrupt(CPUState *cs)
env->status |= CR_STATUS_IH;
env->status &= ~(CR_STATUS_PIE | CR_STATUS_U);
- env->regs[R_EA] = env->pc + 4;
+ nios2_crs(env)[R_EA] = env->pc + 4;
env->pc = cpu->exception_addr;
break;
@@ -83,7 +83,7 @@ void nios2_cpu_do_interrupt(CPUState *cs)
env->tlbmisc &= ~CR_TLBMISC_DBL;
env->tlbmisc |= CR_TLBMISC_WR;
- env->regs[R_EA] = env->pc + 4;
+ nios2_crs(env)[R_EA] = env->pc + 4;
env->pc = cpu->fast_tlb_miss_addr;
} else {
qemu_log_mask(CPU_LOG_INT, "TLB MISS (double) at pc=%x\n", env->pc);
@@ -111,7 +111,7 @@ void nios2_cpu_do_interrupt(CPUState *cs)
env->tlbmisc |= CR_TLBMISC_WR;
}
- env->regs[R_EA] = env->pc + 4;
+ nios2_crs(env)[R_EA] = env->pc + 4;
env->pc = cpu->exception_addr;
break;
@@ -122,7 +122,7 @@ void nios2_cpu_do_interrupt(CPUState *cs)
if ((env->status & CR_STATUS_EH) == 0) {
env->estatus = env->status;
- env->regs[R_EA] = env->pc + 4;
+ nios2_crs(env)[R_EA] = env->pc + 4;
}
env->status |= CR_STATUS_EH;
@@ -137,7 +137,7 @@ void nios2_cpu_do_interrupt(CPUState *cs)
if ((env->status & CR_STATUS_EH) == 0) {
env->estatus = env->status;
- env->regs[R_EA] = env->pc + 4;
+ nios2_crs(env)[R_EA] = env->pc + 4;
}
env->status |= CR_STATUS_EH;
@@ -159,7 +159,7 @@ void nios2_cpu_do_interrupt(CPUState *cs)
if ((env->status & CR_STATUS_EH) == 0) {
env->bstatus = env->status;
- env->regs[R_BA] = env->pc + 4;
+ nios2_crs(env)[R_BA] = env->pc + 4;
}
env->status |= CR_STATUS_EH;
@@ -144,7 +144,7 @@ static bool translate_stat(CPUNios2State *env, target_ulong addr,
static void nios2_semi_return_u32(CPUNios2State *env, uint32_t ret,
uint32_t err)
{
- target_ulong args = env->regs[R_ARG1];
+ target_ulong args = nios2_crs(env)[R_ARG1];
if (put_user_u32(ret, args) ||
put_user_u32(err, args + 4)) {
/*
@@ -160,7 +160,7 @@ static void nios2_semi_return_u32(CPUNios2State *env, uint32_t ret,
static void nios2_semi_return_u64(CPUNios2State *env, uint64_t ret,
uint32_t err)
{
- target_ulong args = env->regs[R_ARG1];
+ target_ulong args = nios2_crs(env)[R_ARG1];
if (put_user_u32(ret >> 32, args) ||
put_user_u32(ret, args + 4) ||
put_user_u32(err, args + 8)) {
@@ -210,13 +210,14 @@ void do_nios2_semihosting(CPUNios2State *env)
void *q;
uint32_t len;
uint32_t result;
+ uint32_t *crs = nios2_crs(env);
- nr = env->regs[R_ARG0];
- args = env->regs[R_ARG1];
+ nr = crs[R_ARG0];
+ args = crs[R_ARG1];
switch (nr) {
case HOSTED_EXIT:
- gdb_exit(env->regs[R_ARG0]);
- exit(env->regs[R_ARG0]);
+ gdb_exit(crs[R_ARG0]);
+ exit(crs[R_ARG0]);
case HOSTED_OPEN:
GET_ARG(0);
GET_ARG(1);
@@ -100,12 +100,16 @@ typedef struct DisasContext {
DisasContextBase base;
target_ulong pc;
int mem_idx;
+ bool crs0;
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);
@@ -127,22 +131,36 @@ 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)) {
- return tcg_constant_tl(0);
+ if (dc->crs0) {
+ if (unlikely(reg == R_ZERO)) {
+ return tcg_constant_tl(0);
+ }
+ return cpu_R[reg];
}
- 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);
- if (unlikely(reg == R_ZERO)) {
- if (dc->sink == NULL) {
- dc->sink = tcg_temp_new();
+ if (dc->crs0) {
+ if (unlikely(reg == R_ZERO)) {
+ if (dc->sink == NULL) {
+ dc->sink = tcg_temp_new();
+ }
+ return dc->sink;
}
- return dc->sink;
+ return cpu_R[reg];
}
- 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,
@@ -174,7 +192,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 (dc->base.tb->flags & R_TBFLAGS_U_MASK) {
/* CPU in user mode, privileged instruction called, stop. */
t_gen_helper_raise_exception(dc, EXCP_SUPERI);
return false;
@@ -773,6 +791,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->crs0 = FIELD_EX32(dc->base.tb->flags, TBFLAGS, CRS0);
/* Bound the number of insns to execute to those left on the page. */
page_insns = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4;
@@ -871,16 +890,13 @@ void nios2_cpu_dump_state(CPUState *cs, FILE *f, int flags)
{
Nios2CPU *cpu = NIOS2_CPU(cs);
CPUNios2State *env = &cpu->env;
+ const uint32_t *crs = nios2_crs(env);
int i;
- if (!env) {
- return;
- }
-
qemu_fprintf(f, "IN: PC=%x %s\n", env->pc, lookup_symbol(env->pc));
for (i = 0; i < NUM_GP_REGS; i++) {
- qemu_fprintf(f, "%9s=%8.8x ", gr_regnames[i], env->regs[i]);
+ qemu_fprintf(f, "%9s=%8.8x ", gr_regnames[i], crs[i]);
if ((i + 1) % 4 == 0) {
qemu_fprintf(f, "\n");
}
@@ -912,13 +928,19 @@ void nios2_cpu_dump_state(CPUState *cs, FILE *f, int flags)
void nios2_tcg_init(void)
{
- int i;
-
- for (i = 0; i < NUM_GP_REGS; i++) {
- cpu_R[i] = tcg_global_mem_new(cpu_env,
- offsetof(CPUNios2State, regs[i]),
- gr_regnames[i]);
+ for (int i = 0; i < NUM_GP_REGS; i++) {
+ cpu_R[i] = tcg_global_mem_new(cpu_env, 4 * i, gr_regnames[i]);
}
+
+#ifndef CONFIG_USER_ONLY
+ TCGv_ptr crs = tcg_global_mem_new_ptr(cpu_env,
+ offsetof(CPUNios2State, crs), "crs");
+
+ for (int i = 0; i < NUM_GP_REGS; i++) {
+ cpu_crs_R[i] = tcg_global_mem_new(crs, 4 * i, gr_regnames[i]);
+ }
+#endif
+
cpu_pc = tcg_global_mem_new(cpu_env,
offsetof(CPUNios2State, pc), "pc");
}
Do not actually enable them so far, but add all of the plumbing to address them. Do not enable them for user-only. Add an env->crs pointer that handles the indirection to the current register set. Add a nios2_crs() function to wrap this for normal uses, which hides the difference between user-only and system modes. From the notes on wrprs, which states that r0 must be initialized before use in shadow register sets, infer that R_ZERO is *not* hardwired to zero in shadow register sets. Adjust load_gpr and dest_gpr to reflect this. At the same time we might as well special case crs == 0 to avoid the indirection through env->crs during translation as well. Given that this is intended to be the most common case for non-interrupt handlers. Drop the zeroing of env->regs at reset, as those are undefined. Do init env->crs at reset. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- target/nios2/cpu.h | 32 ++++++++++++++++++++ hw/nios2/boot.c | 8 ++--- target/nios2/cpu.c | 7 +++-- target/nios2/helper.c | 12 ++++---- target/nios2/nios2-semi.c | 13 ++++---- target/nios2/translate.c | 62 ++++++++++++++++++++++++++------------- 6 files changed, 95 insertions(+), 39 deletions(-)