target/arm: Implement CPACR.D32DIS
On some v7A CPUs, CPACR.D32DIS is a bit allowing the guest to make VFP instructions that touch registers D16..D31 UNDEF. Whether the CPU implements this or not is IMPDEF, and the only two CPUs we implement which have this are the Cortex-A7 and Cortex-A9. In v8A the bit is no longer defined at all. Since the only kind of trapping that needs to be done is a simple UNDEF, this is straightforward enough to implement. 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-8-peter.maydell@linaro.org
Peter Maydell committed
Aug 17, 2026 at 13:38 UTC
282ff9baebba548f8b6bfb63ffd53f40c8a0ce14
7 files changed
+51
-8
target/arm/cpu.h
+4
@@ -2184,6 +2184,8 @@ enum arm_features {
2184
* ARM_FEATURE_NEON CPUs except the Cortex-A8.
2185
*/
2186
ARM_FEATURE_NEON_TRAPS,
2187
+ /* Does the CPU implement CPACR.D32DIS ? */
2188
+ ARM_FEATURE_D32DIS,
2189
};
2190
2191
static inline int arm_feature(const CPUARMState *env, int feature)
@@ -2512,6 +2514,8 @@ FIELD(TBFLAG_A32, SME_TRAP_NONSTREAMING, 11, 1)
2514
* take precedence.
2515
*/
2516
FIELD(TBFLAG_A32, NEONEXC_EL, 12, 2)
2517
+/* Should VFP insns touching D16..D31 UNDEF? (CPACR.D32DIS) */
2518
+FIELD(TBFLAG_A32, D32DIS, 14, 1)
2519
2520
/*
2521
* Bit usage when in AArch32 state, for M-profile only.
target/arm/helper.c
+16
-3
@@ -573,8 +573,7 @@ static void cpacr_write(CPUARMState *env, const ARMCPRegInfo *ri,
573
*/
574
if (cpu_isar_feature(aa32_vfp_simd, env_archcpu(env))) {
575
/* VFP coprocessor: cp10 & cp11 [23:20] */
576
- mask |= R_CPACR_D32DIS_MASK |
577
- R_CPACR_CP11_MASK |
576
+ mask |= R_CPACR_CP11_MASK |
577
R_CPACR_CP10_MASK;
578
579
if (!arm_feature(env, ARM_FEATURE_NEON)) {
@@ -596,6 +595,13 @@ static void cpacr_write(CPUARMState *env, const ARMCPRegInfo *ri,
595
if (!cpu_isar_feature(aa32_simd_r32, env_archcpu(env))) {
596
/* D32DIS [30] is RAO/WI if D16-31 are not implemented. */
597
value |= R_CPACR_D32DIS_MASK;
598
+ mask |= R_CPACR_D32DIS_MASK;
599
+ } else if (arm_feature(env, ARM_FEATURE_D32DIS)) {
600
+ /*
601
+ * Bit is present unless CPU doesn't implement D32DIS,
602
+ * in which case it is RAZ/WI.
603
+ */
604
+ mask |= R_CPACR_D32DIS_MASK;
605
}
606
}
607
value &= mask;
@@ -605,7 +611,7 @@ static void cpacr_write(CPUARMState *env, const ARMCPRegInfo *ri,
611
* For A-profile AArch32 EL3 (but not M-profile secure mode), if NSACR.CP10
612
* is 0 then CPACR.{CP11,CP10} ignore writes and read as 0b00.
613
* Similarly, if NSACR.NSASEDIS is 1 then CPACR.ASEDIS ignores writes
608
- * and reads as 1.
614
+ * and reads as 1, and NSACR.NSD32DIS makes CPACR.D32DIS behave as RAO/WI.
615
*/
616
if (arm_feature(env, ARM_FEATURE_EL3) && !arm_el_is_aa64(env, 3) &&
617
!arm_is_secure(env)) {
@@ -617,6 +623,10 @@ static void cpacr_write(CPUARMState *env, const ARMCPRegInfo *ri,
623
mask = R_CPACR_ASEDIS_MASK;
624
value = (value & ~mask) | (env->cp15.cpacr_el1 & mask);
625
}
626
+ if (FIELD_EX32(env->cp15.nsacr, NSACR, NSD32DIS)) {
627
+ mask = R_CPACR_D32DIS_MASK;
628
+ value = (value & ~mask) | (env->cp15.cpacr_el1 & mask);
629
+ }
630
}
631
632
env->cp15.cpacr_el1 = value;
@@ -639,6 +649,9 @@ static uint64_t cpacr_read(CPUARMState *env, const ARMCPRegInfo *ri)
649
if (FIELD_EX32(env->cp15.nsacr, NSACR, NSASEDIS)) {
650
value |= R_CPACR_ASEDIS_MASK;
651
}
652
+ if (FIELD_EX32(env->cp15.nsacr, NSACR, NSD32DIS)) {
653
+ value |= R_CPACR_D32DIS_MASK;
654
+ }
655
}
656
return value;
657
}
target/arm/tcg/cpu32.c
+2
@@ -336,6 +336,7 @@ static void cortex_a9_initfn(Object *obj)
336
*/
337
set_feature(&cpu->env, ARM_FEATURE_V7MP);
338
set_feature(&cpu->env, ARM_FEATURE_CBAR);
339
+ set_feature(&cpu->env, ARM_FEATURE_D32DIS);
340
cpu->midr = 0x410fc090;
341
cpu->reset_fpsid = 0x41033090;
342
cpu->isar.mvfr0 = 0x11110222;
@@ -403,6 +404,7 @@ static void cortex_a7_initfn(Object *obj)
404
set_feature(&cpu->env, ARM_FEATURE_EL2);
405
set_feature(&cpu->env, ARM_FEATURE_EL3);
406
set_feature(&cpu->env, ARM_FEATURE_PMU);
407
+ set_feature(&cpu->env, ARM_FEATURE_D32DIS);
408
cpu->midr = 0x410fc075;
409
cpu->reset_fpsid = 0x41023075;
410
cpu->isar.mvfr0 = 0x10110222;
target/arm/tcg/hflags.c
+20
@@ -244,6 +244,24 @@ static int neon_exception_el(CPUARMState *env, int cur_el)
244
return 0;
245
}
246
247
+static bool arm_d32dis(CPUARMState *env, int cur_el)
248
+{
249
+ bool cpacr_d32dis = FIELD_EX64(env->cp15.cpacr_el1, CPACR, D32DIS);
250
+
251
+ if (!arm_feature(env, ARM_FEATURE_D32DIS)) {
252
+ return false;
253
+ }
254
+
255
+ /* If NSACR.NSD32DIS is set, CPACR.D32DIS acts as 1 in NonSecure */
256
+ if ((arm_feature(env, ARM_FEATURE_EL3) && !arm_el_is_aa64(env, 3) &&
257
+ cur_el <= 2 && !arm_is_secure_below_el3(env))) {
258
+ if (FIELD_EX32(env->cp15.nsacr, NSACR, NSD32DIS)) {
259
+ cpacr_d32dis = true;
260
+ }
261
+ }
262
+ return cpacr_d32dis;
263
+}
264
+
265
static CPUARMTBFlags rebuild_hflags_a32(CPUARMState *env, int fp_el,
266
ARMMMUIdx mmu_idx)
267
{
@@ -291,6 +309,8 @@ static CPUARMTBFlags rebuild_hflags_a32(CPUARMState *env, int fp_el,
309
310
DP_TBFLAG_A32(flags, NEONEXC_EL, neon_exception_el(env, el));
311
312
+ DP_TBFLAG_A32(flags, D32DIS, arm_d32dis(env, el));
313
+
314
return rebuild_hflags_common_32(env, fp_el, mmu_idx, flags);
315
}
316
target/arm/tcg/translate-vfp.c
+2
-5
@@ -210,8 +210,7 @@ static void gen_update_fp_context(DisasContext *s)
210
/*
211
* Return true if a VFP insn is OK to access the registers indicated
212
* by regmask, false if it should UNDEF. This checks whether the
213
- * D16-D31 regs are implemented by the CPU. Eventually we will also check
214
- * CPACR.D32DIS.
213
+ * D16-D31 regs are implemented by the CPU and not disabled by CPACR.D32DIS.
214
* Note that Neon insns accessing D16..D31 do not need to check D32DIS,
215
* so this function is for VFP insns only.
216
*
@@ -219,9 +218,7 @@ static void gen_update_fp_context(DisasContext *s)
218
*/
219
static bool vfp_dregs_ok(DisasContext *s, int dregmask)
220
{
222
- int invalid_dreg_mask = dc_isar_feature(aa32_simd_r32, s) ? 0 : 0x10;
223
-
224
- return !(dregmask & invalid_dreg_mask);
221
+ return !(dregmask & s->invalid_vfp_dreg_mask);
222
}
223
224
/*
target/arm/tcg/translate.c
+6
@@ -6342,6 +6342,7 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
6342
ARMCPU *cpu = env_archcpu(env);
6343
CPUARMTBFlags tb_flags = arm_tbflags_from_tb(dc->base.tb);
6344
uint32_t condexec, core_mmu_idx;
6345
+ bool d32dis = false;
6346
6347
dc->isar = &cpu->isar;
6348
dc->condjmp = 0;
@@ -6404,7 +6405,12 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
6405
dc->sme_trap_nonstreaming =
6406
EX_TBFLAG_A32(tb_flags, SME_TRAP_NONSTREAMING);
6407
dc->neon_excp_el = EX_TBFLAG_A32(tb_flags, NEONEXC_EL);
6408
+ d32dis = EX_TBFLAG_A32(tb_flags, D32DIS);
6409
}
6410
+
6411
+ dc->invalid_vfp_dreg_mask =
6412
+ (d32dis || !dc_isar_feature(aa32_simd_r32, dc)) ? 0x10 : 0;
6413
+
6414
dc->lse2 = false; /* applies only to aarch64 */
6415
dc->cp_regs = cpu->cp_regs;
6416
dc->features = env->features;
target/arm/tcg/translate.h
+1
@@ -94,6 +94,7 @@ typedef struct DisasContext {
94
int max_svl; /* maximum implemented streaming vector length */
95
int max_any_vl; /* maximum implemented vector length */
96
bool vfp_enabled; /* FP enabled via FPSCR.EN */
97
+ int invalid_vfp_dreg_mask; /* mask for whether VFP D16..D31 should UNDEF */
98
int vec_len;
99
int vec_stride;
100
bool v7m_handler_mode;