Introduce 'query-arm-cpu-props-info' QMP command. This command returns information about ARM CPU model properties, including their name, type, supported values on the given host, and composite parent if any.
This provides management layer necessary information to determine migration compatibility off a VM on different host. Signed-off-by: Khushit Shah <[email protected]> --- qapi/misc-arm.json | 41 ++++++++++++++++ stubs/qmp-arm-gic.c | 6 +++ target/arm/arm-cpu-props-stub.c | 13 +++++ target/arm/arm-cpu-props.c | 85 +++++++++++++++++++++++++++++++++ target/arm/arm-cpu-props.h | 5 ++ target/arm/arm-qmp-cmds.c | 44 +++++++++++++++++ target/arm/cpu-idregs.c | 71 +++++++++++++++++++++++++++ target/arm/cpu-idregs.h | 8 ++++ 8 files changed, 273 insertions(+) diff --git a/qapi/misc-arm.json b/qapi/misc-arm.json index 4dc66d00e5..1a3fb140c4 100644 --- a/qapi/misc-arm.json +++ b/qapi/misc-arm.json @@ -46,6 +46,47 @@ ## { 'command': 'query-gic-capabilities', 'returns': ['GICCapability'] } +## +# @CpuPropInfo: +# +# Information about a specific ARM CPU model property. +# +# @name: the name of the property +# +# @type: the type of the property, any of ('string', 'boolean', +# 'number'). +# +# @supported-values: the set of values the host hardware supports +# for the property, a list of strings. +# +# @composite: name of the encapsulating composite property if any. +# +# Since: 11.1 +## +{ 'struct': 'CpuPropInfo', + 'data': { 'name': 'str', + 'type': 'str', + 'supported-values': 'any', + '*composite': 'str' } } + +## +# @query-arm-cpu-props-info: +# +# Returns information about most of ARM CPU named model properties. +# This does not include composite properties or properties already +# defined by QEMU like (sveNNNN, pmu, etc), This only returns all +# the newly defined properties like ('feat_*', 'hw_prop_*', etc). +# +# With this upper management layers can find out what property values +# are supported on the given host. +# +# Requires KVM to be enabled. +# +# Since: 11.1 +## +{ 'command': 'query-arm-cpu-props-info', + 'returns': ['CpuPropInfo'] } + ## # @SsidSizeMode: # diff --git a/stubs/qmp-arm-gic.c b/stubs/qmp-arm-gic.c index b3429243ef..1691401bf0 100644 --- a/stubs/qmp-arm-gic.c +++ b/stubs/qmp-arm-gic.c @@ -10,3 +10,9 @@ GICCapabilityList *qmp_query_gic_capabilities(Error **errp) error_setg(errp, "GIC hardware is not available on this target"); return NULL; } + +CpuPropInfoList *qmp_query_arm_cpu_props_info(Error **errp) +{ + error_setg(errp, "ARM CPU properties are not available on this target"); + return NULL; +} diff --git a/target/arm/arm-cpu-props-stub.c b/target/arm/arm-cpu-props-stub.c index abac29ae50..1d0a3f676c 100644 --- a/target/arm/arm-cpu-props-stub.c +++ b/target/arm/arm-cpu-props-stub.c @@ -11,6 +11,19 @@ const ArmCpuPropDesc *arm_find_prop(const char *name) return NULL; } +void arm_prop_append_supported_values(const ArmCpuPropDesc *prop, + const ARMISARegisters *host_isar, + QList *out) +{ + return; + +} + +const ArmCpuPropDesc *arm_prop_get_composite_of(const ArmCpuPropDesc *prop) +{ + return NULL; +} + static const ArmCpuPropDesc empty_props[] = { {.name = NULL} }; const ArmCpuPropDesc *get_arm_cpu_props(void) { diff --git a/target/arm/arm-cpu-props.c b/target/arm/arm-cpu-props.c index 94672040c5..dab7786622 100644 --- a/target/arm/arm-cpu-props.c +++ b/target/arm/arm-cpu-props.c @@ -15,6 +15,8 @@ #include "arm-cpu-props.h" #include "cpu-idregs.h" #include "arm-cpu-models.h" +#include "qobject/qlist.h" + #define ARM_SINGLE_FIELD_PROP(prop_name, _type, reg, fld) \ { .name = (prop_name), .type = ARM_PROP_##_type, \ @@ -735,3 +737,86 @@ const ArmCpuPropDesc *arm_prop_for_field(ArmFieldIdx field) } return NULL; } + +static void append_str_to_qlist(const char *name, uint64_t value, void *opaque) +{ + QList *list = opaque; + qlist_append_str(list, name); +} + +void arm_prop_append_supported_values(const ArmCpuPropDesc *prop, + const ARMISARegisters *host_isar, + QList *out) +{ + switch (prop->type) { + case ARM_PROP_STRING: + arm_field_foreach_supported_arch_val(prop->u.field, host_isar, + append_str_to_qlist, out); + break; + + case ARM_PROP_BOOLEAN: { + uint64_t host_val; + arm_idreg_field_read(host_isar, prop->u.field, &host_val); + + if (!arm_field_is_writable(prop->u.field)) { + qlist_append_str(out, host_val ? "on" : "off"); + } else { + if (arm_field_check_val_safe(prop->u.field, 0, host_isar)) { + qlist_append_str(out, "off"); + } + if (arm_field_check_val_safe(prop->u.field, 1, host_isar)) { + qlist_append_str(out, "on"); + } + } + break; + } + + case ARM_PROP_NUMERIC: { + g_autofree char *range = + arm_field_supported_numeric_range(prop->u.field, host_isar); + qlist_append_str(out, range); + break; + } + + case ARM_PROP_FRACTIONAL: { + const ArmFracVal *fv; + uint64_t host_base_val; + uint64_t host_frac_val; + + arm_idreg_field_read(host_isar, prop->u.frac.base_field, + &host_base_val); + arm_idreg_field_read(host_isar, prop->u.frac.frac_field, + &host_frac_val); + + for (fv = prop->u.frac.vals; fv->name; fv++) { + if (arm_field_check_val_safe(prop->u.frac.base_field, + fv->base_val, host_isar) && + arm_field_check_val_safe(prop->u.frac.frac_field, + fv->frac_val, host_isar) && + (arm_field_is_writable(prop->u.frac.base_field) || + fv->base_val == host_base_val) && + (arm_field_is_writable(prop->u.frac.frac_field) || + fv->frac_val == host_frac_val)) { + qlist_append_str(out, fv->name); + } + } + break; + } + + default: + g_assert_not_reached(); + } +} + +const ArmCpuPropDesc *arm_prop_get_composite_of(const ArmCpuPropDesc *prop) +{ + for (const ArmCpuPropDesc *p = arm_composite_props; p->name; p++) { + for (const char **sub_prop = p->u.comp_sub_props; *sub_prop; sub_prop++) { + if (g_str_equal(*sub_prop, prop->name)) { + return p; + } + } + } + + return NULL; +} diff --git a/target/arm/arm-cpu-props.h b/target/arm/arm-cpu-props.h index a108dee1a4..264a8a71de 100644 --- a/target/arm/arm-cpu-props.h +++ b/target/arm/arm-cpu-props.h @@ -13,6 +13,7 @@ #include "cpu-idregs.h" #include "cpu.h" #include "qemu/typedefs.h" +#include "qobject/qlist.h" typedef enum ArmCpuPropType { ARM_PROP_STRING, @@ -50,8 +51,12 @@ const ArmCpuPropDesc *get_arm_composite_props(void); const ArmCpuPropDesc *arm_find_prop(const char *name); +const ArmCpuPropDesc *arm_prop_get_composite_of(const ArmCpuPropDesc *prop); void arm_add_cpu_props(Object *obj); void arm_add_composite_props(Object *obj); const ArmCpuPropDesc *arm_prop_for_field(ArmFieldIdx field); +void arm_prop_append_supported_values(const ArmCpuPropDesc *prop, + const ARMISARegisters *host_isar, + QList *out); #endif diff --git a/target/arm/arm-qmp-cmds.c b/target/arm/arm-qmp-cmds.c index 7cb74b4b8d..9bdf85885f 100644 --- a/target/arm/arm-qmp-cmds.c +++ b/target/arm/arm-qmp-cmds.c @@ -34,6 +34,7 @@ #include "cpu.h" #include "arm-cpu-props.h" #include "arm-cpu-models.h" +#include "qobject/qlist.h" static GICCapability *gic_cap_new(int version) { @@ -63,6 +64,49 @@ GICCapabilityList *qmp_query_gic_capabilities(Error **errp) return head; } +CpuPropInfoList *qmp_query_arm_cpu_props_info(Error **errp) +{ + CpuPropInfoList *info = NULL; + CpuPropInfo *prop_info; + QList *sv; + int i; + const ArmCpuPropDesc *props = get_arm_cpu_props(); + + if (!kvm_enabled()) { + error_setg(errp, "query-arm-cpu-props-info requires KVM"); + return NULL; + } + + for (i = 0; props[i].name; i++) { + const ArmCpuPropDesc *pdesc = &props[i]; + const ArmCpuPropDesc *composite_of = arm_prop_get_composite_of(pdesc); + + prop_info = g_new0(CpuPropInfo, 1); + prop_info->name = g_strdup(pdesc->name); + prop_info->type = g_strdup(pdesc->type == ARM_PROP_STRING ? "string" : + pdesc->type == ARM_PROP_BOOLEAN ? "boolean" : + pdesc->type == ARM_PROP_NUMERIC ? "number" : + pdesc->type == ARM_PROP_FRACTIONAL ? "string" : + "unknown"); + + sv = qlist_new(); + arm_prop_append_supported_values(pdesc, + kvm_arm_get_host_isar(), + sv); + + prop_info->supported_values = QOBJECT(sv); + prop_info->composite = NULL; + + if (composite_of) { + prop_info->composite = g_strdup(composite_of->name); + } + + QAPI_LIST_PREPEND(info, prop_info); + } + + return info; +} + QEMU_BUILD_BUG_ON(ARM_MAX_VQ > 16); /* diff --git a/target/arm/cpu-idregs.c b/target/arm/cpu-idregs.c index 91bb899571..7671925869 100644 --- a/target/arm/cpu-idregs.c +++ b/target/arm/cpu-idregs.c @@ -354,3 +354,74 @@ uint64_t arm_get_field_mask(ArmFieldIdx field) } return field_mask; } + +void arm_field_foreach_supported_arch_val(ArmFieldIdx field, + const ARMISARegisters *host_isar, + ArmFieldValVisitor fn, void *opaque) +{ + const ArmIdRegField *f = arm_get_field_desc(field); + uint64_t host_val; + + assert(!arm_field_is_idreg_any(field)); + + arm_idreg_field_read(host_isar, field, &host_val); + + if (!arm_field_is_writable(field)) { + const char *name = arm_arch_val_name(field, host_val); + if (name) { + fn(name, host_val, opaque); + } + return; + } + + for (uint32_t i = 0; i < f->arch_vals_count; i++) { + if (f->arch_vals[i].name && + arm_field_check_val_safe(f->idx, f->arch_vals[i].value, host_isar)) { + fn(f->arch_vals[i].name, f->arch_vals[i].value, opaque); + } + } +} + +char *arm_field_supported_numeric_range(ArmFieldIdx field, + const ARMISARegisters *host_isar) +{ + const ArmIdRegField *f = arm_get_field_desc(field); + uint64_t host_val; + uint64_t max; + + if (f->length == 64) { + max = UINT64_MAX; + } else { + max = (1ULL << f->length) - 1; + } + + arm_idreg_field_read(host_isar, field, &host_val); + + if (!arm_field_is_writable(field)) { + return g_strdup_printf("%" PRIu64, host_val); + } + + switch (f->safe_rule) { + case IDREG_SAFE_LOWER: + return g_strdup_printf("0-%" PRIu64, host_val); + case IDREG_SAFE_HIGHER: + return g_strdup_printf("%" PRIu64 "-%" PRIu64, host_val, max); + case IDREG_SAFE_HIGHER_OR_ZERO: + if (host_val <= 1) { + return g_strdup_printf("0-%" PRIu64, max); + } + return g_strdup_printf("0,%" PRIu64 "-%" PRIu64, host_val, max); + case IDREG_SAFE_SIGNED_LOWER: + g_assert_not_reached(); + case IDREG_SAFE_EXACT: + if (f->default_val != host_val) { + return g_strdup_printf("%" PRIu64 ",%" PRIu64, + f->default_val, host_val); + } + return g_strdup_printf("%" PRIu64, host_val); + case IDREG_SAFE_ANY: + return g_strdup_printf("0-%" PRIu64, max); + default: + return g_strdup_printf("%" PRIu64, host_val); + } +} diff --git a/target/arm/cpu-idregs.h b/target/arm/cpu-idregs.h index 51cc5ec341..171e03eece 100644 --- a/target/arm/cpu-idregs.h +++ b/target/arm/cpu-idregs.h @@ -104,4 +104,12 @@ bool arm_field_skip_writeback_if_not_writable(ArmFieldIdx field); uint64_t arm_get_field_mask(ArmFieldIdx field); +typedef void (*ArmFieldValVisitor)(const char *name, uint64_t value, + void *opaque); +void arm_field_foreach_supported_arch_val(ArmFieldIdx field, + const ARMISARegisters *host_isar, + ArmFieldValVisitor fn, void *opaque); +char *arm_field_supported_numeric_range(ArmFieldIdx field, + const ARMISARegisters *host_isar); + #endif /* CPU_IDREGS_H */ -- 2.52.0
