[RFC PATCH v3 09/19] target/arm: Add named cpu model infra + graviton3 named model

Khushit Shah <[email protected]>
Newsgroups org.nongnu.qemu-arm,dev.linux.lists.kvmarm,org.nongnu.qemu-devel
Message-ID <[email protected]>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.