From: Shaju Abraham <[email protected]>
Introduce a small named-CPU-model layer on top of the field-backed
property infrastructure. Each model is a flat table of (name, value)
overrides and a parent pointer. At instance-init, the parent chain is
walked root-first and every level's properties are applied via QOM,
so a child entry overrides its ancestors for the same name.
For example, the graviton3-v1 named model hierarchy is:
non-zero-defaults-v1 Known non-zero default ID regs field values
arm-v8_4-a-v1 ARMv8.4-A architectural mandate
neoverse-v1-v1 Neoverse V1
graviton3-v1 AWS Graviton3
non-zero-defaults-v1:
Field that should be populated with some non zero value by default.
arm-v8_4-a-v1:
Only features mandated by the ARM ARM.
neoverse-v1-v1:
Reference manual derived feature values for the reference core.
Values differ from TRM values based on what was exposed to the guest
with -cpu host.
graviton3-v1:
SoC integration choices (crypto pin, cache hints, PAuth alg).
The model values are figured out from ID register dump visible to
QEMU with -cpu host on Graviton3 machine.
Model realization:
1. Zero out ID regs array.
2. Add all properties to the CPU Object
3. arm_realize_model_chain: Walk the parent chain from root first and
apply all the properties.
Co-authored-by: Shaju Abraham <[email protected]>
Signed-off-by: Khushit Shah <[email protected]>
---
hw/arm/virt.c | 5 +
target/arm/arm-cpu-models.c | 336 ++++++++++++++++++++++++++++++++++++
target/arm/arm-cpu-models.h | 35 ++++
target/arm/meson.build | 7 +-
4 files changed, 381 insertions(+), 2 deletions(-)
create mode 100644 target/arm/arm-cpu-models.c
create mode 100644 target/arm/arm-cpu-models.h
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index fe19030886..34e56dc1f1 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -3812,6 +3812,11 @@ static GPtrArray *virt_get_valid_cpu_types(const
MachineState *ms)
g_ptr_array_add(vct, g_strdup(ARM_CPU_TYPE_NAME("host")));
}
}
+ if (kvm_enabled() && target_aarch64()) {
+ g_ptr_array_add(vct, g_strdup(ARM_CPU_TYPE_NAME("arm-v8_4-a-v1")));
+ g_ptr_array_add(vct, g_strdup(ARM_CPU_TYPE_NAME("neoverse-v1-v1")));
+ g_ptr_array_add(vct, g_strdup(ARM_CPU_TYPE_NAME("graviton3-v1")));
+ }
g_ptr_array_add(vct, g_strdup(ARM_CPU_TYPE_NAME("max")));
return vct;
diff --git a/target/arm/arm-cpu-models.c b/target/arm/arm-cpu-models.c
new file mode 100644
index 0000000000..747e0df010
--- /dev/null
+++ b/target/arm/arm-cpu-models.c
@@ -0,0 +1,336 @@
+/*
+ * ARM named CPU model definitions.
+ *
+ * Each model is defined as an array of MODEL_PROP macro, listing only
+ * the properties that DIFFER from the parent model.
+ *
+ * At realisation the parent chain is walked root-first and
+ * every level's props are applied via QOM, so the leaf's values
+ * naturally override its ancestors.
+ *
+ * Hierarchy:
+ *
+ * non-zero-defaults-v1 Known non-zero default ID reg field values
+ * arm-v8_4-a-v1 ARMv8.4-A architectural mandate
+ * neoverse-v1-v1 Neoverse V1 (TRM 102649)
+ * graviton3-v1 AWS Graviton3
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+#include "system/kvm.h"
+#include "cpu.h"
+#include "internals.h"
+#include "kvm_arm.h"
+#include "arm-cpu-models.h"
+#include "cpu-idregs.h"
+
+#define MODEL_PROP_FIELD_STR str
+#define MODEL_PROP_FIELD_BOOL b
+#define MODEL_PROP_FIELD_NUM num
+
+#define MODEL_PROP(_name, _type, _value) \
+ { .name = (_name), .type = ARM_MODEL_PROP_##_type, \
+ .u.MODEL_PROP_FIELD_##_type = (_value) }
+
+#define MODEL_PROP_END { .name = NULL }
+
+/*
+ * These fields should be populated with some non-zero default values.
+ */
+static const ArmModelPropValue non_zero_defaults_v1_props[] = {
+ /* ID_AA64PFR0_EL1 */
+ MODEL_PROP("SYSREG_ID_AA64PFR0_EL1_EL0", NUM, 0b0001),
+ MODEL_PROP("SYSREG_ID_AA64PFR0_EL1_EL1", NUM, 0b0001),
+ MODEL_PROP("SYSREG_ID_AA64PFR0_EL1_FP", NUM, 0b1111),
+ MODEL_PROP("SYSREG_ID_AA64PFR0_EL1_AdvSIMD", NUM, 0b1111),
+
+ /* ID_AA64PFR1_EL1 */
+ MODEL_PROP("SYSREG_ID_AA64PFR1_EL1_MTE_frac", NUM, 0b1111),
+
+ /* ID_AA64MMFR0_EL1 */
+ MODEL_PROP("SYSREG_ID_AA64MMFR0_EL1_TGran64", NUM, 0b1111),
+ MODEL_PROP("SYSREG_ID_AA64MMFR0_EL1_TGran4", NUM, 0b1111),
+ MODEL_PROP("SYSREG_ID_AA64MMFR0_EL1_TGran16_2", NUM, 0b0001),
+ MODEL_PROP("SYSREG_ID_AA64MMFR0_EL1_TGran64_2", NUM, 0b0001),
+ MODEL_PROP("SYSREG_ID_AA64MMFR0_EL1_TGran4_2", NUM, 0b0001),
+
+ /* ID_AA64DFR0_EL1 */
+ MODEL_PROP("SYSREG_ID_AA64DFR0_EL1_DebugVer", NUM, 0x6),
+ MODEL_PROP("SYSREG_ID_AA64DFR0_EL1_DoubleLock", NUM, 0b1111),
+
+ /* CTR_EL0 */
+ MODEL_PROP("SYSREG_CTR_EL0_L1Ip", NUM, 0b10),
+ MODEL_PROP("SYSREG_CTR_EL0_RES1_31", NUM, 1),
+
+ /* ID_AA64MMFR4_EL1 */
+ MODEL_PROP("SYSREG_ID_AA64MMFR4_EL1_E2H0", NUM, 0b1111),
+
+ MODEL_PROP_END,
+};
+
+static const ArmModelPropValue armv8_4_a_v1_props[] = {
+ MODEL_PROP("SYSREG_ID_AA64ISAR0_EL1_CRC32", NUM, 1),
+ MODEL_PROP("SYSREG_ID_AA64ISAR0_EL1_Atomic", NUM, 2),
+ MODEL_PROP("SYSREG_ID_AA64MMFR1_EL1_HPDS", NUM, 1),
+ MODEL_PROP("SYSREG_ID_AA64MMFR1_EL1_LO", NUM, 1),
+
+ MODEL_PROP("SYSREG_ID_AA64ISAR1_EL1_DPB", NUM, 1),
+
+ MODEL_PROP("SYSREG_ID_AA64PFR0_EL1_RAS", NUM, 1),
+ MODEL_PROP("SYSREG_ID_AA64PFR1_EL1_RAS_frac", NUM, 0),
+
+ MODEL_PROP("SYSREG_ID_AA64MMFR1_EL1_PAN", NUM, 2),
+ MODEL_PROP("SYSREG_ID_AA64MMFR2_EL1_UAO", NUM, 1),
+ MODEL_PROP("SYSREG_ID_AA64MMFR2_EL1_CnP", NUM, 1),
+ MODEL_PROP("SYSREG_ID_AA64MMFR2_EL1_IESB", NUM, 1),
+
+ MODEL_PROP("SYSREG_ID_AA64PFR0_EL1_DIT", NUM, 1),
+ MODEL_PROP("SYSREG_ID_AA64DFR0_EL1_DebugVer", NUM, 9),
+ MODEL_PROP("SYSREG_ID_AA64DFR0_EL1_PMUVer", NUM, 5),
+ MODEL_PROP("SYSREG_ID_AA64ISAR0_EL1_TS", NUM, 1),
+ MODEL_PROP("SYSREG_ID_AA64ISAR1_EL1_LRCPC", NUM, 2),
+ MODEL_PROP("SYSREG_ID_AA64MMFR2_EL1_AT", NUM, 1),
+ MODEL_PROP("SYSREG_ID_AA64MMFR2_EL1_IDS", NUM, 1),
+ /* KVM zeroes out these field when MTE is not enabled. */
+ MODEL_PROP("SYSREG_ID_AA64PFR1_EL1_MTE_frac", NUM, 0),
+ MODEL_PROP_END,
+};
+
+static const ArmModelPropValue neoverse_v1_v1_props[] = {
+ MODEL_PROP("SYSREG_MIDR_EL1_Implementer", NUM, 0x41),
+ MODEL_PROP("SYSREG_MIDR_EL1_Variant", NUM, 0x1),
+ MODEL_PROP("SYSREG_MIDR_EL1_Architecture", NUM, 0xF),
+ MODEL_PROP("SYSREG_MIDR_EL1_PartNum", NUM, 0xD40),
+ MODEL_PROP("SYSREG_MIDR_EL1_Revision", NUM, 0x1),
+
+ MODEL_PROP("SYSREG_ID_AA64DFR0_EL1_BRPs", NUM, 0x5),
+ MODEL_PROP("SYSREG_ID_AA64DFR0_EL1_WRPs", NUM, 0x3),
+ MODEL_PROP("SYSREG_ID_AA64DFR0_EL1_CTX_CMPs", NUM, 0x1),
+ MODEL_PROP("SYSREG_ID_AA64DFR0_EL1_DoubleLock", NUM, 0xF),
+
+ MODEL_PROP("SYSREG_ID_AA64ISAR0_EL1_RDM", NUM, 1),
+ MODEL_PROP("SYSREG_ID_AA64ISAR0_EL1_DP", NUM, 1),
+ MODEL_PROP("SYSREG_ID_AA64ISAR0_EL1_FHM", NUM, 1),
+
+ MODEL_PROP("SYSREG_ID_AA64ISAR1_EL1_DPB", NUM, 2),
+ MODEL_PROP("SYSREG_ID_AA64ISAR1_EL1_JSCVT", NUM, 1),
+ MODEL_PROP("SYSREG_ID_AA64ISAR1_EL1_FCMA", NUM, 1),
+ MODEL_PROP("SYSREG_ID_AA64ISAR1_EL1_BF16", NUM, 1),
+ MODEL_PROP("SYSREG_ID_AA64ISAR1_EL1_DGH", NUM, 1),
+ MODEL_PROP("SYSREG_ID_AA64ISAR1_EL1_I8MM", NUM, 1),
+
+ MODEL_PROP("SYSREG_ID_AA64PFR0_EL1_FP", NUM, 1),
+ MODEL_PROP("SYSREG_ID_AA64PFR0_EL1_AdvSIMD", NUM, 1),
+
+ MODEL_PROP("SYSREG_ID_AA64PFR0_EL1_RAS", NUM, 2),
+ MODEL_PROP("SYSREG_ID_AA64PFR1_EL1_RAS_frac", NUM, 0),
+
+ MODEL_PROP("SYSREG_ID_AA64PFR0_EL1_CSV2", NUM, 1),
+ MODEL_PROP("SYSREG_ID_AA64PFR1_EL1_CSV2_frac", NUM, 0),
+
+ MODEL_PROP("SYSREG_ID_AA64PFR0_EL1_CSV3", NUM, 1),
+
+ MODEL_PROP("SYSREG_ID_AA64PFR1_EL1_SSBS", NUM, 2),
+
+ MODEL_PROP("SYSREG_ID_AA64MMFR0_EL1_PARange", NUM, 5),
+ MODEL_PROP("SYSREG_ID_AA64MMFR0_EL1_ASIDBits", NUM, 2),
+ MODEL_PROP("SYSREG_ID_AA64MMFR0_EL1_BigEnd", NUM, 1),
+ MODEL_PROP("SYSREG_ID_AA64MMFR0_EL1_SNSMem", NUM, 1),
+ MODEL_PROP("SYSREG_ID_AA64MMFR0_EL1_TGran4", NUM, 0),
+ MODEL_PROP("SYSREG_ID_AA64MMFR0_EL1_TGran16", NUM, 1),
+ MODEL_PROP("SYSREG_ID_AA64MMFR0_EL1_TGran64", NUM, 0),
+ MODEL_PROP("SYSREG_ID_AA64MMFR0_EL1_TGran4_2", NUM, 0),
+ MODEL_PROP("SYSREG_ID_AA64MMFR0_EL1_TGran16_2", NUM, 0),
+ MODEL_PROP("SYSREG_ID_AA64MMFR0_EL1_TGran64_2", NUM, 0),
+ MODEL_PROP("SYSREG_ID_AA64MMFR1_EL1_HAFDBS", NUM, 2),
+ MODEL_PROP("SYSREG_ID_AA64MMFR1_EL1_VMIDBits", NUM, 2),
+ MODEL_PROP("SYSREG_ID_AA64MMFR1_EL1_VH", NUM, 1),
+ MODEL_PROP("SYSREG_ID_AA64MMFR1_EL1_HPDS", NUM, 2),
+ MODEL_PROP("SYSREG_ID_AA64MMFR1_EL1_XNX", NUM, 1),
+ MODEL_PROP("SYSREG_ID_AA64MMFR1_EL1_SpecSEI", NUM, 0),
+
+ MODEL_PROP("SYSREG_ID_AA64MMFR2_EL1_FWB", NUM, 1),
+ MODEL_PROP("SYSREG_ID_AA64MMFR2_EL1_BBM", NUM, 2),
+ MODEL_PROP("SYSREG_ID_AA64MMFR2_EL1_EVT", NUM, 2),
+
+ MODEL_PROP("SYSREG_ID_AA64MMFR4_EL1_E2H0", NUM, 0),
+
+ MODEL_PROP("SYSREG_CTR_EL0_IminLine", NUM, 4),
+ MODEL_PROP("SYSREG_CTR_EL0_L1Ip", NUM, 3),
+ MODEL_PROP("SYSREG_CTR_EL0_DminLine", NUM, 4),
+ MODEL_PROP("SYSREG_CTR_EL0_ERG", NUM, 4),
+ MODEL_PROP("SYSREG_CTR_EL0_CWG", NUM, 4),
+
+ MODEL_PROP("SYSREG_DCZID_EL0_BS", NUM, 0x4),
+
+ /* pauth */
+ MODEL_PROP("SYSREG_ID_AA64ISAR1_EL1_APA", NUM, 3),
+ MODEL_PROP("SYSREG_ID_AA64ISAR1_EL1_GPA", NUM, 1),
+
+ /* sve */
+ MODEL_PROP("SYSREG_ID_AA64PFR0_EL1_SVE", NUM, 1),
+ MODEL_PROP("SYSREG_ID_AA64ZFR0_EL1_BF16", NUM, 1),
+ MODEL_PROP("SYSREG_ID_AA64ZFR0_EL1_I8MM", NUM, 1),
+ MODEL_PROP("sve128", BOOL, true),
+
+ MODEL_PROP_END,
+};
+
+static const ArmModelPropValue graviton3_v1_props[] = {
+ MODEL_PROP("SYSREG_ID_AA64ISAR0_EL1_AES", NUM, 2),
+ MODEL_PROP("SYSREG_ID_AA64ISAR0_EL1_SHA1", NUM, 1),
+ MODEL_PROP("SYSREG_ID_AA64ISAR0_EL1_SHA2", NUM, 2),
+ MODEL_PROP("SYSREG_ID_AA64ISAR0_EL1_SHA3", NUM, 1),
+ MODEL_PROP("SYSREG_ID_AA64ISAR0_EL1_SM3", NUM, 1),
+ MODEL_PROP("SYSREG_ID_AA64ISAR0_EL1_SM4", NUM, 1),
+ MODEL_PROP("SYSREG_ID_AA64ISAR0_EL1_RNDR", NUM, 1),
+
+ MODEL_PROP("SYSREG_CTR_EL0_IDC", NUM, 1),
+ MODEL_PROP("SYSREG_CTR_EL0_DIC", NUM, 1),
+
+ MODEL_PROP("SYSREG_REVIDR_EL1_VAL", NUM, 1),
+
+ MODEL_PROP_END,
+};
+
+static const ArmNamedCpuModel arm_cpu_models[] = {
+ {
+ .name = "non-zero-defaults-v1",
+ .parent = NULL,
+ .props = non_zero_defaults_v1_props,
+ },
+ {
+ .name = "arm-v8_4-a-v1",
+ .parent = "non-zero-defaults-v1",
+ .props = armv8_4_a_v1_props,
+ },
+ {
+ .name = "neoverse-v1-v1",
+ .parent = "arm-v8_4-a-v1",
+ .props = neoverse_v1_v1_props,
+ },
+ {
+ .name = "graviton3-v1",
+ .parent = "neoverse-v1-v1",
+ .props = graviton3_v1_props,
+ },
+};
+
+static ARMCPUInfo arm_named_cpu_infos[ARRAY_SIZE(arm_cpu_models)];
+const ArmNamedCpuModel *arm_find_model(const char *name)
+{
+ size_t i;
+ for (i = 0; i < ARRAY_SIZE(arm_cpu_models); i++) {
+ if (g_str_equal(arm_cpu_models[i].name, name)) {
+ return &arm_cpu_models[i];
+ }
+ }
+ return NULL;
+}
+
+static void arm_apply_model_props(Object *obj, const ArmModelPropValue *props,
+ Error **errp)
+{
+ const ArmModelPropValue *pv;
+ ERRP_GUARD();
+
+ for (pv = props; pv->name; pv++) {
+ switch (pv->type) {
+ case ARM_MODEL_PROP_BOOL:
+ object_property_set_bool(obj, pv->name, pv->u.b, errp);
+ break;
+ case ARM_MODEL_PROP_NUM:
+ object_property_set_uint(obj, pv->name, pv->u.num, errp);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ if (*errp) {
+ error_prepend(errp, "property '%s': ", pv->name);
+ return;
+ }
+ }
+}
+
+static void arm_realize_model_chain(Object *obj, const ArmNamedCpuModel *model,
+ Error **errp)
+{
+ const ArmNamedCpuModel *cur, *parent;
+ const ArmNamedCpuModel *chain[ARRAY_SIZE(arm_cpu_models)];
+ size_t depth = 0;
+ for (cur = model; cur; ) {
+ if (depth >= ARRAY_SIZE(chain)) {
+ error_setg(errp, "model '%s': parent chain too deep "
+ "(possible cycle)", model->name);
+ return;
+ }
+ chain[depth++] = cur;
+
+ if (!cur->parent) {
+ break;
+ }
+ parent = arm_find_model(cur->parent);
+ if (!parent) {
+ error_setg(errp, "model '%s': unknown parent '%s'",
+ cur->name, cur->parent);
+ return;
+ }
+ cur = parent;
+ }
+
+ while (depth--) {
+ arm_apply_model_props(obj, chain[depth]->props, errp);
+ if (*errp) {
+ return;
+ }
+ }
+}
+
+static void arm_named_cpu_initfn(Object *obj)
+{
+ ARMCPU *cpu = ARM_CPU(obj);
+ ARMCPUClass *acc = ARM_CPU_GET_CLASS(obj);
+ const ArmNamedCpuModel *model = arm_find_model(acc->info->name);
+ if (!model) {
+ error_report("'%s' CPU model entry not found)",
+ acc->info->name);
+ return;
+ }
+
+ if (!kvm_enabled()) {
+ error_report("'%s' CPU model requires KVM (-accel kvm)",
+ acc->info->name);
+ return;
+ }
+
+ kvm_arm_set_cpu_features_from_host(cpu);
+ if (!arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
+ return;
+ }
+
+ /* 1. Reset ID regs to 0 */
+ memset(cpu->isar.idregs, 0, sizeof(cpu->isar.idregs));
+
+ /* 2. Add sveNNNN and SYSREG_ properties */
+ aarch64_add_sve_properties(obj);
+ aarch64_add_idregs_properties(obj);
+
+ /* 3. Realize the model chain. */
+ arm_realize_model_chain(obj, model, &error_abort);
+}
+
+void arm_register_named_cpu_models(void)
+{
+ size_t i;
+ for (i = 0; i < ARRAY_SIZE(arm_cpu_models); i++) {
+ arm_named_cpu_infos[i].name = arm_cpu_models[i].name;
+ arm_named_cpu_infos[i].initfn = arm_named_cpu_initfn;
+ arm_cpu_register(&arm_named_cpu_infos[i]);
+ }
+}
+
+type_init(arm_register_named_cpu_models)
diff --git a/target/arm/arm-cpu-models.h b/target/arm/arm-cpu-models.h
new file mode 100644
index 0000000000..ac4e510499
--- /dev/null
+++ b/target/arm/arm-cpu-models.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * ARM named CPU model definitions - public API.
+ */
+#ifndef ARM_CPU_MODELS_H
+#define ARM_CPU_MODELS_H
+
+#include "qapi/error.h"
+#include "qom/object.h"
+
+typedef enum ArmModelPropType {
+ ARM_MODEL_PROP_BOOL,
+ ARM_MODEL_PROP_NUM,
+} ArmModelPropType;
+
+typedef struct ArmModelPropValue {
+ const char *name;
+ ArmModelPropType type;
+ union {
+ bool b;
+ uint64_t num;
+ } u;
+} ArmModelPropValue;
+
+typedef struct ArmNamedCpuModel {
+ const char *name;
+ const char *parent;
+ const ArmModelPropValue *props;
+} ArmNamedCpuModel;
+
+void arm_register_named_cpu_models(void);
+
+const ArmNamedCpuModel *arm_find_model(const char *name);
+
+#endif /* ARM_CPU_MODELS_H */
diff --git a/target/arm/meson.build b/target/arm/meson.build
index 64d1ec63ab..72eed1bcc6 100644
--- a/target/arm/meson.build
+++ b/target/arm/meson.build
@@ -10,7 +10,7 @@ arm_user_ss.add(files('gdbstub.c'))
arm_ss.add(when: 'TARGET_AARCH64', if_true: files(
'cpu64.c',
- 'gdbstub64.c'
+ 'gdbstub64.c',
))
arm_common_ss.add(files(
@@ -21,7 +21,10 @@ arm_common_system_ss.add(files(
'arm-qmp-cmds.c',
'cpu-idregs.c',
))
-arm_system_ss.add(when: 'CONFIG_KVM', if_true: files('hyp_gdbstub.c', 'kvm.c'))
+arm_system_ss.add(when: 'TARGET_AARCH64', if_true: files('arm-cpu-models.c'))
+arm_system_ss.add(when: 'CONFIG_KVM',
+ if_true: files('hyp_gdbstub.c',
+ 'kvm.c'))
arm_system_ss.add(when: 'CONFIG_HVF', if_true: files('hyp_gdbstub.c'))
arm_user_ss.add(files('cpu.c'))
--
2.52.0