From: Sandesh Patel <sandesh.patel@nutanix.com> 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/20260605083358.1320563-1-khushit.shah@nutan... Signed-off-by: Sandesh Patel <sandesh.patel@nutanix.com> --- 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