summaryrefslogtreecommitdiff
path: root/target
diff options
context:
space:
mode:
Diffstat (limited to 'target')
-rw-r--r--target/arm/cpu.h8
-rw-r--r--target/arm/helper.c65
2 files changed, 50 insertions, 23 deletions
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index 8afceca873..ad6eff498b 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -991,6 +991,11 @@ void pmccntr_sync(CPUARMState *env);
#define PSTATE_MODE_EL1t 4
#define PSTATE_MODE_EL0t 0
+/* Write a new value to v7m.exception, thus transitioning into or out
+ * of Handler mode; this may result in a change of active stack pointer.
+ */
+void write_v7m_exception(CPUARMState *env, uint32_t new_exc);
+
/* Map EL and handler into a PSTATE_MODE. */
static inline unsigned int aarch64_pstate_mode(unsigned int el, bool handler)
{
@@ -1071,7 +1076,8 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
env->condexec_bits |= (val >> 8) & 0xfc;
}
if (mask & XPSR_EXCP) {
- env->v7m.exception = val & XPSR_EXCP;
+ /* Note that this only happens on exception exit */
+ write_v7m_exception(env, val & XPSR_EXCP);
}
}
diff --git a/target/arm/helper.c b/target/arm/helper.c
index 7548d4c6ec..36173e21a7 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -6059,21 +6059,44 @@ static bool v7m_using_psp(CPUARMState *env)
env->v7m.control[env->v7m.secure] & R_V7M_CONTROL_SPSEL_MASK;
}
-/* Switch to V7M main or process stack pointer. */
-static void switch_v7m_sp(CPUARMState *env, bool new_spsel)
+/* 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;
- uint32_t old_control = env->v7m.control[env->v7m.secure];
- bool old_spsel = old_control & R_V7M_CONTROL_SPSEL_MASK;
+ 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_spsel != new_spsel) {
+ if (old_is_psp != new_is_psp) {
tmp = env->v7m.other_sp;
env->v7m.other_sp = env->regs[13];
env->regs[13] = tmp;
+ }
+}
+
+void write_v7m_exception(CPUARMState *env, uint32_t new_exc)
+{
+ /* Write a new value to v7m.exception, thus transitioning into or out
+ * of Handler mode; this may result in a change of active stack pointer.
+ */
+ bool new_is_psp, old_is_psp = v7m_using_psp(env);
+ uint32_t tmp;
- env->v7m.control[env->v7m.secure] = deposit32(old_control,
- R_V7M_CONTROL_SPSEL_SHIFT,
- R_V7M_CONTROL_SPSEL_LENGTH, new_spsel);
+ env->v7m.exception = new_exc;
+
+ 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;
}
}
@@ -6159,13 +6182,11 @@ static uint32_t *get_v7m_sp_ptr(CPUARMState *env, bool secure, bool threadmode,
bool want_psp = threadmode && spsel;
if (secure == env->v7m.secure) {
- /* Currently switch_v7m_sp switches SP as it updates SPSEL,
- * so the SP we want is always in regs[13].
- * When we decouple SPSEL from the actually selected SP
- * we need to check want_psp against v7m_using_psp()
- * to see whether we need regs[13] or v7m.other_sp.
- */
- return &env->regs[13];
+ if (want_psp == v7m_using_psp(env)) {
+ return &env->regs[13];
+ } else {
+ return &env->v7m.other_sp;
+ }
} else {
if (want_psp) {
return &env->v7m.other_ss_psp;
@@ -6208,7 +6229,7 @@ static void v7m_exception_taken(ARMCPU *cpu, uint32_t lr)
uint32_t addr;
armv7m_nvic_acknowledge_irq(env->nvic);
- switch_v7m_sp(env, 0);
+ write_v7m_control_spsel(env, 0);
arm_clear_exclusive(env);
/* Clear IT bits */
env->condexec_bits = 0;
@@ -6354,11 +6375,11 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
return;
}
- /* Set CONTROL.SPSEL from excret.SPSEL. For QEMU this currently
- * causes us to switch the active SP, but we will change this
- * later to not do that so we can support v8M.
+ /* Set CONTROL.SPSEL from excret.SPSEL. Since we're still in
+ * Handler mode (and will be until we write the new XPSR.Interrupt
+ * field) this does not switch around the current stack pointer.
*/
- switch_v7m_sp(env, return_to_sp_process);
+ write_v7m_control_spsel(env, return_to_sp_process);
{
/* The stack pointer we should be reading the exception frame from
@@ -9173,11 +9194,11 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t maskreg, uint32_t val)
case 20: /* CONTROL */
/* Writing to the SPSEL bit only has an effect if we are in
* thread mode; other bits can be updated by any privileged code.
- * switch_v7m_sp() deals with updating the SPSEL bit in
+ * write_v7m_control_spsel() deals with updating the SPSEL bit in
* env->v7m.control, so we only need update the others.
*/
if (!arm_v7m_is_handler_mode(env)) {
- switch_v7m_sp(env, (val & R_V7M_CONTROL_SPSEL_MASK) != 0);
+ write_v7m_control_spsel(env, (val & R_V7M_CONTROL_SPSEL_MASK) != 0);
}
env->v7m.control[env->v7m.secure] &= ~R_V7M_CONTROL_NPRIV_MASK;
env->v7m.control[env->v7m.secure] |= val & R_V7M_CONTROL_NPRIV_MASK;