diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2019-04-29 17:35:58 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2019-04-29 17:35:58 +0100 |
commit | d87513c0abcbcd856f8e1dee2f2d18903b2c3ea2 (patch) | |
tree | 70102f3c689c04fcb82d6df409c228c0e64f9c95 /target/arm/helper.c | |
parent | ef9aae2522c22c05df17dd898099dd5c3f20d688 (diff) |
target/arm: Honour M-profile FP enable bits
Like AArch64, M-profile floating point has no FPEXC enable
bit to gate floating point; so always set the VFPEN TB flag.
M-profile also has CPACR and NSACR similar to A-profile;
they behave slightly differently:
* the CPACR is banked between Secure and Non-Secure
* if the NSACR forces a trap then this is taken to
the Secure state, not the Non-Secure state
Honour the CPACR and NSACR settings. The NSACR handling
requires us to borrow the exception.target_el field
(usually meaningless for M profile) to distinguish the
NOCP UsageFault taken to Secure state from the more
usual fault taken to the current security state.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20190416125744.27770-6-peter.maydell@linaro.org
Diffstat (limited to 'target/arm/helper.c')
-rw-r--r-- | target/arm/helper.c | 55 |
1 files changed, 52 insertions, 3 deletions
diff --git a/target/arm/helper.c b/target/arm/helper.c index 57ef75b3fc..c3d5fe09cd 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -7556,6 +7556,25 @@ uint32_t arm_phys_excp_target_el(CPUState *cs, uint32_t excp_idx, return target_el; } +/* + * Return true if the v7M CPACR permits access to the FPU for the specified + * security state and privilege level. + */ +static bool v7m_cpacr_pass(CPUARMState *env, bool is_secure, bool is_priv) +{ + switch (extract32(env->v7m.cpacr[is_secure], 20, 2)) { + case 0: + case 2: /* UNPREDICTABLE: we treat like 0 */ + return false; + case 1: + return is_priv; + case 3: + return true; + default: + g_assert_not_reached(); + } +} + static bool v7m_stack_write(ARMCPU *cpu, uint32_t addr, uint32_t value, ARMMMUIdx mmu_idx, bool ignfault) { @@ -8815,9 +8834,23 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs) env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_UNDEFINSTR_MASK; break; case EXCP_NOCP: - armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, env->v7m.secure); - env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_NOCP_MASK; + { + /* + * NOCP might be directed to something other than the current + * security state if this fault is because of NSACR; we indicate + * the target security state using exception.target_el. + */ + int target_secstate; + + if (env->exception.target_el == 3) { + target_secstate = M_REG_S; + } else { + target_secstate = env->v7m.secure; + } + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, target_secstate); + env->v7m.cfsr[target_secstate] |= R_V7M_CFSR_NOCP_MASK; break; + } case EXCP_INVSTATE: armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, env->v7m.secure); env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVSTATE_MASK; @@ -12751,6 +12784,22 @@ int fp_exception_el(CPUARMState *env, int cur_el) return 0; } + if (arm_feature(env, ARM_FEATURE_M)) { + /* CPACR can cause a NOCP UsageFault taken to current security state */ + if (!v7m_cpacr_pass(env, env->v7m.secure, cur_el != 0)) { + return 1; + } + + if (arm_feature(env, ARM_FEATURE_M_SECURITY) && !env->v7m.secure) { + if (!extract32(env->v7m.nsacr, 10, 1)) { + /* FP insns cause a NOCP UsageFault taken to Secure */ + return 3; + } + } + + return 0; + } + /* The CPACR controls traps to EL1, or PL1 if we're 32 bit: * 0, 2 : trap EL0 and EL1/PL1 accesses * 1 : trap only EL0 accesses @@ -12938,7 +12987,7 @@ void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, flags = FIELD_DP32(flags, TBFLAG_A32, SCTLR_B, arm_sctlr_b(env)); flags = FIELD_DP32(flags, TBFLAG_A32, NS, !access_secure_reg(env)); if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30) - || arm_el_is_aa64(env, 1)) { + || arm_el_is_aa64(env, 1) || arm_feature(env, ARM_FEATURE_M)) { flags = FIELD_DP32(flags, TBFLAG_A32, VFPEN, 1); } flags = FIELD_DP32(flags, TBFLAG_A32, XSCALE_CPAR, env->cp15.c15_cpar); |