Add scaffolding for named CPU models on Arm on top of the property
layer. A model is a list of (property, value) overrides plus an
optional parent; a child inherits its parent's property values and may
override them.
Instance init steps:
- host feature discovery.
- zero out isar.idregs[] and restore RES1 fields.
- install SYSREG_ and sve* properties.
- walk the parent chain root-first and apply each level's
properties.
This assumes that each property is idempotent. `pauth` is intentionally
not installed because the model's ID register view is authoritative
and can be configured through SYSREG_ properties.
No models are defined yet.
Signed-off-by: Khushit Shah <[email protected]>
---
target/arm/cpu-models.c | 191 ++++++++++++++++++++++++++++++++++++++++
target/arm/cpu-models.h | 36 ++++++++
target/arm/meson.build | 1 +
3 files changed, 228 insertions(+)
create mode 100644 target/arm/cpu-models.c
create mode 100644 target/arm/cpu-models.h
diff --git a/target/arm/cpu-models.c b/target/arm/cpu-models.c
new file mode 100644
index 0000000000..fed3197186
--- /dev/null
+++ b/target/arm/cpu-models.c
@@ -0,0 +1,191 @@
+/*
+ * ARM named CPU models.
+ *
+ * Each model is defined as an array of MODEL_PROP macros, listing only
+ * the properties that DIFFER from the parent model.
+ *
+ * Initialization starts with a zeroed idregs[] array.
+ *
+ * During initialization, the parent chain is walked root-first and every
+ * level's properties are applied via QOM, so the leaf's values naturally
+ * override its ancestors.
+ *
+ * Assumptions:
+ * - All named CPU model properties are idempotent.
+ *
+ * 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 "cpu-idregs.h"
+#include "cpu-models.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 }
+
+/* Maximum parent-chain depth for named CPU models. */
+#define ARM_NAMED_CPU_MODEL_MAX_DEPTH 16
+
+static const ArmNamedCpuModel arm_cpu_models[] = {
+ {NULL, NULL, NULL}
+};
+
+const ArmNamedCpuModel *arm_find_model(const char *name)
+{
+ for (size_t i = 0; i < ARRAY_SIZE(arm_cpu_models); i++) {
+ if (!arm_cpu_models[i].name) {
+ break;
+ }
+
+ if (g_str_equal(arm_cpu_models[i].name, name)) {
+ return &arm_cpu_models[i];
+ }
+ }
+ return NULL;
+}
+
+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;
+ }
+ }
+}
+
+void arm_realize_model_chain(Object *obj, const ArmNamedCpuModel *model,
+ Error **errp)
+{
+ const ArmNamedCpuModel *cur, *parent;
+ const ArmNamedCpuModel *chain[ARM_NAMED_CPU_MODEL_MAX_DEPTH];
+ size_t depth = 0;
+
+ for (cur = model; cur; ) {
+ if (depth >= ARRAY_SIZE(chain)) {
+ error_setg(errp, "model '%s': parent chain too deep", 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 reset_res1_fields(ARMCPU *cpu)
+{
+ for (size_t i = 0; i < NUM_ID_IDX; i++) {
+ for (size_t j = 0; j < arm64_id_regs[i].fields_count; j++) {
+ if (strcmp(arm64_id_regs[i].fields[j].name, "RES1") == 0) {
+ cpu->isar.idregs[i] = deposit64(cpu->isar.idregs[i],
+ arm64_id_regs[i].fields[j].shift,
+ arm64_id_regs[i].fields[j].length,
+ MAKE_64BIT_MASK(0, arm64_id_regs[i].fields[j].length));
+ }
+ }
+ }
+}
+
+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.a Reset ID regs to 0 */
+ memset(cpu->isar.idregs, 0, sizeof(cpu->isar.idregs));
+
+ /* 1.b Properly set RES1 fields. */
+ reset_res1_fields(cpu);
+
+ /* 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)
+{
+ static ARMCPUInfo *arm_named_cpu_infos;
+ size_t i, n = ARRAY_SIZE(arm_cpu_models);
+
+ if (!n) {
+ return;
+ }
+
+ arm_named_cpu_infos = g_new0(ARMCPUInfo, n);
+ for (i = 0; i < n; i++) {
+ if (!arm_cpu_models[i].name) {
+ break;
+ }
+
+ 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/cpu-models.h b/target/arm/cpu-models.h
new file mode 100644
index 0000000000..144778744e
--- /dev/null
+++ b/target/arm/cpu-models.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#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);
+void arm_apply_model_props(Object *obj, const ArmModelPropValue *props,
+ Error **errp);
+void arm_realize_model_chain(Object *obj, const ArmNamedCpuModel *model,
+ Error **errp);
+
+#endif /* ARM_CPU_MODELS_H */
diff --git a/target/arm/meson.build b/target/arm/meson.build
index 384d90c530..dd28146c52 100644
--- a/target/arm/meson.build
+++ b/target/arm/meson.build
@@ -32,6 +32,7 @@ arm_common_system_ss.add(files(
'arm-qmp-cmds.c',
'cpu-idregs.c',
))
+arm_system_ss.add(when: 'TARGET_AARCH64', if_true: files('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'))
--
2.52.0