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
This commit is contained in:
Peter Maydell 2018-02-09 10:40:28 +00:00
parent 600c33f247
commit 95695effe8

View File

@ -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