@samitouri / QOSamiQemu / commits / 282ff9baeb

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;