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:
parent
600c33f247
commit
95695effe8
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue