target/arm: Prepare for CONTROL.SPSEL being nonzero in Handler mode

In the v7M architecture, there is an invariant that if the CPU is
in Handler mode then the CONTROL.SPSEL bit cannot be nonzero.
This in turn means that the current stack pointer is always
indicated by CONTROL.SPSEL, even though Handler mode always uses
the Main stack pointer.

In v8M, this invariant is removed, and CONTROL.SPSEL may now
be nonzero in Handler mode (though Handler mode still always
uses the Main stack pointer). In preparation for this change,
change how we handle this bit: rename switch_v7m_sp() to
the now more accurate write_v7m_control_spsel(), and make it
check both the handler mode state and the SPSEL bit.

Note that this implicitly changes the point at which we switch
active SP on exception exit from before we pop the exception
frame to after it.

Backports commit de2db7ec894f11931932ca78cd14a8d2b1389d5b from qemu
This commit is contained in:
Peter Maydell 2018-03-05 01:29:51 -05:00 committed by Lioncash
parent 8036c5b3de
commit c7b5fccfb8
No known key found for this signature in database
GPG key ID: 4E3C3CC1031BA9C7
16 changed files with 66 additions and 37 deletions

View file

@ -2905,7 +2905,6 @@
#define swap_commutative swap_commutative_aarch64
#define swap_commutative2 swap_commutative2_aarch64
#define switch_mode switch_mode_aarch64
#define switch_v7m_sp switch_v7m_sp_aarch64
#define syn_aa32_bkpt syn_aa32_bkpt_aarch64
#define syn_aa32_hvc syn_aa32_hvc_aarch64
#define syn_aa32_smc syn_aa32_smc_aarch64
@ -3422,6 +3421,7 @@
#define write_cpustate_to_list write_cpustate_to_list_aarch64
#define write_list_to_cpustate write_list_to_cpustate_aarch64
#define write_raw_cp_reg write_raw_cp_reg_aarch64
#define write_v7m_exception write_v7m_exception_aarch64
#define x86_ldl_phys x86_ldl_phys_aarch64
#define x86_ldq_phys x86_ldq_phys_aarch64
#define x86_ldub_phys x86_ldub_phys_aarch64

View file

@ -2905,7 +2905,6 @@
#define swap_commutative swap_commutative_aarch64eb
#define swap_commutative2 swap_commutative2_aarch64eb
#define switch_mode switch_mode_aarch64eb
#define switch_v7m_sp switch_v7m_sp_aarch64eb
#define syn_aa32_bkpt syn_aa32_bkpt_aarch64eb
#define syn_aa32_hvc syn_aa32_hvc_aarch64eb
#define syn_aa32_smc syn_aa32_smc_aarch64eb
@ -3422,6 +3421,7 @@
#define write_cpustate_to_list write_cpustate_to_list_aarch64eb
#define write_list_to_cpustate write_list_to_cpustate_aarch64eb
#define write_raw_cp_reg write_raw_cp_reg_aarch64eb
#define write_v7m_exception write_v7m_exception_aarch64eb
#define x86_ldl_phys x86_ldl_phys_aarch64eb
#define x86_ldq_phys x86_ldq_phys_aarch64eb
#define x86_ldub_phys x86_ldub_phys_aarch64eb

View file

@ -2905,7 +2905,6 @@
#define swap_commutative swap_commutative_arm
#define swap_commutative2 swap_commutative2_arm
#define switch_mode switch_mode_arm
#define switch_v7m_sp switch_v7m_sp_arm
#define syn_aa32_bkpt syn_aa32_bkpt_arm
#define syn_aa32_hvc syn_aa32_hvc_arm
#define syn_aa32_smc syn_aa32_smc_arm
@ -3422,6 +3421,7 @@
#define write_cpustate_to_list write_cpustate_to_list_arm
#define write_list_to_cpustate write_list_to_cpustate_arm
#define write_raw_cp_reg write_raw_cp_reg_arm
#define write_v7m_exception write_v7m_exception_arm
#define x86_ldl_phys x86_ldl_phys_arm
#define x86_ldq_phys x86_ldq_phys_arm
#define x86_ldub_phys x86_ldub_phys_arm

View file

@ -2905,7 +2905,6 @@
#define swap_commutative swap_commutative_armeb
#define swap_commutative2 swap_commutative2_armeb
#define switch_mode switch_mode_armeb
#define switch_v7m_sp switch_v7m_sp_armeb
#define syn_aa32_bkpt syn_aa32_bkpt_armeb
#define syn_aa32_hvc syn_aa32_hvc_armeb
#define syn_aa32_smc syn_aa32_smc_armeb
@ -3422,6 +3421,7 @@
#define write_cpustate_to_list write_cpustate_to_list_armeb
#define write_list_to_cpustate write_list_to_cpustate_armeb
#define write_raw_cp_reg write_raw_cp_reg_armeb
#define write_v7m_exception write_v7m_exception_armeb
#define x86_ldl_phys x86_ldl_phys_armeb
#define x86_ldq_phys x86_ldq_phys_armeb
#define x86_ldub_phys x86_ldub_phys_armeb

