@samitouri / QOSamiQemu / commits / 9eb04c95be

hw/core: Permit devices to define an array of link properties

Currently we allow devices to define "link properties" with DEFINE_PROP_LINK(): these are a way to give a device a pointer to another QOM object. (Under the hood this is done by handing it the canonical QOM path for the object.) We also allow devices to define "array properties" with DEFINE_PROP_ARRAY(): these are a way to give a device a variable-length array of properties. However, there is no way to define an array of link properties. If you try to do it by passing qdev_prop_link as the arrayprop argument to DEFINE_PROP_ARRAY() you will get a crash because qdev_prop_link does not provide the .set and .get methods in its PropertyInfo struct. This patch implements a new DEFINE_PROP_LINK_ARRAY(). In a device you can use it like this: struct MyDevice { ... uint32_t num_cpus; ARMCPU **cpus; } and in your Property array: DEFINE_PROP_LINK_ARRAY("cpus", MyDevice, num_cpus, cpus, TYPE_ARM_CPU, ARMCPU *), The array property code will fill in s->num_cpus, allocate memory in s->cpus, and populate it with pointers. On the device-creation side you set the property in the same way as the existing array properties, using the new qlist_append_link() function to append to the QList: QList *cpulist = qlist_new(); for (int i = 0; i < cpus; i++) { qlist_append_link(cpulist, OBJECT(cpu[i])); } qdev_prop_set_array(mydev, "cpus", cpulist); The implementation is mostly in the provision of the .set and .get methods to the qdev_prop_link PropertyInfo struct. The code of these methods parallels the code in object_set_link_property() and object_get_link_property(). We can't completely share the code with those functions because of differences in where we get the information like the target QOM type, but I have pulled out a new function object_resolve_and_typecheck() for the shared "given a QOM path and a type, give me the object or an error" code. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20260327111700.795099-3-peter.maydell@linaro.org

