aboutsummaryrefslogtreecommitdiff
path: root/target
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2017-02-28 14:50:15 +0000
committerPeter Maydell <peter.maydell@linaro.org>2017-02-28 14:50:17 +0000
commit1bbe5dc66b770d7bedd1d51d7935da948a510dd6 (patch)
tree4fd4e08fe462461373606d16d7e3ccc397f8d2dc /target
parentc8c0a1a784cdf70ecea50e93213137c6c89337a7 (diff)
parentf3a6339a5bbc160d327299c67bb68c6d07fa4a61 (diff)
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20170228' into staging
target-arm queue: * raspi2: implement RNG module * raspi2: implement new SD card controller (but don't wire it up) * sdhci: bugfixes for block transfers * virt: fix cpu object reference leak * Add missing fp_access_check() to aarch64 crypto instructions * cputlb: Don't assume do_unassigned_access() never returns * virt: Add a user option to disallow ITS instantiation * i.MX timers: fix reset handling * ARMv7M NVIC: rewrite to fix broken priority handling and masking * exynos: Fix proper mapping of CPUs by providing real cluster ID * exynos: Fix Linux kernel division by zero for PLLs # gpg: Signature made Tue 28 Feb 2017 12:40:51 GMT # gpg: using RSA key 0x3C2525ED14360CDE # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" # gpg: aka "Peter Maydell <pmaydell@gmail.com>" # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * remotes/pmaydell/tags/pull-target-arm-20170228: (27 commits) hw/arm/exynos: Fix proper mapping of CPUs by providing real cluster ID hw/arm/exynos: Fix Linux kernel division by zero for PLLs bcm2835_sdhost: add bcm2835 sdhost controller armv7m: Allow SHCSR writes to change pending and active bits armv7m: Raise correct kind of UsageFault for attempts to execute ARM code armv7m: Check exception return consistency armv7m: Extract "exception taken" code into functions armv7m: VECTCLRACTIVE and VECTRESET are UNPREDICTABLE armv7m: Simpler and faster exception start armv7m: Remove unused armv7m_nvic_acknowledge_irq() return value armv7m: Escalate exceptions to HardFault if necessary arm: gic: Remove references to NVIC armv7m: Fix condition check for taking exceptions armv7m: Rewrite NVIC to not use any GIC code armv7m: Implement reading and writing of PRIGROUP armv7m: Rename nvic_state to NVICState ARM i.MX timers: fix reset handling hw/arm/virt: Add a user option to disallow ITS instantiation cputlb: Don't assume do_unassigned_access() never returns Add missing fp_access_check() to aarch64 crypto instructions ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'target')
-rw-r--r--target/arm/cpu.c16
-rw-r--r--target/arm/cpu.h23
-rw-r--r--target/arm/helper.c245
-rw-r--r--target/arm/translate-a64.c12
-rw-r--r--target/arm/translate.c8
5 files changed, 226 insertions, 78 deletions
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index f7157dc0e5..04b062cb7e 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -338,13 +338,6 @@ static bool arm_v7m_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
CPUARMState *env = &cpu->env;
bool ret = false;
-
- if (interrupt_request & CPU_INTERRUPT_FIQ
- && !(env->daif & PSTATE_F)) {
- cs->exception_index = EXCP_FIQ;
- cc->do_interrupt(cs);
- ret = true;
- }
/* ARMv7-M interrupt return works by loading a magic value
* into the PC. On real hardware the load causes the
* return to occur. The qemu implementation performs the
@@ -354,9 +347,16 @@ static bool arm_v7m_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
* the stack if an interrupt occurred at the wrong time.
* We avoid this by disabling interrupts when
* pc contains a magic address.
+ *
+ * ARMv7-M interrupt masking works differently than -A or -R.
+ * There is no FIQ/IRQ distinction. Instead of I and F bits
+ * masking FIQ and IRQ interrupts, an exception is taken only
+ * if it is higher priority than the current execution priority
+ * (which depends on state like BASEPRI, FAULTMASK and the
+ * currently active exception).
*/
if (interrupt_request & CPU_INTERRUPT_HARD
- && !(env->daif & PSTATE_I)
+ && (armv7m_nvic_can_take_pending_exception(env->nvic))
&& (env->regs[15] < 0xfffffff0)) {
cs->exception_index = EXCP_IRQ;
cc->do_interrupt(cs);
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index 38a8e00908..9e7b2dfc83 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -57,6 +57,7 @@
#define EXCP_VFIQ 15
#define EXCP_SEMIHOST 16 /* semihosting call */
#define EXCP_NOCP 17 /* v7M NOCP UsageFault */
+#define EXCP_INVSTATE 18 /* v7M INVSTATE UsageFault */
#define ARMV7M_EXCP_RESET 1
#define ARMV7M_EXCP_NMI 2
@@ -1356,9 +1357,27 @@ uint32_t arm_phys_excp_target_el(CPUState *cs, uint32_t excp_idx,
uint32_t cur_el, bool secure);
/* Interface between CPU and Interrupt controller. */
+#ifndef CONFIG_USER_ONLY
+bool armv7m_nvic_can_take_pending_exception(void *opaque);
+#else
+static inline bool armv7m_nvic_can_take_pending_exception(void *opaque)
+{
+ return true;
+}
+#endif
void armv7m_nvic_set_pending(void *opaque, int irq);
-int armv7m_nvic_acknowledge_irq(void *opaque);
-void armv7m_nvic_complete_irq(void *opaque, int irq);
+void armv7m_nvic_acknowledge_irq(void *opaque);
+/**
+ * armv7m_nvic_complete_irq: complete specified interrupt or exception
+ * @opaque: the NVIC
+ * @irq: the exception number to complete
+ *
+ * Returns: -1 if the irq was not active
+ * 1 if completing this irq brought us back to base (no active irqs)
+ * 0 if there is still an irq active after this one was completed
+ * (Ignoring -1, this is the same as the RETTOBASE value before completion.)
+ */
+int armv7m_nvic_complete_irq(void *opaque, int irq);
/* Interface for defining coprocessor registers.
* Registers are defined in tables of arm_cp_reginfo structs
diff --git a/target/arm/helper.c b/target/arm/helper.c
index bcedb4a808..3f4211b572 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -6002,22 +6002,165 @@ static void switch_v7m_sp(CPUARMState *env, bool new_spsel)
}
}
-static void do_v7m_exception_exit(CPUARMState *env)
+static uint32_t arm_v7m_load_vector(ARMCPU *cpu)
{
+ CPUState *cs = CPU(cpu);
+ CPUARMState *env = &cpu->env;
+ MemTxResult result;
+ hwaddr vec = env->v7m.vecbase + env->v7m.exception * 4;
+ uint32_t addr;
+
+ addr = address_space_ldl(cs->as, vec,
+ MEMTXATTRS_UNSPECIFIED, &result);
+ if (result != MEMTX_OK) {
+ /* Architecturally this should cause a HardFault setting HSFR.VECTTBL,
+ * which would then be immediately followed by our failing to load
+ * the entry vector for that HardFault, which is a Lockup case.
+ * Since we don't model Lockup, we just report this guest error
+ * via cpu_abort().
+ */
+ cpu_abort(cs, "Failed to read from exception vector table "
+ "entry %08x\n", (unsigned)vec);
+ }
+ return addr;
+}
+
+static void v7m_exception_taken(ARMCPU *cpu, uint32_t lr)
+{
+ /* Do the "take the exception" parts of exception entry,
+ * but not the pushing of state to the stack. This is
+ * similar to the pseudocode ExceptionTaken() function.
+ */
+ CPUARMState *env = &cpu->env;
+ uint32_t addr;
+
+ armv7m_nvic_acknowledge_irq(env->nvic);
+ switch_v7m_sp(env, 0);
+ /* Clear IT bits */
+ env->condexec_bits = 0;
+ env->regs[14] = lr;
+ addr = arm_v7m_load_vector(cpu);
+ env->regs[15] = addr & 0xfffffffe;
+ env->thumb = addr & 1;
+}
+
+static void v7m_push_stack(ARMCPU *cpu)
+{
+ /* Do the "set up stack frame" part of exception entry,
+ * similar to pseudocode PushStack().
+ */
+ CPUARMState *env = &cpu->env;
+ uint32_t xpsr = xpsr_read(env);
+
+ /* Align stack pointer if the guest wants that */
+ if ((env->regs[13] & 4) && (env->v7m.ccr & R_V7M_CCR_STKALIGN_MASK)) {
+ env->regs[13] -= 4;
+ xpsr |= 0x200;
+ }
+ /* Switch to the handler mode. */
+ v7m_push(env, xpsr);
+ v7m_push(env, env->regs[15]);
+ v7m_push(env, env->regs[14]);
+ v7m_push(env, env->regs[12]);
+ v7m_push(env, env->regs[3]);
+ v7m_push(env, env->regs[2]);
+ v7m_push(env, env->regs[1]);
+ v7m_push(env, env->regs[0]);
+}
+
+static void do_v7m_exception_exit(ARMCPU *cpu)
+{
+ CPUARMState *env = &cpu->env;
uint32_t type;
uint32_t xpsr;
-
+ bool ufault = false;
+ bool return_to_sp_process = false;
+ bool return_to_handler = false;
+ bool rettobase = false;
+
+ /* We can only get here from an EXCP_EXCEPTION_EXIT, and
+ * arm_v7m_do_unassigned_access() enforces the architectural rule
+ * that jumps to magic addresses don't have magic behaviour unless
+ * we're in Handler mode (compare pseudocode BXWritePC()).
+ */
+ assert(env->v7m.exception != 0);
+
+ /* In the spec pseudocode ExceptionReturn() is called directly
+ * from BXWritePC() and gets the full target PC value including
+ * bit zero. In QEMU's implementation we treat it as a normal
+ * jump-to-register (which is then caught later on), and so split
+ * the target value up between env->regs[15] and env->thumb in
+ * gen_bx(). Reconstitute it.
+ */
type = env->regs[15];
+ if (env->thumb) {
+ type |= 1;
+ }
+
+ qemu_log_mask(CPU_LOG_INT, "Exception return: magic PC %" PRIx32
+ " previous exception %d\n",
+ type, env->v7m.exception);
+
+ if (extract32(type, 5, 23) != extract32(-1, 5, 23)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "M profile: zero high bits in exception "
+ "exit PC value 0x%" PRIx32 " are UNPREDICTABLE\n", type);
+ }
+
if (env->v7m.exception != ARMV7M_EXCP_NMI) {
/* Auto-clear FAULTMASK on return from other than NMI */
env->daif &= ~PSTATE_F;
}
- if (env->v7m.exception != 0) {
- armv7m_nvic_complete_irq(env->nvic, env->v7m.exception);
+
+ switch (armv7m_nvic_complete_irq(env->nvic, env->v7m.exception)) {
+ case -1:
+ /* attempt to exit an exception that isn't active */
+ ufault = true;
+ break;
+ case 0:
+ /* still an irq active now */
+ break;
+ case 1:
+ /* we returned to base exception level, no nesting.
+ * (In the pseudocode this is written using "NestedActivation != 1"
+ * where we have 'rettobase == false'.)
+ */
+ rettobase = true;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ switch (type & 0xf) {
+ case 1: /* Return to Handler */
+ return_to_handler = true;
+ break;
+ case 13: /* Return to Thread using Process stack */
+ return_to_sp_process = true;
+ /* fall through */
+ case 9: /* Return to Thread using Main stack */
+ if (!rettobase &&
+ !(env->v7m.ccr & R_V7M_CCR_NONBASETHRDENA_MASK)) {
+ ufault = true;
+ }
+ break;
+ default:
+ ufault = true;
+ }
+
+ if (ufault) {
+ /* Bad exception return: instead of popping the exception
+ * stack, directly take a usage fault on the current stack.
+ */
+ env->v7m.cfsr |= R_V7M_CFSR_INVPC_MASK;
+ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE);
+ v7m_exception_taken(cpu, type | 0xf0000000);
+ qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on existing "
+ "stackframe: failed exception return integrity check\n");
+ return;
}
/* Switch to the target stack. */
- switch_v7m_sp(env, (type & 4) != 0);
+ switch_v7m_sp(env, return_to_sp_process);
/* Pop registers. */
env->regs[0] = v7m_pop(env);
env->regs[1] = v7m_pop(env);
@@ -6041,11 +6184,24 @@ static void do_v7m_exception_exit(CPUARMState *env)
/* Undo stack alignment. */
if (xpsr & 0x200)
env->regs[13] |= 4;
- /* ??? The exception return type specifies Thread/Handler mode. However
- this is also implied by the xPSR value. Not sure what to do
- if there is a mismatch. */
- /* ??? Likewise for mismatches between the CONTROL register and the stack
- pointer. */
+
+ /* The restored xPSR exception field will be zero if we're
+ * resuming in Thread mode. If that doesn't match what the
+ * exception return type specified then this is a UsageFault.
+ */
+ if (return_to_handler == (env->v7m.exception == 0)) {
+ /* Take an INVPC UsageFault by pushing the stack again. */
+ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE);
+ env->v7m.cfsr |= R_V7M_CFSR_INVPC_MASK;
+ v7m_push_stack(cpu);
+ v7m_exception_taken(cpu, type | 0xf0000000);
+ qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on new stackframe: "
+ "failed exception return integrity check\n");
+ return;
+ }
+
+ /* Otherwise, we have a successful exception exit. */
+ qemu_log_mask(CPU_LOG_INT, "...successful exception return\n");
}
static void arm_log_exception(int idx)
@@ -6063,37 +6219,11 @@ static void arm_log_exception(int idx)
}
}
-static uint32_t arm_v7m_load_vector(ARMCPU *cpu)
-
-{
- CPUState *cs = CPU(cpu);
- CPUARMState *env = &cpu->env;
- MemTxResult result;
- hwaddr vec = env->v7m.vecbase + env->v7m.exception * 4;
- uint32_t addr;
-
- addr = address_space_ldl(cs->as, vec,
- MEMTXATTRS_UNSPECIFIED, &result);
- if (result != MEMTX_OK) {
- /* Architecturally this should cause a HardFault setting HSFR.VECTTBL,
- * which would then be immediately followed by our failing to load
- * the entry vector for that HardFault, which is a Lockup case.
- * Since we don't model Lockup, we just report this guest error
- * via cpu_abort().
- */
- cpu_abort(cs, "Failed to read from exception vector table "
- "entry %08x\n", (unsigned)vec);
- }
- return addr;
-}
-
void arm_v7m_cpu_do_interrupt(CPUState *cs)
{
ARMCPU *cpu = ARM_CPU(cs);
CPUARMState *env = &cpu->env;
- uint32_t xpsr = xpsr_read(env);
uint32_t lr;
- uint32_t addr;
arm_log_exception(cs->exception_index);
@@ -6106,28 +6236,30 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
/* For exceptions we just mark as pending on the NVIC, and let that
handle it. */
- /* TODO: Need to escalate if the current priority is higher than the
- one we're raising. */
switch (cs->exception_index) {
case EXCP_UDEF:
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE);
env->v7m.cfsr |= R_V7M_CFSR_UNDEFINSTR_MASK;
- return;
+ break;
case EXCP_NOCP:
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE);
env->v7m.cfsr |= R_V7M_CFSR_NOCP_MASK;
- return;
+ break;
+ case EXCP_INVSTATE:
+ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE);
+ env->v7m.cfsr |= R_V7M_CFSR_INVSTATE_MASK;
+ break;
case EXCP_SWI:
/* The PC already points to the next instruction. */
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SVC);
- return;
+ break;
case EXCP_PREFETCH_ABORT:
case EXCP_DATA_ABORT:
/* TODO: if we implemented the MPU registers, this is where we
* should set the MMFAR, etc from exception.fsr and exception.vaddress.
*/
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_MEM);
- return;
+ break;
case EXCP_BKPT:
if (semihosting_enabled()) {
int nr;
@@ -6142,39 +6274,20 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
}
}
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_DEBUG);
- return;
+ break;
case EXCP_IRQ:
- env->v7m.exception = armv7m_nvic_acknowledge_irq(env->nvic);
break;
case EXCP_EXCEPTION_EXIT:
- do_v7m_exception_exit(env);
+ do_v7m_exception_exit(cpu);
return;
default:
cpu_abort(cs, "Unhandled exception 0x%x\n", cs->exception_index);
return; /* Never happens. Keep compiler happy. */
}
- /* Align stack pointer if the guest wants that */
- if ((env->regs[13] & 4) && (env->v7m.ccr & R_V7M_CCR_STKALIGN_MASK)) {
- env->regs[13] -= 4;
- xpsr |= 0x200;
- }
- /* Switch to the handler mode. */
- v7m_push(env, xpsr);
- v7m_push(env, env->regs[15]);
- v7m_push(env, env->regs[14]);
- v7m_push(env, env->regs[12]);
- v7m_push(env, env->regs[3]);
- v7m_push(env, env->regs[2]);
- v7m_push(env, env->regs[1]);
- v7m_push(env, env->regs[0]);
- switch_v7m_sp(env, 0);
- /* Clear IT bits */
- env->condexec_bits = 0;
- env->regs[14] = lr;
- addr = arm_v7m_load_vector(cpu);
- env->regs[15] = addr & 0xfffffffe;
- env->thumb = addr & 1;
+ v7m_push_stack(cpu);
+ v7m_exception_taken(cpu, lr);
+ qemu_log_mask(CPU_LOG_INT, "... as %d\n", env->v7m.exception);
}
/* Function used to synchronize QEMU's AArch64 register set with AArch32
diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c
index e15eae6d41..24de30d92c 100644
--- a/target/arm/translate-a64.c
+++ b/target/arm/translate-a64.c
@@ -10933,6 +10933,10 @@ static void disas_crypto_aes(DisasContext *s, uint32_t insn)
return;
}
+ if (!fp_access_check(s)) {
+ return;
+ }
+
/* Note that we convert the Vx register indexes into the
* index within the vfp.regs[] array, so we can share the
* helper with the AArch32 instructions.
@@ -10997,6 +11001,10 @@ static void disas_crypto_three_reg_sha(DisasContext *s, uint32_t insn)
return;
}
+ if (!fp_access_check(s)) {
+ return;
+ }
+
tcg_rd_regno = tcg_const_i32(rd << 1);
tcg_rn_regno = tcg_const_i32(rn << 1);
tcg_rm_regno = tcg_const_i32(rm << 1);
@@ -11060,6 +11068,10 @@ static void disas_crypto_two_reg_sha(DisasContext *s, uint32_t insn)
return;
}
+ if (!fp_access_check(s)) {
+ return;
+ }
+
tcg_rd_regno = tcg_const_i32(rd << 1);
tcg_rn_regno = tcg_const_i32(rn << 1);
diff --git a/target/arm/translate.c b/target/arm/translate.c
index abc1f77ee4..b859f10755 100644
--- a/target/arm/translate.c
+++ b/target/arm/translate.c
@@ -7990,9 +7990,13 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
TCGv_i32 addr;
TCGv_i64 tmp64;
- /* M variants do not implement ARM mode. */
+ /* M variants do not implement ARM mode; this must raise the INVSTATE
+ * UsageFault exception.
+ */
if (arm_dc_feature(s, ARM_FEATURE_M)) {
- goto illegal_op;
+ gen_exception_insn(s, 4, EXCP_INVSTATE, syn_uncategorized(),
+ default_exception_el(s));
+ return;
}
cond = insn >> 28;
if (cond == 0xf){