@samitouri / QOSamiQemu / commits / 284a3c2924

target/arm: Implement CPACR.ASEDIS and HCPTR.TASE

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 <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260817123838.1578060-6-peter.maydell@linaro.org

Peter Maydell committed Aug 17, 2026 at 13:38 UTC 284a3c29246097ce8d0afd8676371c97cd796188
5 files changed +103 -3
target/arm/cpu.h
+6
@@ -2506,6 +2506,12 @@ FIELD(TBFLAG_A32, NS, 10, 1)
2506 * This requires an SME trap from AArch32 mode when using NEON.
2507 */
2508 FIELD(TBFLAG_A32, SME_TRAP_NONSTREAMING, 11, 1)
2509 +/*
2510 + * Target EL for a Neon-disabled exception via CPACR.ASEDIS, HCPTR.TASE.
2511 + * If FPEXC_EL indicates a trap to a lower EL than this, that will
2512 + * take precedence.
2513 + */
2514 +FIELD(TBFLAG_A32, NEONEXC_EL, 12, 2)
2515
2516 /*
2517 * Bit usage when in AArch32 state, for M-profile only.
target/arm/tcg/hflags.c
+82
@@ -164,6 +164,86 @@ static bool sme_fa64(CPUARMState *env, int el)
164 return true;
165 }
166
167 +static int neon_exception_el(CPUARMState *env, int cur_el)
168 +{
169 + /*
170 + * Return the EL to trap to for A32 Neon specific traps
171 + * (CPACR.ASEDIS and HCPTR.TASE). In the pseudocode these are
172 + * checked in the same function as the more general trap bits that
173 + * we handle in fp_exception_el(). Fortunately it is always the
174 + * case that if the trap/enable bits specify taking an exception
175 + * to different ELs for the Neon-specific insns and the general fp
176 + * insns then the trap to the lower of the two ELs has priority,
177 + * so we can calculate the two target ELs separately and pick the
178 + * right destination later. Compare AArch32_CheckAdvSIMDOrFPEnabled().
179 + *
180 + * CPACR doesn't exist before v6, but neither does Neon, so we can
181 + * assume that if we're here testing this then the register exists.
182 + * HCPTR always exists if EL2 is present.
183 + */
184 + uint64_t hcr_el2 = arm_hcr_el2_eff(env);
185 + bool cpacr_asedis = FIELD_EX64(env->cp15.cpacr_el1, CPACR, ASEDIS);
186 + bool hcptr_tase = FIELD_EX64(env->cp15.cptr_el[2], HCPTR, TASE);
187 + bool have_aarch32_el3 =
188 + arm_feature(env, ARM_FEATURE_EL3) && !arm_el_is_aa64(env, 3);
189 +
190 + if (!arm_feature(env, ARM_FEATURE_NEON_TRAPS)) {
191 + /* This CPU doesn't implement the trap bits (Cortex-A8) */
192 + return 0;
193 + }
194 +
195 + if (arm_feature(env, ARM_FEATURE_EL2) && arm_el_is_aa64(env, 2)) {
196 + /*
197 + * The AArch64 CPTR_EL2 has no equivalent to HCPTR.TASE; only
198 + * an AArch32 EL2 can trap Neon specifically.
199 + */
200 + hcptr_tase = false;
201 + }
202 +
203 + /*
204 + * We know we're in AArch32, but if this is EL0 and EL1 is AArch64
205 + * then CPACR_EL1 applies rather than CPACR, and it doesn't have
206 + * ASEDIS (instead using the same bit for TCPAC).
207 + */
208 + if (cur_el == 0 && arm_el_is_aa64(env, 1)) {
209 + cpacr_asedis = false;
210 + }
211 +
212 + /* CPACR is ignored if E2H+TGE are both set */
213 + if ((hcr_el2 & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE)) {
214 + cpacr_asedis = false;
215 + }
216 +
217 + /*
218 + * NSACR.NSASEDIS makes the effective values of HCPTR.TASE and
219 + * CPACR.ASEDIS be 1 in NonSecure state. NSACR has no
220 + * effect unless EL3 exists and is AArch32.
221 + */
222 + if (have_aarch32_el3 && cur_el <= 2 && !arm_is_secure_below_el3(env)) {
223 + if (FIELD_EX32(env->cp15.nsacr, NSACR, NSASEDIS)) {
224 + cpacr_asedis = true;
225 + hcptr_tase = true;
226 + }
227 + }
228 +
229 + if (cpacr_asedis) {
230 + if (have_aarch32_el3 && (cur_el == 3 || arm_is_secure_below_el3(env))) {
231 + /* Trap from Secure PL0 or PL1 to Secure PL1 */
232 + return 3;
233 + }
234 + if (cur_el <= 1) {
235 + /* trap from EL0 or EL1 to EL1 */
236 + return 1;
237 + }
238 + }
239 +
240 + /* HCPTR.TASE traps to EL2, including for execution at EL2 */
241 + if (hcptr_tase && cur_el <= 2) {
242 + return 2;
243 + }
244 + return 0;
245 +}
246 +
247 static CPUARMTBFlags rebuild_hflags_a32(CPUARMState *env, int fp_el,
248 ARMMMUIdx mmu_idx)
249 {
@@ -209,6 +289,8 @@ static CPUARMTBFlags rebuild_hflags_a32(CPUARMState *env, int fp_el,
289 DP_TBFLAG_A32(flags, SME_TRAP_NONSTREAMING, 1);
290 }
291
292 + DP_TBFLAG_A32(flags, NEONEXC_EL, neon_exception_el(env, el));
293 +
294 return rebuild_hflags_common_32(env, fp_el, mmu_idx, flags);
295 }
296
target/arm/tcg/translate-vfp.c
+13 -3
@@ -306,15 +306,25 @@ bool vfp_access_check(DisasContext *s)
306
307 /*
308 * Access check for Neon; this is for instructions which can be
309 - * trapped by CPACR.ASEDIS and HCPTR.TASE. Support for those traps
310 - * is optional and we currently do not implement them, so this
311 - * is identical to a VFP access check for now.
309 + * trapped by CPACR.ASEDIS and HCPTR.TASE.
310 */
311 bool neon_access_check(DisasContext *s)
312 {
313 if (arm_dc_feature(s, ARM_FEATURE_M)) {
314 return vfp_access_check_m(s, false);
315 } else {
316 + /*
317 + * If the Neon-specific trap bits request a trap to a lower EL
318 + * than the general FP trap bits, the trap to the lower EL
319 + * has priority.
320 + */
321 + if (s->neon_excp_el &&
322 + (!s->fp_excp_el || s->neon_excp_el < s->fp_excp_el)) {
323 + uint32_t syn = syn_a32_fp_access_trap(1, 0xe, 1, 0);
324 +
325 + gen_exception_insn_el(s, 0, EXCP_UDEF, syn, s->neon_excp_el);
326 + return false;
327 + }
328 return vfp_access_check_a(s, false, true);
329 }
330 }
target/arm/tcg/translate.c
+1
@@ -6403,6 +6403,7 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
6403 dc->vec_stride = EX_TBFLAG_A32(tb_flags, VECSTRIDE);
6404 dc->sme_trap_nonstreaming =
6405 EX_TBFLAG_A32(tb_flags, SME_TRAP_NONSTREAMING);
6406 + dc->neon_excp_el = EX_TBFLAG_A32(tb_flags, NEONEXC_EL);
6407 }
6408 dc->lse2 = false; /* applies only to aarch64 */
6409 dc->cp_regs = cpu->cp_regs;
target/arm/tcg/translate.h
+1
@@ -88,6 +88,7 @@ typedef struct DisasContext {
88 int sve_excp_el; /* SVE exception EL or 0 if enabled */
89 int sme_excp_el; /* SME exception EL or 0 if enabled */
90 int zt0_excp_el; /* ZT0 exception EL or 0 if enabled */
91 + int neon_excp_el; /* A32 Neon exception EL or 0 if enabled */
92 int vl; /* current vector length in bytes */
93 int svl; /* current streaming vector length in bytes */
94 int max_svl; /* maximum implemented streaming vector length */