diff options
Diffstat (limited to 'target')
-rw-r--r-- | target/arm/cpu.h | 13 | ||||
-rw-r--r-- | target/arm/helper.c | 79 | ||||
-rw-r--r-- | target/arm/helper.h | 2 | ||||
-rw-r--r-- | target/arm/machine.c | 2 | ||||
-rw-r--r-- | target/arm/translate.c | 42 | ||||
-rw-r--r-- | target/arm/translate.h | 1 |
6 files changed, 138 insertions, 1 deletions
diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 41e270ccdb..0f40a64206 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -419,7 +419,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]; diff --git a/target/arm/helper.c b/target/arm/helper.c index 00807b4053..329e5178d8 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -5870,6 +5870,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); @@ -6044,6 +6050,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) { @@ -6062,6 +6080,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); diff --git a/target/arm/helper.h b/target/arm/helper.h index df86bf7141..64afbac59f 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -63,6 +63,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) diff --git a/target/arm/machine.c b/target/arm/machine.c index 0bcaa68d18..e5fe083da4 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -257,6 +257,8 @@ static const VMStateDescription vmstate_m_security = { .needed = m_security_needed, .fields = (VMStateField[]) { VMSTATE_UINT32(env.v7m.secure, ARMCPU), + VMSTATE_UINT32(env.v7m.other_ss_msp, ARMCPU), + VMSTATE_UINT32(env.v7m.other_ss_psp, ARMCPU), VMSTATE_UINT32(env.v7m.basepri[M_REG_S], ARMCPU), VMSTATE_UINT32(env.v7m.primask[M_REG_S], ARMCPU), VMSTATE_UINT32(env.v7m.faultmask[M_REG_S], ARMCPU), diff --git a/target/arm/translate.c b/target/arm/translate.c index 6aa2d7c10e..e7966e20ac 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -994,6 +994,25 @@ static inline void gen_bx_excret_final_code(DisasContext *s) gen_exception_internal(EXCP_EXCEPTION_EXIT); } +static inline void gen_bxns(DisasContext *s, int rm) +{ + 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(cpu_env, var); + tcg_temp_free_i32(var); + s->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. */ @@ -11185,12 +11204,31 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) */ 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) { val = (uint32_t)s->pc | 1; @@ -11878,6 +11916,8 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) dc->vec_stride = ARM_TBFLAG_VECSTRIDE(tb->flags); dc->c15_cpar = ARM_TBFLAG_XSCALE_CPAR(tb->flags); dc->v7m_handler_mode = ARM_TBFLAG_HANDLER(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; diff --git a/target/arm/translate.h b/target/arm/translate.h index 2fe144baa9..ef625adaa2 100644 --- a/target/arm/translate.h +++ b/target/arm/translate.h @@ -32,6 +32,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. */ |