> On 16 Sep 2026, at 8:15 PM, Eric Auger <[email protected]> wrote:
>
> !-------------------------------------------------------------------|
> CAUTION: External Email
>
> |-------------------------------------------------------------------!
>
> From: Cornelia Huck <[email protected]>
>
> Enhance the current host model query-cpu-model-expansion with the
> capability to return SYSREG_* options and also test possible
> values.
>
> If no SYSREG prop is set, query-cpu-model-expansion returns the
> default host values for all SYSREG properties. They are extracted
> from a scratch vcpu featuring all the KVM advertised capabilities.
>
> Here is a qmp-shell example:
>
> (QEMU) query-cpu-model-expansion type=full model={"name":"host"}
> {"return": {"model": {"name": "host", "props": {"SYSREG_ID_AA64PFR0_EL1_EL3":
> 1,
> "SYSREG_ID_AA64ISAR2_EL1_CLRBHB": 0, "SYSREG_CTR_EL0_L1Ip": 3,
> "SYSREG_MIDR_EL1_PartNum": 3407, "SYSREG_CTR_EL0_DminLine": 4,
> "SYSREG_ID_AA64MMFR0_EL1_PARange": 5, "SYSREG_ID_AA64MMFR1_EL1_ECBHB": 0
> ../..
>
> It becomes also possible to test new SYSREG property values.
>
> With Full expansion of the host model, the SYSREG props are tested
> against a scratch vcpu. With static expansion, no check is performed
> against KVM but only against qemu code (the value does not exceed the
> field size and if the field is associated to enum values, the set
> value belongs to the list of arch enum values.
>
> Examples
> (QEMU) query-cpu-model-expansion type=full
> model={"name":"host","props":{"SYSREG_ID_AA64ISAR0_EL1_DP":0x13}}
> {"error": {"class": "GenericError", "desc": "idreg SYSREG_ID_AA64ISAR0_EL1_DP
> set value (0x13) exceeds length of field (4)!"}}
>
> (QEMU) query-cpu-model-expansion type=full
> model={"name":"host","props":{"SYSREG_ID_AA64ISAR0_EL1_DP":0x2}}
> {"error": {"class": "GenericError", "desc": "idreg SYSREG_ID_AA64ISAR0_EL1_DP
> set value (0x2) does not match any arch valid enum value!"}}
>
> Signed-off-by: Eric Auger <[email protected]>
> Signed-off-by: Cornelia Huck <[email protected]>
>
> ---
>
> v8 -> v9:
> - fix the leak reported by Khushit
> - implement static mode for host
>
> v7 -> v8:
> - do not check if the prop exists (this is checked elsewhere with a
> different error message)
> - clarify that examples given in the commit msg are based on qmp-shell
>
> v5 -> v6:
> - add the write capability
> ---
> target/arm/arm-qmp-cmds.c | 107 ++++++++++++++++++++++++++++++++++++--
> 1 file changed, 104 insertions(+), 3 deletions(-)
>
> diff --git a/target/arm/arm-qmp-cmds.c b/target/arm/arm-qmp-cmds.c
> index 83ec95c290..b52ca67f83 100644
> --- a/target/arm/arm-qmp-cmds.c
> +++ b/target/arm/arm-qmp-cmds.c
> @@ -21,6 +21,7 @@
> */
>
> #include "qemu/osdep.h"
> +#include "qemu/error-report.h"
> #include "qemu/target-info.h"
> #include "hw/core/boards.h"
> #include "kvm_arm.h"
> @@ -30,6 +31,7 @@
> #include "qapi/qapi-commands-machine.h"
> #include "qapi/qapi-commands-misc-arm.h"
> #include "qobject/qdict.h"
> +#include "qobject/qnum.h"
> #include "qom/qom-qobject.h"
> #include "cpu.h"
>
> @@ -83,15 +85,20 @@ CpuModelExpansionInfo
> *qmp_query_cpu_model_expansion(CpuModelExpansionType type,
> CpuModelInfo *model,
> Error **errp)
> {
> + bool use_scratch_vcpu = kvm_enabled() && type ==
> CPU_MODEL_EXPANSION_TYPE_FULL;
> CpuModelExpansionInfo *expansion_info;
> + ObjectPropertyIterator iter;
> const QDict *qdict_in;
> + ObjectProperty *idregprop;
> QDict *qdict_out;
> ObjectClass *oc;
> Object *obj;
> const char *name;
> + int fdarray[3];
> int i;
>
> - if (type != CPU_MODEL_EXPANSION_TYPE_FULL) {
> + /* we support static expansion for host model only */
> + if (type != CPU_MODEL_EXPANSION_TYPE_FULL && strcmp(model->name,
> "host")) {
> error_setg(errp, "The requested expansion type is not supported");
> return NULL;
> }
> @@ -136,6 +143,7 @@ CpuModelExpansionInfo
> *qmp_query_cpu_model_expansion(CpuModelExpansionType type,
> if (model->props) {
> Visitor *visitor;
> Error *err = NULL;
> + int fd = -1;
>
> visitor = qobject_input_visitor_new(model->props);
> if (!visit_start_struct(visitor, "model.props", NULL, 0, errp)) {
> @@ -146,6 +154,8 @@ CpuModelExpansionInfo
> *qmp_query_cpu_model_expansion(CpuModelExpansionType type,
>
> qdict_in = qobject_to(QDict, model->props);
> i = 0;
> +
> + /* Test legacy composite option settings */
> while ((name = cpu_model_advertised_features[i++]) != NULL) {
> if (qdict_get(qdict_in, name)) {
> if (!object_property_set(obj, name, visitor, &err)) {
> @@ -154,11 +164,85 @@ CpuModelExpansionInfo
> *qmp_query_cpu_model_expansion(CpuModelExpansionType type,
> }
> }
>
> + if (!err) {
> + arm_cpu_finalize_features(ARM_CPU(obj), &err);
> + }
> +
> + /**
> + * Test SYSREG option settings
> + * In full mode, build a scratch vcpu that reflects composite legacy
> + * options
> + */
> + if (use_scratch_vcpu) {
> + Error *local_err = NULL;
> + bool has_virt = object_property_get_bool(OBJECT(current_machine),
> + "virtualization",
> + &local_err);
> +
> + if (local_err) {
Hi Eric,
Nit: indentation
> + error_free(local_err); /* the machine property does not
> exist */
> + } else {
> + if (!has_virt && object_property_find(obj, "has_el2")) {
> + object_property_set_bool(obj, "has_el2", false, NULL);
> + }
> + }
> + fd = kvm_arm_create_init_scratch_vcpu(ARM_CPU(obj), errp);
> + if (fd < 0) {
> + return NULL;
Leaks obj/visitor.
> + }
> + }
> +
> + qdict_in = qobject_to(QDict, model->props);
> + for (const QDictEntry *entry = qdict_first(qdict_in);
> + entry != NULL; entry = qdict_next(qdict_in, entry)) {
> + const char *key = qdict_entry_key(entry);
> + QObject *val_obj = qdict_entry_value(entry);
> + ObjectProperty *prop;
> + Visitor *v;
> + bool success;
> + uint64_t val;
> +
> + prop = object_property_find(obj, key);
> +
> + if (!g_str_has_prefix(key, "SYSREG_")) {
> + continue;
> + }
> +
> + /* consume the prop to avoid unexpected parameter */
> + if (!visit_type_uint64(visitor, key, &val, errp)) {
> + goto bail_out;
> + }
> +
> + v = qobject_input_visitor_new(val_obj);
> +
> + if (!object_property_set(obj, key, v, errp)) {
> + goto bail_out;
> + }
> +
> + if (use_scratch_vcpu) {
> + ARM64SysRegField *field = (ARM64SysRegField *)prop->opaque;
> + uint64_t newfv;
> +
> + if (!visit_type_uint64(v, name, &newfv, errp)) {
The name is not initialised in this scope… It carries value of last processed
legacy prop
> + visit_free(v);
> + goto bail_out;
> + }
> + success = kvm_idreg_write_scratch_vcpu(ARM_CPU(obj), fd,
> + field, newfv, errp);
> + if (!success) {
> + visit_free(v);
> + goto bail_out;
> + }
> + }
> + visit_free(v);
> + }
> +
> if (!err) {
> visit_check_struct(visitor, &err);
> }
> - if (!err) {
> - arm_cpu_finalize_features(ARM_CPU(obj), &err);
> +
> + if (use_scratch_vcpu) {
> + kvm_arm_destroy_scratch_host_vcpu(fdarray);
> }
> visit_end_struct(visitor, NULL);
> visit_free(visitor);
> @@ -190,6 +274,18 @@ CpuModelExpansionInfo
> *qmp_query_cpu_model_expansion(CpuModelExpansionType type,
> }
> }
>
> + object_property_iter_init(&iter, obj);
> +
> + while ((idregprop = object_property_iter_next(&iter))) {
> + QObject *value;
> +
> + if (!g_str_has_prefix(idregprop->name, "SYSREG_")) {
> + continue;
> + }
> + value = object_property_get_qobject(obj, idregprop->name,
> &error_abort);
> + qdict_put_obj(qdict_out, idregprop->name, value);
> + }
> +
> if (!qdict_size(qdict_out)) {
> qobject_unref(qdict_out);
> } else {
> @@ -199,6 +295,11 @@ CpuModelExpansionInfo
> *qmp_query_cpu_model_expansion(CpuModelExpansionType type,
> object_unref(obj);
>
> return expansion_info;
> +bail_out:
Leaks obj.
> + if (use_scratch_vcpu) {
> + kvm_arm_destroy_scratch_host_vcpu(fdarray);
fdarray is actually never written to.
Warm Regards,
Khushit
> + }
> + return NULL;
> }
>
> static void arm_cpu_add_definition(gpointer data, gpointer user_data)
> --
> 2.53.0
>