@@ -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
@@ -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);
/**
@@ -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
@@ -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);
}
}
@@ -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,
@@ -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[]) {
@@ -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;
}
@@ -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);
@@ -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;
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.henderson@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(-)