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 */