> 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
> 

Reply via email to