From: Sandesh Patel <[email protected]>

Unlike x86 boolean CPU features, ARM CPU features are exposed by QEMU
as multi-bit properties whose accepted values depend on the host; only
a subset of the possible values are valid on any given host.

A pending QEMU patch series [1] introduces a new QMP command,
'query-arm-cpu-props-info', which returns, for each ARM CPU property,
its type and the set of values supported on the host.

Add support to query feature supported values

[1] 
https://lore.kernel.org/qemu-arm/[email protected]/

Signed-off-by: Sandesh Patel <[email protected]>
---
 src/qemu/qemu_capabilities.c                  | 13 +++
 src/qemu/qemu_capabilities.h                  |  1 +
 src/qemu/qemu_monitor.c                       | 51 +++++++++++
 src/qemu/qemu_monitor.h                       | 26 ++++++
 src/qemu/qemu_monitor_json.c                  | 66 ++++++++++++++
 src/qemu/qemu_monitor_json.h                  |  5 +
 .../caps_11.1.0_aarch64.replies               | 69 ++++++++++++++
 .../caps_11.1.0_aarch64.xml                   |  1 +
 tests/qemumonitorjsontest.c                   | 91 +++++++++++++++++++
 9 files changed, 323 insertions(+)

diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
index a28f87ad2b..cb933aa630 100644
--- a/src/qemu/qemu_capabilities.c
+++ b/src/qemu/qemu_capabilities.c
@@ -767,6 +767,7 @@ VIR_ENUM_IMPL(virQEMUCaps,
 
               /* 495 */
               "blockdev-mirror.target-is-zero", /* 
QEMU_CAPS_BLOCKDEV_MIRROR_TARGET_IS_ZERO */
+              "query-arm-cpu-props-info", /* 
QEMU_CAPS_QUERY_ARM_CPU_PROPS_INFO */
     );
 
 
@@ -812,6 +813,7 @@ struct _virQEMUCapsAccel {
     virQEMUCapsMachineType *machineTypes;
     virQEMUCapsHostCPUData hostCPU;
     qemuMonitorCPUDefs *cpuModels;
+    qemuMonitorCPUPropsInfoList *cpuProps; /* currently only populated on ARM 
+ KVM */
 };
 
 
@@ -1298,6 +1300,7 @@ struct virQEMUCapsStringFlags virQEMUCapsCommands[] = {
     { "blockdev-set-active", QEMU_CAPS_BLOCKDEV_SET_ACTIVE },
     { "qom-list-get", QEMU_CAPS_QOM_LIST_GET },
     { "query-accelerators", QEMU_CAPS_QUERY_ACCELERATORS },
+    { "query-arm-cpu-props-info", QEMU_CAPS_QUERY_ARM_CPU_PROPS_INFO },
 };
 
 struct virQEMUCapsStringFlags virQEMUCapsObjectTypes[] = {
@@ -2092,6 +2095,7 @@ virQEMUCapsAccelCopy(virQEMUCapsAccel *dst,
     virQEMUCapsHostCPUDataCopy(&dst->hostCPU, &src->hostCPU);
 
     dst->cpuModels = qemuMonitorCPUDefsCopy(src->cpuModels);
+    dst->cpuProps = qemuMonitorCPUPropsInfoListCopy(src->cpuProps);
 }
 
 
@@ -2157,6 +2161,7 @@ virQEMUCapsAccelClear(virQEMUCapsAccel *caps)
 
     virQEMUCapsHostCPUDataClear(&caps->hostCPU);
     qemuMonitorCPUDefsFree(caps->cpuModels);
+    qemuMonitorCPUPropsInfoListFree(caps->cpuProps);
 }
 
 
@@ -3453,6 +3458,14 @@ virQEMUCapsProbeQMPHostCPU(virQEMUCaps *qemuCaps,
     }
 
     accel->hostCPU.info = g_steal_pointer(&modelInfo);