View file

@ -2911,7 +2911,6 @@ symbols = (
'swap_commutative',
'swap_commutative2',
'switch_mode',
'switch_v7m_sp',
'syn_aa32_bkpt',
'syn_aa32_hvc',
'syn_aa32_smc',
@ -3428,6 +3427,7 @@ symbols = (
'write_cpustate_to_list',
'write_list_to_cpustate',
'write_raw_cp_reg',
'write_v7m_exception',
'x86_ldl_phys',
'x86_ldq_phys',
'x86_ldub_phys',

View file

@ -2905,7 +2905,6 @@
#define swap_commutative swap_commutative_m68k
#define swap_commutative2 swap_commutative2_m68k
#define switch_mode switch_mode_m68k
#define switch_v7m_sp switch_v7m_sp_m68k
#define syn_aa32_bkpt syn_aa32_bkpt_m68k
#define syn_aa32_hvc syn_aa32_hvc_m68k
#define syn_aa32_smc syn_aa32_smc_m68k
@ -3422,6 +3421,7 @@
#define write_cpustate_to_list write_cpustate_to_list_m68k
#define write_list_to_cpustate write_list_to_cpustate_m68k
#define write_raw_cp_reg write_raw_cp_reg_m68k
#define write_v7m_exception write_v7m_exception_m68k
#define x86_ldl_phys x86_ldl_phys_m68k
#define x86_ldq_phys x86_ldq_phys_m68k
#define x86_ldub_phys x86_ldub_phys_m68k

View file

@ -2905,7 +2905,6 @@
#define swap_commutative swap_commutative_mips
#define swap_commutative2 swap_commutative2_mips
#define switch_mode switch_mode_mips
#define switch_v7m_sp switch_v7m_sp_mips
#define syn_aa32_bkpt syn_aa32_bkpt_mips
#define syn_aa32_hvc syn_aa32_hvc_mips
#define syn_aa32_smc syn_aa32_smc_mips
@ -3422,6 +3421,7 @@
#define write_cpustate_to_list write_cpustate_to_list_mips
#define write_list_to_cpustate write_list_to_cpustate_mips
#define write_raw_cp_reg write_raw_cp_reg_mips
#define write_v7m_exception write_v7m_exception_mips
#define x86_ldl_phys x86_ldl_phys_mips
#define x86_ldq_phys x86_ldq_phys_mips
#define x86_ldub_phys x86_ldub_phys_mips

View file

@ -2905,7 +2905,6 @@
#define swap_commutative swap_commutative_mips64
#define swap_commutative2 swap_commutative2_mips64
#define switch_mode switch_mode_mips64
#define switch_v7m_sp switch_v7m_sp_mips64
#define syn_aa32_bkpt syn_aa32_bkpt_mips64
#define syn_aa32_hvc syn_aa32_hvc_mips64
#define syn_aa32_smc syn_aa32_smc_mips64
@ -3422,6 +3421,7 @@
#define write_cpustate_to_list write_cpustate_to_list_mips64
#define write_list_to_cpustate write_list_to_cpustate_mips64
#define write_raw_cp_reg write_raw_cp_reg_mips64
#define write_v7m_exception write_v7m_exception_mips64
#define x86_ldl_phys x86_ldl_phys_mips64
#define x86_ldq_phys x86_ldq_phys_mips64
#define x86_ldub_phys x86_ldub_phys_mips64

View file

@ -2905,7 +2905,6 @@
#define swap_commutative swap_commutative_mips64el
#define swap_commutative2 swap_commutative2_mips64el
#define switch_mode switch_mode_mips64el
#define switch_v7m_sp switch_v7m_sp_mips64el
#define syn_aa32_bkpt syn_aa32_bkpt_mips64el
#define syn_aa32_hvc syn_aa32_hvc_mips64el
#define syn_aa32_smc syn_aa32_smc_mips64el
@ -3422,6 +3421,7 @@
#define write_cpustate_to_list write_cpustate_to_list_mips64el
#define write_list_to_cpustate write_list_to_cpustate_mips64el
#define write_raw_cp_reg write_raw_cp_reg_mips64el
#define write_v7m_exception write_v7m_exception_mips64el
#define x86_ldl_phys x86_ldl_phys_mips64el
#define x86_ldq_phys x86_ldq_phys_mips64el
#define x86_ldub_phys x86_ldub_phys_mips64el

View file

@ -2905,7 +2905,6 @@
#define swap_commutative swap_commutative_mipsel
#define swap_commutative2 swap_commutative2_mipsel
#define switch_mode switch_mode_mipsel
#define switch_v7m_sp switch_v7m_sp_mipsel
#define syn_aa32_bkpt syn_aa32_bkpt_mipsel
#define syn_aa32_hvc syn_aa32_hvc_mipsel
#define syn_aa32_smc syn_aa32_smc_mipsel
@ -3422,6 +3421,7 @@
#define write_cpustate_to_list write_cpustate_to_list_mipsel
#define write_list_to_cpustate write_list_to_cpustate_mipsel
#define write_raw_cp_reg write_raw_cp_reg_mipsel
#define write_v7m_exception write_v7m_exception_mipsel
#define x86_ldl_phys x86_ldl_phys_mipsel
#define x86_ldq_phys x86_ldq_phys_mipsel
#define x86_ldub_phys x86_ldub_phys_mipsel

View file

@ -2905,7 +2905,6 @@
#define swap_commutative swap_commutative_powerpc
#define swap_commutative2 swap_commutative2_powerpc
#define switch_mode switch_mode_powerpc
#define switch_v7m_sp switch_v7m_sp_powerpc
#define syn_aa32_bkpt syn_aa32_bkpt_powerpc
#define syn_aa32_hvc syn_aa32_hvc_powerpc
#define syn_aa32_smc syn_aa32_smc_powerpc
@ -3422,6 +3421,7 @@
#define write_cpustate_to_list write_cpustate_to_list_powerpc
#define write_list_to_cpustate write_list_to_cpustate_powerpc
#define write_raw_cp_reg write_raw_cp_reg_powerpc
#define write_v7m_exception write_v7m_exception_powerpc
#define x86_ldl_phys x86_ldl_phys_powerpc
#define x86_ldq_phys x86_ldq_phys_powerpc
#define x86_ldub_phys x86_ldub_phys_powerpc

View file

@ -2905,7 +2905,6 @@
#define swap_commutative swap_commutative_sparc
#define swap_commutative2 swap_commutative2_sparc
#define switch_mode switch_mode_sparc
#define switch_v7m_sp switch_v7m_sp_sparc
#define syn_aa32_bkpt syn_aa32_bkpt_sparc
#define syn_aa32_hvc syn_aa32_hvc_sparc
#define syn_aa32_smc syn_aa32_smc_sparc
@ -3422,6 +3421,7 @@
#define write_cpustate_to_list write_cpustate_to_list_sparc
#define write_list_to_cpustate write_list_to_cpustate_sparc
#define write_raw_cp_reg write_raw_cp_reg_sparc
#define write_v7m_exception write_v7m_exception_sparc
#define x86_ldl_phys x86_ldl_phys_sparc
#define x86_ldq_phys x86_ldq_phys_sparc
#define x86_ldub_phys x86_ldub_phys_sparc

View file

@ -2905,7 +2905,6 @@
#define swap_commutative swap_commutative_sparc64
#define swap_commutative2 swap_commutative2_sparc64
#define switch_mode switch_mode_sparc64
#define switch_v7m_sp switch_v7m_sp_sparc64
#define syn_aa32_bkpt syn_aa32_bkpt_sparc64
#define syn_aa32_hvc syn_aa32_hvc_sparc64
#define syn_aa32_smc syn_aa32_smc_sparc64
@ -3422,6 +3421,7 @@
#define write_cpustate_to_list write_cpustate_to_list_sparc64
#define write_list_to_cpustate write_list_to_cpustate_sparc64
#define write_raw_cp_reg write_raw_cp_reg_sparc64
#define write_v7m_exception write_v7m_exception_sparc64
#define x86_ldl_phys x86_ldl_phys_sparc64
#define x86_ldq_phys x86_ldq_phys_sparc64
#define x86_ldub_phys x86_ldub_phys_sparc64

View file

@ -963,6 +963,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)
{
@ -1043,7 +1048,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);
}
}

