]>
Commit | Line | Data |
---|---|---|
1 | #include "qemu/osdep.h" | |
2 | #include "hw/qdev-properties.h" | |
3 | #include "qapi/error.h" | |
4 | #include "qapi/qapi-types-misc.h" | |
5 | #include "qobject/qlist.h" | |
6 | #include "qemu/ctype.h" | |
7 | #include "qemu/error-report.h" | |
8 | #include "qapi/visitor.h" | |
9 | #include "qemu/units.h" | |
10 | #include "qemu/cutils.h" | |
11 | #include "qdev-prop-internal.h" | |
12 | #include "qom/qom-qobject.h" | |
13 | ||
14 | void qdev_prop_set_after_realize(DeviceState *dev, const char *name, | |
15 | Error **errp) | |
16 | { | |
17 | if (dev->id) { | |
18 | error_setg(errp, "Attempt to set property '%s' on device '%s' " | |
19 | "(type '%s') after it was realized", name, dev->id, | |
20 | object_get_typename(OBJECT(dev))); | |
21 | } else { | |
22 | error_setg(errp, "Attempt to set property '%s' on anonymous device " | |
23 | "(type '%s') after it was realized", name, | |
24 | object_get_typename(OBJECT(dev))); | |
25 | } | |
26 | } | |
27 | ||
28 | /* returns: true if property is allowed to be set, false otherwise */ | |
29 | static bool qdev_prop_allow_set(Object *obj, const char *name, | |
30 | const PropertyInfo *info, Error **errp) | |
31 | { | |
32 | DeviceState *dev = DEVICE(obj); | |
33 | ||
34 | if (dev->realized && !info->realized_set_allowed) { | |
35 | qdev_prop_set_after_realize(dev, name, errp); | |
36 | return false; | |
37 | } | |
38 | return true; | |
39 | } | |
40 | ||
41 | void qdev_prop_allow_set_link_before_realize(const Object *obj, | |
42 | const char *name, | |
43 | Object *val, Error **errp) | |
44 | { | |
45 | DeviceState *dev = DEVICE(obj); | |
46 | ||
47 | if (dev->realized) { | |
48 | error_setg(errp, "Attempt to set link property '%s' on device '%s' " | |
49 | "(type '%s') after it was realized", | |
50 | name, dev->id, object_get_typename(obj)); | |
51 | } | |
52 | } | |
53 | ||
54 | void *object_field_prop_ptr(Object *obj, const Property *prop) | |
55 | { | |
56 | void *ptr = obj; | |
57 | ptr += prop->offset; | |
58 | return ptr; | |
59 | } | |
60 | ||
61 | static void field_prop_get(Object *obj, Visitor *v, const char *name, | |
62 | void *opaque, Error **errp) | |
63 | { | |
64 | const Property *prop = opaque; | |
65 | return prop->info->get(obj, v, name, opaque, errp); | |
66 | } | |
67 | ||
68 | /** | |
69 | * field_prop_getter: Return getter function to be used for property | |
70 | * | |
71 | * Return value can be NULL if @info has no getter function. | |
72 | */ | |
73 | static ObjectPropertyAccessor *field_prop_getter(const PropertyInfo *info) | |
74 | { | |
75 | return info->get ? field_prop_get : NULL; | |
76 | } | |
77 | ||
78 | static void field_prop_set(Object *obj, Visitor *v, const char *name, | |
79 | void *opaque, Error **errp) | |
80 | { | |
81 | const Property *prop = opaque; | |
82 | ||
83 | if (!qdev_prop_allow_set(obj, name, prop->info, errp)) { | |
84 | return; | |
85 | } | |
86 | ||
87 | return prop->info->set(obj, v, name, opaque, errp); | |
88 | } | |
89 | ||
90 | /** | |
91 | * field_prop_setter: Return setter function to be used for property | |
92 | * | |
93 | * Return value can be NULL if @info has not setter function. | |
94 | */ | |
95 | static ObjectPropertyAccessor *field_prop_setter(const PropertyInfo *info) | |
96 | { | |
97 | return info->set ? field_prop_set : NULL; | |
98 | } | |
99 | ||
100 | void qdev_propinfo_get_enum(Object *obj, Visitor *v, const char *name, | |
101 | void *opaque, Error **errp) | |
102 | { | |
103 | const Property *prop = opaque; | |
104 | int *ptr = object_field_prop_ptr(obj, prop); | |
105 | ||
106 | visit_type_enum(v, name, ptr, prop->info->enum_table, errp); | |
107 | } | |
108 | ||
109 | void qdev_propinfo_set_enum(Object *obj, Visitor *v, const char *name, | |
110 | void *opaque, Error **errp) | |
111 | { | |
112 | const Property *prop = opaque; | |
113 | int *ptr = object_field_prop_ptr(obj, prop); | |
114 | ||
115 | visit_type_enum(v, name, ptr, prop->info->enum_table, errp); | |
116 | } | |
117 | ||
118 | void qdev_propinfo_set_default_value_enum(ObjectProperty *op, | |
119 | const Property *prop) | |
120 | { | |
121 | object_property_set_default_str(op, | |
122 | qapi_enum_lookup(prop->info->enum_table, prop->defval.i)); | |
123 | } | |
124 | ||
125 | /* Bit */ | |
126 | ||
127 | static uint32_t qdev_get_prop_mask(const Property *prop) | |
128 | { | |
129 | assert(prop->info == &qdev_prop_bit); | |
130 | return 0x1 << prop->bitnr; | |
131 | } | |
132 | ||
133 | static void bit_prop_set(Object *obj, const Property *props, bool val) | |
134 | { | |
135 | uint32_t *p = object_field_prop_ptr(obj, props); | |
136 | uint32_t mask = qdev_get_prop_mask(props); | |
137 | if (val) { | |
138 | *p |= mask; | |
139 | } else { | |
140 | *p &= ~mask; | |
141 | } | |
142 | } | |
143 | ||
144 | static void prop_get_bit(Object *obj, Visitor *v, const char *name, | |
145 | void *opaque, Error **errp) | |
146 | { | |
147 | const Property *prop = opaque; | |
148 | uint32_t *p = object_field_prop_ptr(obj, prop); | |
149 | bool value = (*p & qdev_get_prop_mask(prop)) != 0; | |
150 | ||
151 | visit_type_bool(v, name, &value, errp); | |
152 | } | |
153 | ||
154 | static void prop_set_bit(Object *obj, Visitor *v, const char *name, | |
155 | void *opaque, Error **errp) | |
156 | { | |
157 | const Property *prop = opaque; | |
158 | bool value; | |
159 | ||
160 | if (!visit_type_bool(v, name, &value, errp)) { | |
161 | return; | |
162 | } | |
163 | bit_prop_set(obj, prop, value); | |
164 | } | |
165 | ||
166 | static void set_default_value_bool(ObjectProperty *op, const Property *prop) | |
167 | { | |
168 | object_property_set_default_bool(op, prop->defval.u); | |
169 | } | |
170 | ||
171 | const PropertyInfo qdev_prop_bit = { | |
172 | .type = "bool", | |
173 | .description = "on/off", | |
174 | .get = prop_get_bit, | |
175 | .set = prop_set_bit, | |
176 | .set_default_value = set_default_value_bool, | |
177 | }; | |
178 | ||
179 | /* Bit64 */ | |
180 | ||
181 | static uint64_t qdev_get_prop_mask64(const Property *prop) | |
182 | { | |
183 | assert(prop->info == &qdev_prop_bit64); | |
184 | return 0x1ull << prop->bitnr; | |
185 | } | |
186 | ||
187 | static void bit64_prop_set(Object *obj, const Property *props, bool val) | |
188 | { | |
189 | uint64_t *p = object_field_prop_ptr(obj, props); | |
190 | uint64_t mask = qdev_get_prop_mask64(props); | |
191 | if (val) { | |
192 | *p |= mask; | |
193 | } else { | |
194 | *p &= ~mask; | |
195 | } | |
196 | } | |
197 | ||
198 | static void prop_get_bit64(Object *obj, Visitor *v, const char *name, | |
199 | void *opaque, Error **errp) | |
200 | { | |
201 | const Property *prop = opaque; | |
202 | uint64_t *p = object_field_prop_ptr(obj, prop); | |
203 | bool value = (*p & qdev_get_prop_mask64(prop)) != 0; | |
204 | ||
205 | visit_type_bool(v, name, &value, errp); | |
206 | } | |
207 | ||
208 | static void prop_set_bit64(Object *obj, Visitor *v, const char *name, | |
209 | void *opaque, Error **errp) | |
210 | { | |
211 | const Property *prop = opaque; | |
212 | bool value; | |
213 | ||
214 | if (!visit_type_bool(v, name, &value, errp)) { | |
215 | return; | |
216 | } | |
217 | bit64_prop_set(obj, prop, value); | |
218 | } | |
219 | ||
220 | const PropertyInfo qdev_prop_bit64 = { | |
221 | .type = "bool", | |
222 | .description = "on/off", | |
223 | .get = prop_get_bit64, | |
224 | .set = prop_set_bit64, | |
225 | .set_default_value = set_default_value_bool, | |
226 | }; | |
227 | ||
228 | /* --- bool --- */ | |
229 | ||
230 | static void get_bool(Object *obj, Visitor *v, const char *name, void *opaque, | |
231 | Error **errp) | |
232 | { | |
233 | const Property *prop = opaque; | |
234 | bool *ptr = object_field_prop_ptr(obj, prop); | |
235 | ||
236 | visit_type_bool(v, name, ptr, errp); | |
237 | } | |
238 | ||
239 | static void set_bool(Object *obj, Visitor *v, const char *name, void *opaque, | |
240 | Error **errp) | |
241 | { | |
242 | const Property *prop = opaque; | |
243 | bool *ptr = object_field_prop_ptr(obj, prop); | |
244 | ||
245 | visit_type_bool(v, name, ptr, errp); | |
246 | } | |
247 | ||
248 | const PropertyInfo qdev_prop_bool = { | |
249 | .type = "bool", | |
250 | .description = "on/off", | |
251 | .get = get_bool, | |
252 | .set = set_bool, | |
253 | .set_default_value = set_default_value_bool, | |
254 | }; | |
255 | ||
256 | /* --- 8bit integer --- */ | |
257 | ||
258 | static void get_uint8(Object *obj, Visitor *v, const char *name, void *opaque, | |
259 | Error **errp) | |
260 | { | |
261 | const Property *prop = opaque; | |
262 | uint8_t *ptr = object_field_prop_ptr(obj, prop); | |
263 | ||
264 | visit_type_uint8(v, name, ptr, errp); | |
265 | } | |
266 | ||
267 | static void set_uint8(Object *obj, Visitor *v, const char *name, void *opaque, | |
268 | Error **errp) | |
269 | { | |
270 | const Property *prop = opaque; | |
271 | uint8_t *ptr = object_field_prop_ptr(obj, prop); | |
272 | ||
273 | visit_type_uint8(v, name, ptr, errp); | |
274 | } | |
275 | ||
276 | void qdev_propinfo_set_default_value_int(ObjectProperty *op, | |
277 | const Property *prop) | |
278 | { | |
279 | object_property_set_default_int(op, prop->defval.i); | |
280 | } | |
281 | ||
282 | void qdev_propinfo_set_default_value_uint(ObjectProperty *op, | |
283 | const Property *prop) | |
284 | { | |
285 | object_property_set_default_uint(op, prop->defval.u); | |
286 | } | |
287 | ||
288 | const PropertyInfo qdev_prop_uint8 = { | |
289 | .type = "uint8", | |
290 | .get = get_uint8, | |
291 | .set = set_uint8, | |
292 | .set_default_value = qdev_propinfo_set_default_value_uint, | |
293 | }; | |
294 | ||
295 | /* --- 16bit integer --- */ | |
296 | ||
297 | static void get_uint16(Object *obj, Visitor *v, const char *name, | |
298 | void *opaque, Error **errp) | |
299 | { | |
300 | const Property *prop = opaque; | |
301 | uint16_t *ptr = object_field_prop_ptr(obj, prop); | |
302 | ||
303 | visit_type_uint16(v, name, ptr, errp); | |
304 | } | |
305 | ||
306 | static void set_uint16(Object *obj, Visitor *v, const char *name, | |
307 | void *opaque, Error **errp) | |
308 | { | |
309 | const Property *prop = opaque; | |
310 | uint16_t *ptr = object_field_prop_ptr(obj, prop); | |
311 | ||
312 | visit_type_uint16(v, name, ptr, errp); | |
313 | } | |
314 | ||
315 | const PropertyInfo qdev_prop_uint16 = { | |
316 | .type = "uint16", | |
317 | .get = get_uint16, | |
318 | .set = set_uint16, | |
319 | .set_default_value = qdev_propinfo_set_default_value_uint, | |
320 | }; | |
321 | ||
322 | /* --- 32bit integer --- */ | |
323 | ||
324 | static void get_uint32(Object *obj, Visitor *v, const char *name, | |
325 | void *opaque, Error **errp) | |
326 | { | |
327 | const Property *prop = opaque; | |
328 | uint32_t *ptr = object_field_prop_ptr(obj, prop); | |
329 | ||
330 | visit_type_uint32(v, name, ptr, errp); | |
331 | } | |
332 | ||
333 | static void set_uint32(Object *obj, Visitor *v, const char *name, | |
334 | void *opaque, Error **errp) | |
335 | { | |
336 | const Property *prop = opaque; | |
337 | uint32_t *ptr = object_field_prop_ptr(obj, prop); | |
338 | ||
339 | visit_type_uint32(v, name, ptr, errp); | |
340 | } | |
341 | ||
342 | void qdev_propinfo_get_int32(Object *obj, Visitor *v, const char *name, | |
343 | void *opaque, Error **errp) | |
344 | { | |
345 | const Property *prop = opaque; | |
346 | int32_t *ptr = object_field_prop_ptr(obj, prop); | |
347 | ||
348 | visit_type_int32(v, name, ptr, errp); | |
349 | } | |
350 | ||
351 | static void set_int32(Object *obj, Visitor *v, const char *name, void *opaque, | |
352 | Error **errp) | |
353 | { | |
354 | const Property *prop = opaque; | |
355 | int32_t *ptr = object_field_prop_ptr(obj, prop); | |
356 | ||
357 | visit_type_int32(v, name, ptr, errp); | |
358 | } | |
359 | ||
360 | const PropertyInfo qdev_prop_uint32 = { | |
361 | .type = "uint32", | |
362 | .get = get_uint32, | |
363 | .set = set_uint32, | |
364 | .set_default_value = qdev_propinfo_set_default_value_uint, | |
365 | }; | |
366 | ||
367 | const PropertyInfo qdev_prop_int32 = { | |
368 | .type = "int32", | |
369 | .get = qdev_propinfo_get_int32, | |
370 | .set = set_int32, | |
371 | .set_default_value = qdev_propinfo_set_default_value_int, | |
372 | }; | |
373 | ||
374 | /* --- 64bit integer --- */ | |
375 | ||
376 | static void get_uint64(Object *obj, Visitor *v, const char *name, | |
377 | void *opaque, Error **errp) | |
378 | { | |
379 | const Property *prop = opaque; | |
380 | uint64_t *ptr = object_field_prop_ptr(obj, prop); | |
381 | ||
382 | visit_type_uint64(v, name, ptr, errp); | |
383 | } | |
384 | ||
385 | static void set_uint64(Object *obj, Visitor *v, const char *name, | |
386 | void *opaque, Error **errp) | |
387 | { | |
388 | const Property *prop = opaque; | |
389 | uint64_t *ptr = object_field_prop_ptr(obj, prop); | |
390 | ||
391 | visit_type_uint64(v, name, ptr, errp); | |
392 | } | |
393 | ||
394 | static void get_int64(Object *obj, Visitor *v, const char *name, | |
395 | void *opaque, Error **errp) | |
396 | { | |
397 | const Property *prop = opaque; | |
398 | int64_t *ptr = object_field_prop_ptr(obj, prop); | |
399 | ||
400 | visit_type_int64(v, name, ptr, errp); | |
401 | } | |
402 | ||
403 | static void set_int64(Object *obj, Visitor *v, const char *name, | |
404 | void *opaque, Error **errp) | |
405 | { | |
406 | const Property *prop = opaque; | |
407 | int64_t *ptr = object_field_prop_ptr(obj, prop); | |
408 | ||
409 | visit_type_int64(v, name, ptr, errp); | |
410 | } | |
411 | ||
412 | const PropertyInfo qdev_prop_uint64 = { | |
413 | .type = "uint64", | |
414 | .get = get_uint64, | |
415 | .set = set_uint64, | |
416 | .set_default_value = qdev_propinfo_set_default_value_uint, | |
417 | }; | |
418 | ||
419 | const PropertyInfo qdev_prop_int64 = { | |
420 | .type = "int64", | |
421 | .get = get_int64, | |
422 | .set = set_int64, | |
423 | .set_default_value = qdev_propinfo_set_default_value_int, | |
424 | }; | |
425 | ||
426 | static void set_uint64_checkmask(Object *obj, Visitor *v, const char *name, | |
427 | void *opaque, Error **errp) | |
428 | { | |
429 | const Property *prop = opaque; | |
430 | uint64_t *ptr = object_field_prop_ptr(obj, prop); | |
431 | ||
432 | visit_type_uint64(v, name, ptr, errp); | |
433 | if (*ptr & ~prop->bitmask) { | |
434 | error_setg(errp, "Property value for '%s' has bits outside mask '0x%" PRIx64 "'", | |
435 | name, prop->bitmask); | |
436 | } | |
437 | } | |
438 | ||
439 | const PropertyInfo qdev_prop_uint64_checkmask = { | |
440 | .type = "uint64", | |
441 | .get = get_uint64, | |
442 | .set = set_uint64_checkmask, | |
443 | }; | |
444 | ||
445 | /* --- pointer-size integer --- */ | |
446 | ||
447 | static void get_usize(Object *obj, Visitor *v, const char *name, void *opaque, | |
448 | Error **errp) | |
449 | { | |
450 | const Property *prop = opaque; | |
451 | ||
452 | #if HOST_LONG_BITS == 32 | |
453 | uint32_t *ptr = object_field_prop_ptr(obj, prop); | |
454 | visit_type_uint32(v, name, ptr, errp); | |
455 | #else | |
456 | uint64_t *ptr = object_field_prop_ptr(obj, prop); | |
457 | visit_type_uint64(v, name, ptr, errp); | |
458 | #endif | |
459 | } | |
460 | ||
461 | static void set_usize(Object *obj, Visitor *v, const char *name, void *opaque, | |
462 | Error **errp) | |
463 | { | |
464 | const Property *prop = opaque; | |
465 | ||
466 | #if HOST_LONG_BITS == 32 | |
467 | uint32_t *ptr = object_field_prop_ptr(obj, prop); | |
468 | visit_type_uint32(v, name, ptr, errp); | |
469 | #else | |
470 | uint64_t *ptr = object_field_prop_ptr(obj, prop); | |
471 | visit_type_uint64(v, name, ptr, errp); | |
472 | #endif | |
473 | } | |
474 | ||
475 | const PropertyInfo qdev_prop_usize = { | |
476 | .type = "usize", | |
477 | .get = get_usize, | |
478 | .set = set_usize, | |
479 | .set_default_value = qdev_propinfo_set_default_value_uint, | |
480 | }; | |
481 | ||
482 | /* --- string --- */ | |
483 | ||
484 | static void release_string(Object *obj, const char *name, void *opaque) | |
485 | { | |
486 | const Property *prop = opaque; | |
487 | g_free(*(char **)object_field_prop_ptr(obj, prop)); | |
488 | } | |
489 | ||
490 | static void get_string(Object *obj, Visitor *v, const char *name, | |
491 | void *opaque, Error **errp) | |
492 | { | |
493 | const Property *prop = opaque; | |
494 | char **ptr = object_field_prop_ptr(obj, prop); | |
495 | ||
496 | if (!*ptr) { | |
497 | char *str = (char *)""; | |
498 | visit_type_str(v, name, &str, errp); | |
499 | } else { | |
500 | visit_type_str(v, name, ptr, errp); | |
501 | } | |
502 | } | |
503 | ||
504 | static void set_string(Object *obj, Visitor *v, const char *name, | |
505 | void *opaque, Error **errp) | |
506 | { | |
507 | const Property *prop = opaque; | |
508 | char **ptr = object_field_prop_ptr(obj, prop); | |
509 | char *str; | |
510 | ||
511 | if (!visit_type_str(v, name, &str, errp)) { | |
512 | return; | |
513 | } | |
514 | g_free(*ptr); | |
515 | *ptr = str; | |
516 | } | |
517 | ||
518 | const PropertyInfo qdev_prop_string = { | |
519 | .type = "str", | |
520 | .release = release_string, | |
521 | .get = get_string, | |
522 | .set = set_string, | |
523 | }; | |
524 | ||
525 | /* --- on/off/auto --- */ | |
526 | ||
527 | const PropertyInfo qdev_prop_on_off_auto = { | |
528 | .type = "OnOffAuto", | |
529 | .description = "on/off/auto", | |
530 | .enum_table = &OnOffAuto_lookup, | |
531 | .get = qdev_propinfo_get_enum, | |
532 | .set = qdev_propinfo_set_enum, | |
533 | .set_default_value = qdev_propinfo_set_default_value_enum, | |
534 | }; | |
535 | ||
536 | /* --- 32bit unsigned int 'size' type --- */ | |
537 | ||
538 | void qdev_propinfo_get_size32(Object *obj, Visitor *v, const char *name, | |
539 | void *opaque, Error **errp) | |
540 | { | |
541 | const Property *prop = opaque; | |
542 | uint32_t *ptr = object_field_prop_ptr(obj, prop); | |
543 | uint64_t value = *ptr; | |
544 | ||
545 | visit_type_size(v, name, &value, errp); | |
546 | } | |
547 | ||
548 | static void set_size32(Object *obj, Visitor *v, const char *name, void *opaque, | |
549 | Error **errp) | |
550 | { | |
551 | const Property *prop = opaque; | |
552 | uint32_t *ptr = object_field_prop_ptr(obj, prop); | |
553 | uint64_t value; | |
554 | ||
555 | if (!visit_type_size(v, name, &value, errp)) { | |
556 | return; | |
557 | } | |
558 | ||
559 | if (value > UINT32_MAX) { | |
560 | error_setg(errp, | |
561 | "Property %s.%s doesn't take value %" PRIu64 | |
562 | " (maximum: %u)", | |
563 | object_get_typename(obj), name, value, UINT32_MAX); | |
564 | return; | |
565 | } | |
566 | ||
567 | *ptr = value; | |
568 | } | |
569 | ||
570 | const PropertyInfo qdev_prop_size32 = { | |
571 | .type = "size", | |
572 | .get = qdev_propinfo_get_size32, | |
573 | .set = set_size32, | |
574 | .set_default_value = qdev_propinfo_set_default_value_uint, | |
575 | }; | |
576 | ||
577 | /* --- support for array properties --- */ | |
578 | ||
579 | typedef struct ArrayElementList ArrayElementList; | |
580 | ||
581 | struct ArrayElementList { | |
582 | ArrayElementList *next; | |
583 | void *value; | |
584 | }; | |
585 | ||
586 | /* | |
587 | * Given an array property @parent_prop in @obj, return a Property for a | |
588 | * specific element of the array. Arrays are backed by an uint32_t length field | |
589 | * and an element array. @elem points at an element in this element array. | |
590 | */ | |
591 | static Property array_elem_prop(Object *obj, const Property *parent_prop, | |
592 | const char *name, char *elem) | |
593 | { | |
594 | return (Property) { | |
595 | .info = parent_prop->arrayinfo, | |
596 | .name = name, | |
597 | /* | |
598 | * This ugly piece of pointer arithmetic sets up the offset so | |
599 | * that when the underlying release hook calls qdev_get_prop_ptr | |
600 | * they get the right answer despite the array element not actually | |
601 | * being inside the device struct. | |
602 | */ | |
603 | .offset = (uintptr_t)elem - (uintptr_t)obj, | |
604 | }; | |
605 | } | |
606 | ||
607 | /* | |
608 | * Object property release callback for array properties: We call the | |
609 | * underlying element's property release hook for each element. | |
610 | * | |
611 | * Note that it is the responsibility of the individual device's deinit | |
612 | * to free the array proper. | |
613 | */ | |
614 | static void release_prop_array(Object *obj, const char *name, void *opaque) | |
615 | { | |
616 | const Property *prop = opaque; | |
617 | uint32_t *alenptr = object_field_prop_ptr(obj, prop); | |
618 | void **arrayptr = (void *)obj + prop->arrayoffset; | |
619 | char *elem = *arrayptr; | |
620 | int i; | |
621 | ||
622 | if (!prop->arrayinfo->release) { | |
623 | return; | |
624 | } | |
625 | ||
626 | for (i = 0; i < *alenptr; i++) { | |
627 | Property elem_prop = array_elem_prop(obj, prop, name, elem); | |
628 | prop->arrayinfo->release(obj, NULL, &elem_prop); | |
629 | elem += prop->arrayfieldsize; | |
630 | } | |
631 | } | |
632 | ||
633 | /* | |
634 | * Setter for an array property. This sets both the array length (which | |
635 | * is technically the property field in the object) and the array itself | |
636 | * (a pointer to which is stored in the additional field described by | |
637 | * prop->arrayoffset). | |
638 | */ | |
639 | static void set_prop_array(Object *obj, Visitor *v, const char *name, | |
640 | void *opaque, Error **errp) | |
641 | { | |
642 | ERRP_GUARD(); | |
643 | const Property *prop = opaque; | |
644 | uint32_t *alenptr = object_field_prop_ptr(obj, prop); | |
645 | void **arrayptr = (void *)obj + prop->arrayoffset; | |
646 | ArrayElementList *list, *elem, *next; | |
647 | const size_t size = sizeof(*list); | |
648 | char *elemptr; | |
649 | bool ok = true; | |
650 | ||
651 | if (*alenptr) { | |
652 | error_setg(errp, "array size property %s may not be set more than once", | |
653 | name); | |
654 | return; | |
655 | } | |
656 | ||
657 | if (!visit_start_list(v, name, (GenericList **) &list, size, errp)) { | |
658 | return; | |
659 | } | |
660 | ||
661 | /* Read the whole input into a temporary list */ | |
662 | elem = list; | |
663 | while (elem) { | |
664 | Property elem_prop; | |
665 | ||
666 | elem->value = g_malloc0(prop->arrayfieldsize); | |
667 | elem_prop = array_elem_prop(obj, prop, name, elem->value); | |
668 | prop->arrayinfo->set(obj, v, NULL, &elem_prop, errp); | |
669 | if (*errp) { | |
670 | ok = false; | |
671 | goto out_obj; | |
672 | } | |
673 | if (*alenptr == INT_MAX) { | |
674 | error_setg(errp, "array is too big"); | |
675 | return; | |
676 | } | |
677 | (*alenptr)++; | |
678 | elem = (ArrayElementList *) visit_next_list(v, (GenericList*) elem, | |
679 | size); | |
680 | } | |
681 | ||
682 | ok = visit_check_list(v, errp); | |
683 | out_obj: | |
684 | visit_end_list(v, (void**) &list); | |
685 | ||
686 | if (!ok) { | |
687 | for (elem = list; elem; elem = next) { | |
688 | Property elem_prop = array_elem_prop(obj, prop, name, | |
689 | elem->value); | |
690 | if (prop->arrayinfo->release) { | |
691 | prop->arrayinfo->release(obj, NULL, &elem_prop); | |
692 | } | |
693 | next = elem->next; | |
694 | g_free(elem->value); | |
695 | g_free(elem); | |
696 | } | |
697 | return; | |
698 | } | |
699 | ||
700 | /* | |
701 | * Now that we know how big the array has to be, move the data over to a | |
702 | * linear array and free the temporary list. | |
703 | */ | |
704 | *arrayptr = g_malloc_n(*alenptr, prop->arrayfieldsize); | |
705 | elemptr = *arrayptr; | |
706 | for (elem = list; elem; elem = next) { | |
707 | memcpy(elemptr, elem->value, prop->arrayfieldsize); | |
708 | elemptr += prop->arrayfieldsize; | |
709 | next = elem->next; | |
710 | g_free(elem->value); | |
711 | g_free(elem); | |
712 | } | |
713 | } | |
714 | ||
715 | static void get_prop_array(Object *obj, Visitor *v, const char *name, | |
716 | void *opaque, Error **errp) | |
717 | { | |
718 | ERRP_GUARD(); | |
719 | const Property *prop = opaque; | |
720 | uint32_t *alenptr = object_field_prop_ptr(obj, prop); | |
721 | void **arrayptr = (void *)obj + prop->arrayoffset; | |
722 | char *elemptr = *arrayptr; | |
723 | ArrayElementList *list = NULL, *elem; | |
724 | ArrayElementList **tail = &list; | |
725 | const size_t size = sizeof(*list); | |
726 | int i; | |
727 | bool ok; | |
728 | ||
729 | /* At least the string output visitor needs a real list */ | |
730 | for (i = 0; i < *alenptr; i++) { | |
731 | elem = g_new0(ArrayElementList, 1); | |
732 | elem->value = elemptr; | |
733 | elemptr += prop->arrayfieldsize; | |
734 | ||
735 | *tail = elem; | |
736 | tail = &elem->next; | |
737 | } | |
738 | ||
739 | if (!visit_start_list(v, name, (GenericList **) &list, size, errp)) { | |
740 | return; | |
741 | } | |
742 | ||
743 | elem = list; | |
744 | while (elem) { | |
745 | Property elem_prop = array_elem_prop(obj, prop, name, elem->value); | |
746 | prop->arrayinfo->get(obj, v, NULL, &elem_prop, errp); | |
747 | if (*errp) { | |
748 | goto out_obj; | |
749 | } | |
750 | elem = (ArrayElementList *) visit_next_list(v, (GenericList*) elem, | |
751 | size); | |
752 | } | |
753 | ||
754 | /* visit_check_list() can only fail for input visitors */ | |
755 | ok = visit_check_list(v, errp); | |
756 | assert(ok); | |
757 | ||
758 | out_obj: | |
759 | visit_end_list(v, (void**) &list); | |
760 | ||
761 | while (list) { | |
762 | elem = list; | |
763 | list = elem->next; | |
764 | g_free(elem); | |
765 | } | |
766 | } | |
767 | ||
768 | static void default_prop_array(ObjectProperty *op, const Property *prop) | |
769 | { | |
770 | object_property_set_default_list(op); | |
771 | } | |
772 | ||
773 | const PropertyInfo qdev_prop_array = { | |
774 | .type = "list", | |
775 | .get = get_prop_array, | |
776 | .set = set_prop_array, | |
777 | .release = release_prop_array, | |
778 | .set_default_value = default_prop_array, | |
779 | }; | |
780 | ||
781 | /* --- public helpers --- */ | |
782 | ||
783 | static const Property *qdev_prop_walk(DeviceClass *cls, const char *name) | |
784 | { | |
785 | for (int i = 0, n = cls->props_count_; i < n; ++i) { | |
786 | const Property *prop = &cls->props_[i]; | |
787 | if (strcmp(prop->name, name) == 0) { | |
788 | return prop; | |
789 | } | |
790 | } | |
791 | return NULL; | |
792 | } | |
793 | ||
794 | static const Property *qdev_prop_find(DeviceState *dev, const char *name) | |
795 | { | |
796 | ObjectClass *class; | |
797 | const Property *prop; | |
798 | ||
799 | /* device properties */ | |
800 | class = object_get_class(OBJECT(dev)); | |
801 | do { | |
802 | prop = qdev_prop_walk(DEVICE_CLASS(class), name); | |
803 | if (prop) { | |
804 | return prop; | |
805 | } | |
806 | class = object_class_get_parent(class); | |
807 | } while (class != object_class_by_name(TYPE_DEVICE)); | |
808 | ||
809 | return NULL; | |
810 | } | |
811 | ||
812 | void error_set_from_qdev_prop_error(Error **errp, int ret, Object *obj, | |
813 | const char *name, const char *value) | |
814 | { | |
815 | switch (ret) { | |
816 | case -EEXIST: | |
817 | error_setg(errp, "Property '%s.%s' can't take value '%s', it's in use", | |
818 | object_get_typename(obj), name, value); | |
819 | break; | |
820 | default: | |
821 | case -EINVAL: | |
822 | error_setg(errp, "Property '%s.%s' doesn't take value '%s'", | |
823 | object_get_typename(obj), name, value); | |
824 | break; | |
825 | case -ENOENT: | |
826 | error_setg(errp, "Property '%s.%s' can't find value '%s'", | |
827 | object_get_typename(obj), name, value); | |
828 | break; | |
829 | case 0: | |
830 | break; | |
831 | } | |
832 | } | |
833 | ||
834 | void qdev_prop_set_bit(DeviceState *dev, const char *name, bool value) | |
835 | { | |
836 | object_property_set_bool(OBJECT(dev), name, value, &error_abort); | |
837 | } | |
838 | ||
839 | void qdev_prop_set_uint8(DeviceState *dev, const char *name, uint8_t value) | |
840 | { | |
841 | object_property_set_int(OBJECT(dev), name, value, &error_abort); | |
842 | } | |
843 | ||
844 | void qdev_prop_set_uint16(DeviceState *dev, const char *name, uint16_t value) | |
845 | { | |
846 | object_property_set_int(OBJECT(dev), name, value, &error_abort); | |
847 | } | |
848 | ||
849 | void qdev_prop_set_uint32(DeviceState *dev, const char *name, uint32_t value) | |
850 | { | |
851 | object_property_set_int(OBJECT(dev), name, value, &error_abort); | |
852 | } | |
853 | ||
854 | void qdev_prop_set_int32(DeviceState *dev, const char *name, int32_t value) | |
855 | { | |
856 | object_property_set_int(OBJECT(dev), name, value, &error_abort); | |
857 | } | |
858 | ||
859 | void qdev_prop_set_uint64(DeviceState *dev, const char *name, uint64_t value) | |
860 | { | |
861 | object_property_set_int(OBJECT(dev), name, value, &error_abort); | |
862 | } | |
863 | ||
864 | void qdev_prop_set_string(DeviceState *dev, const char *name, const char *value) | |
865 | { | |
866 | object_property_set_str(OBJECT(dev), name, value, &error_abort); | |
867 | } | |
868 | ||
869 | void qdev_prop_set_enum(DeviceState *dev, const char *name, int value) | |
870 | { | |
871 | const Property *prop; | |
872 | ||
873 | prop = qdev_prop_find(dev, name); | |
874 | object_property_set_str(OBJECT(dev), name, | |
875 | qapi_enum_lookup(prop->info->enum_table, value), | |
876 | &error_abort); | |
877 | } | |
878 | ||
879 | void qdev_prop_set_array(DeviceState *dev, const char *name, QList *values) | |
880 | { | |
881 | object_property_set_qobject(OBJECT(dev), name, QOBJECT(values), | |
882 | &error_abort); | |
883 | qobject_unref(values); | |
884 | } | |
885 | ||
886 | static GPtrArray *global_props(void) | |
887 | { | |
888 | static GPtrArray *gp; | |
889 | ||
890 | if (!gp) { | |
891 | gp = g_ptr_array_new(); | |
892 | } | |
893 | ||
894 | return gp; | |
895 | } | |
896 | ||
897 | void qdev_prop_register_global(GlobalProperty *prop) | |
898 | { | |
899 | g_ptr_array_add(global_props(), prop); | |
900 | } | |
901 | ||
902 | const GlobalProperty *qdev_find_global_prop(Object *obj, | |
903 | const char *name) | |
904 | { | |
905 | GPtrArray *props = global_props(); | |
906 | const GlobalProperty *p; | |
907 | int i; | |
908 | ||
909 | for (i = 0; i < props->len; i++) { | |
910 | p = g_ptr_array_index(props, i); | |
911 | if (object_dynamic_cast(obj, p->driver) | |
912 | && !strcmp(p->property, name)) { | |
913 | return p; | |
914 | } | |
915 | } | |
916 | return NULL; | |
917 | } | |
918 | ||
919 | int qdev_prop_check_globals(void) | |
920 | { | |
921 | int i, ret = 0; | |
922 | ||
923 | for (i = 0; i < global_props()->len; i++) { | |
924 | GlobalProperty *prop; | |
925 | ObjectClass *oc; | |
926 | DeviceClass *dc; | |
927 | ||
928 | prop = g_ptr_array_index(global_props(), i); | |
929 | if (prop->used) { | |
930 | continue; | |
931 | } | |
932 | oc = object_class_by_name(prop->driver); | |
933 | oc = object_class_dynamic_cast(oc, TYPE_DEVICE); | |
934 | if (!oc) { | |
935 | warn_report("global %s.%s has invalid class name", | |
936 | prop->driver, prop->property); | |
937 | ret = 1; | |
938 | continue; | |
939 | } | |
940 | dc = DEVICE_CLASS(oc); | |
941 | if (!dc->hotpluggable && !prop->used) { | |
942 | warn_report("global %s.%s=%s not used", | |
943 | prop->driver, prop->property, prop->value); | |
944 | ret = 1; | |
945 | continue; | |
946 | } | |
947 | } | |
948 | return ret; | |
949 | } | |
950 | ||
951 | void qdev_prop_set_globals(DeviceState *dev) | |
952 | { | |
953 | object_apply_global_props(OBJECT(dev), global_props(), | |
954 | dev->hotplugged ? NULL : &error_fatal); | |
955 | } | |
956 | ||
957 | /* --- 64bit unsigned int 'size' type --- */ | |
958 | ||
959 | static void get_size(Object *obj, Visitor *v, const char *name, void *opaque, | |
960 | Error **errp) | |
961 | { | |
962 | const Property *prop = opaque; | |
963 | uint64_t *ptr = object_field_prop_ptr(obj, prop); | |
964 | ||
965 | visit_type_size(v, name, ptr, errp); | |
966 | } | |
967 | ||
968 | static void set_size(Object *obj, Visitor *v, const char *name, void *opaque, | |
969 | Error **errp) | |
970 | { | |
971 | const Property *prop = opaque; | |
972 | uint64_t *ptr = object_field_prop_ptr(obj, prop); | |
973 | ||
974 | visit_type_size(v, name, ptr, errp); | |
975 | } | |
976 | ||
977 | const PropertyInfo qdev_prop_size = { | |
978 | .type = "size", | |
979 | .get = get_size, | |
980 | .set = set_size, | |
981 | .set_default_value = qdev_propinfo_set_default_value_uint, | |
982 | }; | |
983 | ||
984 | /* --- object link property --- */ | |
985 | ||
986 | static ObjectProperty *create_link_property(ObjectClass *oc, const char *name, | |
987 | const Property *prop) | |
988 | { | |
989 | return object_class_property_add_link(oc, name, prop->link_type, | |
990 | prop->offset, | |
991 | qdev_prop_allow_set_link_before_realize, | |
992 | OBJ_PROP_LINK_STRONG); | |
993 | } | |
994 | ||
995 | const PropertyInfo qdev_prop_link = { | |
996 | .type = "link", | |
997 | .create = create_link_property, | |
998 | }; | |
999 | ||
1000 | void qdev_property_add_static(DeviceState *dev, const Property *prop) | |
1001 | { | |
1002 | Object *obj = OBJECT(dev); | |
1003 | ObjectProperty *op; | |
1004 | ||
1005 | assert(!prop->info->create); | |
1006 | ||
1007 | op = object_property_add(obj, prop->name, prop->info->type, | |
1008 | field_prop_getter(prop->info), | |
1009 | field_prop_setter(prop->info), | |
1010 | prop->info->release, | |
1011 | (Property *)prop); | |
1012 | ||
1013 | object_property_set_description(obj, prop->name, | |
1014 | prop->info->description); | |
1015 | ||
1016 | if (prop->set_default) { | |
1017 | prop->info->set_default_value(op, prop); | |
1018 | if (op->init) { | |
1019 | op->init(obj, op); | |
1020 | } | |
1021 | } | |
1022 | } | |
1023 | ||
1024 | static void qdev_class_add_property(DeviceClass *klass, const char *name, | |
1025 | const Property *prop) | |
1026 | { | |
1027 | ObjectClass *oc = OBJECT_CLASS(klass); | |
1028 | ObjectProperty *op; | |
1029 | ||
1030 | if (prop->info->create) { | |
1031 | op = prop->info->create(oc, name, prop); | |
1032 | } else { | |
1033 | op = object_class_property_add(oc, | |
1034 | name, prop->info->type, | |
1035 | field_prop_getter(prop->info), | |
1036 | field_prop_setter(prop->info), | |
1037 | prop->info->release, | |
1038 | (Property *)prop); | |
1039 | } | |
1040 | if (prop->set_default) { | |
1041 | prop->info->set_default_value(op, prop); | |
1042 | } | |
1043 | object_class_property_set_description(oc, name, prop->info->description); | |
1044 | } | |
1045 | ||
1046 | /** | |
1047 | * Legacy property handling | |
1048 | */ | |
1049 | ||
1050 | static void qdev_get_legacy_property(Object *obj, Visitor *v, | |
1051 | const char *name, void *opaque, | |
1052 | Error **errp) | |
1053 | { | |
1054 | const Property *prop = opaque; | |
1055 | ||
1056 | char buffer[1024]; | |
1057 | char *ptr = buffer; | |
1058 | ||
1059 | prop->info->print(obj, prop, buffer, sizeof(buffer)); | |
1060 | visit_type_str(v, name, &ptr, errp); | |
1061 | } | |
1062 | ||
1063 | /** | |
1064 | * qdev_class_add_legacy_property: | |
1065 | * @dev: Device to add the property to. | |
1066 | * @prop: The qdev property definition. | |
1067 | * | |
1068 | * Add a legacy QOM property to @dev for qdev property @prop. | |
1069 | * | |
1070 | * Legacy properties are string versions of QOM properties. The format of | |
1071 | * the string depends on the property type. Legacy properties are only | |
1072 | * needed for "info qtree". | |
1073 | * | |
1074 | * Do not use this in new code! QOM Properties added through this interface | |
1075 | * will be given names in the "legacy" namespace. | |
1076 | */ | |
1077 | static void qdev_class_add_legacy_property(DeviceClass *dc, const Property *prop) | |
1078 | { | |
1079 | g_autofree char *name = NULL; | |
1080 | ||
1081 | /* Register pointer properties as legacy properties */ | |
1082 | if (!prop->info->print && prop->info->get) { | |
1083 | return; | |
1084 | } | |
1085 | ||
1086 | name = g_strdup_printf("legacy-%s", prop->name); | |
1087 | object_class_property_add(OBJECT_CLASS(dc), name, "str", | |
1088 | prop->info->print ? qdev_get_legacy_property : prop->info->get, | |
1089 | NULL, NULL, (Property *)prop); | |
1090 | } | |
1091 | ||
1092 | void device_class_set_props_n(DeviceClass *dc, const Property *props, size_t n) | |
1093 | { | |
1094 | /* We used a hole in DeviceClass because that's still a lot. */ | |
1095 | assert(n <= UINT16_MAX); | |
1096 | assert(n != 0); | |
1097 | ||
1098 | dc->props_ = props; | |
1099 | dc->props_count_ = n; | |
1100 | ||
1101 | for (size_t i = 0; i < n; ++i) { | |
1102 | const Property *prop = &props[i]; | |
1103 | assert(prop->name); | |
1104 | qdev_class_add_legacy_property(dc, prop); | |
1105 | qdev_class_add_property(dc, prop->name, prop); | |
1106 | } | |
1107 | } | |
1108 | ||
1109 | void qdev_alias_all_properties(DeviceState *target, Object *source) | |
1110 | { | |
1111 | ObjectClass *class; | |
1112 | ObjectPropertyIterator iter; | |
1113 | ObjectProperty *prop; | |
1114 | ||
1115 | class = object_get_class(OBJECT(target)); | |
1116 | ||
1117 | object_class_property_iter_init(&iter, class); | |
1118 | while ((prop = object_property_iter_next(&iter))) { | |
1119 | if (object_property_find(source, prop->name)) { | |
1120 | continue; /* skip duplicate properties */ | |
1121 | } | |
1122 | ||
1123 | object_property_add_alias(source, prop->name, | |
1124 | OBJECT(target), prop->name); | |
1125 | } | |
1126 | } |