+
+    /* Query the ARM CPU property info. Only applicable on ARM + KVM. */
+    if (ARCH_IS_ARM(qemuCaps->arch) &&
+        virQEMUCapsTypeIsAccelerated(virtType) &&
+        virQEMUCapsGet(qemuCaps, QEMU_CAPS_QUERY_ARM_CPU_PROPS_INFO) &&
+        qemuMonitorGetCPUPropsInfoList(mon, &accel->cpuProps) < 0)
+        return -1;
+
     return 0;
 }
 
diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h
index 3014f3dc5e..5bb6d5e1da 100644
--- a/src/qemu/qemu_capabilities.h
+++ b/src/qemu/qemu_capabilities.h
@@ -741,6 +741,7 @@ typedef enum { /* virQEMUCapsFlags grouping marker for 
syntax-check */
 
     /* 495 */
     QEMU_CAPS_BLOCKDEV_MIRROR_TARGET_IS_ZERO, /* 'blockdev-mirror' supports 
'target-is-zero' */
+    QEMU_CAPS_QUERY_ARM_CPU_PROPS_INFO, /* 'query-arm-cpu-props-info' qmp 
command is supported */
 
     QEMU_CAPS_LAST /* this must always be the last item */
 } virQEMUCapsFlags;
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index baa78dd6fe..38a46cffc4 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -3339,6 +3339,57 @@ qemuMonitorGetCPUModelComparison(qemuMonitor *mon,
 }
 
 
