These properties apply to the class itself, as opposed to object_class_property_* in which the property is attached to the class but applies to each object instance.
Apply global properties via a class_late_init on DeviceClass. Signed-off-by: Richard Henderson <richard.hender...@linaro.org> --- hw/core/qdev-prop-internal.h | 2 + include/qom/object.h | 63 ++++++++++++ include/qom/qom-qobject.h | 28 +++++ hw/core/cpu-common.c | 61 ++++++++--- hw/core/qdev-properties.c | 36 +++++++ hw/core/qdev.c | 2 + qom/object.c | 194 +++++++++++++++++++++++++++++++++++ qom/object_interfaces.c | 13 +++ qom/qom-qmp-cmds.c | 37 +++++++ 9 files changed, 419 insertions(+), 17 deletions(-) diff --git a/hw/core/qdev-prop-internal.h b/hw/core/qdev-prop-internal.h index d7b77844fe..21cd3bca27 100644 --- a/hw/core/qdev-prop-internal.h +++ b/hw/core/qdev-prop-internal.h @@ -25,4 +25,6 @@ void qdev_propinfo_get_int32(Object *obj, Visitor *v, const char *name, void qdev_propinfo_get_size32(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp); +bool device_class_late_init(ObjectClass *class, Error **errp); + #endif diff --git a/include/qom/object.h b/include/qom/object.h index 86958abe15..caa4774f80 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -27,8 +27,25 @@ typedef struct InterfaceInfo InterfaceInfo; #define TYPE_OBJECT "object" +typedef struct ClassProperty ClassProperty; typedef struct ObjectProperty ObjectProperty; +/** + * typedef ClassPropertyAccessor: + * @klass: the class that owns the property + * @v: the visitor that contains the property data + * @name: the name of the property + * @opaque: the class property opaque + * @errp: a pointer to an Error that is filled if getting/setting fails. + * + * Called when trying to get/set a property. + */ +typedef bool (ClassPropertyAccessor)(ObjectClass *klass, + Visitor *v, + const char *name, + void *opaque, + Error **errp); + /** * typedef ObjectPropertyAccessor: * @obj: the object that owns the property @@ -85,6 +102,16 @@ typedef void (ObjectPropertyRelease)(Object *obj, */ typedef void (ObjectPropertyInit)(Object *obj, ObjectProperty *prop); +struct ClassProperty +{ + char *name; + char *type; + char *description; + ClassPropertyAccessor *get; + ClassPropertyAccessor *set; + void *opaque; +}; + struct ObjectProperty { char *name; @@ -135,6 +162,7 @@ struct ObjectClass ObjectUnparent *unparent; + GHashTable *class_properties; GHashTable *properties; }; @@ -1072,6 +1100,11 @@ ObjectProperty *object_property_add(Object *obj, const char *name, void object_property_del(Object *obj, const char *name); +/** + * object_class_property_add: + * + * Add a property to the class, as if added to each object instance. + */ ObjectProperty *object_class_property_add(ObjectClass *klass, const char *name, const char *type, ObjectPropertyAccessor *get, @@ -1079,6 +1112,33 @@ ObjectProperty *object_class_property_add(ObjectClass *klass, const char *name, ObjectPropertyRelease *release, void *opaque); +/** + * class_property_add: + * + * Add a property to the class, affecting the class as a whole + * rather than each instance. All such properties are resolved + * before the first object instance is created. + */ +void class_property_add(ObjectClass *klass, const char *name, + const char *type, const char *desc, + ClassPropertyAccessor *get, + ClassPropertyAccessor *set, + void *opaque); + +ClassProperty *class_property_find(ObjectClass *klass, const char *name); + +bool class_property_set(ObjectClass *klass, ClassProperty *cp, + Visitor *v, Error **errp); +bool class_property_set_bool(ObjectClass *klass, const char *name, + bool value, Error **errp); +bool class_property_set_uint(ObjectClass *klass, const char *name, + uint64_t value, Error **errp); + +bool class_property_get(ObjectClass *klass, ClassProperty *cp, + Visitor *v, Error **errp); +bool class_property_get_bool(ObjectClass *klass, const char *name, + Error **errp); + /** * object_property_set_default_bool: * @prop: the property to set @@ -1229,6 +1289,9 @@ void object_class_property_iter_init(ObjectPropertyIterator *iter, */ ObjectProperty *object_property_iter_next(ObjectPropertyIterator *iter); +void class_property_iter_init(ObjectPropertyIterator *iter, ObjectClass *klass); +ClassProperty *class_property_iter_next(ObjectPropertyIterator *iter); + void object_unparent(Object *obj); /** diff --git a/include/qom/qom-qobject.h b/include/qom/qom-qobject.h index 73e4e0e474..4026fe6964 100644 --- a/include/qom/qom-qobject.h +++ b/include/qom/qom-qobject.h @@ -40,4 +40,32 @@ bool object_property_set_qobject(Object *obj, const char *name, struct QObject *value, struct Error **errp); +/* + * class_property_get_qobject: + * @cls: the class object + * @name: the name of the property + * @errp: returns an error if this function fails + * + * Returns: the value of the property, converted to QObject, or NULL if + * an error occurs. + */ +struct QObject *class_property_get_qobject(ObjectClass *cls, + const char *name, + struct Error **errp); + +/** + * class_property_set_qobject: + * @cls: the class object + * @name: the name of the property + * @value: The value that will be written to the property. + * @errp: returns an error if this function fails + * + * Writes a property to a class object. + * + * Returns: %true on success, %false on failure. + */ +bool class_property_set_qobject(ObjectClass *cls, + const char *name, struct QObject *value, + struct Error **errp); + #endif diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 78b5f350a0..34cab4ef31 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -34,6 +34,7 @@ #include "hw/qdev-properties.h" #include "trace/trace-root.h" #include "qemu/plugin.h" +#include "qapi/string-input-visitor.h" CPUState *cpu_by_arch_id(int64_t id) { @@ -158,31 +159,57 @@ ObjectClass *cpu_class_by_name(const char *typename, const char *cpu_model) static void cpu_common_parse_features(const char *typename, char *features, Error **errp) { - char *val; static bool cpu_globals_initialized; - /* Single "key=value" string being parsed */ - char *featurestr = features ? strtok(features, ",") : NULL; + ObjectClass *klass; + char *f; /* should be called only once, catch invalid users */ assert(!cpu_globals_initialized); cpu_globals_initialized = true; - while (featurestr) { - val = strchr(featurestr, '='); - if (val) { - GlobalProperty *prop = g_new0(typeof(*prop), 1); - *val = 0; - val++; - prop->driver = typename; - prop->property = g_strdup(featurestr); - prop->value = g_strdup(val); - qdev_prop_register_global(prop); - } else { - error_setg(errp, "Expected key=value format, found %s.", - featurestr); + if (!features) { + return; + } + + /* + * If typename is invalid, we'll register the global properties anyway + * and report a warning in qdev_prop_check_globals. + * TODO: Report an error early if -cpu typename is invalid; all classes + * will have been registered by now, whether or not the target is using + * class properties or object properties. + */ + klass = object_class_by_name(typename); + + /* Single "key=value" string being parsed */ + for (f = strtok(features, ","); f != NULL; f = strtok(NULL, ",")) { + char *val = strchr(f, '='); + GlobalProperty *prop; + + if (!val) { + error_setg(errp, "Expected key=value format, found %s.", f); return; } - featurestr = strtok(NULL, ","); + *val++ = 0; + + if (klass) { + ClassProperty *cp = class_property_find(klass, f); + if (cp) { + Visitor *v = string_input_visitor_new(val); + bool ok = class_property_set(klass, cp, v, errp); + + visit_free(v); + if (!ok) { + return; + } + continue; + } + } + + prop = g_new0(typeof(*prop), 1); + prop->driver = typename; + prop->property = g_strdup(f); + prop->value = g_strdup(val); + qdev_prop_register_global(prop); } } diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index f7775d0ea4..30037ddfb2 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -6,6 +6,7 @@ #include "qemu/ctype.h" #include "qemu/error-report.h" #include "qapi/visitor.h" +#include "qapi/string-input-visitor.h" #include "qemu/units.h" #include "qemu/cutils.h" #include "qdev-prop-internal.h" @@ -821,6 +822,41 @@ void qdev_prop_set_globals(DeviceState *dev) } } +bool device_class_late_init(ObjectClass *class, Error **errp) +{ + GPtrArray *props = global_properties; + int i, len = props ? props->len : 0; + + for (i = 0; i < len; i++) { + GlobalProperty *p = g_ptr_array_index(props, i); + ClassProperty *cp; + Visitor *v; + bool ok; + + if (object_class_dynamic_cast(class, p->driver) == NULL) { + continue; + } + + cp = class_property_find(class, p->property); + if (!cp) { + /* The property may be on the object. */ + continue; + } + p->used = true; + + v = string_input_visitor_new(p->value); + ok = class_property_set(class, cp, v, errp); + visit_free(v); + + if (!ok) { + error_prepend(errp, "can't apply global %s.%s=%s: ", + p->driver, p->property, p->value); + return false; + } + } + return true; +} + /* --- 64bit unsigned int 'size' type --- */ static void get_size(Object *obj, Visitor *v, const char *name, void *opaque, diff --git a/hw/core/qdev.c b/hw/core/qdev.c index d759c4602c..772aedc914 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -40,6 +40,7 @@ #include "hw/qdev-clock.h" #include "migration/vmstate.h" #include "trace.h" +#include "qdev-prop-internal.h" static bool qdev_hot_added = false; bool qdev_hot_removed = false; @@ -901,6 +902,7 @@ static const TypeInfo device_type_info = { .instance_finalize = device_finalize, .class_base_init = device_class_base_init, .class_init = device_class_init, + .class_late_init = device_class_late_init, .abstract = true, .class_size = sizeof(DeviceClass), .interfaces = (InterfaceInfo[]) { diff --git a/qom/object.c b/qom/object.c index 82a5c7d36e..344ca03877 100644 --- a/qom/object.c +++ b/qom/object.c @@ -21,6 +21,7 @@ #include "qapi/string-input-visitor.h" #include "qapi/string-output-visitor.h" #include "qapi/qobject-input-visitor.h" +#include "qapi/qobject-output-visitor.h" #include "qapi/forward-visitor.h" #include "qapi/qapi-builtin-visit.h" #include "qapi/qmp/qerror.h" @@ -34,6 +35,7 @@ #include "qapi/qmp/qnum.h" #include "qapi/qmp/qstring.h" #include "qemu/error-report.h" +#include "sysemu/qtest.h" #define MAX_INTERFACES 32 @@ -76,6 +78,190 @@ struct TypeImpl InterfaceImpl interfaces[MAX_INTERFACES]; }; +ClassProperty *class_property_find(ObjectClass *klass, const char *name) +{ + ObjectClass *parent_klass; + + if (klass->class_properties) { + ClassProperty *p = g_hash_table_lookup(klass->class_properties, name); + if (p) { + return p; + } + } + + parent_klass = object_class_get_parent(klass); + if (parent_klass) { + return class_property_find(parent_klass, name); + } + return NULL; +} + +static ClassProperty *class_property_find_err(ObjectClass *klass, + const char *name, + Error **errp) +{ + ClassProperty *cp = class_property_find(klass, name); + if (!cp) { + error_setg(errp, "Property '%s.%s' not found", + klass->type->name, name); + } + return cp; +} + +void class_property_add(ObjectClass *klass, const char *name, + const char *type, const char *desc, + ClassPropertyAccessor *get, + ClassPropertyAccessor *set, + void *opaque) +{ + ClassProperty *prop; + + assert(!class_property_find(klass, name)); + + prop = g_new0(ClassProperty, 1); + + prop->name = g_strdup(name); + prop->type = g_strdup(type); + prop->description = g_strdup(desc); + + prop->get = get; + prop->set = set; + prop->opaque = opaque; + + if (!klass->class_properties) { + klass->class_properties = g_hash_table_new(g_str_hash, g_str_equal); + } + g_hash_table_insert(klass->class_properties, prop->name, prop); +} + +bool class_property_set(ObjectClass *klass, ClassProperty *cp, + Visitor *v, Error **errp) +{ + /* + * FIXME: qtest/device-introspect-test creates one of each board, + * inside the same qemu instance. The class properties for the + * cpus may well be adjusted for each board. This cannot happen + * during normal usage. + */ + if (!qtest_enabled() && klass->type->object_created) { + error_setg(errp, "Property '%s.%s' set after object creation", + klass->type->name, cp->name); + return false; + } + return cp->set(klass, v, cp->name, cp->opaque, errp); +} + +static bool class_property_set_lookup(ObjectClass *klass, const char *name, + Visitor *v, Error **errp) +{ + ClassProperty *cp = class_property_find_err(klass, name, errp); + if (!cp) { + return false; + } + return class_property_set(klass, cp, v, errp); +} + +bool class_property_set_qobject(ObjectClass *klass, const char *name, + QObject *value, Error **errp) +{ + Visitor *v = qobject_input_visitor_new(value); + bool ok; + + ok = class_property_set_lookup(klass, name, v, errp); + visit_free(v); + return ok; +} + +bool class_property_set_bool(ObjectClass *klass, const char *name, + bool value, Error **errp) +{ + QBool *qbool = qbool_from_bool(value); + bool ok; + + ok = class_property_set_qobject(klass, name, QOBJECT(qbool), errp); + qobject_unref(qbool); + return ok; +} + +bool class_property_set_uint(ObjectClass *klass, const char *name, + uint64_t value, Error **errp) +{ + QNum *qnum = qnum_from_uint(value); + bool ok; + + ok = class_property_set_qobject(klass, name, QOBJECT(qnum), errp); + qobject_unref(qnum); + return ok; +} + +bool class_property_get(ObjectClass *klass, ClassProperty *cp, + Visitor *v, Error **errp) +{ + return cp->get(klass, v, cp->name, cp->opaque, errp); +} + +static bool class_property_get_lookup(ObjectClass *klass, const char *name, + Visitor *v, Error **errp) +{ + ClassProperty *cp = class_property_find_err(klass, name, errp); + if (!cp) { + return false; + } + return class_property_get(klass, cp, v, errp); +} + + +QObject *class_property_get_qobject(ObjectClass *klass, const char *name, + Error **errp) +{ + QObject *ret = NULL; + Visitor *v = qobject_output_visitor_new(&ret); + + if (class_property_get_lookup(klass, name, v, errp)) { + visit_complete(v, &ret); + } + visit_free(v); + return ret; +} + +bool class_property_get_bool(ObjectClass *klass, const char *name, + Error **errp) +{ + QObject *qobj = class_property_get_qobject(klass, name, errp); + bool ret = false; + + if (qobj) { + QBool *qbool = qobject_to(QBool, qobj); + if (!qbool) { + error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, "boolean"); + } else { + ret = qbool_get_bool(qbool); + } + qobject_unref(qobj); + } + return ret; +} + +void class_property_iter_init(ObjectPropertyIterator *iter, + ObjectClass *klass) +{ + g_hash_table_iter_init(&iter->iter, klass->class_properties); + iter->nextclass = object_class_get_parent(klass); +} + +ClassProperty *class_property_iter_next(ObjectPropertyIterator *iter) +{ + gpointer key, val; + while (!g_hash_table_iter_next(&iter->iter, &key, &val)) { + if (!iter->nextclass) { + return NULL; + } + g_hash_table_iter_init(&iter->iter, iter->nextclass->class_properties); + iter->nextclass = object_class_get_parent(iter->nextclass); + } + return val; +} + static Type type_interface; static GHashTable *type_table_get(void) @@ -322,6 +508,7 @@ static void type_initialize(TypeImpl *ti) g_assert(parent->instance_size <= ti->instance_size); memcpy(ti->class, parent->class, parent->class_size); ti->class->interfaces = NULL; + ti->class->class_properties = NULL; for (e = parent->class->interfaces; e; e = e->next) { InterfaceClass *iface = e->data; @@ -415,12 +602,15 @@ static void object_post_init_with_type(Object *obj, TypeImpl *ti) bool object_apply_global_props(Object *obj, const GPtrArray *props, Error **errp) { + ObjectClass *klass; int i; if (!props) { return true; } + klass = object_get_class(obj); + for (i = 0; i < props->len; i++) { GlobalProperty *p = g_ptr_array_index(props, i); Error *err = NULL; @@ -428,6 +618,10 @@ bool object_apply_global_props(Object *obj, const GPtrArray *props, if (object_dynamic_cast(obj, p->driver) == NULL) { continue; } + if (class_property_find(klass, p->property)) { + /* This was handled in device_class_late_init. */ + continue; + } if (p->optional && !object_property_find(obj, p->property)) { continue; } diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c index f94b6c3193..aee86eb708 100644 --- a/qom/object_interfaces.c +++ b/qom/object_interfaces.c @@ -203,6 +203,7 @@ bool type_print_class_properties(const char *type) ObjectClass *klass; ObjectPropertyIterator iter; ObjectProperty *prop; + ClassProperty *cprop; GPtrArray *array; int i; @@ -212,6 +213,7 @@ bool type_print_class_properties(const char *type) } array = g_ptr_array_new(); + object_class_property_iter_init(&iter, klass); while ((prop = object_property_iter_next(&iter))) { if (!prop->set) { @@ -222,6 +224,17 @@ bool type_print_class_properties(const char *type) object_property_help(prop->name, prop->type, prop->defval, prop->description)); } + + class_property_iter_init(&iter, klass); + while ((cprop = class_property_iter_next(&iter))) { + if (!cprop->set) { + continue; + } + g_ptr_array_add(array, + object_property_help(cprop->name, cprop->type, + NULL, cprop->description)); + } + g_ptr_array_sort(array, (GCompareFunc)qemu_pstrcmp0); if (array->len > 0) { qemu_printf("%s options:\n", type); diff --git a/qom/qom-qmp-cmds.c b/qom/qom-qmp-cmds.c index 7c087299de..ea3f542a1d 100644 --- a/qom/qom-qmp-cmds.c +++ b/qom/qom-qmp-cmds.c @@ -34,6 +34,7 @@ ObjectPropertyInfoList *qmp_qom_list(const char *path, Error **errp) bool ambiguous = false; ObjectPropertyInfoList *props = NULL; ObjectProperty *prop; + ClassProperty *cprop; ObjectPropertyIterator iter; obj = object_resolve_path(path, &ambiguous); @@ -57,6 +58,16 @@ ObjectPropertyInfoList *qmp_qom_list(const char *path, Error **errp) value->type = g_strdup(prop->type); } + class_property_iter_init(&iter, object_get_class(obj)); + while ((cprop = class_property_iter_next(&iter))) { + ObjectPropertyInfo *value = g_new0(ObjectPropertyInfo, 1); + + QAPI_LIST_PREPEND(props, value); + + value->name = g_strdup(cprop->name); + value->type = g_strdup(cprop->type); + } + return props; } @@ -124,6 +135,7 @@ ObjectPropertyInfoList *qmp_device_list_properties(const char *typename, ObjectClass *klass; Object *obj; ObjectProperty *prop; + ClassProperty *cprop; ObjectPropertyIterator iter; ObjectPropertyInfoList *prop_list = NULL; @@ -172,6 +184,18 @@ ObjectPropertyInfoList *qmp_device_list_properties(const char *typename, QAPI_LIST_PREPEND(prop_list, info); } + class_property_iter_init(&iter, klass); + while ((cprop = class_property_iter_next(&iter))) { + ObjectPropertyInfo *info; + + info = g_new0(ObjectPropertyInfo, 1); + info->name = g_strdup(cprop->name); + info->type = g_strdup(cprop->type); + info->description = g_strdup(cprop->description); + + QAPI_LIST_PREPEND(prop_list, info); + } + object_unref(obj); return prop_list; @@ -183,6 +207,7 @@ ObjectPropertyInfoList *qmp_qom_list_properties(const char *typename, ObjectClass *klass; Object *obj = NULL; ObjectProperty *prop; + ClassProperty *cprop; ObjectPropertyIterator iter; ObjectPropertyInfoList *prop_list = NULL; @@ -216,6 +241,18 @@ ObjectPropertyInfoList *qmp_qom_list_properties(const char *typename, QAPI_LIST_PREPEND(prop_list, info); } + class_property_iter_init(&iter, klass); + while ((cprop = class_property_iter_next(&iter))) { + ObjectPropertyInfo *info; + + info = g_malloc0(sizeof(*info)); + info->name = g_strdup(cprop->name); + info->type = g_strdup(cprop->type); + info->description = g_strdup(cprop->description); + + QAPI_LIST_PREPEND(prop_list, info); + } + object_unref(obj); return prop_list; -- 2.34.1