[PATCH v2 5/8] target/arm: Implement CPACR.ASEDIS and HCPTR.TASE
Peter Maydell <[email protected]>
| Newsgroups | org.nongnu.qemu-arm,org.nongnu.qemu-devel |
|---|---|
| Message-ID | <[email protected]> |
When executing at AArch32, there are optional trap bits for Neon instructions in CPACR and HCPTR. We don't currently implement these. Now we have a separate code path for access checks for Neon insns, we can straightforwardly add the check there. We need to track the target EL for Neon-specific trapping in a new TB flag. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/1499 Signed-off-by: Peter Maydell <[email protected]> --- target/arm/cpu.h | 6 +++ target/arm/tcg/hflags.c | 82 ++++++++++++++++++++++++++++++++++ target/arm/tcg/translate-vfp.c | 16 +++++-- target/arm/tcg/translate.c | 1 + target/arm/tcg/translate.h | 1 + 5 files changed, 103 insertions(+), 3 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 4f07332543..afc3fda3b8 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -2506,6 +2506,12 @@ FIELD(TBFLAG_A32, NS, 10, 1) * This requires an SME trap from AArch32 mode when using NEON. */ FIELD(TBFLAG_A32, SME_TRAP_NONSTREAMING, 11, 1) +/* + * Target EL for a Neon-disabled exception via CPACR.ASEDIS, HCPTR.TASE. + * If FPEXC_EL indicates a trap to a lower EL than this, that will + * take precedence. + */ +FIELD(TBFLAG_A32, NEONEXC_EL, 12, 2) /* * Bit usage when in AArch32 state, for M-profile only. diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c index 0716ca98fd..14824e3dff 100644 --- a/target/arm/tcg/hflags.c +++ b/target/arm/tcg/hflags.c @@ -164,6 +164,86 @@ static bool sme_fa64(CPUARMState *env, int el) return true; } +static int neon_exception_el(CPUARMState *env, int cur_el) +{ + /* + * Return the EL to trap to for A32 Neon specific traps + * (CPACR.ASEDIS and HCPTR.TASE). In the pseudocode these are + * checked in the same function as the more general trap bits that + * we handle in fp_exception_el(). Fortunately it is always the + * case that if the trap/enable bits specify taking an exception + * to different ELs for the Neon-specific insns and the general fp + * insns then the trap to the lower of the two ELs has priority, + * so we can calculate the two target ELs separately and pick the + * right destination later. Compare AArch32_CheckAdvSIMDOrFPEnabled(). + * + * CPACR doesn't exist before v6, but neither does Neon, so we can + * assume that if we're here testing this then the register exists. + * HCPTR always exists if EL2 is present. + */ + uint64_t hcr_el2 = arm_hcr_el2_eff(env); + bool cpacr_asedis = FIELD_EX64(env->cp15.cpacr_el1, CPACR, ASEDIS); + bool hcptr_tase = FIELD_EX64(env->cp15.cptr_el[2], HCPTR, TASE); + bool have_aarch32_el3 = + arm_feature(env, ARM_FEATURE_EL3) && !arm_el_is_aa64(env, 3); + + if (!arm_feature(env, ARM_FEATURE_NEON_TRAPS)) { + /* This CPU doesn't implement the trap bits (Cortex-A8) */ + return 0; + } + + if (arm_feature(env, ARM_FEATURE_EL2) && arm_el_is_aa64(env, 2)) { + /* + * The AArch64 CPTR_EL2 has no equivalent to HCPTR.TASE; only + * an AArch32 EL2 can trap Neon specifically. + */ + hcptr_tase = false; + } + + /* + * We know we're in AArch32, but if this is EL0 and EL1 is AArch64 + * then CPACR_EL1 applies rather than CPACR, and it doesn't have + * ASEDIS (instead using the same bit for TCPAC). + */ + if (cur_el == 0 && arm_el_is_aa64(env, 1)) { + cpacr_asedis = false; + } + + /* CPACR is ignored if E2H+TGE are both set */ + if ((hcr_el2 & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE)) { + cpacr_asedis = false; + } + + /* + * NSACR.NSASEDIS makes the effective values of HCPTR.TASE and + * CPACR.ASEDIS be 1 in NonSecure state. NSACR has no + * effect unless EL3 exists and is AArch32. + */ + if (have_aarch32_el3 && cur_el <= 2 && !arm_is_secure_below_el3(env)) { + if (FIELD_EX32(env->cp15.nsacr, NSACR, NSASEDIS)) { + cpacr_asedis = true; + hcptr_tase = true; + } + } + + if (cpacr_asedis) { + if (have_aarch32_el3 && (cur_el == 3 || arm_is_secure_below_el3(env))) { + /* Trap from Secure PL0 or PL1 to Secure PL1 */ + return 3; + } + if (cur_el <= 1) { + /* trap from EL0 or EL1 to EL1 */ + return 1; + } + } + + /* HCPTR.TASE traps to EL2, including for execution at EL2 */ + if (hcptr_tase && cur_el <= 2) { + return 2; + } + return 0; +} + static CPUARMTBFlags rebuild_hflags_a32(CPUARMState *env, int fp_el, ARMMMUIdx mmu_idx) { @@ -209,6 +289,8 @@ static CPUARMTBFlags rebuild_hflags_a32(CPUARMState *env, int fp_el, DP_TBFLAG_A32(flags, SME_TRAP_NONSTREAMING, 1); } + DP_TBFLAG_A32(flags, NEONEXC_EL, neon_exception_el(env, el)); + return rebuild_hflags_common_32(env, fp_el, mmu_idx, flags); } diff --git a/target/arm/tcg/translate-vfp.c b/target/arm/tcg/translate-vfp.c index 6e944a0322..4db4b14841 100644 --- a/target/arm/tcg/translate-vfp.c +++ b/target/arm/tcg/translate-vfp.c @@ -306,15 +306,25 @@ bool vfp_access_check(DisasContext *s) /* * Access check for Neon; this is for instructions which can be - * trapped by CPACR.ASEDIS and HCPTR.TASE. Support for those traps - * is optional and we currently do not implement them, so this - * is identical to a VFP access check for now. + * trapped by CPACR.ASEDIS and HCPTR.TASE. */ bool neon_access_check(DisasContext *s) { if (arm_dc_feature(s, ARM_FEATURE_M)) { return vfp_access_check_m(s, false); } else { + /* + * If the Neon-specific trap bits request a trap to a lower EL + * than the general FP trap bits, the trap to the lower EL + * has priority. + */ + if (s->neon_excp_el && + (!s->fp_excp_el || s->neon_excp_el < s->fp_excp_el)) { + uint32_t syn = syn_a32_fp_access_trap(1, 0xe, 1, 0); + + gen_exception_insn_el(s, 0, EXCP_UDEF, syn, s->neon_excp_el); + return false; + } return vfp_access_check_a(s, false, true); } } diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index 055f3c5b40..ebd917f16c 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -6403,6 +6403,7 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) dc->vec_stride = EX_TBFLAG_A32(tb_flags, VECSTRIDE); dc->sme_trap_nonstreaming = EX_TBFLAG_A32(tb_flags, SME_TRAP_NONSTREAMING); + dc->neon_excp_el = EX_TBFLAG_A32(tb_flags, NEONEXC_EL); } dc->lse2 = false; /* applies only to aarch64 */ dc->cp_regs = cpu->cp_regs; diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index a3d03159ad..cce84f29c4 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -88,6 +88,7 @@ typedef struct DisasContext { int sve_excp_el; /* SVE exception EL or 0 if enabled */ int sme_excp_el; /* SME exception EL or 0 if enabled */ int zt0_excp_el; /* ZT0 exception EL or 0 if enabled */ + int neon_excp_el; /* A32 Neon exception EL or 0 if enabled */ int vl; /* current vector length in bytes */ int svl; /* current streaming vector length in bytes */ int max_svl; /* maximum implemented streaming vector length */ -- 2.43.0