target/arm: Implement BXNS, and banked stack pointers

Implement the BXNS v8M instruction, which is like BX but will do a
jump-and-switch-to-NonSecure if the branch target address has bit 0
clear.

This is the first piece of code which implements "switch to the
other security state", so the commit also includes the code to
switch the stack pointers around, which is the only complicated
part of switching security state.

BLXNS is more complicated than just "BXNS but set the link register",
so we leave it for a separate commit.

Backports commit fb602cb726b3ebdd01ef3b1732d74baf9fee7ec9 from qemu
This commit is contained in:
Peter Maydell 2018-03-04 21:18:56 -05:00 committed by Lioncash
parent 221232fb35
commit 4b816fe0aa
No known key found for this signature in database
GPG key ID: 4E3C3CC1031BA9C7
19 changed files with 165 additions and 1 deletions

View file

@ -1093,6 +1093,7 @@
#define gen_helper_usub8 gen_helper_usub8_aarch64
#define gen_helper_usubaddx gen_helper_usubaddx_aarch64
#define gen_helper_uxtb16 gen_helper_uxtb16_aarch64
#define gen_helper_v7m_bxns gen_helper_v7m_bxns_aarch64
#define gen_helper_v7m_mrs gen_helper_v7m_mrs_aarch64
#define gen_helper_v7m_msr gen_helper_v7m_msr_aarch64
#define gen_helper_vfp_absd gen_helper_vfp_absd_aarch64
@ -2052,6 +2053,7 @@
#define helper_usub8 helper_usub8_aarch64
#define helper_usubaddx helper_usubaddx_aarch64
#define helper_uxtb16 helper_uxtb16_aarch64
#define helper_v7m_bxns helper_v7m_bxns_aarch64
#define helper_v7m_mrs helper_v7m_mrs_aarch64
#define helper_v7m_msr helper_v7m_msr_aarch64
#define helper_vfp_absd helper_vfp_absd_aarch64

View file

@ -1093,6 +1093,7 @@
#define gen_helper_usub8 gen_helper_usub8_aarch64eb
#define gen_helper_usubaddx gen_helper_usubaddx_aarch64eb
#define gen_helper_uxtb16 gen_helper_uxtb16_aarch64eb
#define gen_helper_v7m_bxns gen_helper_v7m_bxns_aarch64eb
#define gen_helper_v7m_mrs gen_helper_v7m_mrs_aarch64eb
#define gen_helper_v7m_msr gen_helper_v7m_msr_aarch64eb
#define gen_helper_vfp_absd gen_helper_vfp_absd_aarch64eb
@ -2052,6 +2053,7 @@
#define helper_usub8 helper_usub8_aarch64eb
#define helper_usubaddx helper_usubaddx_aarch64eb
#define helper_uxtb16 helper_uxtb16_aarch64eb
#define helper_v7m_bxns helper_v7m_bxns_aarch64eb
#define helper_v7m_mrs helper_v7m_mrs_aarch64eb
#define helper_v7m_msr helper_v7m_msr_aarch64eb
#define helper_vfp_absd helper_vfp_absd_aarch64eb

View file

@ -1093,6 +1093,7 @@
#define gen_helper_usub8 gen_helper_usub8_arm
#define gen_helper_usubaddx gen_helper_usubaddx_arm
#define gen_helper_uxtb16 gen_helper_uxtb16_arm
#define gen_helper_v7m_bxns gen_helper_v7m_bxns_arm
#define gen_helper_v7m_mrs gen_helper_v7m_mrs_arm
#define gen_helper_v7m_msr gen_helper_v7m_msr_arm
#define gen_helper_vfp_absd gen_helper_vfp_absd_arm
@ -2052,6 +2053,7 @@
#define helper_usub8 helper_usub8_arm
#define helper_usubaddx helper_usubaddx_arm
#define helper_uxtb16 helper_uxtb16_arm
#define helper_v7m_bxns helper_v7m_bxns_arm
#define helper_v7m_mrs helper_v7m_mrs_arm
#define helper_v7m_msr helper_v7m_msr_arm
#define helper_vfp_absd helper_vfp_absd_arm