+int
+qemuMonitorGetCPUPropsInfoList(qemuMonitor *mon,
+                               qemuMonitorCPUPropsInfoList **props_info)
+{
+    QEMU_CHECK_MONITOR(mon);
+    return qemuMonitorJSONGetCPUPropsInfoList(mon, props_info);
+}
+
+
+void
+qemuMonitorCPUPropsInfoListFree(qemuMonitorCPUPropsInfoList *list)
+{
+    size_t i;
+
+    if (!list)
+        return;
+
+    for (i = 0; i < list->nprops; i++) {
+        g_free(list->props[i].name);
+        g_free(list->props[i].composite);
+        g_strfreev(list->props[i].supportedValues);
+    }
+    g_free(list->props);
+    g_free(list);
+}
+
+
+qemuMonitorCPUPropsInfoList *
+qemuMonitorCPUPropsInfoListCopy(const qemuMonitorCPUPropsInfoList *orig)
+{
+    qemuMonitorCPUPropsInfoList *copy;
+    size_t i;
+
+    if (!orig)
+        return NULL;
+
+    copy = g_new0(qemuMonitorCPUPropsInfoList, 1);
+    copy->nprops = orig->nprops;
+    copy->props = g_new0(qemuMonitorCPUPropsInfo, orig->nprops);
+
+    for (i = 0; i < orig->nprops; i++) {
+        copy->props[i].name = g_strdup(orig->props[i].name);
+        copy->props[i].type = orig->props[i].type;
+        copy->props[i].supportedValues = 
g_strdupv(orig->props[i].supportedValues);
+        copy->props[i].composite = g_strdup(orig->props[i].composite);
+    }
+
+    return copy;
+}
+
+
 void
 qemuMonitorCPUModelInfoFree(qemuMonitorCPUModelInfo *model_info)
 {
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index c2afb580e4..1653d63722 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -1455,6 +1455,20 @@ struct _qemuMonitorCPUModelInfo {
     bool migratability;
 };
 
+typedef struct _qemuMonitorCPUPropsInfo qemuMonitorCPUPropsInfo;
+struct _qemuMonitorCPUPropsInfo {
+    char *name;
+    qemuMonitorCPUPropertyType type;
+    GStrv supportedValues;
+    char *composite;
+};
+
+typedef struct _qemuMonitorCPUPropsInfoList qemuMonitorCPUPropsInfoList;
+struct _qemuMonitorCPUPropsInfoList {
+    size_t nprops;
+    qemuMonitorCPUPropsInfo *props;
+};
+
 typedef enum {
     QEMU_MONITOR_CPU_MODEL_EXPANSION_STATIC,
     QEMU_MONITOR_CPU_MODEL_EXPANSION_STATIC_FULL,
@@ -1490,6 +1504,18 @@ qemuMonitorGetCPUModelComparison(qemuMonitor *mon,
 qemuMonitorCPUModelInfo *
 qemuMonitorCPUModelInfoCopy(const qemuMonitorCPUModelInfo *orig);
 
+int
+qemuMonitorGetCPUPropsInfoList(qemuMonitor *mon,
+                               qemuMonitorCPUPropsInfoList **props_list);
+
+void
+qemuMonitorCPUPropsInfoListFree(qemuMonitorCPUPropsInfoList *list);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuMonitorCPUPropsInfoList, 
qemuMonitorCPUPropsInfoListFree);
+
+qemuMonitorCPUPropsInfoList *
+qemuMonitorCPUPropsInfoListCopy(const qemuMonitorCPUPropsInfoList *orig);
+
 GHashTable *
 qemuMonitorGetCommandLineOptions(qemuMonitor *mon);
 
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index 28f7d93834..d44a6ab270 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -5727,6 +5727,72 @@ qemuMonitorJSONGetCPUModelComparison(qemuMonitor *mon,
 }
 
 
+int
+qemuMonitorJSONGetCPUPropsInfoList(qemuMonitor *mon,
+                                   qemuMonitorCPUPropsInfoList **props_list)
+{
+    g_autoptr(virJSONValue) cmd = NULL;
+    g_autoptr(virJSONValue) reply = NULL;
+    g_autoptr(qemuMonitorCPUPropsInfoList) props_info = NULL;
+    virJSONValue *data;
+    size_t nprops;
+    size_t i;
+
+    if (!(cmd = qemuMonitorJSONMakeCommand("query-arm-cpu-props-info", NULL)))
+        return -1;
+
+    if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
+        return -1;
+
+    if (!(data = qemuMonitorJSONGetReply(cmd, reply, VIR_JSON_TYPE_ARRAY)))
+        return -1;
+
+    nprops = virJSONValueArraySize(data);
+
+    props_info = g_new0(qemuMonitorCPUPropsInfoList, 1);
+    props_info->nprops = nprops;
+    props_info->props = g_new0(qemuMonitorCPUPropsInfo, nprops);
+
+    for (i = 0; i < nprops; i++) {
+        virJSONValue *item = virJSONValueArrayGet(data, i);
+        virJSONValue *sv_array;
+        const char *name;
+        const char *type_str;
+        int type;
+
+        if (!(name = virJSONValueObjectGetString(item, "name"))) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("query-arm-cpu-props-info reply is missing 
'name'"));
+            return -1;
+        }
+
+        if (!(type_str = virJSONValueObjectGetString(item, "type"))) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("query-arm-cpu-props-info reply for '%1$s' is 
missing 'type'"),
+                           name);
+            return -1;
+        }
+
+        if ((type = qemuMonitorCPUPropertyTypeFromString(type_str)) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("query-arm-cpu-props-info reply for '%1$s' has 
unknown type '%2$s'"),
+                           name, type_str);
+            return -1;
+        }
+
+        props_info->props[i].name = g_strdup(name);
+        props_info->props[i].type = type;
+        props_info->props[i].composite = 
g_strdup(virJSONValueObjectGetString(item, "composite"));
+
+        if ((sv_array = virJSONValueObjectGetArray(item, "supported-values")))
+            props_info->props[i].supportedValues = 
virJSONValueArrayToStringList(sv_array);
+    }
+
+    *props_list = g_steal_pointer(&props_info);
+    return 0;
+}
+
+
 static int
 qemuMonitorJSONGetCommandLineOptionsWorker(size_t pos G_GNUC_UNUSED,
                                            virJSONValue *item,
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
index f4c093d717..11b07a6770 100644
--- a/src/qemu/qemu_monitor_json.h
+++ b/src/qemu/qemu_monitor_json.h
@@ -446,6 +446,11 @@ qemuMonitorJSONGetCPUModelComparison(qemuMonitor *mon,
                                      char **result)
     ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
 
+int
+qemuMonitorJSONGetCPUPropsInfoList(qemuMonitor *mon,
+                                   qemuMonitorCPUPropsInfoList **props_list)
+    ATTRIBUTE_NONNULL(2);
+
 GHashTable *
 qemuMonitorJSONGetCommandLineOptions(qemuMonitor *mon);
 
diff --git a/tests/qemucapabilitiesdata/caps_11.1.0_aarch64.replies 
b/tests/qemucapabilitiesdata/caps_11.1.0_aarch64.replies
index 41cd5496c6..40fd157680 100644
--- a/tests/qemucapabilitiesdata/caps_11.1.0_aarch64.replies
+++ b/tests/qemucapabilitiesdata/caps_11.1.0_aarch64.replies
@@ -24932,6 +24932,40 @@
         "in-use",
         "auto"
       ]
+    },
+    {
+      "name": "query-arm-cpu-props-info",
+      "ret-type": "[999]",
+      "meta-type": "command",
+      "arg-type": "0"
+    },
+    {
+      "name": "[999]",
+      "element-type": "999",
+      "meta-type": "array"
+    },
+    {
+      "name": "999",
+      "members": [
+        {
+          "name": "name",
+          "type": "str"
+        },
+        {
+          "name": "type",
+          "type": "str"
+        },
+        {
+          "name": "supported-values",
+          "type": "[str]"
+        },
+        {
+          "name": "composite",
+          "default": null,
+          "type": "str"
+        }
+      ],
+      "meta-type": "object"
     }
   ],
   "id": "libvirt-4"
