summaryrefslogtreecommitdiffstats
path: root/target
diff options
context:
space:
mode:
Diffstat (limited to 'target')
-rw-r--r--target/nios2/cpu.c209
-rw-r--r--target/nios2/cpu.h252
-rw-r--r--target/nios2/helper.c363
-rw-r--r--target/nios2/helper.h5
-rw-r--r--target/nios2/meson.build7
-rw-r--r--target/nios2/mmu.c78
-rw-r--r--target/nios2/op_helper.c88
-rw-r--r--target/nios2/translate.c755
8 files changed, 1161 insertions, 596 deletions
diff --git a/target/nios2/cpu.c b/target/nios2/cpu.c
index b0877cb39e..19b2409974 100644
--- a/target/nios2/cpu.c
+++ b/target/nios2/cpu.c
@@ -31,12 +31,12 @@ static void nios2_cpu_set_pc(CPUState *cs, vaddr value)
Nios2CPU *cpu = NIOS2_CPU(cs);
CPUNios2State *env = &cpu->env;
- env->regs[R_PC] = value;
+ env->pc = value;
}
static bool nios2_cpu_has_work(CPUState *cs)
{
- return cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI);
+ return cs->interrupt_request & CPU_INTERRUPT_HARD;
}
static void nios2_cpu_reset(DeviceState *dev)
@@ -48,27 +48,42 @@ static void nios2_cpu_reset(DeviceState *dev)
ncc->parent_reset(dev);
- memset(env->regs, 0, sizeof(uint32_t) * NUM_CORE_REGS);
- env->regs[R_PC] = cpu->reset_addr;
+ 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->regs[CR_STATUS] = CR_STATUS_U | CR_STATUS_PIE;
+ env->ctrl[CR_STATUS] = CR_STATUS_RSIE | CR_STATUS_U | CR_STATUS_PIE;
+ memset(env->regs, 0, sizeof(env->regs));
#else
- env->regs[CR_STATUS] = 0;
+ env->ctrl[CR_STATUS] = CR_STATUS_RSIE;
+ nios2_update_crs(env);
+ memset(env->shadow_regs, 0, sizeof(env->shadow_regs));
#endif
}
#ifndef CONFIG_USER_ONLY
-static void nios2_cpu_set_irq(void *opaque, int irq, int level)
+static void eic_set_irq(void *opaque, int irq, int level)
+{
+ Nios2CPU *cpu = opaque;
+ CPUState *cs = CPU(cpu);
+
+ if (level) {
+ cpu_interrupt(cs, CPU_INTERRUPT_HARD);
+ } else {
+ cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
+ }
+}
+
+static void iic_set_irq(void *opaque, int irq, int level)
{
Nios2CPU *cpu = opaque;
CPUNios2State *env = &cpu->env;
CPUState *cs = CPU(cpu);
- env->regs[CR_IPENDING] = deposit32(env->regs[CR_IPENDING], irq, 1, !!level);
+ env->ctrl[CR_IPENDING] = deposit32(env->ctrl[CR_IPENDING], irq, 1, !!level);
- if (env->regs[CR_IPENDING]) {
+ if (env->ctrl[CR_IPENDING]) {
cpu_interrupt(cs, CPU_INTERRUPT_HARD);
} else {
cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
@@ -84,15 +99,6 @@ static void nios2_cpu_initfn(Object *obj)
#if !defined(CONFIG_USER_ONLY)
mmu_init(&cpu->env);
-
- /*
- * These interrupt lines model the IIC (internal interrupt
- * controller). QEMU does not currently support the EIC
- * (external interrupt controller) -- if we did it would be
- * a separate device in hw/intc with a custom interface to
- * the CPU, and boards using it would not wire up these IRQ lines.
- */
- qdev_init_gpio_in_named(DEVICE(cpu), nios2_cpu_set_irq, "IRQ", 32);
#endif
}
@@ -101,37 +107,148 @@ static ObjectClass *nios2_cpu_class_by_name(const char *cpu_model)
return object_class_by_name(TYPE_NIOS2_CPU);
}
+static void realize_cr_status(CPUState *cs)
+{
+ Nios2CPU *cpu = NIOS2_CPU(cs);
+
+ /* Begin with all fields of all registers are reserved. */
+ memset(cpu->cr_state, 0, sizeof(cpu->cr_state));
+
+ /*
+ * The combination of writable and readonly is the set of all
+ * non-reserved fields. We apply writable as a mask to bits,
+ * and merge in existing readonly bits, before storing.
+ */
+#define WR_REG(C) cpu->cr_state[C].writable = -1
+#define RO_REG(C) cpu->cr_state[C].readonly = -1
+#define WR_FIELD(C, F) cpu->cr_state[C].writable |= R_##C##_##F##_MASK
+#define RO_FIELD(C, F) cpu->cr_state[C].readonly |= R_##C##_##F##_MASK
+
+ WR_FIELD(CR_STATUS, PIE);
+ WR_REG(CR_ESTATUS);
+ WR_REG(CR_BSTATUS);
+ RO_REG(CR_CPUID);
+ RO_REG(CR_EXCEPTION);
+ WR_REG(CR_BADADDR);
+
+ if (cpu->eic_present) {
+ WR_FIELD(CR_STATUS, RSIE);
+ RO_FIELD(CR_STATUS, NMI);
+ WR_FIELD(CR_STATUS, PRS);
+ RO_FIELD(CR_STATUS, CRS);
+ WR_FIELD(CR_STATUS, IL);
+ WR_FIELD(CR_STATUS, IH);
+ } else {
+ RO_FIELD(CR_STATUS, RSIE);
+ WR_REG(CR_IENABLE);
+ RO_REG(CR_IPENDING);
+ }
+
+ if (cpu->mmu_present) {
+ WR_FIELD(CR_STATUS, U);
+ WR_FIELD(CR_STATUS, EH);
+
+ WR_FIELD(CR_PTEADDR, VPN);
+ WR_FIELD(CR_PTEADDR, PTBASE);
+
+ RO_FIELD(CR_TLBMISC, D);
+ RO_FIELD(CR_TLBMISC, PERM);
+ RO_FIELD(CR_TLBMISC, BAD);
+ RO_FIELD(CR_TLBMISC, DBL);
+ WR_FIELD(CR_TLBMISC, PID);
+ WR_FIELD(CR_TLBMISC, WE);
+ WR_FIELD(CR_TLBMISC, RD);
+ WR_FIELD(CR_TLBMISC, WAY);
+
+ WR_REG(CR_TLBACC);
+ }
+
+ /*
+ * TODO: ECC (config, eccinj) and MPU (config, mpubase, mpuacc) are
+ * unimplemented, so their corresponding control regs remain reserved.
+ */
+
+#undef WR_REG
+#undef RO_REG
+#undef WR_FIELD
+#undef RO_FIELD
+}
+
static void nios2_cpu_realizefn(DeviceState *dev, Error **errp)
{
CPUState *cs = CPU(dev);
+ Nios2CPU *cpu = NIOS2_CPU(cs);
Nios2CPUClass *ncc = NIOS2_CPU_GET_CLASS(dev);
Error *local_err = NULL;
+#ifndef CONFIG_USER_ONLY
+ if (cpu->eic_present) {
+ qdev_init_gpio_in_named(DEVICE(cpu), eic_set_irq, "EIC", 1);
+ } else {
+ qdev_init_gpio_in_named(DEVICE(cpu), iic_set_irq, "IRQ", 32);
+ }
+#endif
+
cpu_exec_realizefn(cs, &local_err);
if (local_err != NULL) {
error_propagate(errp, local_err);
return;
}
+ realize_cr_status(cs);
qemu_init_vcpu(cs);
cpu_reset(cs);
+ /* We have reserved storage for cpuid; might as well use it. */
+ cpu->env.ctrl[CR_CPUID] = cs->cpu_index;
+
ncc->parent_realize(dev, errp);
}
#ifndef CONFIG_USER_ONLY
-static bool nios2_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
+static bool eic_take_interrupt(Nios2CPU *cpu)
{
- Nios2CPU *cpu = NIOS2_CPU(cs);
CPUNios2State *env = &cpu->env;
+ const uint32_t status = env->ctrl[CR_STATUS];
- if ((interrupt_request & CPU_INTERRUPT_HARD) &&
- (env->regs[CR_STATUS] & CR_STATUS_PIE) &&
- (env->regs[CR_IPENDING] & env->regs[CR_IENABLE])) {
- cs->exception_index = EXCP_IRQ;
- nios2_cpu_do_interrupt(cs);
+ if (cpu->rnmi) {
+ return !(status & CR_STATUS_NMI);
+ }
+ if (!(status & CR_STATUS_PIE)) {
+ return false;
+ }
+ if (cpu->ril <= FIELD_EX32(status, CR_STATUS, IL)) {
+ return false;
+ }
+ if (cpu->rrs != FIELD_EX32(status, CR_STATUS, CRS)) {
return true;
}
+ return status & CR_STATUS_RSIE;
+}
+
+static bool iic_take_interrupt(Nios2CPU *cpu)
+{
+ CPUNios2State *env = &cpu->env;
+
+ if (!(env->ctrl[CR_STATUS] & CR_STATUS_PIE)) {
+ return false;
+ }
+ return env->ctrl[CR_IPENDING] & env->ctrl[CR_IENABLE];
+}
+
+static bool nios2_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
+{
+ Nios2CPU *cpu = NIOS2_CPU(cs);
+
+ if (interrupt_request & CPU_INTERRUPT_HARD) {
+ if (cpu->eic_present
+ ? eic_take_interrupt(cpu)
+ : iic_take_interrupt(cpu)) {
+ cs->exception_index = EXCP_IRQ;
+ nios2_cpu_do_interrupt(cs);
+ return true;
+ }
+ }
return false;
}
#endif /* !CONFIG_USER_ONLY */
@@ -146,23 +263,26 @@ static void nios2_cpu_disas_set_info(CPUState *cpu, disassemble_info *info)
static int nios2_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n)
{
Nios2CPU *cpu = NIOS2_CPU(cs);
- CPUClass *cc = CPU_GET_CLASS(cs);
CPUNios2State *env = &cpu->env;
-
- if (n > cc->gdb_num_core_regs) {
- return 0;
- }
+ uint32_t val;
if (n < 32) { /* GP regs */
- return gdb_get_reg32(mem_buf, env->regs[n]);
+ val = env->regs[n];
} else if (n == 32) { /* PC */
- return gdb_get_reg32(mem_buf, env->regs[R_PC]);
+ val = env->pc;
} else if (n < 49) { /* Status regs */
- return gdb_get_reg32(mem_buf, env->regs[n - 1]);
+ unsigned cr = n - 33;
+ if (nios2_cr_reserved(&cpu->cr_state[cr])) {
+ val = 0;
+ } else {
+ val = env->ctrl[n - 33];
+ }
+ } else {
+ /* Invalid regs */
+ return 0;
}
- /* Invalid regs */
- return 0;
+ return gdb_get_reg32(mem_buf, val);
}
static int nios2_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
@@ -170,23 +290,32 @@ static int nios2_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
Nios2CPU *cpu = NIOS2_CPU(cs);
CPUClass *cc = CPU_GET_CLASS(cs);
CPUNios2State *env = &cpu->env;
+ uint32_t val;
if (n > cc->gdb_num_core_regs) {
return 0;
}
+ val = ldl_p(mem_buf);
if (n < 32) { /* GP regs */
- env->regs[n] = ldl_p(mem_buf);
+ env->regs[n] = val;
} else if (n == 32) { /* PC */
- env->regs[R_PC] = ldl_p(mem_buf);
+ env->pc = val;
} else if (n < 49) { /* Status regs */
- env->regs[n - 1] = ldl_p(mem_buf);
+ unsigned cr = n - 33;
+ /* ??? Maybe allow the debugger to write to readonly fields. */
+ val &= cpu->cr_state[cr].writable;
+ val |= cpu->cr_state[cr].readonly & env->ctrl[cr];
+ env->ctrl[cr] = val;
+ } else {
+ g_assert_not_reached();
}
return 4;
}
static Property nios2_properties[] = {
+ DEFINE_PROP_BOOL("diverr_present", Nios2CPU, diverr_present, true),
DEFINE_PROP_BOOL("mmu_present", Nios2CPU, mmu_present, true),
/* ALTR,pid-num-bits */
DEFINE_PROP_UINT32("mmu_pid_num_bits", Nios2CPU, pid_num_bits, 8),
@@ -210,9 +339,7 @@ static const struct SysemuCPUOps nios2_sysemu_ops = {
static const struct TCGCPUOps nios2_tcg_ops = {
.initialize = nios2_tcg_init,
-#ifdef CONFIG_USER_ONLY
- .record_sigsegv = nios2_cpu_record_sigsegv,
-#else
+#ifndef CONFIG_USER_ONLY
.tlb_fill = nios2_cpu_tlb_fill,
.cpu_exec_interrupt = nios2_cpu_exec_interrupt,
.do_interrupt = nios2_cpu_do_interrupt,
diff --git a/target/nios2/cpu.h b/target/nios2/cpu.h
index 1bab805bb0..f85581ee56 100644
--- a/target/nios2/cpu.h
+++ b/target/nios2/cpu.h
@@ -23,6 +23,7 @@
#include "exec/cpu-defs.h"
#include "hw/core/cpu.h"
+#include "hw/registerfields.h"
#include "qom/object.h"
typedef struct CPUArchState CPUNios2State;
@@ -56,83 +57,114 @@ struct Nios2CPUClass {
#define EXCEPTION_ADDRESS 0x00000004
#define FAST_TLB_MISS_ADDRESS 0x00000008
+#define NUM_GP_REGS 32
+#define NUM_CR_REGS 32
-/* GP regs + CR regs + PC */
-#define NUM_CORE_REGS (32 + 32 + 1)
+#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 */
-#define R_ZERO 0
-#define R_AT 1
-#define R_RET0 2
-#define R_RET1 3
-#define R_ARG0 4
-#define R_ARG1 5
-#define R_ARG2 6
-#define R_ARG3 7
-#define R_ET 24
-#define R_BT 25
-#define R_GP 26
-#define R_SP 27
-#define R_FP 28
-#define R_EA 29
-#define R_BA 30
-#define R_RA 31
+enum {
+ R_ZERO = 0,
+ R_AT = 1,
+ R_RET0 = 2,
+ R_RET1 = 3,
+ R_ARG0 = 4,
+ R_ARG1 = 5,
+ R_ARG2 = 6,
+ R_ARG3 = 7,
+ R_ET = 24,
+ R_BT = 25,
+ R_GP = 26,
+ R_SP = 27,
+ R_FP = 28,
+ R_EA = 29,
+ R_BA = 30,
+ R_SSTATUS = 30,
+ R_RA = 31,
+};
/* Control register aliases */
-#define CR_BASE 32
-#define CR_STATUS (CR_BASE + 0)
-#define CR_STATUS_PIE (1 << 0)
-#define CR_STATUS_U (1 << 1)
-#define CR_STATUS_EH (1 << 2)
-#define CR_STATUS_IH (1 << 3)
-#define CR_STATUS_IL (63 << 4)
-#define CR_STATUS_CRS (63 << 10)
-#define CR_STATUS_PRS (63 << 16)
-#define CR_STATUS_NMI (1 << 22)
-#define CR_STATUS_RSIE (1 << 23)
-#define CR_ESTATUS (CR_BASE + 1)
-#define CR_BSTATUS (CR_BASE + 2)
-#define CR_IENABLE (CR_BASE + 3)
-#define CR_IPENDING (CR_BASE + 4)
-#define CR_CPUID (CR_BASE + 5)
-#define CR_CTL6 (CR_BASE + 6)
-#define CR_EXCEPTION (CR_BASE + 7)
-#define CR_PTEADDR (CR_BASE + 8)
-#define CR_PTEADDR_PTBASE_SHIFT 22
-#define CR_PTEADDR_PTBASE_MASK (0x3FF << CR_PTEADDR_PTBASE_SHIFT)
-#define CR_PTEADDR_VPN_SHIFT 2
-#define CR_PTEADDR_VPN_MASK (0xFFFFF << CR_PTEADDR_VPN_SHIFT)
-#define CR_TLBACC (CR_BASE + 9)
-#define CR_TLBACC_IGN_SHIFT 25
-#define CR_TLBACC_IGN_MASK (0x7F << CR_TLBACC_IGN_SHIFT)
-#define CR_TLBACC_C (1 << 24)
-#define CR_TLBACC_R (1 << 23)
-#define CR_TLBACC_W (1 << 22)
-#define CR_TLBACC_X (1 << 21)
-#define CR_TLBACC_G (1 << 20)
-#define CR_TLBACC_PFN_MASK 0x000FFFFF
-#define CR_TLBMISC (CR_BASE + 10)
-#define CR_TLBMISC_WAY_SHIFT 20
-#define CR_TLBMISC_WAY_MASK (0xF << CR_TLBMISC_WAY_SHIFT)
-#define CR_TLBMISC_RD (1 << 19)
-#define CR_TLBMISC_WR (1 << 18)
-#define CR_TLBMISC_PID_SHIFT 4
-#define CR_TLBMISC_PID_MASK (0x3FFF << CR_TLBMISC_PID_SHIFT)
-#define CR_TLBMISC_DBL (1 << 3)
-#define CR_TLBMISC_BAD (1 << 2)
-#define CR_TLBMISC_PERM (1 << 1)
-#define CR_TLBMISC_D (1 << 0)
-#define CR_ENCINJ (CR_BASE + 11)
-#define CR_BADADDR (CR_BASE + 12)
-#define CR_CONFIG (CR_BASE + 13)
-#define CR_MPUBASE (CR_BASE + 14)
-#define CR_MPUACC (CR_BASE + 15)
-
-/* Other registers */
-#define R_PC 64
+enum {
+ CR_STATUS = 0,
+ CR_ESTATUS = 1,
+ CR_BSTATUS = 2,
+ CR_IENABLE = 3,
+ CR_IPENDING = 4,
+ CR_CPUID = 5,
+ CR_EXCEPTION = 7,
+ CR_PTEADDR = 8,
+ CR_TLBACC = 9,
+ CR_TLBMISC = 10,
+ CR_ENCINJ = 11,
+ CR_BADADDR = 12,
+ CR_CONFIG = 13,
+ CR_MPUBASE = 14,
+ CR_MPUACC = 15,
+};
+
+FIELD(CR_STATUS, PIE, 0, 1)
+FIELD(CR_STATUS, U, 1, 1)
+FIELD(CR_STATUS, EH, 2, 1)
+FIELD(CR_STATUS, IH, 3, 1)
+FIELD(CR_STATUS, IL, 4, 6)
+FIELD(CR_STATUS, CRS, 10, 6)
+FIELD(CR_STATUS, PRS, 16, 6)
+FIELD(CR_STATUS, NMI, 22, 1)
+FIELD(CR_STATUS, RSIE, 23, 1)
+FIELD(CR_STATUS, SRS, 31, 1) /* only in sstatus */
+
+#define CR_STATUS_PIE R_CR_STATUS_PIE_MASK
+#define CR_STATUS_U R_CR_STATUS_U_MASK
+#define CR_STATUS_EH R_CR_STATUS_EH_MASK
+#define CR_STATUS_IH R_CR_STATUS_IH_MASK
+#define CR_STATUS_NMI R_CR_STATUS_NMI_MASK
+#define CR_STATUS_RSIE R_CR_STATUS_RSIE_MASK
+#define CR_STATUS_SRS R_CR_STATUS_SRS_MASK
+
+FIELD(CR_EXCEPTION, CAUSE, 2, 5)
+FIELD(CR_EXCEPTION, ECCFTL, 31, 1)
+
+FIELD(CR_PTEADDR, VPN, 2, 20)
+FIELD(CR_PTEADDR, PTBASE, 22, 10)
+
+FIELD(CR_TLBACC, PFN, 0, 20)
+FIELD(CR_TLBACC, G, 20, 1)
+FIELD(CR_TLBACC, X, 21, 1)
+FIELD(CR_TLBACC, W, 22, 1)
+FIELD(CR_TLBACC, R, 23, 1)
+FIELD(CR_TLBACC, C, 24, 1)
+FIELD(CR_TLBACC, IG, 25, 7)
+
+#define CR_TLBACC_C R_CR_TLBACC_C_MASK
+#define CR_TLBACC_R R_CR_TLBACC_R_MASK
+#define CR_TLBACC_W R_CR_TLBACC_W_MASK
+#define CR_TLBACC_X R_CR_TLBACC_X_MASK
+#define CR_TLBACC_G R_CR_TLBACC_G_MASK
+
+FIELD(CR_TLBMISC, D, 0, 1)
+FIELD(CR_TLBMISC, PERM, 1, 1)
+FIELD(CR_TLBMISC, BAD, 2, 1)
+FIELD(CR_TLBMISC, DBL, 3, 1)
+FIELD(CR_TLBMISC, PID, 4, 14)
+FIELD(CR_TLBMISC, WE, 18, 1)
+FIELD(CR_TLBMISC, RD, 19, 1)
+FIELD(CR_TLBMISC, WAY, 20, 4)
+FIELD(CR_TLBMISC, EE, 24, 1)
+
+#define CR_TLBMISC_EE R_CR_TLBMISC_EE_MASK
+#define CR_TLBMISC_RD R_CR_TLBMISC_RD_MASK
+#define CR_TLBMISC_WE R_CR_TLBMISC_WE_MASK
+#define CR_TLBMISC_DBL R_CR_TLBMISC_DBL_MASK
+#define CR_TLBMISC_BAD R_CR_TLBMISC_BAD_MASK
+#define CR_TLBMISC_PERM R_CR_TLBMISC_PERM_MASK
+#define CR_TLBMISC_D R_CR_TLBMISC_D_MASK
/* Exceptions */
#define EXCP_BREAK 0x1000
+#define EXCP_SEMIHOST 0x1001
#define EXCP_RESET 0
#define EXCP_PRESET 1
#define EXCP_IRQ 2
@@ -142,20 +174,27 @@ struct Nios2CPUClass {
#define EXCP_UNALIGN 6
#define EXCP_UNALIGND 7
#define EXCP_DIV 8
-#define EXCP_SUPERA 9
+#define EXCP_SUPERA_X 9
#define EXCP_SUPERI 10
-#define EXCP_SUPERD 11
-#define EXCP_TLBD 12
-#define EXCP_TLBX 13
-#define EXCP_TLBR 14
-#define EXCP_TLBW 15
+#define EXCP_SUPERA_D 11
+#define EXCP_TLB_X 12
+#define EXCP_TLB_D (0x1000 | EXCP_TLB_X)
+#define EXCP_PERM_X 13
+#define EXCP_PERM_R 14
+#define EXCP_PERM_W 15
#define EXCP_MPUI 16
#define EXCP_MPUD 17
-#define CPU_INTERRUPT_NMI CPU_INTERRUPT_TGT_EXT_3
-
struct CPUArchState {
- uint32_t regs[NUM_CORE_REGS];
+#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;
#if !defined(CONFIG_USER_ONLY)
Nios2MMU mmu;
@@ -163,6 +202,11 @@ struct CPUArchState {
int error_code;
};
+typedef struct {
+ uint32_t writable;
+ uint32_t readonly;
+} ControlRegState;
+
/**
* Nios2CPU:
* @env: #CPUNios2State
@@ -177,7 +221,10 @@ struct ArchCPU {
CPUNegativeOffsetState neg;
CPUNios2State env;
+ bool diverr_present;
bool mmu_present;
+ bool eic_present;
+
uint32_t pid_num_bits;
uint32_t tlb_num_ways;
uint32_t tlb_num_entries;
@@ -186,9 +233,31 @@ struct ArchCPU {
uint32_t reset_addr;
uint32_t exception_addr;
uint32_t fast_tlb_miss_addr;
+
+ /* Bits within each control register which are reserved or readonly. */
+ ControlRegState cr_state[NUM_CR_REGS];
+
+ /* External Interrupt Controller Interface */
+ uint32_t rha; /* Requested handler address */
+ uint32_t ril; /* Requested interrupt level */
+ uint32_t rrs; /* Requested register set */
+ bool rnmi; /* Requested nonmaskable interrupt */
};
+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);
@@ -197,6 +266,8 @@ hwaddr nios2_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
G_NORETURN void nios2_cpu_do_unaligned_access(CPUState *cpu, vaddr addr,
MMUAccessType access_type, int mmu_idx,
uintptr_t retaddr);
+G_NORETURN void nios2_cpu_loop_exit_advance(CPUNios2State *env,
+ uintptr_t retaddr);
void do_nios2_semihosting(CPUNios2State *env);
@@ -212,36 +283,35 @@ void do_nios2_semihosting(CPUNios2State *env);
static inline int cpu_mmu_index(CPUNios2State *env, bool ifetch)
{
- return (env->regs[CR_STATUS] & CR_STATUS_U) ? MMU_USER_IDX :
+ return (env->ctrl[CR_STATUS] & CR_STATUS_U) ? MMU_USER_IDX :
MMU_SUPERVISOR_IDX;
}
-#ifdef CONFIG_USER_ONLY
-void nios2_cpu_record_sigsegv(CPUState *cpu, vaddr addr,
- MMUAccessType access_type,
- bool maperr, uintptr_t ra);
-#else
+#ifndef CONFIG_USER_ONLY
bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
MMUAccessType access_type, int mmu_idx,
bool probe, uintptr_t retaddr);
#endif
-static inline int cpu_interrupts_enabled(CPUNios2State *env)
-{
- return env->regs[CR_STATUS] & CR_STATUS_PIE;
-}
-
typedef CPUNios2State CPUArchState;
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)
{
- *pc = env->regs[R_PC];
+ unsigned crs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, CRS);
+
+ *pc = env->pc;
*cs_base = 0;
- *flags = (env->regs[CR_STATUS] & (CR_STATUS_EH | 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 */
diff --git a/target/nios2/helper.c b/target/nios2/helper.c
index e5c98650e1..bb3b09e5a7 100644
--- a/target/nios2/helper.c
+++ b/target/nios2/helper.c
@@ -28,176 +28,234 @@
#include "exec/helper-proto.h"
#include "semihosting/semihost.h"
-#if defined(CONFIG_USER_ONLY)
-void nios2_cpu_do_interrupt(CPUState *cs)
+static void do_exception(Nios2CPU *cpu, uint32_t exception_addr,
+ uint32_t tlbmisc_set, bool is_break)
{
- Nios2CPU *cpu = NIOS2_CPU(cs);
CPUNios2State *env = &cpu->env;
- cs->exception_index = -1;
- env->regs[R_EA] = env->regs[R_PC] + 4;
-}
+ CPUState *cs = CPU(cpu);
+ uint32_t old_status = env->ctrl[CR_STATUS];
+ uint32_t new_status = old_status;
-void nios2_cpu_record_sigsegv(CPUState *cs, vaddr addr,
- MMUAccessType access_type,
- bool maperr, uintptr_t retaddr)
-{
- /* FIXME: Disentangle kuser page from linux-user sigsegv handling. */
- cs->exception_index = 0xaa;
- cpu_loop_exit_restore(cs, retaddr);
-}
+ /* With shadow regs, exceptions are always taken into CRS 0. */
+ new_status &= ~R_CR_STATUS_CRS_MASK;
+ env->regs = env->shadow_regs[0];
-#else /* !CONFIG_USER_ONLY */
+ if ((old_status & CR_STATUS_EH) == 0) {
+ int r_ea = R_EA, cr_es = CR_ESTATUS;
-void nios2_cpu_do_interrupt(CPUState *cs)
-{
- Nios2CPU *cpu = NIOS2_CPU(cs);
- CPUNios2State *env = &cpu->env;
+ if (is_break) {
+ r_ea = R_BA;
+ cr_es = CR_BSTATUS;
+ }
+ env->ctrl[cr_es] = old_status;
+ env->regs[r_ea] = env->pc;
+
+ if (cpu->mmu_present) {
+ new_status |= CR_STATUS_EH;
+
+ /*
+ * There are 4 bits that are always written.
+ * Explicitly clear them, to be set via the argument.
+ */
+ env->ctrl[CR_TLBMISC] &= ~(CR_TLBMISC_D |
+ CR_TLBMISC_PERM |
+ CR_TLBMISC_BAD |
+ CR_TLBMISC_DBL);
+ env->ctrl[CR_TLBMISC] |= tlbmisc_set;
+ }
- switch (cs->exception_index) {
- case EXCP_IRQ:
- assert(env->regs[CR_STATUS] & CR_STATUS_PIE);
+ /*
+ * With shadow regs, and EH == 0, PRS is set from CRS.
+ * At least, so says Table 3-9, and some other text,
+ * though Table 3-38 says otherwise.
+ */
+ new_status = FIELD_DP32(new_status, CR_STATUS, PRS,
+ FIELD_EX32(old_status, CR_STATUS, CRS));
+ }
- qemu_log_mask(CPU_LOG_INT, "interrupt at pc=%x\n", env->regs[R_PC]);
+ new_status &= ~(CR_STATUS_PIE | CR_STATUS_U);
- env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
- env->regs[CR_STATUS] |= CR_STATUS_IH;
- env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
+ env->ctrl[CR_STATUS] = new_status;
+ if (!is_break) {
+ env->ctrl[CR_EXCEPTION] = FIELD_DP32(0, CR_EXCEPTION, CAUSE,
+ cs->exception_index);
+ }
+ env->pc = exception_addr;
+}
- env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
- env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
+static void do_iic_irq(Nios2CPU *cpu)
+{
+ do_exception(cpu, cpu->exception_addr, 0, false);
+}
- env->regs[R_EA] = env->regs[R_PC] + 4;
- env->regs[R_PC] = cpu->exception_addr;
- break;
+static void do_eic_irq(Nios2CPU *cpu)
+{
+ CPUNios2State *env = &cpu->env;
+ uint32_t old_status = env->ctrl[CR_STATUS];
+ uint32_t new_status = old_status;
+ uint32_t old_rs = FIELD_EX32(old_status, CR_STATUS, CRS);
+ uint32_t new_rs = cpu->rrs;
+
+ new_status = FIELD_DP32(new_status, CR_STATUS, CRS, new_rs);
+ new_status = FIELD_DP32(new_status, CR_STATUS, IL, cpu->ril);
+ new_status = FIELD_DP32(new_status, CR_STATUS, NMI, cpu->rnmi);
+ new_status &= ~(CR_STATUS_RSIE | CR_STATUS_U);
+ new_status |= CR_STATUS_IH;
+
+ if (!(new_status & CR_STATUS_EH)) {
+ new_status = FIELD_DP32(new_status, CR_STATUS, PRS, old_rs);
+ if (new_rs == 0) {
+ env->ctrl[CR_ESTATUS] = old_status;
+ } else {
+ if (new_rs != old_rs) {
+ old_status |= CR_STATUS_SRS;
+ }
+ env->shadow_regs[new_rs][R_SSTATUS] = old_status;
+ }
+ env->shadow_regs[new_rs][R_EA] = env->pc;
+ }
- case EXCP_TLBD:
- if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
- qemu_log_mask(CPU_LOG_INT, "TLB MISS (fast) at pc=%x\n",
- env->regs[R_PC]);
+ env->ctrl[CR_STATUS] = new_status;
+ nios2_update_crs(env);
- /* Fast TLB miss */
- /* Variation from the spec. Table 3-35 of the cpu reference shows
- * estatus not being changed for TLB miss but this appears to
- * be incorrect. */
- env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
- env->regs[CR_STATUS] |= CR_STATUS_EH;
- env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
+ env->pc = cpu->rha;
+}
- env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
- env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
+void nios2_cpu_do_interrupt(CPUState *cs)
+{
+ Nios2CPU *cpu = NIOS2_CPU(cs);
+ CPUNios2State *env = &cpu->env;
+ uint32_t tlbmisc_set = 0;
- env->regs[CR_TLBMISC] &= ~CR_TLBMISC_DBL;
- env->regs[CR_TLBMISC] |= CR_TLBMISC_WR;
+ if (qemu_loglevel_mask(CPU_LOG_INT)) {
+ const char *name = NULL;
- env->regs[R_EA] = env->regs[R_PC] + 4;
- env->regs[R_PC] = cpu->fast_tlb_miss_addr;
+ switch (cs->exception_index) {
+ case EXCP_IRQ:
+ name = "interrupt";
+ break;
+ case EXCP_TLB_X:
+ case EXCP_TLB_D:
+ if (env->ctrl[CR_STATUS] & CR_STATUS_EH) {
+ name = "TLB MISS (double)";
+ } else {
+ name = "TLB MISS (fast)";
+ }
+ break;
+ case EXCP_PERM_R:
+ case EXCP_PERM_W:
+ case EXCP_PERM_X:
+ name = "TLB PERM";
+ break;
+ case EXCP_SUPERA_X:
+ case EXCP_SUPERA_D:
+ name = "SUPERVISOR (address)";
+ break;
+ case EXCP_SUPERI:
+ name = "SUPERVISOR (insn)";
+ break;
+ case EXCP_ILLEGAL:
+ name = "ILLEGAL insn";
+ break;
+ case EXCP_UNALIGN:
+ name = "Misaligned (data)";
+ break;
+ case EXCP_UNALIGND:
+ name = "Misaligned (destination)";
+ break;
+ case EXCP_DIV:
+ name = "DIV error";
+ break;
+ case EXCP_TRAP:
+ name = "TRAP insn";
+ break;
+ case EXCP_BREAK:
+ name = "BREAK insn";
+ break;
+ case EXCP_SEMIHOST:
+ name = "SEMIHOST insn";
+ break;
+ }
+ if (name) {
+ qemu_log("%s at pc=0x%08x\n", name, env->pc);
} else {
- qemu_log_mask(CPU_LOG_INT, "TLB MISS (double) at pc=%x\n",
- env->regs[R_PC]);
-
- /* Double TLB miss */
- env->regs[CR_STATUS] |= CR_STATUS_EH;
- env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
-
- env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
- env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
-
- env->regs[CR_TLBMISC] |= CR_TLBMISC_DBL;
+ qemu_log("Unknown exception %d at pc=0x%08x\n",
+ cs->exception_index, env->pc);
+ }
+ }
- env->regs[R_PC] = cpu->exception_addr;
+ switch (cs->exception_index) {
+ case EXCP_IRQ:
+ /* Note that PC is advanced for interrupts as well. */
+ env->pc += 4;
+ if (cpu->eic_present) {
+ do_eic_irq(cpu);
+ } else {
+ do_iic_irq(cpu);
}
break;
- case EXCP_TLBR:
- case EXCP_TLBW:
- case EXCP_TLBX:
- qemu_log_mask(CPU_LOG_INT, "TLB PERM at pc=%x\n", env->regs[R_PC]);
-
- env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
- env->regs[CR_STATUS] |= CR_STATUS_EH;
- env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
-
- env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
- env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
-
- if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
- env->regs[CR_TLBMISC] |= CR_TLBMISC_WR;
+ case EXCP_TLB_D:
+ tlbmisc_set = CR_TLBMISC_D;
+ /* fall through */
+ case EXCP_TLB_X:
+ if (env->ctrl[CR_STATUS] & CR_STATUS_EH) {
+ tlbmisc_set |= CR_TLBMISC_DBL;
+ /*
+ * Normally, we don't write to tlbmisc unless !EH,
+ * so do it manually for the double-tlb miss exception.
+ */
+ env->ctrl[CR_TLBMISC] &= ~(CR_TLBMISC_D |
+ CR_TLBMISC_PERM |
+ CR_TLBMISC_BAD);
+ env->ctrl[CR_TLBMISC] |= tlbmisc_set;
+ do_exception(cpu, cpu->exception_addr, 0, false);
+ } else {
+ tlbmisc_set |= CR_TLBMISC_WE;
+ do_exception(cpu, cpu->fast_tlb_miss_addr, tlbmisc_set, false);
}
-
- env->regs[R_EA] = env->regs[R_PC] + 4;
- env->regs[R_PC] = cpu->exception_addr;
break;
- case EXCP_SUPERA:
- case EXCP_SUPERI:
- case EXCP_SUPERD:
- qemu_log_mask(CPU_LOG_INT, "SUPERVISOR exception at pc=%x\n",
- env->regs[R_PC]);
-
- if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
- env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
- env->regs[R_EA] = env->regs[R_PC] + 4;
+ case EXCP_PERM_R:
+ case EXCP_PERM_W:
+ tlbmisc_set = CR_TLBMISC_D;
+ /* fall through */
+ case EXCP_PERM_X:
+ tlbmisc_set |= CR_TLBMISC_PERM;
+ if (!(env->ctrl[CR_STATUS] & CR_STATUS_EH)) {
+ tlbmisc_set |= CR_TLBMISC_WE;
}
+ do_exception(cpu, cpu->exception_addr, tlbmisc_set, false);
+ break;
- env->regs[CR_STATUS] |= CR_STATUS_EH;
- env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
-
- env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
- env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
-
- env->regs[R_PC] = cpu->exception_addr;
+ case EXCP_SUPERA_D:
+ case EXCP_UNALIGN:
+ tlbmisc_set = CR_TLBMISC_D;
+ /* fall through */
+ case EXCP_SUPERA_X:
+ case EXCP_UNALIGND:
+ tlbmisc_set |= CR_TLBMISC_BAD;
+ do_exception(cpu, cpu->exception_addr, tlbmisc_set, false);
break;
+ case EXCP_SUPERI:
case EXCP_ILLEGAL:
+ case EXCP_DIV:
case EXCP_TRAP:
- qemu_log_mask(CPU_LOG_INT, "TRAP exception at pc=%x\n",
- env->regs[R_PC]);
-
- if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
- env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
- env->regs[R_EA] = env->regs[R_PC] + 4;
- }
-
- env->regs[CR_STATUS] |= CR_STATUS_EH;
- env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
-
- env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
- env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
-
- env->regs[R_PC] = cpu->exception_addr;
+ do_exception(cpu, cpu->exception_addr, 0, false);
break;
case EXCP_BREAK:
- qemu_log_mask(CPU_LOG_INT, "BREAK exception at pc=%x\n",
- env->regs[R_PC]);
- /* The semihosting instruction is "break 1". */
- if (semihosting_enabled() &&
- cpu_ldl_code(env, env->regs[R_PC]) == 0x003da07a) {
- qemu_log_mask(CPU_LOG_INT, "Entering semihosting\n");
- env->regs[R_PC] += 4;
- do_nios2_semihosting(env);
- break;
- }
-
- if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
- env->regs[CR_BSTATUS] = env->regs[CR_STATUS];
- env->regs[R_BA] = env->regs[R_PC] + 4;
- }
-
- env->regs[CR_STATUS] |= CR_STATUS_EH;
- env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
-
- env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
- env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
+ do_exception(cpu, cpu->exception_addr, 0, true);
+ break;
- env->regs[R_PC] = cpu->exception_addr;
+ case EXCP_SEMIHOST:
+ do_nios2_semihosting(env);
break;
default:
- cpu_abort(cs, "unhandled exception type=%d\n",
- cs->exception_index);
- break;
+ cpu_abort(cs, "unhandled exception type=%d\n", cs->exception_index);
}
}
@@ -232,9 +290,9 @@ void nios2_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
Nios2CPU *cpu = NIOS2_CPU(cs);
CPUNios2State *env = &cpu->env;
- env->regs[CR_BADADDR] = addr;
- env->regs[CR_EXCEPTION] = EXCP_UNALIGN << 2;
- helper_raise_exception(env, EXCP_UNALIGN);
+ env->ctrl[CR_BADADDR] = addr;
+ cs->exception_index = EXCP_UNALIGN;
+ nios2_cpu_loop_exit_advance(env, retaddr);
}
bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
@@ -243,7 +301,7 @@ bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
{
Nios2CPU *cpu = NIOS2_CPU(cs);
CPUNios2State *env = &cpu->env;
- unsigned int excp = EXCP_TLBD;
+ unsigned int excp;
target_ulong vaddr, paddr;
Nios2MMULookup lu;
unsigned int hit;
@@ -270,9 +328,10 @@ bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
if (probe) {
return false;
}
- cs->exception_index = EXCP_SUPERA;
- env->regs[CR_BADADDR] = address;
- cpu_loop_exit_restore(cs, retaddr);
+ cs->exception_index = (access_type == MMU_INST_FETCH
+ ? EXCP_SUPERA_X : EXCP_SUPERA_D);
+ env->ctrl[CR_BADADDR] = address;
+ nios2_cpu_loop_exit_advance(env, retaddr);
}
}
@@ -291,25 +350,23 @@ bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
}
/* Permission violation */
- excp = (access_type == MMU_DATA_LOAD ? EXCP_TLBR :
- access_type == MMU_DATA_STORE ? EXCP_TLBW : EXCP_TLBX);
+ excp = (access_type == MMU_DATA_LOAD ? EXCP_PERM_R :
+ access_type == MMU_DATA_STORE ? EXCP_PERM_W : EXCP_PERM_X);
+ } else {
+ excp = (access_type == MMU_INST_FETCH ? EXCP_TLB_X: EXCP_TLB_D);
}
if (probe) {
return false;
}
- if (access_type == MMU_INST_FETCH) {
- env->regs[CR_TLBMISC] &= ~CR_TLBMISC_D;
- } else {
- env->regs[CR_TLBMISC] |= CR_TLBMISC_D;
- }
- env->regs[CR_PTEADDR] &= CR_PTEADDR_PTBASE_MASK;
- env->regs[CR_PTEADDR] |= (address >> 10) & CR_PTEADDR_VPN_MASK;
- env->mmu.pteaddr_wr = env->regs[CR_PTEADDR];
+ env->ctrl[CR_TLBMISC] = FIELD_DP32(env->ctrl[CR_TLBMISC], CR_TLBMISC, D,
+ access_type != MMU_INST_FETCH);
+ env->ctrl[CR_PTEADDR] = FIELD_DP32(env->ctrl[CR_PTEADDR], CR_PTEADDR, VPN,
+ address >> TARGET_PAGE_BITS);
+ env->mmu.pteaddr_wr = env->ctrl[CR_PTEADDR];
cs->exception_index = excp;
- env->regs[CR_BADADDR] = address;
- cpu_loop_exit_restore(cs, retaddr);
+ env->ctrl[CR_BADADDR] = address;
+ nios2_cpu_loop_exit_advance(env, retaddr);
}
-#endif /* !CONFIG_USER_ONLY */
diff --git a/target/nios2/helper.h b/target/nios2/helper.h
index a44ecfdf7a..1648d76ade 100644
--- a/target/nios2/helper.h
+++ b/target/nios2/helper.h
@@ -19,8 +19,13 @@
*/
DEF_HELPER_FLAGS_2(raise_exception, TCG_CALL_NO_WG, noreturn, env, i32)
+DEF_HELPER_FLAGS_3(divs, TCG_CALL_NO_WG, s32, env, s32, s32)
+DEF_HELPER_FLAGS_3(divu, TCG_CALL_NO_WG, i32, env, i32, i32)
#if !defined(CONFIG_USER_ONLY)
+DEF_HELPER_3(eret, noreturn, env, i32, i32)
+DEF_HELPER_FLAGS_2(rdprs, TCG_CALL_NO_WG, i32, env, i32)
+DEF_HELPER_3(wrprs, void, env, i32, i32)
DEF_HELPER_2(mmu_write_tlbacc, void, env, i32)
DEF_HELPER_2(mmu_write_tlbmisc, void, env, i32)
DEF_HELPER_2(mmu_write_pteaddr, void, env, i32)
diff --git a/target/nios2/meson.build b/target/nios2/meson.build
index 62b384702d..2bd60ba306 100644
--- a/target/nios2/meson.build
+++ b/target/nios2/meson.build
@@ -1,14 +1,17 @@
nios2_ss = ss.source_set()
nios2_ss.add(files(
'cpu.c',
- 'helper.c',
'nios2-semi.c',
'op_helper.c',
'translate.c',
))
nios2_softmmu_ss = ss.source_set()
-nios2_softmmu_ss.add(files('monitor.c', 'mmu.c'))
+nios2_softmmu_ss.add(files(
+ 'helper.c',
+ 'monitor.c',
+ 'mmu.c'
+))
target_arch += {'nios2': nios2_ss}
target_softmmu_arch += {'nios2': nios2_softmmu_ss}
diff --git a/target/nios2/mmu.c b/target/nios2/mmu.c
index 4daab2a7ab..d9b690b78e 100644
--- a/target/nios2/mmu.c
+++ b/target/nios2/mmu.c
@@ -33,7 +33,7 @@ unsigned int mmu_translate(CPUNios2State *env,
target_ulong vaddr, int rw, int mmu_idx)
{
Nios2CPU *cpu = env_archcpu(env);
- int pid = (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4;
+ int pid = FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID);
int vpn = vaddr >> 12;
int way, n_ways = cpu->tlb_num_ways;
@@ -49,7 +49,7 @@ unsigned int mmu_translate(CPUNios2State *env,
}
lu->vaddr = vaddr & TARGET_PAGE_MASK;
- lu->paddr = (entry->data & CR_TLBACC_PFN_MASK) << TARGET_PAGE_BITS;
+ lu->paddr = FIELD_EX32(entry->data, CR_TLBACC, PFN) << TARGET_PAGE_BITS;
lu->prot = ((entry->data & CR_TLBACC_R) ? PAGE_READ : 0) |
((entry->data & CR_TLBACC_W) ? PAGE_WRITE : 0) |
((entry->data & CR_TLBACC_X) ? PAGE_EXEC : 0);
@@ -86,27 +86,27 @@ void helper_mmu_write_tlbacc(CPUNios2State *env, uint32_t v)
CPUState *cs = env_cpu(env);
Nios2CPU *cpu = env_archcpu(env);
- trace_nios2_mmu_write_tlbacc(v >> CR_TLBACC_IGN_SHIFT,
+ trace_nios2_mmu_write_tlbacc(FIELD_EX32(v, CR_TLBACC, IG),
(v & CR_TLBACC_C) ? 'C' : '.',
(v & CR_TLBACC_R) ? 'R' : '.',
(v & CR_TLBACC_W) ? 'W' : '.',
(v & CR_TLBACC_X) ? 'X' : '.',
(v & CR_TLBACC_G) ? 'G' : '.',
- v & CR_TLBACC_PFN_MASK);
+ FIELD_EX32(v, CR_TLBACC, PFN));
/* if tlbmisc.WE == 1 then trigger a TLB write on writes to TLBACC */
- if (env->regs[CR_TLBMISC] & CR_TLBMISC_WR) {
- int way = (env->regs[CR_TLBMISC] >> CR_TLBMISC_WAY_SHIFT);
- int vpn = (env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK) >> 2;
- int pid = (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4;
- int g = (v & CR_TLBACC_G) ? 1 : 0;
- int valid = ((vpn & CR_TLBACC_PFN_MASK) < 0xC0000) ? 1 : 0;
+ if (env->ctrl[CR_TLBMISC] & CR_TLBMISC_WE) {
+ int way = FIELD_EX32(env->ctrl[CR_TLBMISC], CR_TLBMISC, WAY);
+ int vpn = FIELD_EX32(env->mmu.pteaddr_wr, CR_PTEADDR, VPN);
+ int pid = FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID);
+ int g = FIELD_EX32(v, CR_TLBACC, G);
+ int valid = FIELD_EX32(vpn, CR_TLBACC, PFN) < 0xC0000;
Nios2TLBEntry *entry =
&env->mmu.tlb[(way * cpu->tlb_num_ways) +
(vpn & env->mmu.tlb_entry_mask)];
uint32_t newTag = (vpn << 12) | (g << 11) | (valid << 10) | pid;
uint32_t newData = v & (CR_TLBACC_C | CR_TLBACC_R | CR_TLBACC_W |
- CR_TLBACC_X | CR_TLBACC_PFN_MASK);
+ CR_TLBACC_X | R_CR_TLBACC_PFN_MASK);
if ((entry->tag != newTag) || (entry->data != newData)) {
if (entry->tag & (1 << 10)) {
@@ -117,10 +117,9 @@ void helper_mmu_write_tlbacc(CPUNios2State *env, uint32_t v)
entry->data = newData;
}
/* Auto-increment tlbmisc.WAY */
- env->regs[CR_TLBMISC] =
- (env->regs[CR_TLBMISC] & ~CR_TLBMISC_WAY_MASK) |
- (((way + 1) & (cpu->tlb_num_ways - 1)) <<
- CR_TLBMISC_WAY_SHIFT);
+ env->ctrl[CR_TLBMISC] = FIELD_DP32(env->ctrl[CR_TLBMISC],
+ CR_TLBMISC, WAY,
+ (way + 1) & (cpu->tlb_num_ways - 1));
}
/* Writes to TLBACC don't change the read-back value */
@@ -130,40 +129,41 @@ void helper_mmu_write_tlbacc(CPUNios2State *env, uint32_t v)
void helper_mmu_write_tlbmisc(CPUNios2State *env, uint32_t v)
{
Nios2CPU *cpu = env_archcpu(env);
+ uint32_t new_pid = FIELD_EX32(v, CR_TLBMISC, PID);
+ uint32_t old_pid = FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID);
+ uint32_t way = FIELD_EX32(v, CR_TLBMISC, WAY);
- trace_nios2_mmu_write_tlbmisc(v >> CR_TLBMISC_WAY_SHIFT,
+ trace_nios2_mmu_write_tlbmisc(way,
(v & CR_TLBMISC_RD) ? 'R' : '.',
- (v & CR_TLBMISC_WR) ? 'W' : '.',
+ (v & CR_TLBMISC_WE) ? 'W' : '.',
(v & CR_TLBMISC_DBL) ? '2' : '.',
(v & CR_TLBMISC_BAD) ? 'B' : '.',
(v & CR_TLBMISC_PERM) ? 'P' : '.',
(v & CR_TLBMISC_D) ? 'D' : '.',
- (v & CR_TLBMISC_PID_MASK) >> 4);
+ new_pid);
- if ((v & CR_TLBMISC_PID_MASK) !=
- (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK)) {
- mmu_flush_pid(env, (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >>
- CR_TLBMISC_PID_SHIFT);
+ if (new_pid != old_pid) {
+ mmu_flush_pid(env, old_pid);
}
+
/* if tlbmisc.RD == 1 then trigger a TLB read on writes to TLBMISC */
if (v & CR_TLBMISC_RD) {
- int way = (v >> CR_TLBMISC_WAY_SHIFT);
- int vpn = (env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK) >> 2;
+ int vpn = FIELD_EX32(env->mmu.pteaddr_wr, CR_PTEADDR, VPN);
Nios2TLBEntry *entry =
&env->mmu.tlb[(way * cpu->tlb_num_ways) +
(vpn & env->mmu.tlb_entry_mask)];
- env->regs[CR_TLBACC] &= CR_TLBACC_IGN_MASK;
- env->regs[CR_TLBACC] |= entry->data;
- env->regs[CR_TLBACC] |= (entry->tag & (1 << 11)) ? CR_TLBACC_G : 0;
- env->regs[CR_TLBMISC] =
- (v & ~CR_TLBMISC_PID_MASK) |
- ((entry->tag & ((1 << cpu->pid_num_bits) - 1)) <<
- CR_TLBMISC_PID_SHIFT);
- env->regs[CR_PTEADDR] &= ~CR_PTEADDR_VPN_MASK;
- env->regs[CR_PTEADDR] |= (entry->tag >> 12) << CR_PTEADDR_VPN_SHIFT;
+ env->ctrl[CR_TLBACC] &= R_CR_TLBACC_IG_MASK;
+ env->ctrl[CR_TLBACC] |= entry->data;
+ env->ctrl[CR_TLBACC] |= (entry->tag & (1 << 11)) ? CR_TLBACC_G : 0;
+ env->ctrl[CR_TLBMISC] = FIELD_DP32(v, CR_TLBMISC, PID,
+ entry->tag &
+ ((1 << cpu->pid_num_bits) - 1));
+ env->ctrl[CR_PTEADDR] = FIELD_DP32(env->ctrl[CR_PTEADDR],
+ CR_PTEADDR, VPN,
+ entry->tag >> TARGET_PAGE_BITS);
} else {
- env->regs[CR_TLBMISC] = v;
+ env->ctrl[CR_TLBMISC] = v;
}
env->mmu.tlbmisc_wr = v;
@@ -171,12 +171,12 @@ void helper_mmu_write_tlbmisc(CPUNios2State *env, uint32_t v)
void helper_mmu_write_pteaddr(CPUNios2State *env, uint32_t v)
{
- trace_nios2_mmu_write_pteaddr(v >> CR_PTEADDR_PTBASE_SHIFT,
- (v & CR_PTEADDR_VPN_MASK) >> CR_PTEADDR_VPN_SHIFT);
+ trace_nios2_mmu_write_pteaddr(FIELD_EX32(v, CR_PTEADDR, PTBASE),
+ FIELD_EX32(v, CR_PTEADDR, VPN));
/* Writes to PTEADDR don't change the read-back VPN value */
- env->regs[CR_PTEADDR] = (v & ~CR_PTEADDR_VPN_MASK) |
- (env->regs[CR_PTEADDR] & CR_PTEADDR_VPN_MASK);
+ env->ctrl[CR_PTEADDR] = ((v & ~R_CR_PTEADDR_VPN_MASK) |
+ (env->ctrl[CR_PTEADDR] & R_CR_PTEADDR_VPN_MASK));
env->mmu.pteaddr_wr = v;
}
@@ -207,7 +207,7 @@ void dump_mmu(CPUNios2State *env)
entry->tag >> 12,
entry->tag & ((1 << cpu->pid_num_bits) - 1),
(entry->tag & (1 << 11)) ? 'G' : '-',
- entry->data & CR_TLBACC_PFN_MASK,
+ FIELD_EX32(entry->data, CR_TLBACC, PFN),
(entry->data & CR_TLBACC_C) ? 'C' : '-',
(entry->data & CR_TLBACC_R) ? 'R' : '-',
(entry->data & CR_TLBACC_W) ? 'W' : '-',
diff --git a/target/nios2/op_helper.c b/target/nios2/op_helper.c
index caa885f7b4..2e30d0a908 100644
--- a/target/nios2/op_helper.c
+++ b/target/nios2/op_helper.c
@@ -30,3 +30,91 @@ void helper_raise_exception(CPUNios2State *env, uint32_t index)
cs->exception_index = index;
cpu_loop_exit(cs);
}
+
+void nios2_cpu_loop_exit_advance(CPUNios2State *env, uintptr_t retaddr)
+{
+ CPUState *cs = env_cpu(env);
+
+ /*
+ * Note that PC is advanced for all hardware exceptions.
+ * Do this here, rather than in restore_state_to_opc(),
+ * lest we affect QEMU internal exceptions, like EXCP_DEBUG.
+ */
+ cpu_restore_state(cs, retaddr, true);
+ env->pc += 4;
+ cpu_loop_exit(cs);
+}
+
+static void maybe_raise_div(CPUNios2State *env, uintptr_t ra)
+{
+ Nios2CPU *cpu = env_archcpu(env);
+ CPUState *cs = env_cpu(env);
+
+ if (cpu->diverr_present) {
+ cs->exception_index = EXCP_DIV;
+ nios2_cpu_loop_exit_advance(env, ra);
+ }
+}
+
+int32_t helper_divs(CPUNios2State *env, int32_t num, int32_t den)
+{
+ if (unlikely(den == 0) || unlikely(den == -1 && num == INT32_MIN)) {
+ maybe_raise_div(env, GETPC());
+ return num; /* undefined */
+ }
+ return num / den;
+}
+
+uint32_t helper_divu(CPUNios2State *env, uint32_t num, uint32_t den)
+{
+ if (unlikely(den == 0)) {
+ maybe_raise_div(env, GETPC());
+ return num; /* undefined */
+ }
+ return num / den;
+}
+
+#ifndef CONFIG_USER_ONLY
+void helper_eret(CPUNios2State *env, uint32_t new_status, uint32_t new_pc)
+{
+ Nios2CPU *cpu = env_archcpu(env);
+ CPUState *cs = env_cpu(env);
+
+ if (unlikely(new_pc & 3)) {
+ env->ctrl[CR_BADADDR] = new_pc;
+ cs->exception_index = EXCP_UNALIGND;
+ nios2_cpu_loop_exit_advance(env, GETPC());
+ }
+
+ /*
+ * None of estatus, bstatus, or sstatus have constraints on write;
+ * do not allow reserved fields in status to be set.
+ * When shadow registers are enabled, eret *does* restore CRS.
+ * Rather than testing eic_present to decide, mask CRS out of
+ * the set of readonly fields.
+ */
+ new_status &= cpu->cr_state[CR_STATUS].writable |
+ (cpu->cr_state[CR_STATUS].readonly & R_CR_STATUS_CRS_MASK);
+
+ env->ctrl[CR_STATUS] = new_status;
+ env->pc = new_pc;
+ nios2_update_crs(env);
+ cpu_loop_exit(cs);
+}
+
+/*
+ * RDPRS and WRPRS are implemented out of line so that if PRS == CRS,
+ * all of the tcg global temporaries are synced back to ENV.
+ */
+uint32_t helper_rdprs(CPUNios2State *env, uint32_t regno)
+{
+ unsigned prs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, PRS);
+ return env->shadow_regs[prs][regno];
+}
+
+void helper_wrprs(CPUNios2State *env, uint32_t regno, uint32_t val)
+{
+ unsigned prs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, PRS);
+ env->shadow_regs[prs][regno] = val;
+}
+#endif /* !CONFIG_USER_ONLY */
diff --git a/target/nios2/translate.c b/target/nios2/translate.c
index 89b97ef520..3a037a68cc 100644
--- a/target/nios2/translate.c
+++ b/target/nios2/translate.c
@@ -33,9 +33,9 @@
#include "exec/translator.h"
#include "qemu/qemu-print.h"
#include "exec/gen-icount.h"
+#include "semihosting/semihost.h"
/* is_jmp field values */
-#define DISAS_JUMP DISAS_TARGET_0 /* only pc was modified dynamically */
#define DISAS_UPDATE DISAS_TARGET_1 /* cpu state was modified dynamically */
#define INSTRUCTION_FLG(func, flags) { (func), (flags) }
@@ -52,32 +52,53 @@
#define INSN_R_TYPE 0x3A
/* I-Type instruction parsing */
+typedef struct {
+ uint8_t op;
+ union {
+ uint16_t u;
+ int16_t s;
+ } imm16;
+ uint8_t b;
+ uint8_t a;
+} InstrIType;
+
#define I_TYPE(instr, code) \
- struct { \
- uint8_t op; \
- union { \
- uint16_t u; \
- int16_t s; \
- } imm16; \
- uint8_t b; \
- uint8_t a; \
- } (instr) = { \
+ InstrIType (instr) = { \
.op = extract32((code), 0, 6), \
.imm16.u = extract32((code), 6, 16), \
.b = extract32((code), 22, 5), \
.a = extract32((code), 27, 5), \
}
+typedef target_ulong ImmFromIType(const InstrIType *);
+
+static target_ulong imm_unsigned(const InstrIType *i)
+{
+ return i->imm16.u;
+}
+
+static target_ulong imm_signed(const InstrIType *i)
+{
+ return i->imm16.s;
+}
+
+static target_ulong imm_shifted(const InstrIType *i)
+{
+ return i->imm16.u << 16;
+}
+
/* R-Type instruction parsing */
+typedef struct {
+ uint8_t op;
+ uint8_t imm5;
+ uint8_t opx;
+ uint8_t c;
+ uint8_t b;
+ uint8_t a;
+} InstrRType;
+
#define R_TYPE(instr, code) \
- struct { \
- uint8_t op; \
- uint8_t imm5; \
- uint8_t opx; \
- uint8_t c; \
- uint8_t b; \
- uint8_t a; \
- } (instr) = { \
+ InstrRType (instr) = { \
.op = extract32((code), 0, 6), \
.imm5 = extract32((code), 6, 5), \
.opx = extract32((code), 11, 6), \
@@ -87,23 +108,36 @@
}
/* J-Type instruction parsing */
+typedef struct {
+ uint8_t op;
+ uint32_t imm26;
+} InstrJType;
+
#define J_TYPE(instr, code) \
- struct { \
- uint8_t op; \
- uint32_t imm26; \
- } (instr) = { \
+ InstrJType (instr) = { \
.op = extract32((code), 0, 6), \
.imm26 = extract32((code), 6, 26), \
}
+typedef void GenFn2i(TCGv, TCGv, target_long);
+typedef void GenFn3(TCGv, TCGv, TCGv);
+typedef void GenFn4(TCGv, TCGv, TCGv, TCGv);
+
typedef struct DisasContext {
DisasContextBase base;
- TCGv_i32 zero;
target_ulong pc;
int mem_idx;
+ uint32_t tb_flags;
+ TCGv sink;
+ const ControlRegState *cr_state;
+ bool eic_present;
} DisasContext;
-static TCGv cpu_R[NUM_CORE_REGS];
+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);
@@ -122,31 +156,57 @@ static uint8_t get_opxcode(uint32_t code)
return instr.opx;
}
-static TCGv load_zero(DisasContext *dc)
+static TCGv load_gpr(DisasContext *dc, unsigned reg)
{
- if (!dc->zero) {
- dc->zero = tcg_const_i32(0);
+ assert(reg < NUM_GP_REGS);
+
+ /*
+ * 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);
+ }
+ if (FIELD_EX32(dc->tb_flags, TBFLAGS, CRS0)) {
+ return cpu_R[reg];
}
- return dc->zero;
+#ifdef CONFIG_USER_ONLY
+ g_assert_not_reached();
+#else
+ return cpu_crs_R[reg];
+#endif
}
-static TCGv load_gpr(DisasContext *dc, uint8_t reg)
+static TCGv dest_gpr(DisasContext *dc, unsigned reg)
{
- if (likely(reg != R_ZERO)) {
+ 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;
+ }
+ if (FIELD_EX32(dc->tb_flags, TBFLAGS, CRS0)) {
return cpu_R[reg];
- } else {
- return load_zero(dc);
}
+#ifdef CONFIG_USER_ONLY
+ g_assert_not_reached();
+#else
+ return cpu_crs_R[reg];
+#endif
}
-static void t_gen_helper_raise_exception(DisasContext *dc,
- uint32_t index)
+static void t_gen_helper_raise_exception(DisasContext *dc, uint32_t index)
{
- TCGv_i32 tmp = tcg_const_i32(index);
-
- tcg_gen_movi_tl(cpu_R[R_PC], dc->pc);
- gen_helper_raise_exception(cpu_env, tmp);
- tcg_temp_free_i32(tmp);
+ /* Note that PC is advanced for all hardware exceptions. */
+ tcg_gen_movi_tl(cpu_pc, dc->base.pc_next);
+ gen_helper_raise_exception(cpu_env, tcg_constant_i32(index));
dc->base.is_jmp = DISAS_NORETURN;
}
@@ -156,12 +216,36 @@ static void gen_goto_tb(DisasContext *dc, int n, uint32_t dest)
if (translator_use_goto_tb(&dc->base, dest)) {
tcg_gen_goto_tb(n);
- tcg_gen_movi_tl(cpu_R[R_PC], dest);
+ tcg_gen_movi_tl(cpu_pc, dest);
tcg_gen_exit_tb(tb, n);
} else {
- tcg_gen_movi_tl(cpu_R[R_PC], dest);
- tcg_gen_exit_tb(NULL, 0);
+ tcg_gen_movi_tl(cpu_pc, dest);
+ tcg_gen_lookup_and_goto_ptr();
}
+ dc->base.is_jmp = DISAS_NORETURN;
+}
+
+static void gen_jumpr(DisasContext *dc, int regno, bool is_call)
+{
+ TCGLabel *l = gen_new_label();
+ TCGv test = tcg_temp_new();
+ TCGv dest = load_gpr(dc, regno);
+
+ tcg_gen_andi_tl(test, dest, 3);
+ tcg_gen_brcondi_tl(TCG_COND_NE, test, 0, l);
+ tcg_temp_free(test);
+
+ tcg_gen_mov_tl(cpu_pc, dest);
+ if (is_call) {
+ tcg_gen_movi_tl(dest_gpr(dc, R_RA), dc->base.pc_next);
+ }
+ tcg_gen_lookup_and_goto_ptr();
+
+ gen_set_label(l);
+ tcg_gen_st_tl(dest, cpu_env, offsetof(CPUNios2State, ctrl[CR_BADADDR]));
+ t_gen_helper_raise_exception(dc, EXCP_UNALIGND);
+
+ dc->base.is_jmp = DISAS_NORETURN;
}
static void gen_excp(DisasContext *dc, uint32_t code, uint32_t flags)
@@ -169,12 +253,14 @@ static void gen_excp(DisasContext *dc, uint32_t code, uint32_t flags)
t_gen_helper_raise_exception(dc, flags);
}
-static void gen_check_supervisor(DisasContext *dc)
+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;
}
+ return true;
}
/*
@@ -193,12 +279,11 @@ static void jmpi(DisasContext *dc, uint32_t code, uint32_t flags)
{
J_TYPE(instr, code);
gen_goto_tb(dc, 0, (dc->pc & 0xF0000000) | (instr.imm26 << 2));
- dc->base.is_jmp = DISAS_NORETURN;
}
static void call(DisasContext *dc, uint32_t code, uint32_t flags)
{
- tcg_gen_movi_tl(cpu_R[R_RA], dc->base.pc_next);
+ tcg_gen_movi_tl(dest_gpr(dc, R_RA), dc->base.pc_next);
jmpi(dc, code, flags);
}
@@ -211,27 +296,10 @@ static void gen_ldx(DisasContext *dc, uint32_t code, uint32_t flags)
I_TYPE(instr, code);
TCGv addr = tcg_temp_new();
- TCGv data;
-
- /*
- * WARNING: Loads into R_ZERO are ignored, but we must generate the
- * memory access itself to emulate the CPU precisely. Load
- * from a protected page to R_ZERO will cause SIGSEGV on
- * the Nios2 CPU.
- */
- if (likely(instr.b != R_ZERO)) {
- data = cpu_R[instr.b];
- } else {
- data = tcg_temp_new();
- }
+ TCGv data = dest_gpr(dc, instr.b);
tcg_gen_addi_tl(addr, load_gpr(dc, instr.a), instr.imm16.s);
tcg_gen_qemu_ld_tl(data, addr, dc->mem_idx, flags);
-
- if (unlikely(instr.b == R_ZERO)) {
- tcg_temp_free(data);
- }
-
tcg_temp_free(addr);
}
@@ -253,7 +321,6 @@ static void br(DisasContext *dc, uint32_t code, uint32_t flags)
I_TYPE(instr, code);
gen_goto_tb(dc, 0, dc->base.pc_next + (instr.imm16.s & -4));
- dc->base.is_jmp = DISAS_NORETURN;
}
static void gen_bxx(DisasContext *dc, uint32_t code, uint32_t flags)
@@ -261,48 +328,86 @@ static void gen_bxx(DisasContext *dc, uint32_t code, uint32_t flags)
I_TYPE(instr, code);
TCGLabel *l1 = gen_new_label();
- tcg_gen_brcond_tl(flags, cpu_R[instr.a], cpu_R[instr.b], l1);
+ tcg_gen_brcond_tl(flags, load_gpr(dc, instr.a), load_gpr(dc, instr.b), l1);
gen_goto_tb(dc, 0, dc->base.pc_next);
gen_set_label(l1);
gen_goto_tb(dc, 1, dc->base.pc_next + (instr.imm16.s & -4));
- dc->base.is_jmp = DISAS_NORETURN;
}
/* Comparison instructions */
-#define gen_i_cmpxx(fname, op3) \
-static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \
-{ \
- I_TYPE(instr, (code)); \
- tcg_gen_setcondi_tl(flags, cpu_R[instr.b], cpu_R[instr.a], (op3)); \
+static void do_i_cmpxx(DisasContext *dc, uint32_t insn,
+ TCGCond cond, ImmFromIType *imm)
+{
+ I_TYPE(instr, insn);
+ tcg_gen_setcondi_tl(cond, dest_gpr(dc, instr.b),
+ load_gpr(dc, instr.a), imm(&instr));
}
-gen_i_cmpxx(gen_cmpxxsi, instr.imm16.s)
-gen_i_cmpxx(gen_cmpxxui, instr.imm16.u)
+#define gen_i_cmpxx(fname, imm) \
+ static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \
+ { do_i_cmpxx(dc, code, flags, imm); }
+
+gen_i_cmpxx(gen_cmpxxsi, imm_signed)
+gen_i_cmpxx(gen_cmpxxui, imm_unsigned)
/* Math/logic instructions */
-#define gen_i_math_logic(fname, insn, resimm, op3) \
-static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \
-{ \
- I_TYPE(instr, (code)); \
- if (unlikely(instr.b == R_ZERO)) { /* Store to R_ZERO is ignored */ \
- return; \
- } else if (instr.a == R_ZERO) { /* MOVxI optimizations */ \
- tcg_gen_movi_tl(cpu_R[instr.b], (resimm) ? (op3) : 0); \
- } else { \
- tcg_gen_##insn##_tl(cpu_R[instr.b], cpu_R[instr.a], (op3)); \
- } \
-}
-
-gen_i_math_logic(addi, addi, 1, instr.imm16.s)
-gen_i_math_logic(muli, muli, 0, instr.imm16.s)
-
-gen_i_math_logic(andi, andi, 0, instr.imm16.u)
-gen_i_math_logic(ori, ori, 1, instr.imm16.u)
-gen_i_math_logic(xori, xori, 1, instr.imm16.u)
-
-gen_i_math_logic(andhi, andi, 0, instr.imm16.u << 16)
-gen_i_math_logic(orhi , ori, 1, instr.imm16.u << 16)
-gen_i_math_logic(xorhi, xori, 1, instr.imm16.u << 16)
+static void do_i_math_logic(DisasContext *dc, uint32_t insn,
+ GenFn2i *fn, ImmFromIType *imm,
+ bool x_op_0_eq_x)
+{
+ I_TYPE(instr, insn);
+ target_ulong val;
+
+ if (unlikely(instr.b == R_ZERO)) {
+ /* Store to R_ZERO is ignored -- this catches the canonical NOP. */
+ return;
+ }
+
+ val = imm(&instr);
+
+ 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 {
+ fn(dest_gpr(dc, instr.b), load_gpr(dc, instr.a), val);
+ }
+}
+
+#define gen_i_math_logic(fname, insn, x_op_0, imm) \
+ static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \
+ { do_i_math_logic(dc, code, tcg_gen_##insn##_tl, imm, x_op_0); }
+
+gen_i_math_logic(addi, addi, 1, imm_signed)
+gen_i_math_logic(muli, muli, 0, imm_signed)
+
+gen_i_math_logic(andi, andi, 0, imm_unsigned)
+gen_i_math_logic(ori, ori, 1, imm_unsigned)
+gen_i_math_logic(xori, xori, 1, imm_unsigned)
+
+gen_i_math_logic(andhi, andi, 0, imm_shifted)
+gen_i_math_logic(orhi , ori, 1, imm_shifted)
+gen_i_math_logic(xorhi, xori, 1, imm_shifted)
+
+/* rB <- prs.rA + sigma(IMM16) */
+static void rdprs(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+ if (!dc->eic_present) {
+ t_gen_helper_raise_exception(dc, EXCP_ILLEGAL);
+ return;
+ }
+ if (!gen_check_supervisor(dc)) {
+ return;
+ }
+
+#ifdef CONFIG_USER_ONLY
+ g_assert_not_reached();
+#else
+ I_TYPE(instr, code);
+ TCGv dest = dest_gpr(dc, instr.b);
+ gen_helper_rdprs(dest, cpu_env, tcg_constant_i32(instr.a));
+ tcg_gen_addi_tl(dest, dest, instr.imm16.s);
+#endif
+}
/* Prototype only, defined below */
static void handle_r_type_instr(DisasContext *dc, uint32_t code,
@@ -365,7 +470,7 @@ static const Nios2Instruction i_type_instructions[] = {
INSTRUCTION_FLG(gen_stx, MO_SL), /* stwio */
INSTRUCTION_FLG(gen_bxx, TCG_COND_LTU), /* bltu */
INSTRUCTION_FLG(gen_ldx, MO_UL), /* ldwio */
- INSTRUCTION_UNIMPLEMENTED(), /* rdprs */
+ INSTRUCTION(rdprs), /* rdprs */
INSTRUCTION_ILLEGAL(),
INSTRUCTION_FLG(handle_r_type_instr, 0), /* R-Type */
INSTRUCTION_NOP(), /* flushd */
@@ -384,26 +489,51 @@ static const Nios2Instruction i_type_instructions[] = {
*/
static void eret(DisasContext *dc, uint32_t code, uint32_t flags)
{
- tcg_gen_mov_tl(cpu_R[CR_STATUS], cpu_R[CR_ESTATUS]);
- tcg_gen_mov_tl(cpu_R[R_PC], cpu_R[R_EA]);
+ if (!gen_check_supervisor(dc)) {
+ return;
+ }
- dc->base.is_jmp = DISAS_JUMP;
+#ifdef CONFIG_USER_ONLY
+ g_assert_not_reached();
+#else
+ if (FIELD_EX32(dc->tb_flags, TBFLAGS, CRS0)) {
+ TCGv tmp = tcg_temp_new();
+ tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPUNios2State, ctrl[CR_ESTATUS]));
+ gen_helper_eret(cpu_env, tmp, load_gpr(dc, R_EA));
+ tcg_temp_free(tmp);
+ } else {
+ gen_helper_eret(cpu_env, load_gpr(dc, R_SSTATUS), load_gpr(dc, R_EA));
+ }
+ dc->base.is_jmp = DISAS_NORETURN;
+#endif
}
/* PC <- ra */
static void ret(DisasContext *dc, uint32_t code, uint32_t flags)
{
- tcg_gen_mov_tl(cpu_R[R_PC], cpu_R[R_RA]);
-
- dc->base.is_jmp = DISAS_JUMP;
+ gen_jumpr(dc, R_RA, false);
}
-/* PC <- ba */
+/*
+ * status <- bstatus
+ * PC <- ba
+ */
static void bret(DisasContext *dc, uint32_t code, uint32_t flags)
{
- tcg_gen_mov_tl(cpu_R[R_PC], cpu_R[R_BA]);
+ if (!gen_check_supervisor(dc)) {
+ return;
+ }
- dc->base.is_jmp = DISAS_JUMP;
+#ifdef CONFIG_USER_ONLY
+ g_assert_not_reached();
+#else
+ TCGv tmp = tcg_temp_new();
+ tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPUNios2State, ctrl[CR_BSTATUS]));
+ gen_helper_eret(cpu_env, tmp, load_gpr(dc, R_BA));
+ tcg_temp_free(tmp);
+
+ dc->base.is_jmp = DISAS_NORETURN;
+#endif
}
/* PC <- rA */
@@ -411,9 +541,7 @@ static void jmp(DisasContext *dc, uint32_t code, uint32_t flags)
{
R_TYPE(instr, code);
- tcg_gen_mov_tl(cpu_R[R_PC], load_gpr(dc, instr.a));
-
- dc->base.is_jmp = DISAS_JUMP;
+ gen_jumpr(dc, instr.a, false);
}
/* rC <- PC + 4 */
@@ -421,9 +549,7 @@ static void nextpc(DisasContext *dc, uint32_t code, uint32_t flags)
{
R_TYPE(instr, code);
- if (likely(instr.c != R_ZERO)) {
- tcg_gen_movi_tl(cpu_R[instr.c], dc->base.pc_next);
- }
+ tcg_gen_movi_tl(dest_gpr(dc, instr.c), dc->base.pc_next);
}
/*
@@ -434,24 +560,29 @@ static void callr(DisasContext *dc, uint32_t code, uint32_t flags)
{
R_TYPE(instr, code);
- tcg_gen_mov_tl(cpu_R[R_PC], load_gpr(dc, instr.a));
- tcg_gen_movi_tl(cpu_R[R_RA], dc->base.pc_next);
-
- dc->base.is_jmp = DISAS_JUMP;
+ gen_jumpr(dc, instr.a, true);
}
/* rC <- ctlN */
static void rdctl(DisasContext *dc, uint32_t code, uint32_t flags)
{
- R_TYPE(instr, code);
+ if (!gen_check_supervisor(dc)) {
+ return;
+ }
- gen_check_supervisor(dc);
+#ifdef CONFIG_USER_ONLY
+ g_assert_not_reached();
+#else
+ R_TYPE(instr, code);
+ TCGv t1, t2, dest = dest_gpr(dc, instr.c);
- if (unlikely(instr.c == R_ZERO)) {
+ /* Reserved registers read as zero. */
+ if (nios2_cr_reserved(&dc->cr_state[instr.imm5])) {
+ tcg_gen_movi_tl(dest, 0);
return;
}
- switch (instr.imm5 + CR_BASE) {
+ switch (instr.imm5) {
case CR_IPENDING:
/*
* The value of the ipending register is synthetic.
@@ -461,24 +592,44 @@ static void rdctl(DisasContext *dc, uint32_t code, uint32_t flags)
* must perform the AND here, and anywhere else we need the
* guest value of ipending.
*/
- tcg_gen_and_tl(cpu_R[instr.c], cpu_R[CR_IPENDING], cpu_R[CR_IENABLE]);
+ t1 = tcg_temp_new();
+ t2 = tcg_temp_new();
+ tcg_gen_ld_tl(t1, cpu_env, offsetof(CPUNios2State, ctrl[CR_IPENDING]));
+ tcg_gen_ld_tl(t2, cpu_env, offsetof(CPUNios2State, ctrl[CR_IENABLE]));
+ tcg_gen_and_tl(dest, t1, t2);
+ tcg_temp_free(t1);
+ tcg_temp_free(t2);
break;
default:
- tcg_gen_mov_tl(cpu_R[instr.c], cpu_R[instr.imm5 + CR_BASE]);
+ tcg_gen_ld_tl(dest, cpu_env,
+ offsetof(CPUNios2State, ctrl[instr.imm5]));
break;
}
+#endif
}
/* ctlN <- rA */
static void wrctl(DisasContext *dc, uint32_t code, uint32_t flags)
{
- gen_check_supervisor(dc);
+ if (!gen_check_supervisor(dc)) {
+ return;
+ }
-#ifndef CONFIG_USER_ONLY
+#ifdef CONFIG_USER_ONLY
+ g_assert_not_reached();
+#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 + CR_BASE) {
+ switch (instr.imm5) {
case CR_PTEADDR:
gen_helper_mmu_write_pteaddr(cpu_env, v);
break;
@@ -488,145 +639,163 @@ 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_mov_tl(cpu_R[instr.imm5 + CR_BASE], v);
+ 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
}
+/* prs.rC <- rA */
+static void wrprs(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+ if (!dc->eic_present) {
+ t_gen_helper_raise_exception(dc, EXCP_ILLEGAL);
+ return;
+ }
+ if (!gen_check_supervisor(dc)) {
+ return;
+ }
+
+#ifdef CONFIG_USER_ONLY
+ g_assert_not_reached();
+#else
+ R_TYPE(instr, code);
+ gen_helper_wrprs(cpu_env, tcg_constant_i32(instr.c),
+ load_gpr(dc, instr.a));
+ /*
+ * The expected write to PRS[r0] is 0, from CRS[r0].
+ * If not, and CRS == PRS (which we cannot tell from here),
+ * we may now have a non-zero value in our current r0.
+ * By ending the TB, we re-evaluate tb_flags and find out.
+ */
+ if (instr.c == 0
+ && (instr.a != 0 || !FIELD_EX32(dc->tb_flags, TBFLAGS, R0_0))) {
+ dc->base.is_jmp = DISAS_UPDATE;
+ }
+#endif
+}
+
/* Comparison instructions */
static void gen_cmpxx(DisasContext *dc, uint32_t code, uint32_t flags)
{
R_TYPE(instr, code);
- if (likely(instr.c != R_ZERO)) {
- tcg_gen_setcond_tl(flags, cpu_R[instr.c], cpu_R[instr.a],
- cpu_R[instr.b]);
- }
+ tcg_gen_setcond_tl(flags, dest_gpr(dc, instr.c),
+ load_gpr(dc, instr.a), load_gpr(dc, instr.b));
}
/* Math/logic instructions */
-#define gen_r_math_logic(fname, insn, op3) \
-static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \
-{ \
- R_TYPE(instr, (code)); \
- if (likely(instr.c != R_ZERO)) { \
- tcg_gen_##insn(cpu_R[instr.c], load_gpr((dc), instr.a), (op3)); \
- } \
-}
-
-gen_r_math_logic(add, add_tl, load_gpr(dc, instr.b))
-gen_r_math_logic(sub, sub_tl, load_gpr(dc, instr.b))
-gen_r_math_logic(mul, mul_tl, load_gpr(dc, instr.b))
-
-gen_r_math_logic(and, and_tl, load_gpr(dc, instr.b))
-gen_r_math_logic(or, or_tl, load_gpr(dc, instr.b))
-gen_r_math_logic(xor, xor_tl, load_gpr(dc, instr.b))
-gen_r_math_logic(nor, nor_tl, load_gpr(dc, instr.b))
-
-gen_r_math_logic(srai, sari_tl, instr.imm5)
-gen_r_math_logic(srli, shri_tl, instr.imm5)
-gen_r_math_logic(slli, shli_tl, instr.imm5)
-gen_r_math_logic(roli, rotli_tl, instr.imm5)
-
-#define gen_r_mul(fname, insn) \
-static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \
-{ \
- R_TYPE(instr, (code)); \
- if (likely(instr.c != R_ZERO)) { \
- TCGv t0 = tcg_temp_new(); \
- tcg_gen_##insn(t0, cpu_R[instr.c], \
- load_gpr(dc, instr.a), load_gpr(dc, instr.b)); \
- tcg_temp_free(t0); \
- } \
-}
-
-gen_r_mul(mulxss, muls2_tl)
-gen_r_mul(mulxuu, mulu2_tl)
-gen_r_mul(mulxsu, mulsu2_tl)
-
-#define gen_r_shift_s(fname, insn) \
-static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \
-{ \
- R_TYPE(instr, (code)); \
- if (likely(instr.c != R_ZERO)) { \
- TCGv t0 = tcg_temp_new(); \
- tcg_gen_andi_tl(t0, load_gpr((dc), instr.b), 31); \
- tcg_gen_##insn(cpu_R[instr.c], load_gpr((dc), instr.a), t0); \
- tcg_temp_free(t0); \
- } \
-}
-
-gen_r_shift_s(sra, sar_tl)
-gen_r_shift_s(srl, shr_tl)
-gen_r_shift_s(sll, shl_tl)
-gen_r_shift_s(rol, rotl_tl)
-gen_r_shift_s(ror, rotr_tl)
+static void do_ri_math_logic(DisasContext *dc, uint32_t insn, GenFn2i *fn)
+{
+ R_TYPE(instr, insn);
+ fn(dest_gpr(dc, instr.c), load_gpr(dc, instr.a), instr.imm5);
+}
-static void divs(DisasContext *dc, uint32_t code, uint32_t flags)
+static void do_rr_math_logic(DisasContext *dc, uint32_t insn, GenFn3 *fn)
{
- R_TYPE(instr, (code));
+ R_TYPE(instr, insn);
+ fn(dest_gpr(dc, instr.c), load_gpr(dc, instr.a), load_gpr(dc, instr.b));
+}
- /* Stores into R_ZERO are ignored */
- if (unlikely(instr.c == R_ZERO)) {
- return;
- }
+#define gen_ri_math_logic(fname, insn) \
+ static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \
+ { do_ri_math_logic(dc, code, tcg_gen_##insn##_tl); }
+
+#define gen_rr_math_logic(fname, insn) \
+ static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \
+ { do_rr_math_logic(dc, code, tcg_gen_##insn##_tl); }
+
+gen_rr_math_logic(add, add)
+gen_rr_math_logic(sub, sub)
+gen_rr_math_logic(mul, mul)
- TCGv t0 = tcg_temp_new();
- TCGv t1 = tcg_temp_new();
- TCGv t2 = tcg_temp_new();
- TCGv t3 = tcg_temp_new();
+gen_rr_math_logic(and, and)
+gen_rr_math_logic(or, or)
+gen_rr_math_logic(xor, xor)
+gen_rr_math_logic(nor, nor)
- tcg_gen_ext32s_tl(t0, load_gpr(dc, instr.a));
- tcg_gen_ext32s_tl(t1, load_gpr(dc, instr.b));
- tcg_gen_setcondi_tl(TCG_COND_EQ, t2, t0, INT_MIN);
- tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, -1);
- tcg_gen_and_tl(t2, t2, t3);
- tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, 0);
- tcg_gen_or_tl(t2, t2, t3);
- tcg_gen_movi_tl(t3, 0);
- tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1);
- tcg_gen_div_tl(cpu_R[instr.c], t0, t1);
- tcg_gen_ext32s_tl(cpu_R[instr.c], cpu_R[instr.c]);
+gen_ri_math_logic(srai, sari)
+gen_ri_math_logic(srli, shri)
+gen_ri_math_logic(slli, shli)
+gen_ri_math_logic(roli, rotli)
- tcg_temp_free(t3);
- tcg_temp_free(t2);
- tcg_temp_free(t1);
- tcg_temp_free(t0);
+static void do_rr_mul_high(DisasContext *dc, uint32_t insn, GenFn4 *fn)
+{
+ R_TYPE(instr, insn);
+ TCGv discard = tcg_temp_new();
+
+ fn(discard, dest_gpr(dc, instr.c),
+ load_gpr(dc, instr.a), load_gpr(dc, instr.b));
+ tcg_temp_free(discard);
}
-static void divu(DisasContext *dc, uint32_t code, uint32_t flags)
+#define gen_rr_mul_high(fname, insn) \
+ static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \
+ { do_rr_mul_high(dc, code, tcg_gen_##insn##_tl); }
+
+gen_rr_mul_high(mulxss, muls2)
+gen_rr_mul_high(mulxuu, mulu2)
+gen_rr_mul_high(mulxsu, mulsu2)
+
+static void do_rr_shift(DisasContext *dc, uint32_t insn, GenFn3 *fn)
{
- R_TYPE(instr, (code));
+ R_TYPE(instr, insn);
+ TCGv sh = tcg_temp_new();
- /* Stores into R_ZERO are ignored */
- if (unlikely(instr.c == R_ZERO)) {
- return;
- }
+ tcg_gen_andi_tl(sh, load_gpr(dc, instr.b), 31);
+ fn(dest_gpr(dc, instr.c), load_gpr(dc, instr.a), sh);
+ tcg_temp_free(sh);
+}
- TCGv t0 = tcg_temp_new();
- TCGv t1 = tcg_temp_new();
- TCGv t2 = tcg_const_tl(0);
- TCGv t3 = tcg_const_tl(1);
+#define gen_rr_shift(fname, insn) \
+ static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \
+ { do_rr_shift(dc, code, tcg_gen_##insn##_tl); }
- tcg_gen_ext32u_tl(t0, load_gpr(dc, instr.a));
- tcg_gen_ext32u_tl(t1, load_gpr(dc, instr.b));
- tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1);
- tcg_gen_divu_tl(cpu_R[instr.c], t0, t1);
- tcg_gen_ext32s_tl(cpu_R[instr.c], cpu_R[instr.c]);
+gen_rr_shift(sra, sar)
+gen_rr_shift(srl, shr)
+gen_rr_shift(sll, shl)
+gen_rr_shift(rol, rotl)
+gen_rr_shift(ror, rotr)
- tcg_temp_free(t3);
- tcg_temp_free(t2);
- tcg_temp_free(t1);
- tcg_temp_free(t0);
+static void divs(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+ R_TYPE(instr, (code));
+ gen_helper_divs(dest_gpr(dc, instr.c), cpu_env,
+ load_gpr(dc, instr.a), load_gpr(dc, instr.b));
+}
+
+static void divu(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+ R_TYPE(instr, (code));
+ gen_helper_divu(dest_gpr(dc, instr.c), cpu_env,
+ load_gpr(dc, instr.a), load_gpr(dc, instr.b));
}
static void trap(DisasContext *dc, uint32_t code, uint32_t flags)
@@ -644,6 +813,20 @@ static void trap(DisasContext *dc, uint32_t code, uint32_t flags)
t_gen_helper_raise_exception(dc, EXCP_TRAP);
}
+static void gen_break(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+#ifndef CONFIG_USER_ONLY
+ /* The semihosting instruction is "break 1". */
+ R_TYPE(instr, code);
+ if (semihosting_enabled() && instr.imm5 == 1) {
+ t_gen_helper_raise_exception(dc, EXCP_SEMIHOST);
+ return;
+ }
+#endif
+
+ t_gen_helper_raise_exception(dc, EXCP_BREAK);
+}
+
static const Nios2Instruction r_type_instructions[] = {
INSTRUCTION_ILLEGAL(),
INSTRUCTION(eret), /* eret */
@@ -665,7 +848,7 @@ static const Nios2Instruction r_type_instructions[] = {
INSTRUCTION_ILLEGAL(),
INSTRUCTION(slli), /* slli */
INSTRUCTION(sll), /* sll */
- INSTRUCTION_UNIMPLEMENTED(), /* wrprs */
+ INSTRUCTION(wrprs), /* wrprs */
INSTRUCTION_ILLEGAL(),
INSTRUCTION(or), /* or */
INSTRUCTION(mulxsu), /* mulxsu */
@@ -697,7 +880,7 @@ static const Nios2Instruction r_type_instructions[] = {
INSTRUCTION(add), /* add */
INSTRUCTION_ILLEGAL(),
INSTRUCTION_ILLEGAL(),
- INSTRUCTION_FLG(gen_excp, EXCP_BREAK), /* break */
+ INSTRUCTION(gen_break), /* break */
INSTRUCTION_ILLEGAL(),
INSTRUCTION(nop), /* nop */
INSTRUCTION_ILLEGAL(),
@@ -730,7 +913,7 @@ illegal_op:
t_gen_helper_raise_exception(dc, EXCP_ILLEGAL);
}
-static const char * const regnames[] = {
+static const char * const gr_regnames[NUM_GP_REGS] = {
"zero", "at", "r2", "r3",
"r4", "r5", "r6", "r7",
"r8", "r9", "r10", "r11",
@@ -739,16 +922,20 @@ static const char * const regnames[] = {
"r20", "r21", "r22", "r23",
"et", "bt", "gp", "sp",
"fp", "ea", "ba", "ra",
+};
+
+#ifndef CONFIG_USER_ONLY
+static const char * const cr_regnames[NUM_CR_REGS] = {
"status", "estatus", "bstatus", "ienable",
- "ipending", "cpuid", "reserved0", "exception",
+ "ipending", "cpuid", "res6", "exception",
"pteaddr", "tlbacc", "tlbmisc", "reserved1",
"badaddr", "config", "mpubase", "mpuacc",
- "reserved2", "reserved3", "reserved4", "reserved5",
- "reserved6", "reserved7", "reserved8", "reserved9",
- "reserved10", "reserved11", "reserved12", "reserved13",
- "reserved14", "reserved15", "reserved16", "reserved17",
- "rpc"
+ "res16", "res17", "res18", "res19",
+ "res20", "res21", "res22", "res23",
+ "res24", "res25", "res26", "res27",
+ "res28", "res29", "res30", "res31",
};
+#endif
#include "exec/gen-icount.h"
@@ -757,9 +944,13 @@ 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;
+ dc->tb_flags = dc->base.tb->flags;
+ dc->eic_present = cpu->eic_present;
/* Bound the number of insns to execute to those left on the page. */
page_insns = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4;
@@ -796,13 +987,13 @@ static void nios2_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
return;
}
- dc->zero = NULL;
+ dc->sink = NULL;
instr = &i_type_instructions[op];
instr->handler(dc, code, instr->flags);
- if (dc->zero) {
- tcg_temp_free(dc->zero);
+ if (dc->sink) {
+ tcg_temp_free(dc->sink);
}
}
@@ -813,14 +1004,12 @@ static void nios2_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
/* Indicate where the next block should start */
switch (dc->base.is_jmp) {
case DISAS_TOO_MANY:
- case DISAS_UPDATE:
- /* Save the current PC back into the CPU register */
- tcg_gen_movi_tl(cpu_R[R_PC], dc->base.pc_next);
- tcg_gen_exit_tb(NULL, 0);
+ gen_goto_tb(dc, 0, dc->base.pc_next);
break;
- case DISAS_JUMP:
- /* The jump will already have updated the PC register */
+ case DISAS_UPDATE:
+ /* Save the current PC, and return to the main loop. */
+ tcg_gen_movi_tl(cpu_pc, dc->base.pc_next);
tcg_gen_exit_tb(NULL, 0);
break;
@@ -861,41 +1050,67 @@ void nios2_cpu_dump_state(CPUState *cs, FILE *f, int flags)
CPUNios2State *env = &cpu->env;
int i;
- if (!env) {
- return;
- }
-
- qemu_fprintf(f, "IN: PC=%x %s\n",
- env->regs[R_PC], lookup_symbol(env->regs[R_PC]));
+ qemu_fprintf(f, "IN: PC=%x %s\n", env->pc, lookup_symbol(env->pc));
- for (i = 0; i < NUM_CORE_REGS; i++) {
- qemu_fprintf(f, "%9s=%8.8x ", regnames[i], env->regs[i]);
+ for (i = 0; i < NUM_GP_REGS; i++) {
+ qemu_fprintf(f, "%9s=%8.8x ", gr_regnames[i], env->regs[i]);
if ((i + 1) % 4 == 0) {
qemu_fprintf(f, "\n");
}
}
+
#if !defined(CONFIG_USER_ONLY)
- qemu_fprintf(f, " mmu write: VPN=%05X PID %02X TLBACC %08X\n",
- env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK,
- (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4,
- env->mmu.tlbacc_wr);
+ 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");
+ }
+ }
+ }
+ 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");
}
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_CORE_REGS; i++) {
- cpu_R[i] = tcg_global_mem_new(cpu_env,
- offsetof(CPUNios2State, regs[i]),
- regnames[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");
}
void restore_state_to_opc(CPUNios2State *env, TranslationBlock *tb,
target_ulong *data)
{
- env->regs[R_PC] = data[0];
+ env->pc = data[0];
}