View file

@ -5318,20 +5318,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);
if (old_spsel != new_spsel) {
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;
}
}
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.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;
env->v7m.control[env->v7m.secure] = deposit32(old_control,
R_V7M_CONTROL_SPSEL_SHIFT,
R_V7M_CONTROL_SPSEL_LENGTH, new_spsel);
}
}
@ -5417,13 +5441,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;
@ -5467,7 +5489,7 @@ static void v7m_exception_taken(ARMCPU *cpu, uint32_t lr)
// Unicorn: commented out
//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;
@ -5617,11 +5639,12 @@ 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
* depends on bits in the magic exception return type value (and
@ -8447,11 +8470,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;

View file

@ -2905,7 +2905,6 @@
#define swap_commutative swap_commutative_x86_64
#define swap_commutative2 swap_commutative2_x86_64
#define switch_mode switch_mode_x86_64
#define switch_v7m_sp switch_v7m_sp_x86_64
#define syn_aa32_bkpt syn_aa32_bkpt_x86_64
#define syn_aa32_hvc syn_aa32_hvc_x86_64
#define syn_aa32_smc syn_aa32_smc_x86_64
@ -3422,6 +3421,7 @@
#define write_cpustate_to_list write_cpustate_to_list_x86_64
#define write_list_to_cpustate write_list_to_cpustate_x86_64
#define write_raw_cp_reg write_raw_cp_reg_x86_64
#define write_v7m_exception write_v7m_exception_x86_64
#define x86_ldl_phys x86_ldl_phys_x86_64
#define x86_ldq_phys x86_ldq_phys_x86_64
#define x86_ldub_phys x86_ldub_phys_x86_64