Now, report blocker for named cpu models in query-cpu-definitions.
Heavy handling is done by validating the realized ID registers values
against the host values. sveNNNN needs special handling as those
are not stored in any ID registers.

Introduces arm_cpu_class_check_missing_features() and
aarch64_idregs_get_blockers() to achieve this. This way we need not
instantiate each model to check if it can be realized and we also get
granular information on the ID reg fields blocking the realization.

Signed-off-by: Khushit Shah <[email protected]>
---
 target/arm/arm-cpu-models-stub.c |  5 ++++
 target/arm/arm-cpu-models.c      | 35 ++++++++++++++++++++++
 target/arm/arm-cpu-models.h      |  2 ++
 target/arm/arm-qmp-cmds.c        | 12 ++++++++
 target/arm/cpu64.c               | 51 ++++++++++++++++++++++++++++++++
 target/arm/internals.h           |  3 ++
 target/arm/kvm-stub.c            | 10 +++++++
 target/arm/kvm.c                 |  4 +--
 target/arm/kvm_arm.h             | 17 +++++++++++
 9 files changed, 137 insertions(+), 2 deletions(-)

diff --git a/target/arm/arm-cpu-models-stub.c b/target/arm/arm-cpu-models-stub.c
index 0481f0f946..dadd3b7647 100644
--- a/target/arm/arm-cpu-models-stub.c
+++ b/target/arm/arm-cpu-models-stub.c
@@ -11,3 +11,8 @@ const ArmNamedCpuModel *arm_find_model(const char *name)
 {
     return NULL;
 }
+
+void arm_cpu_class_check_missing_features(ARMCPUClass *acc, strList **blockers)
+{
+    return;
+}
diff --git a/target/arm/arm-cpu-models.c b/target/arm/arm-cpu-models.c
index 40e8578591..44e7d3cfa2 100644
--- a/target/arm/arm-cpu-models.c
+++ b/target/arm/arm-cpu-models.c
@@ -475,6 +475,41 @@ static void arm_named_cpu_initfn(Object *obj)
     arm_realize_model_chain(obj, model, &error_abort);
 }
 
+void arm_cpu_class_check_missing_features(ARMCPUClass *acc, strList **blockers)
+{
+    Error *err = NULL;
+    Object *obj = object_new_with_class(OBJECT_CLASS(acc));
+    ARMCPU *cpu = ARM_CPU(obj);
+
+    if (!kvm_enabled() || !arm_find_model(acc->info->name)) {
+        goto out;
+    }
+
+    if (cpu_isar_feature(aa64_sve, cpu)) {
+        arm_cpu_sve_finalize(cpu, &err);
+        if (err) {
+            error_free(err);
+            err = NULL;
+            uint32_t host_vq_map = cpu->sve_vq.supported;
+            uint32_t model_vq_map = cpu->sve_vq.map;
+
+            if ((model_vq_map & host_vq_map) != model_vq_map) {
+                for (int vq = 1; vq <= ARM_MAX_VQ; vq++) {
+                    uint32_t vq_bit = (1 << (vq - 1));
+                    if ((model_vq_map & vq_bit) && !(host_vq_map & vq_bit)) {
+                        QAPI_LIST_PREPEND(*blockers,
+                            g_strdup_printf("sve%d", vq * 128));
+                    }
+                }
+            }
+        }
+    }
+
+    aarch64_idregs_get_blockers(cpu, kvm_arm_get_host_isar(), blockers);
+out:
+    object_unref(obj);
+}
+
 void arm_register_named_cpu_models(void)
 {
     size_t i;
diff --git a/target/arm/arm-cpu-models.h b/target/arm/arm-cpu-models.h
index ac4e510499..6c8fba2aeb 100644
--- a/target/arm/arm-cpu-models.h
+++ b/target/arm/arm-cpu-models.h
@@ -7,6 +7,7 @@
 
 #include "qapi/error.h"
 #include "qom/object.h"
+#include "cpu.h"
 
 typedef enum ArmModelPropType {
     ARM_MODEL_PROP_BOOL,
@@ -31,5 +32,6 @@ typedef struct ArmNamedCpuModel {
 void arm_register_named_cpu_models(void);
 
 const ArmNamedCpuModel *arm_find_model(const char *name);
+void arm_cpu_class_check_missing_features(ARMCPUClass *acc, strList 
**blockers);
 
 #endif /* ARM_CPU_MODELS_H */
diff --git a/target/arm/arm-qmp-cmds.c b/target/arm/arm-qmp-cmds.c
index 457cc3edf3..6f11681c87 100644
--- a/target/arm/arm-qmp-cmds.c
+++ b/target/arm/arm-qmp-cmds.c
@@ -341,6 +341,18 @@ static void arm_cpu_add_definition(gpointer data, gpointer 
user_data)
     info->name = cpu_model_from_type(typename);
     info->q_typename = g_strdup(typename);
 
+    /* calculate blockers */
+    if (kvm_enabled()
+            && !g_str_equal(info->name, "host")
+            && !g_str_equal(info->name, "max")
+            && arm_find_model(info->name) != NULL) {
+        arm_cpu_class_check_missing_features(ARM_CPU_CLASS(oc),
+                                    &info->unavailable_features);
+        if (info->unavailable_features) {
+            info->has_unavailable_features = true;
+        }
+    }
+
     QAPI_LIST_PREPEND(*cpu_list, info);
 }
 
diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c
index 44dd9acf80..f41bcf2e81 100644
--- a/target/arm/cpu64.c
+++ b/target/arm/cpu64.c
@@ -20,6 +20,7 @@
 
 #include "qemu/osdep.h"
 #include "qapi/error.h"
+#include "qapi/util.h"
 #include "cpu.h"
 #include "cpregs.h"
 #include "qemu/module.h"
@@ -779,6 +780,56 @@ void aarch64_add_idregs_properties(Object *obj)
         aarch64_add_idreg_properties(obj, reg);
     }
 }
