aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2018-02-09 10:40:28 +0000
committerPeter Maydell <peter.maydell@linaro.org>2018-02-09 10:40:28 +0000
commit95695effe8caa552b8f243bceb3a08de4003c882 (patch)
tree019f6e35e97b939943c6703827ffcf46b0a0fc74
parent600c33f24752a00e81e9372261e35c2befea612b (diff)
downloadqemu-95695effe8caa552b8f243bceb3a08de4003c882.zip
qemu-95695effe8caa552b8f243bceb3a08de4003c882.tar.gz
qemu-95695effe8caa552b8f243bceb3a08de4003c882.tar.bz2
target/arm: Handle exceptions during exception stack pop
Handle possible MPU faults, SAU faults or bus errors when popping register state off the stack during exception return. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 1517324542-6607-8-git-send-email-peter.maydell@linaro.org
-rw-r--r--target/arm/helper.c115
1 files changed, 94 insertions, 21 deletions
diff --git a/target/arm/helper.c b/target/arm/helper.c
index c74f076..c6e3b39 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -6223,6 +6223,67 @@ pend_fault:
return false;
}
+static bool v7m_stack_read(ARMCPU *cpu, uint32_t *dest, uint32_t addr,
+ ARMMMUIdx mmu_idx)
+{
+ CPUState *cs = CPU(cpu);
+ CPUARMState *env = &cpu->env;
+ MemTxAttrs attrs = {};
+ MemTxResult txres;
+ target_ulong page_size;
+ hwaddr physaddr;
+ int prot;
+ ARMMMUFaultInfo fi;
+ bool secure = mmu_idx & ARM_MMU_IDX_M_S;
+ int exc;
+ bool exc_secure;
+ uint32_t value;
+
+ if (get_phys_addr(env, addr, MMU_DATA_LOAD, mmu_idx, &physaddr,
+ &attrs, &prot, &page_size, &fi, NULL)) {
+ /* MPU/SAU lookup failed */
+ if (fi.type == ARMFault_QEMU_SFault) {
+ qemu_log_mask(CPU_LOG_INT,
+ "...SecureFault with SFSR.AUVIOL during unstack\n");
+ env->v7m.sfsr |= R_V7M_SFSR_AUVIOL_MASK | R_V7M_SFSR_SFARVALID_MASK;
+ env->v7m.sfar = addr;
+ exc = ARMV7M_EXCP_SECURE;
+ exc_secure = false;
+ } else {
+ qemu_log_mask(CPU_LOG_INT,
+ "...MemManageFault with CFSR.MUNSTKERR\n");
+ env->v7m.cfsr[secure] |= R_V7M_CFSR_MUNSTKERR_MASK;
+ exc = ARMV7M_EXCP_MEM;
+ exc_secure = secure;
+ }
+ goto pend_fault;
+ }
+
+ value = address_space_ldl(arm_addressspace(cs, attrs), physaddr,
+ attrs, &txres);
+ if (txres != MEMTX_OK) {
+ /* BusFault trying to read the data */
+ qemu_log_mask(CPU_LOG_INT, "...BusFault with BFSR.UNSTKERR\n");
+ env->v7m.cfsr[M_REG_NS] |= R_V7M_CFSR_UNSTKERR_MASK;
+ exc = ARMV7M_EXCP_BUS;
+ exc_secure = false;
+ goto pend_fault;
+ }
+
+ *dest = value;
+ return true;
+
+pend_fault:
+ /* By pending the exception at this point we are making
+ * the IMPDEF choice "overridden exceptions pended" (see the
+ * MergeExcInfo() pseudocode). The other choice would be to not
+ * pend them now and then make a choice about which to throw away
+ * later if we have two derived exceptions.
+ */
+ armv7m_nvic_set_pending(env->nvic, exc, exc_secure);
+ return false;
+}
+
/* Return true if we're using the process stack pointer (not the MSP) */
static bool v7m_using_psp(CPUARMState *env)
{
@@ -6912,6 +6973,11 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
!return_to_handler,
return_to_sp_process);
uint32_t frameptr = *frame_sp_p;
+ bool pop_ok = true;
+ ARMMMUIdx mmu_idx;
+
+ mmu_idx = arm_v7m_mmu_idx_for_secstate_and_priv(env, return_to_secure,
+ !return_to_handler);
if (!QEMU_IS_ALIGNED(frameptr, 8) &&
arm_feature(env, ARM_FEATURE_V8)) {
@@ -6938,29 +7004,38 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
return;
}
- env->regs[4] = ldl_phys(cs->as, frameptr + 0x8);
- env->regs[5] = ldl_phys(cs->as, frameptr + 0xc);
- env->regs[6] = ldl_phys(cs->as, frameptr + 0x10);
- env->regs[7] = ldl_phys(cs->as, frameptr + 0x14);
- env->regs[8] = ldl_phys(cs->as, frameptr + 0x18);
- env->regs[9] = ldl_phys(cs->as, frameptr + 0x1c);
- env->regs[10] = ldl_phys(cs->as, frameptr + 0x20);
- env->regs[11] = ldl_phys(cs->as, frameptr + 0x24);
+ pop_ok =
+ v7m_stack_read(cpu, &env->regs[4], frameptr + 0x8, mmu_idx) &&
+ v7m_stack_read(cpu, &env->regs[4], frameptr + 0x8, mmu_idx) &&
+ v7m_stack_read(cpu, &env->regs[5], frameptr + 0xc, mmu_idx) &&
+ v7m_stack_read(cpu, &env->regs[6], frameptr + 0x10, mmu_idx) &&
+ v7m_stack_read(cpu, &env->regs[7], frameptr + 0x14, mmu_idx) &&
+ v7m_stack_read(cpu, &env->regs[8], frameptr + 0x18, mmu_idx) &&
+ v7m_stack_read(cpu, &env->regs[9], frameptr + 0x1c, mmu_idx) &&
+ v7m_stack_read(cpu, &env->regs[10], frameptr + 0x20, mmu_idx) &&
+ v7m_stack_read(cpu, &env->regs[11], frameptr + 0x24, mmu_idx);
frameptr += 0x28;
}
- /* Pop registers. TODO: make these accesses use the correct
- * attributes and address space (S/NS, priv/unpriv) and handle
- * memory transaction failures.
- */
- env->regs[0] = ldl_phys(cs->as, frameptr);
- env->regs[1] = ldl_phys(cs->as, frameptr + 0x4);
- env->regs[2] = ldl_phys(cs->as, frameptr + 0x8);
- env->regs[3] = ldl_phys(cs->as, frameptr + 0xc);
- env->regs[12] = ldl_phys(cs->as, frameptr + 0x10);
- env->regs[14] = ldl_phys(cs->as, frameptr + 0x14);
- env->regs[15] = ldl_phys(cs->as, frameptr + 0x18);
+ /* Pop registers */
+ pop_ok = pop_ok &&
+ v7m_stack_read(cpu, &env->regs[0], frameptr, mmu_idx) &&
+ v7m_stack_read(cpu, &env->regs[1], frameptr + 0x4, mmu_idx) &&
+ v7m_stack_read(cpu, &env->regs[2], frameptr + 0x8, mmu_idx) &&
+ v7m_stack_read(cpu, &env->regs[3], frameptr + 0xc, mmu_idx) &&
+ v7m_stack_read(cpu, &env->regs[12], frameptr + 0x10, mmu_idx) &&
+ v7m_stack_read(cpu, &env->regs[14], frameptr + 0x14, mmu_idx) &&
+ v7m_stack_read(cpu, &env->regs[15], frameptr + 0x18, mmu_idx) &&
+ v7m_stack_read(cpu, &xpsr, frameptr + 0x1c, mmu_idx);
+
+ if (!pop_ok) {
+ /* v7m_stack_read() pended a fault, so take it (as a tail
+ * chained exception on the same stack frame)
+ */
+ v7m_exception_taken(cpu, excret, true, false);
+ return;
+ }
/* Returning from an exception with a PC with bit 0 set is defined
* behaviour on v8M (bit 0 is ignored), but for v7M it was specified
@@ -6979,8 +7054,6 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
}
}
- xpsr = ldl_phys(cs->as, frameptr + 0x1c);
-
if (arm_feature(env, ARM_FEATURE_V8)) {
/* For v8M we have to check whether the xPSR exception field
* matches the EXCRET value for return to handler/thread