target/arm: Separate cpu types max-v8 and max-v9
Do not include v9-only features in "max-v8". Do not include the prohibited v8 features in "max-v9". In aarch64 tcg mode, define "max" as "max-v9". Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260827041557.775080-7-richard.henderson@linaro.org Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Richard Henderson committed
Aug 26, 2026 at 21:15 UTC
29a92d9861155e421d6e14adcf820dd20dfaa318
6 files changed
+128
-59
docs/system/arm/emulation.rst
+12
-1
@@ -230,7 +230,18 @@ to the `Arm Architecture Reference Manual for A-profile architecture
230
When a specific named CPU is being emulated, only those features which
231
are present in hardware for that CPU are emulated. (If a feature is
232
not in the list above then it is not supported, even if the real
233
-hardware should have it.) The ``max`` CPU enables all features.
233
+hardware should have it.)
234
+
235
+The ``max-v8`` CPU enables all of the listed Armv8-A architecture
236
+extensions but none of the Armv9-A architecture extensions.
237
+
238
+The ``max-v9`` CPU enables all of the listed extensions except for
239
+those that are prohibited by the Armv9-A architecture: FEAT_AA32EL1,
240
+FEAT_AA32EL2, FEAT_AA32EL3, and FEAT_DoubleLock.
241
+
242
+The ``max`` CPU is an alias for ``max-v9`` under AArch64 TCG emulation,
243
+an alias for ``max-v8`` under AArch32 TCG emulation, or an alias
244
+for ``host`` under hardware virtualization.
245
246
R-profile CPU architecture support
247
==================================
target/arm/cpu-max.c
+1
-1
@@ -110,7 +110,7 @@ static void cpu_max_initfn(Object *obj)
110
if (!aarch64_enabled) {
111
aarch32_max_v8_tcg_initfn(obj);
112
} else {
113
- aarch64_max_tcg_initfn(obj);
113
+ aarch64_max_v9_tcg_initfn(obj);
114
}
115
return;
116
}
target/arm/internals.h
+2
-1
@@ -1809,7 +1809,8 @@ void aarch64_cpu_sve_finalize(ARMCPU *cpu, Error **errp);
1809
void aarch64_cpu_sme_finalize(ARMCPU *cpu, Error **errp);
1810
void aarch64_cpu_pauth_finalize(ARMCPU *cpu, Error **errp);
1811
void aarch64_cpu_lpa2_finalize(ARMCPU *cpu, Error **errp);
1812
-void aarch64_max_tcg_initfn(Object *obj);
1812
+void aarch64_max_v8_tcg_initfn(Object *obj);
1813
+void aarch64_max_v9_tcg_initfn(Object *obj);
1814
void aarch64_add_pauth_properties(Object *obj);
1815
void aarch64_add_sve_properties(Object *obj);
1816
void aarch64_add_sme_properties(Object *obj);
target/arm/tcg/cpu-max-v8.c
+1
-1
@@ -123,7 +123,7 @@ static void cpu_max_v8_register_type(void)
123
{
124
static const ARMCPUInfo max_v8[2] = {
125
{ .name = "max-v8", .initfn = aarch32_max_v8_tcg_initfn },
126
- { .name = "max-v8", .initfn = aarch64_max_tcg_initfn }
126
+ { .name = "max-v8", .initfn = aarch64_max_v8_tcg_initfn },
127
};
128
arm_cpu_register(&max_v8[target_aarch64()]);
129
}
target/arm/tcg/cpu64.c
+106
-54
@@ -1166,11 +1166,10 @@ static void aarch64_neoverse_n2_initfn(Object *obj)
1166
}
1167
1168
/*
1169
- * -cpu max: a CPU with as many features enabled as our emulation supports.
1170
- * The version of '-cpu max' for qemu-system-arm is defined in cpu32.c;
1171
- * this only needs to handle 64 bits.
1169
+ * -cpu max-v8: an ARMv8 CPU with as many features enabled as
1170
+ * our emulation supports.
1171
*/
1173
-void aarch64_max_tcg_initfn(Object *obj)
1172
+void aarch64_max_v8_tcg_initfn(Object *obj)
1173
{
1174
ARMCPU *cpu = ARM_CPU(obj);
1175
ARMISARegisters *isar = &cpu->isar;
@@ -1244,7 +1243,7 @@ void aarch64_max_tcg_initfn(Object *obj)
1243
t = FIELD_DP64(t, ID_AA64ISAR0, SHA1, 1); /* v8.0: FEAT_SHA1 */
1244
t = FIELD_DP64(t, ID_AA64ISAR0, SHA2, 2); /* v8.1: FEAT_SHA512 */
1245
t = FIELD_DP64(t, ID_AA64ISAR0, CRC32, 1); /* v8.0: FEAT_CRC32 */
1247
- t = FIELD_DP64(t, ID_AA64ISAR0, ATOMIC, 3); /* v9.3: FEAT_LSE128 */
1246
+ t = FIELD_DP64(t, ID_AA64ISAR0, ATOMIC, 2); /* v8.0: FEAT_LSE */
1247
t = FIELD_DP64(t, ID_AA64ISAR0, RDM, 1); /* v8.0: FEAT_RDM */
1248
t = FIELD_DP64(t, ID_AA64ISAR0, SHA3, 1); /* v8.1: FEAT_SHA3 */
1249
t = FIELD_DP64(t, ID_AA64ISAR0, SM3, 1); /* v8.1: FEAT_SM3 */
@@ -1277,16 +1276,10 @@ void aarch64_max_tcg_initfn(Object *obj)
1276
t = FIELD_DP64(t, ID_AA64ISAR2, MOPS, 1); /* v8.7: FEAT_MOPS */
1277
t = FIELD_DP64(t, ID_AA64ISAR2, BC, 1); /* v8.7: FEAT_HBC */
1278
t = FIELD_DP64(t, ID_AA64ISAR2, WFXT, 2); /* v8.6: FEAT_WFxT */
1280
- t = FIELD_DP64(t, ID_AA64ISAR2, CSSC, 2); /* v9.3: FEAT_CMPBR */
1281
- t = FIELD_DP64(t, ID_AA64ISAR2, LUT, 1); /* v9.2: FEAT_LUT */
1279
+ t = FIELD_DP64(t, ID_AA64ISAR2, CSSC, 1); /* v8.7: FEAT_CSSC */
1280
t = FIELD_DP64(t, ID_AA64ISAR2, ATS1A, 1); /* v8.8: FEAT_ATS1A */
1281
SET_IDREG(isar, ID_AA64ISAR2, t);
1282
1285
- t = GET_IDREG(isar, ID_AA64ISAR3);
1286
- t = FIELD_DP64(t, ID_AA64ISAR3, FAMINMAX, 1); /* v9.2: FEAT_FAMINMAX */
1287
- t = FIELD_DP64(t, ID_AA64ISAR3, FPRCVT, 1); /* v9.5: FEAT_FPRCVT */
1288
- SET_IDREG(isar, ID_AA64ISAR3, t);
1289
-
1283
t = GET_IDREG(isar, ID_AA64PFR0);
1284
t = FIELD_DP64(t, ID_AA64PFR0, FP, 1); /* v8.2: FEAT_FP16 */
1285
t = FIELD_DP64(t, ID_AA64PFR0, ADVSIMD, 1); /* v8.2: FEAT_FP16 */
@@ -1308,17 +1301,14 @@ void aarch64_max_tcg_initfn(Object *obj)
1301
*/
1302
t = FIELD_DP64(t, ID_AA64PFR1, MTE, 3); /* v8.5: FEAT_MTE3 */
1303
t = FIELD_DP64(t, ID_AA64PFR1, RAS_FRAC, 0); /* v8.3: FEAT_DoubleFault */
1311
- t = FIELD_DP64(t, ID_AA64PFR1, SME, 2); /* v9.2: FEAT_SME2 */
1304
t = FIELD_DP64(t, ID_AA64PFR1, RNDR_TRAP, 1); /* v8.4: FEAT_RNG_TRAP */
1305
t = FIELD_DP64(t, ID_AA64PFR1, CSV2_FRAC, 0); /* v8.0: FEAT_CSV2_3 */
1306
t = FIELD_DP64(t, ID_AA64PFR1, NMI, 1); /* v8.7: FEAT_NMI */
1315
- t = FIELD_DP64(t, ID_AA64PFR1, GCS, 1); /* v9.3: FEAT_GCS */
1307
/* v8.7: FEAT_MTE_NO_ADDRESS_TAGS + FEAT_MTE_CANONICAL_TAGS */
1308
t = FIELD_DP64(t, ID_AA64PFR1, MTEX, 1);
1309
SET_IDREG(isar, ID_AA64PFR1, t);
1310
1311
t = GET_IDREG(isar, ID_AA64PFR2);
1321
- t = FIELD_DP64(t, ID_AA64PFR2, FPMR, 1); /* v9.2: FEAT_FPMR */
1312
t = FIELD_DP64(t, ID_AA64PFR2, MTEFAR, 1); /* v8.7: FEAT_MTE_TAGGED_FAR */
1313
t = FIELD_DP64(t, ID_AA64PFR2, MTESTOREONLY, 1); /* v8.7: FEAT_MTE_STORE_ONLY */
1314
t = FIELD_DP64(t, ID_AA64PFR2, MTEPERM, 1); /* v8.7: FEAT_MTE_PERM */
@@ -1368,25 +1358,14 @@ void aarch64_max_tcg_initfn(Object *obj)
1358
t = GET_IDREG(isar, ID_AA64MMFR3);
1359
t = FIELD_DP64(t, ID_AA64MMFR3, TCRX, 1); /* v8.0: FEAT_TCR2 */
1360
t = FIELD_DP64(t, ID_AA64MMFR3, SCTLRX, 1); /* v8.0: FEAT_SCTLR2 */
1371
- t = FIELD_DP64(t, ID_AA64MMFR3, MEC, 1); /* v9.2: FEAT_MEC */
1361
t = FIELD_DP64(t, ID_AA64MMFR3, SPEC_FPACC, 1); /* v8.2: FEAT_FPACC_SPEC */
1362
t = FIELD_DP64(t, ID_AA64MMFR3, S1PIE, 1); /* v8.8: FEAT_S1PIE */
1363
t = FIELD_DP64(t, ID_AA64MMFR3, S2PIE, 1); /* v8.8: FEAT_S2PIE */
1364
t = FIELD_DP64(t, ID_AA64MMFR3, AIE, 1); /* v8.8: FEAT_AIE */
1365
SET_IDREG(isar, ID_AA64MMFR3, t);
1366
1378
- t = GET_IDREG(isar, ID_AA64MMFR4);
1379
- t = FIELD_DP64(t, ID_AA64MMFR4, ASID2, 1); /* v9.4: FEAT_ASID2 */
1380
- SET_IDREG(isar, ID_AA64MMFR4, t);
1381
-
1367
t = GET_IDREG(isar, ID_AA64ZFR0);
1383
- t = FIELD_DP64(t, ID_AA64ZFR0, SVEVER, 2); /* v9.2: FEAT_SVE2p1 */
1384
- t = FIELD_DP64(t, ID_AA64ZFR0, AES, 2); /* v9.0: FEAT_SVE_PMULL128 */
1385
- t = FIELD_DP64(t, ID_AA64ZFR0, BITPERM, 1); /* v9.0: FEAT_SVE_BitPerm */
1368
t = FIELD_DP64(t, ID_AA64ZFR0, BFLOAT16, 2); /* v8.2: FEAT_EBF16 */
1387
- t = FIELD_DP64(t, ID_AA64ZFR0, B16B16, 1); /* v9.2: FEAT_SVE_B16B16 */
1388
- t = FIELD_DP64(t, ID_AA64ZFR0, SHA3, 1); /* v9.0: FEAT_SVE_SHA3 */
1389
- t = FIELD_DP64(t, ID_AA64ZFR0, SM4, 1); /* v9.0: FEAT_SVE_SM4 */
1369
t = FIELD_DP64(t, ID_AA64ZFR0, I8MM, 1); /* v8.1: FEAT_I8MM */
1370
t = FIELD_DP64(t, ID_AA64ZFR0, F32MM, 1); /* v8.2: FEAT_F32MM */
1371
t = FIELD_DP64(t, ID_AA64ZFR0, F64MM, 1); /* v8.2: FEAT_F64MM */
@@ -1398,6 +1377,106 @@ void aarch64_max_tcg_initfn(Object *obj)
1377
t = FIELD_DP64(t, ID_AA64DFR0, HPMN0, 1); /* v8.5: FEAT_HPMN0 */
1378
SET_IDREG(isar, ID_AA64DFR0, t);
1379
1380
+ /* Replicate the same data to the 32-bit id registers. */
1381
+ aa32_max_features(cpu);
1382
+
1383
+#ifdef CONFIG_USER_ONLY
1384
+ /*
1385
+ * For usermode -cpu max we can use a larger and more efficient DCZ
1386
+ * blocksize since we don't have to follow what the hardware does.
1387
+ */
1388
+ cpu->ctr = 0x80038003; /* 32 byte I and D cacheline size, VIPT icache */
1389
+ set_dczid_bs(cpu, 7); /* 512 bytes */
1390
+#endif
1391
+
1392
+ /* v8.2: FEAT_SVE */
1393
+ cpu->sve_vq.supported = MAKE_64BIT_MASK(0, ARM_MAX_VQ);
1394
+ aarch64_add_sve_properties(OBJECT(cpu));
1395
+ object_property_add(OBJECT(cpu), "sve-max-vq", "uint32",
1396
+ cpu_max_get_sve_max_vq, cpu_max_set_sve_max_vq,
1397
+ NULL, NULL);
1398
+
1399
+ /* v8.2: FEAT_PAuth2 */
1400
+ aarch64_add_pauth_properties(OBJECT(cpu));
1401
+
1402
+ /* v8.4: FEAT_MTE2 */
1403
+ cpu->gm_blocksize = 6; /* 256 bytes */
1404
+
1405
+ /* v8.6: FEAT_LPA2 */
1406
+ qdev_property_add_static(DEVICE(cpu), &arm_cpu_lpa2_property);
1407
+}
1408
+
1409
+/*
1410
+ * -cpu max-v9: an ARMv9 CPU with as many features enabled as
1411
+ * our emulation supports.
1412
+ */
1413
+void aarch64_max_v9_tcg_initfn(Object *obj)
1414
+{
1415
+ ARMCPU *cpu = ARM_CPU(obj);
1416
+ ARMISARegisters *isar = &cpu->isar;
1417
+ uint64_t t;
1418
+
1419
+ /* Armv9.0 is based on a minimum of Armv8.5. */
1420
+ aarch64_max_v8_tcg_initfn(obj);
1421
+
1422
+ /*
1423
+ * Armv9.0 does not support AArch32 except at EL0,
1424
+ * therefore indicate EL1 through EL3 are AArch64-only.
1425
+ */
1426
+ t = GET_IDREG(isar, ID_AA64PFR0);
1427
+ t = FIELD_DP64(t, ID_AA64PFR0, EL1, 1);
1428
+ t = FIELD_DP64(t, ID_AA64PFR0, EL2, 1);
1429
+ t = FIELD_DP64(t, ID_AA64PFR0, EL3, 1);
1430
+ SET_IDREG(isar, ID_AA64PFR0, t);
1431
+
1432
+ /* v9.0 prohibits FEAT_DoubleLock. */
1433
+ FIELD_DP64_IDREG(isar, ID_AA64DFR0, DOUBLELOCK, -1);
1434
+ FIELD_DP64_IDREG(isar, ID_AA64PFR0, RAS, 1);
1435
+ isar->dbgdevid = FIELD_DP32(isar->dbgdevid, DBGDEVID, DOUBLELOCK, 0);
1436
+
1437
+ /*
1438
+ * Below, note the revision from which the feature is OPTIONAL.
1439
+ */
1440
+ t = GET_IDREG(isar, ID_AA64ISAR0);
1441
+ t = FIELD_DP64(t, ID_AA64ISAR0, ATOMIC, 3); /* v9.3: FEAT_LSE128 */
1442
+ SET_IDREG(isar, ID_AA64ISAR0, t);
1443
+
1444
+ t = GET_IDREG(isar, ID_AA64ISAR2);
1445
+ t = FIELD_DP64(t, ID_AA64ISAR2, CSSC, 2); /* v9.3: FEAT_CMPBR */
1446
+ t = FIELD_DP64(t, ID_AA64ISAR2, LUT, 1); /* v9.2: FEAT_LUT */
1447
+ SET_IDREG(isar, ID_AA64ISAR2, t);
1448
+
1449
+ t = GET_IDREG(isar, ID_AA64ISAR3);
1450
+ t = FIELD_DP64(t, ID_AA64ISAR3, FAMINMAX, 1); /* v9.2: FEAT_FAMINMAX */
1451
+ t = FIELD_DP64(t, ID_AA64ISAR3, FPRCVT, 1); /* v9.5: FEAT_FPRCVT */
1452
+ SET_IDREG(isar, ID_AA64ISAR3, t);
1453
+
1454
+ t = GET_IDREG(isar, ID_AA64PFR1);
1455
+ t = FIELD_DP64(t, ID_AA64PFR1, SME, 2); /* v9.2: FEAT_SME2 */
1456
+ t = FIELD_DP64(t, ID_AA64PFR1, GCS, 1); /* v9.3: FEAT_GCS */
1457
+ SET_IDREG(isar, ID_AA64PFR1, t);
1458
+
1459
+ t = GET_IDREG(isar, ID_AA64PFR2);
1460
+ t = FIELD_DP64(t, ID_AA64PFR2, FPMR, 1); /* v9.2: FEAT_FPMR */
1461
+ SET_IDREG(isar, ID_AA64PFR2, t);
1462
+
1463
+ t = GET_IDREG(isar, ID_AA64MMFR3);
1464
+ t = FIELD_DP64(t, ID_AA64MMFR3, MEC, 1); /* v9.2: FEAT_MEC */
1465
+ SET_IDREG(isar, ID_AA64MMFR3, t);
1466
+
1467
+ t = GET_IDREG(isar, ID_AA64MMFR4);
1468
+ t = FIELD_DP64(t, ID_AA64MMFR4, ASID2, 1); /* v9.4: FEAT_ASID2 */
1469
+ SET_IDREG(isar, ID_AA64MMFR4, t);
1470
+
1471
+ t = GET_IDREG(isar, ID_AA64ZFR0);
1472
+ t = FIELD_DP64(t, ID_AA64ZFR0, SVEVER, 2); /* v9.2: FEAT_SVE2p1 */
1473
+ t = FIELD_DP64(t, ID_AA64ZFR0, AES, 2); /* v9.0: FEAT_SVE_PMULL128 */
1474
+ t = FIELD_DP64(t, ID_AA64ZFR0, BITPERM, 1); /* v9.0: FEAT_SVE_BitPerm */
1475
+ t = FIELD_DP64(t, ID_AA64ZFR0, B16B16, 1); /* v9.2: FEAT_SVE_B16B16 */
1476
+ t = FIELD_DP64(t, ID_AA64ZFR0, SHA3, 1); /* v9.0: FEAT_SVE_SHA3 */
1477
+ t = FIELD_DP64(t, ID_AA64ZFR0, SM4, 1); /* v9.0: FEAT_SVE_SM4 */
1478
+ SET_IDREG(isar, ID_AA64ZFR0, t);
1479
+
1480
t = GET_IDREG(isar, ID_AA64SMFR0);
1481
t = FIELD_DP64(t, ID_AA64SMFR0, SMOP4, 1); /* v9.4: FEAT_SME_MOP4 */
1482
t = FIELD_DP64(t, ID_AA64SMFR0, STMOP, 1); /* v9.4: FEAT_SME_TMOP */
@@ -1434,43 +1513,16 @@ void aarch64_max_tcg_initfn(Object *obj)
1513
t = FIELD_DP64(t, ID_AA64FPFR0, F8CVT, 1); /* v9.2: FEAT_FP8 */
1514
SET_IDREG(isar, ID_AA64FPFR0, t);
1515
1437
- /* Replicate the same data to the 32-bit id registers. */
1438
- aa32_max_features(cpu);
1439
-
1440
-#ifdef CONFIG_USER_ONLY
1441
- /*
1442
- * For usermode -cpu max we can use a larger and more efficient DCZ
1443
- * blocksize since we don't have to follow what the hardware does.
1444
- */
1445
- cpu->ctr = 0x80038003; /* 32 byte I and D cacheline size, VIPT icache */
1446
- set_dczid_bs(cpu, 7); /* 512 bytes */
1447
-#endif
1448
-
1449
- /* v8.4: FEAT_MTE2 */
1450
- cpu->gm_blocksize = 6; /* 256 bytes */
1451
-
1452
- /* v8.2: FEAT_SVE */
1453
- cpu->sve_vq.supported = MAKE_64BIT_MASK(0, ARM_MAX_VQ);
1454
- aarch64_add_sve_properties(obj);
1455
- object_property_add(obj, "sve-max-vq", "uint32", cpu_max_get_sve_max_vq,
1456
- cpu_max_set_sve_max_vq, NULL, NULL);
1457
-
1516
/* v9.2: FEAT_SME */
1517
cpu->sme_vq.supported = SVE_VQ_POW2_MAP;
1518
aarch64_add_sme_properties(obj);
1519
1462
- /* v8.2: FEAT_PAuth2 */
1463
- aarch64_add_pauth_properties(obj);
1464
-
1520
/* v9.1: FEAT_RME */
1521
object_property_add_bool(obj, "x-rme", cpu_arm_get_rme, cpu_arm_set_rme);
1522
1523
/* v9.4: FEAT_RME_GPC2 */
1524
object_property_add(obj, "x-l0gptsz", "uint32", cpu_max_get_l0gptsz,
1525
cpu_max_set_l0gptsz, NULL, NULL);
1471
-
1472
- /* v8.6: FEAT_LPA2 */
1473
- qdev_property_add_static(DEVICE(obj), &arm_cpu_lpa2_property);
1526
}
1527
1528
static const ARMCPUInfo aarch64_cpus[] = {
@@ -1488,7 +1540,7 @@ static const ARMCPUInfo aarch64_cpus[] = {
1540
{ .name = "neoverse-n1", .initfn = aarch64_neoverse_n1_initfn },
1541
{ .name = "neoverse-v1", .initfn = aarch64_neoverse_v1_initfn },
1542
{ .name = "neoverse-n2", .initfn = aarch64_neoverse_n2_initfn },
1491
- { .name = "max-v9", .initfn = aarch64_max_tcg_initfn },
1543
+ { .name = "max-v9", .initfn = aarch64_max_v9_tcg_initfn },
1544
};
1545
1546
static void aarch64_cpu_register_types(void)
target/arm/tcg/stubs32.c
+6
-1
@@ -28,7 +28,12 @@ void aarch64_host_initfn(Object *obj)
28
g_assert_not_reached();
29
}
30
31
-void aarch64_max_tcg_initfn(Object *obj)
31
+void aarch64_max_v8_tcg_initfn(Object *obj)
32
+{
33
+ g_assert_not_reached();
34
+}
35
+
36
+void aarch64_max_v9_tcg_initfn(Object *obj)
37
{
38
g_assert_not_reached();
39
}