diff options
Diffstat (limited to 'target/arm/helper.c')
-rw-r--r-- | target/arm/helper.c | 79 |
1 files changed, 79 insertions, 0 deletions
diff --git a/target/arm/helper.c b/target/arm/helper.c index 00807b4..329e517 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); |