View file

@ -1093,6 +1093,7 @@
#define gen_helper_usub8 gen_helper_usub8_armeb
#define gen_helper_usubaddx gen_helper_usubaddx_armeb
#define gen_helper_uxtb16 gen_helper_uxtb16_armeb
#define gen_helper_v7m_bxns gen_helper_v7m_bxns_armeb
#define gen_helper_v7m_mrs gen_helper_v7m_mrs_armeb
#define gen_helper_v7m_msr gen_helper_v7m_msr_armeb
#define gen_helper_vfp_absd gen_helper_vfp_absd_armeb
@ -2052,6 +2053,7 @@
#define helper_usub8 helper_usub8_armeb
#define helper_usubaddx helper_usubaddx_armeb
#define helper_uxtb16 helper_uxtb16_armeb
#define helper_v7m_bxns helper_v7m_bxns_armeb
#define helper_v7m_mrs helper_v7m_mrs_armeb
#define helper_v7m_msr helper_v7m_msr_armeb
#define helper_vfp_absd helper_vfp_absd_armeb

View file

@ -1099,6 +1099,7 @@ symbols = (
'gen_helper_usub8',
'gen_helper_usubaddx',
'gen_helper_uxtb16',
'gen_helper_v7m_bxns',
'gen_helper_v7m_mrs',
'gen_helper_v7m_msr',
'gen_helper_vfp_absd',
@ -2058,6 +2059,7 @@ symbols = (
'helper_usub8',
'helper_usubaddx',
'helper_uxtb16',
'helper_v7m_bxns',
'helper_v7m_mrs',
'helper_v7m_msr',
'helper_vfp_absd',

View file

@ -1093,6 +1093,7 @@
#define gen_helper_usub8 gen_helper_usub8_m68k
#define gen_helper_usubaddx gen_helper_usubaddx_m68k
#define gen_helper_uxtb16 gen_helper_uxtb16_m68k
#define gen_helper_v7m_bxns gen_helper_v7m_bxns_m68k
#define gen_helper_v7m_mrs gen_helper_v7m_mrs_m68k
#define gen_helper_v7m_msr gen_helper_v7m_msr_m68k
#define gen_helper_vfp_absd gen_helper_vfp_absd_m68k
@ -2052,6 +2053,7 @@
#define helper_usub8 helper_usub8_m68k
#define helper_usubaddx helper_usubaddx_m68k
#define helper_uxtb16 helper_uxtb16_m68k
#define helper_v7m_bxns helper_v7m_bxns_m68k
#define helper_v7m_mrs helper_v7m_mrs_m68k
#define helper_v7m_msr helper_v7m_msr_m68k
#define helper_vfp_absd helper_vfp_absd_m68k

View file

@ -1093,6 +1093,7 @@
#define gen_helper_usub8 gen_helper_usub8_mips
#define gen_helper_usubaddx gen_helper_usubaddx_mips
#define gen_helper_uxtb16 gen_helper_uxtb16_mips
#define gen_helper_v7m_bxns gen_helper_v7m_bxns_mips
#define gen_helper_v7m_mrs gen_helper_v7m_mrs_mips
#define gen_helper_v7m_msr gen_helper_v7m_msr_mips
#define gen_helper_vfp_absd gen_helper_vfp_absd_mips
@ -2052,6 +2053,7 @@
#define helper_usub8 helper_usub8_mips
#define helper_usubaddx helper_usubaddx_mips
#define helper_uxtb16 helper_uxtb16_mips
#define helper_v7m_bxns helper_v7m_bxns_mips
#define helper_v7m_mrs helper_v7m_mrs_mips
#define helper_v7m_msr helper_v7m_msr_mips
#define helper_vfp_absd helper_vfp_absd_mips

View file

@ -1093,6 +1093,7 @@
#define gen_helper_usub8 gen_helper_usub8_mips64
#define gen_helper_usubaddx gen_helper_usubaddx_mips64
#define gen_helper_uxtb16 gen_helper_uxtb16_mips64
#define gen_helper_v7m_bxns gen_helper_v7m_bxns_mips64
#define gen_helper_v7m_mrs gen_helper_v7m_mrs_mips64
#define gen_helper_v7m_msr gen_helper_v7m_msr_mips64
#define gen_helper_vfp_absd gen_helper_vfp_absd_mips64
@ -2052,6 +2053,7 @@
#define helper_usub8 helper_usub8_mips64
#define helper_usubaddx helper_usubaddx_mips64
#define helper_uxtb16 helper_uxtb16_mips64
#define helper_v7m_bxns helper_v7m_bxns_mips64
#define helper_v7m_mrs helper_v7m_mrs_mips64
#define helper_v7m_msr helper_v7m_msr_mips64
#define helper_vfp_absd helper_vfp_absd_mips64

View file

@ -1093,6 +1093,7 @@
#define gen_helper_usub8 gen_helper_usub8_mips64el
#define gen_helper_usubaddx gen_helper_usubaddx_mips64el
#define gen_helper_uxtb16 gen_helper_uxtb16_mips64el
#define gen_helper_v7m_bxns gen_helper_v7m_bxns_mips64el
#define gen_helper_v7m_mrs gen_helper_v7m_mrs_mips64el
#define gen_helper_v7m_msr gen_helper_v7m_msr_mips64el
#define gen_helper_vfp_absd gen_helper_vfp_absd_mips64el
@ -2052,6 +2053,7 @@
#define helper_usub8 helper_usub8_mips64el
#define helper_usubaddx helper_usubaddx_mips64el
#define helper_uxtb16 helper_uxtb16_mips64el
#define helper_v7m_bxns helper_v7m_bxns_mips64el
#define helper_v7m_mrs helper_v7m_mrs_mips64el
#define helper_v7m_msr helper_v7m_msr_mips64el
#define helper_vfp_absd helper_vfp_absd_mips64el

View file

@ -1093,6 +1093,7 @@
#define gen_helper_usub8 gen_helper_usub8_mipsel
#define gen_helper_usubaddx gen_helper_usubaddx_mipsel
#define gen_helper_uxtb16 gen_helper_uxtb16_mipsel
#define gen_helper_v7m_bxns gen_helper_v7m_bxns_mipsel
#define gen_helper_v7m_mrs gen_helper_v7m_mrs_mipsel
#define gen_helper_v7m_msr gen_helper_v7m_msr_mipsel
#define gen_helper_vfp_absd gen_helper_vfp_absd_mipsel
@ -2052,6 +2053,7 @@
#define helper_usub8 helper_usub8_mipsel
#define helper_usubaddx helper_usubaddx_mipsel
#define helper_uxtb16 helper_uxtb16_mipsel
#define helper_v7m_bxns helper_v7m_bxns_mipsel
#define helper_v7m_mrs helper_v7m_mrs_mipsel
#define helper_v7m_msr helper_v7m_msr_mipsel
#define helper_vfp_absd helper_vfp_absd_mipsel

View file

@ -1093,6 +1093,7 @@
#define gen_helper_usub8 gen_helper_usub8_powerpc
#define gen_helper_usubaddx gen_helper_usubaddx_powerpc
#define gen_helper_uxtb16 gen_helper_uxtb16_powerpc
#define gen_helper_v7m_bxns gen_helper_v7m_bxns_powerpc
#define gen_helper_v7m_mrs gen_helper_v7m_mrs_powerpc
#define gen_helper_v7m_msr gen_helper_v7m_msr_powerpc
#define gen_helper_vfp_absd gen_helper_vfp_absd_powerpc
@ -2052,6 +2053,7 @@
#define helper_usub8 helper_usub8_powerpc
#define helper_usubaddx helper_usubaddx_powerpc
#define helper_uxtb16 helper_uxtb16_powerpc
#define helper_v7m_bxns helper_v7m_bxns_powerpc
#define helper_v7m_mrs helper_v7m_mrs_powerpc
#define helper_v7m_msr helper_v7m_msr_powerpc
#define helper_vfp_absd helper_vfp_absd_powerpc

View file

@ -1093,6 +1093,7 @@
#define gen_helper_usub8 gen_helper_usub8_sparc
#define gen_helper_usubaddx gen_helper_usubaddx_sparc
#define gen_helper_uxtb16 gen_helper_uxtb16_sparc
#define gen_helper_v7m_bxns gen_helper_v7m_bxns_sparc
#define gen_helper_v7m_mrs gen_helper_v7m_mrs_sparc
#define gen_helper_v7m_msr gen_helper_v7m_msr_sparc
#define gen_helper_vfp_absd gen_helper_vfp_absd_sparc
@ -2052,6 +2053,7 @@
#define helper_usub8 helper_usub8_sparc
#define helper_usubaddx helper_usubaddx_sparc
#define helper_uxtb16 helper_uxtb16_sparc
#define helper_v7m_bxns helper_v7m_bxns_sparc
#define helper_v7m_mrs helper_v7m_mrs_sparc
#define helper_v7m_msr helper_v7m_msr_sparc
#define helper_vfp_absd helper_vfp_absd_sparc

View file

@ -1093,6 +1093,7 @@
#define gen_helper_usub8 gen_helper_usub8_sparc64
#define gen_helper_usubaddx gen_helper_usubaddx_sparc64
#define gen_helper_uxtb16 gen_helper_uxtb16_sparc64
#define gen_helper_v7m_bxns gen_helper_v7m_bxns_sparc64
#define gen_helper_v7m_mrs gen_helper_v7m_mrs_sparc64
#define gen_helper_v7m_msr gen_helper_v7m_msr_sparc64
#define gen_helper_vfp_absd gen_helper_vfp_absd_sparc64
@ -2052,6 +2053,7 @@
#define helper_usub8 helper_usub8_sparc64
#define helper_usubaddx helper_usubaddx_sparc64
#define helper_uxtb16 helper_uxtb16_sparc64
#define helper_v7m_bxns helper_v7m_bxns_sparc64
#define helper_v7m_mrs helper_v7m_mrs_sparc64
#define helper_v7m_msr helper_v7m_msr_sparc64
#define helper_vfp_absd helper_vfp_absd_sparc64

View file

@ -424,7 +424,20 @@ typedef struct CPUARMState {
} cp15;
struct {
/* M profile has up to 4 stack pointers:
* a Main Stack Pointer and a Process Stack Pointer for each
* of the Secure and Non-Secure states. (If the CPU doesn't support
* the security extension then it has only two SPs.)
* In QEMU we always store the currently active SP in regs[13],
* and the non-active SP for the current security state in
* v7m.other_sp. The stack pointers for the inactive security state
* are stored in other_ss_msp and other_ss_psp.
* switch_v7m_security_state() is responsible for rearranging them
* when we change security state.
*/
uint32_t other_sp;
uint32_t other_ss_msp;
uint32_t other_ss_psp;
uint32_t vecbase[2];
uint32_t basepri[2];
uint32_t control[2];

View file

@ -5129,6 +5129,12 @@ uint32_t HELPER(v7m_mrs)(CPUARMState *env, uint32_t reg)
return 0;
}
void HELPER(v7m_bxns)(CPUARMState *env, uint32_t dest)
{
/* translate.c should never generate calls here in user-only mode */
g_assert_not_reached();
}
void switch_mode(CPUARMState *env, int mode)
{
ARMCPU *cpu = arm_env_get_cpu(env);
@ -5303,6 +5309,18 @@ static uint32_t v7m_pop(CPUARMState *env)
return val;
}
/* Return true if we're using the process stack pointer (not the MSP) */
static bool v7m_using_psp(CPUARMState *env)
{
/* Handler mode always uses the main stack; for thread mode
* the CONTROL.SPSEL bit determines the answer.
* Note that in v7M it is not possible to be in Handler mode with
* CONTROL.SPSEL non-zero, but in v8M it is, so we must check both.
*/
return !arm_v7m_is_handler_mode(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)
{
@ -5320,6 +5338,67 @@ static void switch_v7m_sp(CPUARMState *env, bool new_spsel)
}
}
/* Switch M profile security state between NS and S */
static void switch_v7m_security_state(CPUARMState *env, bool new_secstate)
{
uint32_t new_ss_msp, new_ss_psp;
if (env->v7m.secure == new_secstate) {
return;
}
/* All the banked state is accessed by looking at env->v7m.secure
* except for the stack pointer; rearrange the SP appropriately.
*/
new_ss_msp = env->v7m.other_ss_msp;
new_ss_psp = env->v7m.other_ss_psp;
if (v7m_using_psp(env)) {
env->v7m.other_ss_psp = env->regs[13];
env->v7m.other_ss_msp = env->v7m.other_sp;
} else {
env->v7m.other_ss_msp = env->regs[13];
env->v7m.other_ss_psp = env->v7m.other_sp;
}
env->v7m.secure = new_secstate;
if (v7m_using_psp(env)) {
env->regs[13] = new_ss_psp;
env->v7m.other_sp = new_ss_msp;
} else {
env->regs[13] = new_ss_msp;
env->v7m.other_sp = new_ss_psp;
}
}
void HELPER(v7m_bxns)(CPUARMState *env, uint32_t dest)
{
/* Handle v7M BXNS:
* - if the return value is a magic value, do exception return (like BX)
* - otherwise bit 0 of the return value is the target security state
*/
if (dest >= 0xff000000) {
/* This is an exception return magic value; put it where
* do_v7m_exception_exit() expects and raise EXCEPTION_EXIT.
* Note that if we ever add gen_ss_advance() singlestep support to
* M profile this should count as an "instruction execution complete"
* event (compare gen_bx_excret_final_code()).
*/
env->regs[15] = dest & ~1;
env->thumb = dest & 1;
HELPER(exception_internal)(env, EXCP_EXCEPTION_EXIT);
/* notreached */
}
/* translate.c should have made BXNS UNDEF unless we're secure */
assert(env->v7m.secure);
switch_v7m_security_state(env, dest & 1);
env->thumb = 1;
env->regs[15] = dest & ~1;
}
static uint32_t arm_v7m_load_vector(ARMCPU *cpu)
{
CPUState *cs = CPU(cpu);

View file

@ -65,6 +65,8 @@ DEF_HELPER_1(cpsr_read, i32, env)
DEF_HELPER_3(v7m_msr, void, env, i32, i32)
DEF_HELPER_2(v7m_mrs, i32, env, i32)
DEF_HELPER_2(v7m_bxns, void, env, i32)
DEF_HELPER_4(access_check_cp_reg, void, env, ptr, i32, i32)
DEF_HELPER_3(set_cp_reg, void, env, ptr, i32)
DEF_HELPER_2(get_cp_reg, i32, env, ptr)

View file

@ -1023,6 +1023,26 @@ static inline void gen_bx_excret_final_code(DisasContext *s)
gen_exception_internal(s, EXCP_EXCEPTION_EXIT);
}
static inline void gen_bxns(DisasContext *s, int rm)
{
TCGContext *tcg_ctx = s->uc->tcg_ctx;
TCGv_i32 var = load_reg(s, rm);
/* The bxns helper may raise an EXCEPTION_EXIT exception, so in theory
* we need to sync state before calling it, but:
* - we don't need to do gen_set_pc_im() because the bxns helper will
* always set the PC itself
* - we don't need to do gen_set_condexec() because BXNS is UNPREDICTABLE
* unless it's outside an IT block or the last insn in an IT block,
* so we know that condexec == 0 (already set at the top of the TB)
* is correct in the non-UNPREDICTABLE cases, and we can choose
* "zeroes the IT bits" as our UNPREDICTABLE behaviour otherwise.
*/
gen_helper_v7m_bxns(tcg_ctx, tcg_ctx->cpu_env, var);
tcg_temp_free_i32(tcg_ctx, var);
s->base.is_jmp = DISAS_EXIT;
}
/* Variant of store_reg which uses branch&exchange logic when storing
to r15 in ARM architecture v7 and above. The source must be a temporary
and will be marked as dead. */
@ -11391,12 +11411,31 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) // qq
*/
bool link = insn & (1 << 7);
if (insn & 7) {
if (insn & 3) {
goto undef;
}
if (link) {
ARCH(5);
}
if ((insn & 4)) {
/* BXNS/BLXNS: only exists for v8M with the
* security extensions, and always UNDEF if NonSecure.
* We don't implement these in the user-only mode
* either (in theory you can use them from Secure User
* mode but they are too tied in to system emulation.)
*/
if (!s->v8m_secure || IS_USER_ONLY) {
goto undef;
}
if (link) {
/* BLXNS: not yet implemented */
goto undef;
} else {
gen_bxns(s, rm);
}
break;
}
/* BLX/BX */
tmp = load_reg(s, rm);
if (link) {
ARCH(5);
@ -12067,6 +12106,8 @@ static int arm_tr_init_disas_context(DisasContextBase *dcbase,
dc->vec_stride = ARM_TBFLAG_VECSTRIDE(dc->base.tb->flags);
dc->c15_cpar = ARM_TBFLAG_XSCALE_CPAR(dc->base.tb->flags);
dc->v7m_handler_mode = ARM_TBFLAG_HANDLER(dc->base.tb->flags);
dc->v8m_secure = arm_feature(env, ARM_FEATURE_M_SECURITY) &&
regime_is_secure(env, dc->mmu_idx);
dc->cp_regs = cpu->cp_regs;
dc->features = env->features;

View file

@ -34,6 +34,7 @@ typedef struct DisasContext {
int vec_len;
int vec_stride;
bool v7m_handler_mode;
bool v8m_secure; /* true if v8M and we're in Secure mode */
/* Immediate value in AArch32 SVC insn; must be set if is_jmp == DISAS_SWI
* so that top level loop can generate correct syndrome information.
*/

View file

@ -1093,6 +1093,7 @@
#define gen_helper_usub8 gen_helper_usub8_x86_64
#define gen_helper_usubaddx gen_helper_usubaddx_x86_64
#define gen_helper_uxtb16 gen_helper_uxtb16_x86_64
#define gen_helper_v7m_bxns gen_helper_v7m_bxns_x86_64
#define gen_helper_v7m_mrs gen_helper_v7m_mrs_x86_64
#define gen_helper_v7m_msr gen_helper_v7m_msr_x86_64
#define gen_helper_vfp_absd gen_helper_vfp_absd_x86_64
@ -2052,6 +2053,7 @@
#define helper_usub8 helper_usub8_x86_64
#define helper_usubaddx helper_usubaddx_x86_64
#define helper_uxtb16 helper_uxtb16_x86_64
#define helper_v7m_bxns helper_v7m_bxns_x86_64
#define helper_v7m_mrs helper_v7m_mrs_x86_64
#define helper_v7m_msr helper_v7m_msr_x86_64
#define helper_vfp_absd helper_vfp_absd_x86_64