@@ -37945,6 +37979,41 @@
   }
 }
 
+{
+  "execute": "query-arm-cpu-props-info",
+  "id": "libvirt-46"
+}
+
+{
+  "return": [
+    {
+      "name": "hw_prop_WRPs",
+      "supported-values": [
+        "0-3"
+      ],
+      "type": "number"
+    },
+    {
+      "name": "hw_prop_API",
+      "composite": "pauth",
+      "supported-values": [
+        "off"
+      ],
+      "type": "string"
+    },
+    {
+      "name": "feat_RAS",
+      "supported-values": [
+        "0.0",
+        "1.0",
+        "1.1_base"
+      ],
+      "type": "string"
+    }
+  ],
+  "id": "libvirt-46"
+}
+
 {
   "execute": "qmp_capabilities",
   "id": "libvirt-1"
diff --git a/tests/qemucapabilitiesdata/caps_11.1.0_aarch64.xml 
b/tests/qemucapabilitiesdata/caps_11.1.0_aarch64.xml
index f7dd14a1ad..f2340830fd 100644
--- a/tests/qemucapabilitiesdata/caps_11.1.0_aarch64.xml
+++ b/tests/qemucapabilitiesdata/caps_11.1.0_aarch64.xml
@@ -188,6 +188,7 @@
   <flag name='uefi-vars'/>
   <flag name='query-block-flat'/>
   <flag name='blockdev-mirror.target-is-zero'/>
+  <flag name='query-arm-cpu-props-info'/>
   <version>11000050</version>
   <microcodeVersion>61700287</microcodeVersion>
   <package>v11.0.0-1600-g5611a9268d</package>
diff --git a/tests/qemumonitorjsontest.c b/tests/qemumonitorjsontest.c
index f59b97c1c3..0808c23c18 100644
--- a/tests/qemumonitorjsontest.c
+++ b/tests/qemumonitorjsontest.c
@@ -2682,6 +2682,86 @@ 
testQemuMonitorJSONqemuMonitorJSONGetCPUModelBaseline(const void *opaque)
 }
 
 
