target/arm: Add v8M stack checks on exception entry

Add checks for breaches of the v8M stack limit when the
stack pointer is decremented to push the exception frame
for exception entry.

Note that the exception-entry case is unique in that the
stack pointer is updated to be the limit value if the limit
is hit (per rule R_ZLZG).

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20181002163556.10279-7-peter.maydell@linaro.org
master
Peter Maydell 2018-10-08 14:55:04 +01:00
parent a2d12f0f34
commit c32da7aa62
1 changed files with 46 additions and 8 deletions

View File

@ -6867,6 +6867,8 @@ static bool v7m_push_callee_stack(ARMCPU *cpu, uint32_t lr, bool dotailchain,
uint32_t frameptr;
ARMMMUIdx mmu_idx;
bool stacked_ok;
uint32_t limit;
bool want_psp;
if (dotailchain) {
bool mode = lr & R_V7M_EXCRET_MODE_MASK;
@ -6876,12 +6878,34 @@ static bool v7m_push_callee_stack(ARMCPU *cpu, uint32_t lr, bool dotailchain,
mmu_idx = arm_v7m_mmu_idx_for_secstate_and_priv(env, M_REG_S, priv);
frame_sp_p = get_v7m_sp_ptr(env, M_REG_S, mode,
lr & R_V7M_EXCRET_SPSEL_MASK);
want_psp = mode && (lr & R_V7M_EXCRET_SPSEL_MASK);
if (want_psp) {
limit = env->v7m.psplim[M_REG_S];
} else {
limit = env->v7m.msplim[M_REG_S];
}
} else {
mmu_idx = core_to_arm_mmu_idx(env, cpu_mmu_index(env, false));
frame_sp_p = &env->regs[13];
limit = v7m_sp_limit(env);
}
frameptr = *frame_sp_p - 0x28;
if (frameptr < limit) {
/*
* Stack limit failure: set SP to the limit value, and generate
* STKOF UsageFault. Stack pushes below the limit must not be
* performed. It is IMPDEF whether pushes above the limit are
* performed; we choose not to.
*/
qemu_log_mask(CPU_LOG_INT,
"...STKOF during callee-saves register stacking\n");
env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_STKOF_MASK;
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE,
env->v7m.secure);
*frame_sp_p = limit;
return true;
}
/* Write as much of the stack frame as we can. A write failure may
* cause us to pend a derived exception.
@ -6905,10 +6929,7 @@ static bool v7m_push_callee_stack(ARMCPU *cpu, uint32_t lr, bool dotailchain,
v7m_stack_write(cpu, frameptr + 0x24, env->regs[11], mmu_idx,
ignore_faults);
/* Update SP regardless of whether any of the stack accesses failed.
* When we implement v8M stack limit checking then this attempt to
* update SP might also fail and result in a derived exception.
*/
/* Update SP regardless of whether any of the stack accesses failed. */
*frame_sp_p = frameptr;
return !stacked_ok;
@ -7056,6 +7077,26 @@ static bool v7m_push_stack(ARMCPU *cpu)
frameptr -= 0x20;
if (arm_feature(env, ARM_FEATURE_V8)) {
uint32_t limit = v7m_sp_limit(env);
if (frameptr < limit) {
/*
* Stack limit failure: set SP to the limit value, and generate
* STKOF UsageFault. Stack pushes below the limit must not be
* performed. It is IMPDEF whether pushes above the limit are
* performed; we choose not to.
*/
qemu_log_mask(CPU_LOG_INT,
"...STKOF during stacking\n");
env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_STKOF_MASK;
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE,
env->v7m.secure);
env->regs[13] = limit;
return true;
}
}
/* Write as much of the stack frame as we can. If we fail a stack
* write this will result in a derived exception being pended
* (which may be taken in preference to the one we started with
@ -7071,10 +7112,7 @@ static bool v7m_push_stack(ARMCPU *cpu)
v7m_stack_write(cpu, frameptr + 24, env->regs[15], mmu_idx, false) &&
v7m_stack_write(cpu, frameptr + 28, xpsr, mmu_idx, false);
/* Update SP regardless of whether any of the stack accesses failed.
* When we implement v8M stack limit checking then this attempt to
* update SP might also fail and result in a derived exception.
*/
/* Update SP regardless of whether any of the stack accesses failed. */
env->regs[13] = frameptr;
return !stacked_ok;