target/arm: Restore SPSEL to correct CONTROL register on exception return

On exception return for v8M, the SPSEL bit in the EXC_RETURN magic
value should be restored to the SPSEL bit in the CONTROL register
banked specified by the EXC_RETURN.ES bit.

Add write_v7m_control_spsel_for_secstate() which behaves like
write_v7m_control_spsel() but allows the caller to specify which
CONTROL bank to use, reimplement write_v7m_control_spsel() in
terms of it, and use it in exception return.

Backports commit 3f0cddeee1f266d43c956581f3050058360a810d from qemu
This commit is contained in:
Peter Maydell 2018-03-05 01:33:40 -05:00 committed by Lioncash
parent 0bb50b9a7e
commit 6f08acdcfe
No known key found for this signature in database
GPG key ID: 4E3C3CC1031BA9C7

View file

@ -5318,26 +5318,39 @@ static bool v7m_using_psp(CPUARMState *env)
env->v7m.control[env->v7m.secure] & R_V7M_CONTROL_SPSEL_MASK;
}
/* Write to v7M CONTROL.SPSEL bit for the specified security bank.
* This may change the current stack pointer between Main and Process
* stack pointers if it is done for the CONTROL register for the current
* security state.
*/
static void write_v7m_control_spsel_for_secstate(CPUARMState *env,
bool new_spsel,
bool secstate)
{
bool old_is_psp = v7m_using_psp(env);
env->v7m.control[secstate] =
deposit32(env->v7m.control[secstate],
R_V7M_CONTROL_SPSEL_SHIFT,
R_V7M_CONTROL_SPSEL_LENGTH, new_spsel);
if (secstate == env->v7m.secure) {
bool new_is_psp = v7m_using_psp(env);
uint32_t tmp;
if (old_is_psp != new_is_psp) {
tmp = env->v7m.other_sp;
env->v7m.other_sp = env->regs[13];
env->regs[13] = tmp;
}
}
}
/* Write to v7M CONTROL.SPSEL bit. This may change the current
* stack pointer between Main and Process stack pointers.
*/
static void write_v7m_control_spsel(CPUARMState *env, bool new_spsel)
{
uint32_t tmp;
bool new_is_psp, old_is_psp = v7m_using_psp(env);
env->v7m.control[env->v7m.secure] =
deposit32(env->v7m.control[env->v7m.secure],
R_V7M_CONTROL_SPSEL_SHIFT,
R_V7M_CONTROL_SPSEL_LENGTH, new_spsel);
new_is_psp = v7m_using_psp(env);
if (old_is_psp != new_is_psp) {
tmp = env->v7m.other_sp;
env->v7m.other_sp = env->regs[13];
env->regs[13] = tmp;
}
write_v7m_control_spsel_for_secstate(env, new_spsel, env->v7m.secure);
}
void write_v7m_exception(CPUARMState *env, uint32_t new_exc)
@ -5535,6 +5548,7 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
bool return_to_sp_process = false;
bool return_to_handler = false;
bool rettobase = false;
bool exc_secure = false;
bool return_to_secure;
/* We can only get here from an EXCP_EXCEPTION_EXIT, and
@ -5573,13 +5587,13 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
* value of the ES bit in the exception return value indicates
* which security state's faultmask to clear. (v8M ARM ARM R_KBNF.)
*/
/* Unicorn: commented out
if (arm_feature(env, ARM_FEATURE_M_SECURITY)) {
int es = excret & R_V7M_EXCRET_ES_MASK;
if (armv7m_nvic_raw_execution_priority(env->nvic) >= 0) {
env->v7m.faultmask[es] = 0;
}
} else*/ {
exc_secure = excret & R_V7M_EXCRET_ES_MASK;
// Unicorn: commented out
//if (armv7m_nvic_raw_execution_priority(env->nvic) >= 0) {
// env->v7m.faultmask[es] = 0;
//}
} else {
env->v7m.faultmask[M_REG_NS] = 0;
}
}
@ -5643,7 +5657,7 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
* Handler mode (and will be until we write the new XPSR.Interrupt
* field) this does not switch around the current stack pointer.
*/
write_v7m_control_spsel(env, return_to_sp_process);
write_v7m_control_spsel_for_secstate(env, return_to_sp_process, exc_secure);
switch_v7m_security_state(env, return_to_secure);