+static int
+testQemuMonitorJSONqemuMonitorJSONGetCPUPropsInfoList(const void *opaque)
+{
+    const testGenericData *data = opaque;
+    g_autoptr(qemuMonitorTest) test = NULL;
+    g_autoptr(qemuMonitorCPUPropsInfoList) props_list = NULL;
+
+    if (!(test = qemuMonitorTestNewSchema(data->xmlopt, data->schema)))
+        return -1;
+
+    if (qemuMonitorTestAddItem(test, "query-arm-cpu-props-info",
+                               "{ "
+                               "    \"return\": [ "
+                               "        { "
+                               "            \"name\": \"hw_prop_API\", "
+                               "            \"supported-values\": [\"off\"], "
+                               "            \"type\": \"string\", "
+                               "            \"composite\": \"pauth\" "
+                               "        }, "
+                               "        { "
+                               "            \"name\": \"feat_RAS\", "
+                               "            \"supported-values\": [\"0.0\", 
\"1.0\", \"1.1_base\"], "
+                               "            \"type\": \"string\" "
+                               "        } "
+                               "    ] "
+                               "}") < 0)
+        return -1;
+
+    if (qemuMonitorJSONGetCPUPropsInfoList(qemuMonitorTestGetMonitor(test), 
&props_list) < 0)
+        return -1;
+
+    if (props_list->nprops != 2) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       "Expected 2 properties, got %zu", props_list->nprops);
+        return -1;
+    }
+
+    if (STRNEQ(props_list->props[0].name, "hw_prop_API") ||
+        props_list->props[0].type != QEMU_MONITOR_CPU_PROPERTY_STRING ||
+        STRNEQ(props_list->props[1].name, "feat_RAS") ||
+        props_list->props[1].type != QEMU_MONITOR_CPU_PROPERTY_STRING) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       "Unexpected property name or type");
+        return -1;
+    }
+
+    if (!props_list->props[0].supportedValues ||
+        STRNEQ_NULLABLE(props_list->props[0].supportedValues[0], "off") ||
+        props_list->props[0].supportedValues[1] != NULL) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       "Unexpected supportedValues for hw_prop_API");
+        return -1;
+    }
+
+    if (!props_list->props[1].supportedValues ||
+        STRNEQ_NULLABLE(props_list->props[1].supportedValues[0], "0.0") ||
+        STRNEQ_NULLABLE(props_list->props[1].supportedValues[1], "1.0") ||
+        STRNEQ_NULLABLE(props_list->props[1].supportedValues[2], "1.1_base") ||
+        props_list->props[1].supportedValues[3] != NULL) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       "Unexpected supportedValues for feat_RAS");
+        return -1;
+    }
+
+    if (STRNEQ_NULLABLE(props_list->props[0].composite, "pauth")) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       "Expected composite 'pauth' for hw_prop_API");
+        return -1;
+    }
+
+    if (props_list->props[1].composite != NULL) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       "Expected NULL composite for feat_RAS");
+        return -1;
+    }
+
+    return 0;
+}
+
+
 static int
 testQemuMonitorJSONGetSEVInfo(const void *opaque)
 {
@@ -2933,6 +3013,7 @@ mymain(void)
     testQemuMonitorJSONSimpleFuncData simpleFunc;
     g_autoptr(GHashTable) qapischema_x86_64 = NULL;
     g_autoptr(GHashTable) qapischema_s390x = NULL;
+    g_autoptr(GHashTable) qapischema_aarch64 = NULL;
     struct testQAPISchemaData qapiData;
 
     if (qemuTestDriverInit(&driver) < 0)
@@ -3195,6 +3276,16 @@ mymain(void)
     DO_TEST(qemuMonitorJSONGetCPUModelComparison);
     DO_TEST(qemuMonitorJSONGetCPUModelBaseline);
 
+    if (!(qapischema_aarch64 = testQEMUSchemaLoadLatest("aarch64"))) {
+        VIR_TEST_VERBOSE("failed to load qapi schema for aarch64");
+        ret = -1;
+        goto cleanup;
+    }
+
+    qapiData.schema = qapischema_aarch64;
+
+    DO_TEST(qemuMonitorJSONGetCPUPropsInfoList);
+
  cleanup:
     qemuTestDriverFree(&driver);
     return (ret == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
-- 
2.43.7

Reply via email to