Peter Maydell committed Mar 27, 2026 at 11:15 UTC 9eb04c95bef307d69ac077b84c6989bfe5b28f97
2 files changed +119
hw/core/qdev-properties.c
+78
@@ -670,6 +670,7 @@ static Property array_elem_prop(Object *obj, const Property *parent_prop,
670 * being inside the device struct.
671 */
672 .offset = (uintptr_t)elem - (uintptr_t)obj,
673 + .link_type = parent_prop->link_type,
674 };
675 }
676
@@ -951,6 +952,12 @@ void qdev_prop_set_array(DeviceState *dev, const char *name, QList *values)
952 qobject_unref(values);
953 }
954
955 +void qlist_append_link(QList *qlist, Object *obj)
956 +{
957 + g_autofree char *path = object_get_canonical_path(obj);
958 + qlist_append_str(qlist, path);
959 +}
960 +
961 static GPtrArray *global_props(void)
962 {
963 static GPtrArray *gp;
@@ -1060,9 +1067,80 @@ static ObjectProperty *create_link_property(ObjectClass *oc, const char *name,
1067 OBJ_PROP_LINK_STRONG);
1068 }
1069
1070 +/*
1071 + * The logic in these get_link() and set_link() functions is similar
1072 + * to that used for single-element link properties in the
1073 + * object_get_link_property() and object_set_link_property() functions.
1074 + * The difference is largely in how we get the expected type of the
1075 + * link: for us it is in the Property struct, and for a single link
1076 + * property it is part of the property name on the object.
1077 + */
1078 +static void get_link(Object *obj, Visitor *v, const char *name, void *opaque,
1079 + Error **errp)
1080 +{
1081 + const Property *prop = opaque;
1082 + Object **targetp = object_field_prop_ptr(obj, prop);
1083 + g_autofree char *path = NULL;
1084 +
1085 + if (*targetp) {
1086 + path = object_get_canonical_path(*targetp);
1087 + visit_type_str(v, name, &path, errp);
1088 + } else {
1089 + path = g_strdup("");
1090 + visit_type_str(v, name, &path, errp);
1091 + }
1092 +}
1093 +
1094 +static void set_link(Object *obj, Visitor *v, const char *name, void *opaque,
1095 + Error **errp)
1096 +{
1097 + const Property *prop = opaque;
1098 + Object **targetp = object_field_prop_ptr(obj, prop);
1099 + g_autofree char *path = NULL;
1100 + Object *new_target, *old_target = *targetp;
1101 +
1102 + ERRP_GUARD();
1103 +
1104 + /* Get the path to the object we want to set the link to */
1105 + if (!visit_type_str(v, name, &path, errp)) {
1106 + return;
1107 + }
1108 +
1109 + /* Now get the pointer to the actual object */
1110 + if (*path) {
1111 + new_target = object_resolve_and_typecheck(path, prop->name,
1112 + prop->link_type, errp);
1113 + if (!new_target) {
1114 + return;
1115 + }
1116 + } else {
1117 + new_target = NULL;
1118 + }
1119 +
1120 + /*
1121 + * Our link properties are always OBJ_PROP_LINK_STRONG and
1122 + * have the allow_set_link_before_realize check.
1123 + */
1124 + qdev_prop_allow_set_link_before_realize(obj, prop->name, new_target, errp);
1125 + if (*errp) {
1126 + return;
1127 + }
1128 +
1129 + *targetp = new_target;
1130 + object_ref(new_target);
1131 + object_unref(old_target);
1132 +}
1133 +
1134 const PropertyInfo qdev_prop_link = {
1135 .type = "link",
1136 .create = create_link_property,
1137 + /*
1138 + * Since we have a create method, the get and set are used
1139 + * only in get_prop_array() and set_prop_array() for the case
1140 + * where we have an array of link properties.
1141 + */
1142 + .get = get_link,
1143 + .set = set_link,
1144 };
1145
1146 void qdev_property_add_static(DeviceState *dev, const Property *prop)
include/hw/core/qdev-properties.h
+41
@@ -169,6 +169,32 @@ extern const PropertyInfo qdev_prop_link;
169 DEFINE_PROP(_name, _state, _field, qdev_prop_link, _ptr_type, \
170 .link_type = _type)
171
172 +/**
173 + * DEFINE_PROP_LINK_ARRAY:
174 + * @_name: name of the array
175 + * @_state: name of the device state structure type
176 + * @_field: uint32_t field in @_state to hold the array length
177 + * @_arrayfield: field in @_state (of type '@_arraytype *') which
178 + * will point to the array
179 + * @_linktype: QOM type name of the link type
180 + * @_arraytype: C type of the array elements
181 + *
182 + * Define device properties for a variable-length array _name of links
183 + * (i.e. this is the array version of DEFINE_PROP_LINK).
184 + *
185 + * The array is represented as a list of QStrings in the visitor interface,
186 + * where each string is the QOM path of the object to be linked.
187 + */
188 +#define DEFINE_PROP_LINK_ARRAY(_name, _state, _field, _arrayfield, \
189 + _linktype, _arraytype) \
190 + DEFINE_PROP(_name, _state, _field, qdev_prop_array, uint32_t, \
191 + .set_default = true, \
192 + .defval.u = 0, \
193 + .arrayinfo = &qdev_prop_link, \
194 + .arrayfieldsize = sizeof(_arraytype), \
195 + .arrayoffset = offsetof(_state, _arrayfield), \
196 + .link_type = _linktype)
197 +
198 #define DEFINE_PROP_UINT8(_n, _s, _f, _d) \
199 DEFINE_PROP_UNSIGNED(_n, _s, _f, _d, qdev_prop_uint8, uint8_t)
200 #define DEFINE_PROP_UINT16(_n, _s, _f, _d) \
@@ -220,6 +246,21 @@ void qdev_prop_set_enum(DeviceState *dev, const char *name, int value);
246 /* Takes ownership of @values */
247 void qdev_prop_set_array(DeviceState *dev, const char *name, QList *values);
248
249 +/**
250 + * qlist_append_link: Add a QOM object to a QList of link properties
251 + * @qlist: list to append to
252 + * @obj: object to append
253 + *
254 + * This is a helper function for constructing a QList to pass to
255 + * qdev_prop_set_array() when the qdev property array is an array of
256 + * link properties (i.e. one defined with DEFINE_PROP_LINK_ARRAY).
257 + *
258 + * The object is encoded into the list as a QString which is the
259 + * canonical path of the object; this is the same encoding that
260 + * object_set_link_property() and object_get_link_property() use.
261 + */
262 +void qlist_append_link(QList *qlist, Object *obj);
263 +
264 void *object_field_prop_ptr(Object *obj, const Property *prop);
265
266 void qdev_prop_register_global(GlobalProperty *prop);