+
+void aarch64_idregs_get_blockers(ARMCPU *cpu,
+                             const ARMISARegisters *host_isar,
+                             strList **blockers)
+{
+    for (int i = 0; i < NUM_ID_IDX; i++) {
+        ARM64SysReg *reg = &arm64_id_regs[i];
+
+        if (!reg->name) {
+            continue;
+        }
+
+        for (uint32_t j = 0; j < reg->fields_count; j++) {
+            const ARM64SysRegField *field = &reg->fields[j];
+            ArmFieldValueSet *vs = NULL;
+            bool supported = false;
+            uint64_t model_val;
+
+            /* Not a blocker if the field is never written back to KVM. */
+            if (arm_field_skip_writeback_always(field) ||
+                (!arm_field_is_writable(field) &&
+                 arm_field_skip_writeback_if_not_writable(field))) {
+                continue;
+            }
+
+            model_val = extract64(cpu->isar.idregs[field->index],
+                                  field->shift, field->length);
+
+            /*
+             * If the field is written back, make sure a "supported" value
+             * is requested.
+             */
+            arm_field_get_supported_values(field, host_isar, &vs);
+            for (size_t k = 0; k < vs->n_ranges; k++) {
+                if (model_val >= vs->ranges[k].min &&
+                    model_val <= vs->ranges[k].max) {
+                    supported = true;
+                    break;
+                }
+            }
+            g_free(vs->ranges);
+            g_free(vs);
+
+            if (!supported) {
+                QAPI_LIST_PREPEND(*blockers,
+                    g_strdup_printf("SYSREG_%s_%s", reg->name, field->name));
+            }
+        }
+    }
+}
 #endif
 
 void aarch64_add_pauth_properties(Object *obj)
diff --git a/target/arm/internals.h b/target/arm/internals.h
index 7502b6db8a..d05e35ba83 100644
--- a/target/arm/internals.h
+++ b/target/arm/internals.h
@@ -1756,6 +1756,9 @@ void aarch64_add_sve_properties(Object *obj);
 void aarch64_add_sme_properties(Object *obj);
 #ifndef CONFIG_USER_ONLY
 void aarch64_add_idregs_properties(Object *obj);
+void aarch64_idregs_get_blockers(ARMCPU *cpu,
+                                 const ARMISARegisters *host_isar,
+                                 strList **blockers);
 #endif
 
 /* Return true if the gdbstub is presenting an AArch64 CPU */
diff --git a/target/arm/kvm-stub.c b/target/arm/kvm-stub.c
index d19c53927d..eb8fdda236 100644
--- a/target/arm/kvm-stub.c
+++ b/target/arm/kvm-stub.c
@@ -131,3 +131,13 @@ void arm_field_get_supported_values(const ARM64SysRegField 
*field,
 {
     g_assert_not_reached();
 }
+
+bool arm_field_skip_writeback_always(const ARM64SysRegField *field)
+{
+    g_assert_not_reached();
+}
+
+bool arm_field_skip_writeback_if_not_writable(const ARM64SysRegField *field)
+{
+    g_assert_not_reached();
+}
diff --git a/target/arm/kvm.c b/target/arm/kvm.c
index ce77758c87..b909a11dfd 100644
--- a/target/arm/kvm.c
+++ b/target/arm/kvm.c
@@ -1394,7 +1394,7 @@ done:
     (*value_set)->ranges = (ArmFieldRange *)g_array_free(ranges, false);
 }
 
-static bool arm_field_skip_writeback_always(const ARM64SysRegField *field)
+bool arm_field_skip_writeback_always(const ARM64SysRegField *field)
 {
     /*
      * GIC is controlled by the gic-version property and fabricated by KVM
@@ -1412,7 +1412,7 @@ static bool arm_field_skip_writeback_always(const 
ARM64SysRegField *field)
         || field->index == CLIDR_EL1_IDX;
 }
 
-static bool arm_field_skip_writeback_if_not_writable(const ARM64SysRegField 
*field)
+bool arm_field_skip_writeback_if_not_writable(const ARM64SysRegField *field)
 {
     /*
      * KVM populates ID_DFR0_EL1.PerfMon even for AArch64-only guests but
diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h
index 4a04d72e39..6409bec735 100644
--- a/target/arm/kvm_arm.h
+++ b/target/arm/kvm_arm.h
@@ -172,6 +172,23 @@ void arm_field_get_supported_values(const ARM64SysRegField 
*field,
                                     const ARMISARegisters *host_isar,
                                     ArmFieldValueSet **value_set);
 
+/**
+ * arm_field_skip_writeback_always:
+ * @field: the ID-register field to test
+ *
+ * Returns: true if @field must never be written back to KVM (and so may
+ * legitimately differ from the host value).
+ */
+bool arm_field_skip_writeback_always(const ARM64SysRegField *field);
+
+/**
+ * arm_field_skip_writeback_if_not_writable:
+ * @field: the ID-register field to test
+ *
+ * Returns: true if @field should be skipped when it is not writable.
+ */
+bool arm_field_skip_writeback_if_not_writable(const ARM64SysRegField *field);
+
 /**
  * kvm_arm_steal_time_finalize:
  * @cpu: ARMCPU for which to finalize kvm-steal-time
-- 
2.52.0


Reply via email to