1 /*
2 * qdev property parsing
3 * (parts specific for qemu-system-*)
4 *
5 * This file is based on code from hw/qdev-properties.c from
6 * commit 074a86fccd185616469dfcdc0e157f438aebba18,
7 * Copyright (c) Gerd Hoffmann <kraxel@redhat.com> and other contributors.
8 *
9 * This work is licensed under the terms of the GNU GPL, version 2 or later.
10 * See the COPYING file in the top-level directory.
11 */
12
13 #include "qemu/osdep.h"
14 #include "audio/audio.h"
15 #include "net/net.h"
16 #include "hw/qdev-properties.h"
17 #include "qapi/error.h"
18 #include "qapi/qmp/qerror.h"
19 #include "sysemu/block-backend.h"
20 #include "sysemu/blockdev.h"
21 #include "hw/block/block.h"
22 #include "net/hub.h"
23 #include "qapi/visitor.h"
24 #include "chardev/char-fe.h"
25 #include "sysemu/iothread.h"
26 #include "sysemu/tpm_backend.h"
27
get_pointer(Object * obj,Visitor * v,Property * prop,char * (* print)(void * ptr),const char * name,Error ** errp)28 static void get_pointer(Object *obj, Visitor *v, Property *prop,
29 char *(*print)(void *ptr),
30 const char *name, Error **errp)
31 {
32 DeviceState *dev = DEVICE(obj);
33 void **ptr = qdev_get_prop_ptr(dev, prop);
34 char *p;
35
36 p = *ptr ? print(*ptr) : g_strdup("");
37 visit_type_str(v, name, &p, errp);
38 g_free(p);
39 }
40
set_pointer(Object * obj,Visitor * v,Property * prop,void (* parse)(DeviceState * dev,const char * str,void ** ptr,const char * propname,Error ** errp),const char * name,Error ** errp)41 static void set_pointer(Object *obj, Visitor *v, Property *prop,
42 void (*parse)(DeviceState *dev, const char *str,
43 void **ptr, const char *propname,
44 Error **errp),
45 const char *name, Error **errp)
46 {
47 DeviceState *dev = DEVICE(obj);
48 Error *local_err = NULL;
49 void **ptr = qdev_get_prop_ptr(dev, prop);
50 char *str;
51
52 if (dev->realized) {
53 qdev_prop_set_after_realize(dev, name, errp);
54 return;
55 }
56
57 visit_type_str(v, name, &str, &local_err);
58 if (local_err) {
59 error_propagate(errp, local_err);
60 return;
61 }
62 if (!*str) {
63 g_free(str);
64 *ptr = NULL;
65 return;
66 }
67 parse(dev, str, ptr, prop->name, errp);
68 g_free(str);
69 }
70
71 /* --- drive --- */
72
do_parse_drive(DeviceState * dev,const char * str,void ** ptr,const char * propname,bool iothread,Error ** errp)73 static void do_parse_drive(DeviceState *dev, const char *str, void **ptr,
74 const char *propname, bool iothread, Error **errp)
75 {
76 BlockBackend *blk;
77 bool blk_created = false;
78 int ret;
79
80 blk = blk_by_name(str);
81 if (!blk) {
82 BlockDriverState *bs = bdrv_lookup_bs(NULL, str, NULL);
83 if (bs) {
84 /*
85 * If the device supports iothreads, it will make sure to move the
86 * block node to the right AioContext if necessary (or fail if this
87 * isn't possible because of other users). Devices that are not
88 * aware of iothreads require their BlockBackends to be in the main
89 * AioContext.
90 */
91 AioContext *ctx = iothread ? bdrv_get_aio_context(bs) :
92 qemu_get_aio_context();
93 blk = blk_new(ctx, 0, BLK_PERM_ALL);
94 blk_created = true;
95
96 ret = blk_insert_bs(blk, bs, errp);
97 if (ret < 0) {
98 goto fail;
99 }
100 }
101 }
102 if (!blk) {
103 error_setg(errp, "Property '%s.%s' can't find value '%s'",
104 object_get_typename(OBJECT(dev)), propname, str);
105 goto fail;
106 }
107 if (blk_attach_dev(blk, dev) < 0) {
108 DriveInfo *dinfo = blk_legacy_dinfo(blk);
109
110 if (dinfo && dinfo->type != IF_NONE) {
111 error_setg(errp, "Drive '%s' is already in use because "
112 "it has been automatically connected to another "
113 "device (did you need 'if=none' in the drive options?)",
114 str);
115 } else {
116 error_setg(errp, "Drive '%s' is already in use by another device",
117 str);
118 }
119 goto fail;
120 }
121
122 *ptr = blk;
123
124 fail:
125 if (blk_created) {
126 /* If we need to keep a reference, blk_attach_dev() took it */
127 blk_unref(blk);
128 }
129 }
130
parse_drive(DeviceState * dev,const char * str,void ** ptr,const char * propname,Error ** errp)131 static void parse_drive(DeviceState *dev, const char *str, void **ptr,
132 const char *propname, Error **errp)
133 {
134 do_parse_drive(dev, str, ptr, propname, false, errp);
135 }
136
parse_drive_iothread(DeviceState * dev,const char * str,void ** ptr,const char * propname,Error ** errp)137 static void parse_drive_iothread(DeviceState *dev, const char *str, void **ptr,
138 const char *propname, Error **errp)
139 {
140 do_parse_drive(dev, str, ptr, propname, true, errp);
141 }
142
release_drive(Object * obj,const char * name,void * opaque)143 static void release_drive(Object *obj, const char *name, void *opaque)
144 {
145 DeviceState *dev = DEVICE(obj);
146 Property *prop = opaque;
147 BlockBackend **ptr = qdev_get_prop_ptr(dev, prop);
148
149 if (*ptr) {
150 AioContext *ctx = blk_get_aio_context(*ptr);
151
152 aio_context_acquire(ctx);
153 blockdev_auto_del(*ptr);
154 blk_detach_dev(*ptr, dev);
155 aio_context_release(ctx);
156 }
157 }
158
print_drive(void * ptr)159 static char *print_drive(void *ptr)
160 {
161 const char *name;
162
163 name = blk_name(ptr);
164 if (!*name) {
165 BlockDriverState *bs = blk_bs(ptr);
166 if (bs) {
167 name = bdrv_get_node_name(bs);
168 }
169 }
170 return g_strdup(name);
171 }
172
get_drive(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)173 static void get_drive(Object *obj, Visitor *v, const char *name, void *opaque,
174 Error **errp)
175 {
176 get_pointer(obj, v, opaque, print_drive, name, errp);
177 }
178
set_drive(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)179 static void set_drive(Object *obj, Visitor *v, const char *name, void *opaque,
180 Error **errp)
181 {
182 set_pointer(obj, v, opaque, parse_drive, name, errp);
183 }
184
set_drive_iothread(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)185 static void set_drive_iothread(Object *obj, Visitor *v, const char *name,
186 void *opaque, Error **errp)
187 {
188 set_pointer(obj, v, opaque, parse_drive_iothread, name, errp);
189 }
190
191 const PropertyInfo qdev_prop_drive = {
192 .name = "str",
193 .description = "Node name or ID of a block device to use as a backend",
194 .get = get_drive,
195 .set = set_drive,
196 .release = release_drive,
197 };
198
199 const PropertyInfo qdev_prop_drive_iothread = {
200 .name = "str",
201 .description = "Node name or ID of a block device to use as a backend",
202 .get = get_drive,
203 .set = set_drive_iothread,
204 .release = release_drive,
205 };
206
207 /* --- character device --- */
208
get_chr(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)209 static void get_chr(Object *obj, Visitor *v, const char *name, void *opaque,
210 Error **errp)
211 {
212 DeviceState *dev = DEVICE(obj);
213 CharBackend *be = qdev_get_prop_ptr(dev, opaque);
214 char *p;
215
216 p = g_strdup(be->chr && be->chr->label ? be->chr->label : "");
217 visit_type_str(v, name, &p, errp);
218 g_free(p);
219 }
220
set_chr(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)221 static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque,
222 Error **errp)
223 {
224 DeviceState *dev = DEVICE(obj);
225 Error *local_err = NULL;
226 Property *prop = opaque;
227 CharBackend *be = qdev_get_prop_ptr(dev, prop);
228 Chardev *s;
229 char *str;
230
231 if (dev->realized) {
232 qdev_prop_set_after_realize(dev, name, errp);
233 return;
234 }
235
236 visit_type_str(v, name, &str, &local_err);
237 if (local_err) {
238 error_propagate(errp, local_err);
239 return;
240 }
241
242 if (!*str) {
243 g_free(str);
244 be->chr = NULL;
245 return;
246 }
247
248 s = qemu_chr_find(str);
249 if (s == NULL) {
250 error_setg(errp, "Property '%s.%s' can't find value '%s'",
251 object_get_typename(obj), prop->name, str);
252 } else if (!qemu_chr_fe_init(be, s, errp)) {
253 error_prepend(errp, "Property '%s.%s' can't take value '%s': ",
254 object_get_typename(obj), prop->name, str);
255 }
256 g_free(str);
257 }
258
release_chr(Object * obj,const char * name,void * opaque)259 static void release_chr(Object *obj, const char *name, void *opaque)
260 {
261 DeviceState *dev = DEVICE(obj);
262 Property *prop = opaque;
263 CharBackend *be = qdev_get_prop_ptr(dev, prop);
264
265 qemu_chr_fe_deinit(be, false);
266 }
267
268 const PropertyInfo qdev_prop_chr = {
269 .name = "str",
270 .description = "ID of a chardev to use as a backend",
271 .get = get_chr,
272 .set = set_chr,
273 .release = release_chr,
274 };
275
276 /* --- netdev device --- */
get_netdev(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)277 static void get_netdev(Object *obj, Visitor *v, const char *name,
278 void *opaque, Error **errp)
279 {
280 DeviceState *dev = DEVICE(obj);
281 Property *prop = opaque;
282 NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop);
283 char *p = g_strdup(peers_ptr->ncs[0] ? peers_ptr->ncs[0]->name : "");
284
285 visit_type_str(v, name, &p, errp);
286 g_free(p);
287 }
288
set_netdev(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)289 static void set_netdev(Object *obj, Visitor *v, const char *name,
290 void *opaque, Error **errp)
291 {
292 DeviceState *dev = DEVICE(obj);
293 Property *prop = opaque;
294 NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop);
295 NetClientState **ncs = peers_ptr->ncs;
296 NetClientState *peers[MAX_QUEUE_NUM];
297 Error *local_err = NULL;
298 int queues, err = 0, i = 0;
299 char *str;
300
301 if (dev->realized) {
302 qdev_prop_set_after_realize(dev, name, errp);
303 return;
304 }
305
306 visit_type_str(v, name, &str, &local_err);
307 if (local_err) {
308 error_propagate(errp, local_err);
309 return;
310 }
311
312 queues = qemu_find_net_clients_except(str, peers,
313 NET_CLIENT_DRIVER_NIC,
314 MAX_QUEUE_NUM);
315 if (queues == 0) {
316 err = -ENOENT;
317 goto out;
318 }
319
320 if (queues > MAX_QUEUE_NUM) {
321 error_setg(errp, "queues of backend '%s'(%d) exceeds QEMU limitation(%d)",
322 str, queues, MAX_QUEUE_NUM);
323 goto out;
324 }
325
326 for (i = 0; i < queues; i++) {
327
328 if (peers[i]->peer) {
329 err = -EEXIST;
330 goto out;
331 }
332
333 if (ncs[i]) {
334 err = -EINVAL;
335 goto out;
336 }
337
338 ncs[i] = peers[i];
339 ncs[i]->queue_index = i;
340 }
341
342 peers_ptr->queues = queues;
343
344 out:
345 error_set_from_qdev_prop_error(errp, err, dev, prop, str);
346 g_free(str);
347 }
348
349 const PropertyInfo qdev_prop_netdev = {
350 .name = "str",
351 .description = "ID of a netdev to use as a backend",
352 .get = get_netdev,
353 .set = set_netdev,
354 };
355
356
357 /* --- audiodev --- */
get_audiodev(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)358 static void get_audiodev(Object *obj, Visitor *v, const char* name,
359 void *opaque, Error **errp)
360 {
361 DeviceState *dev = DEVICE(obj);
362 Property *prop = opaque;
363 QEMUSoundCard *card = qdev_get_prop_ptr(dev, prop);
364 char *p = g_strdup(audio_get_id(card));
365
366 visit_type_str(v, name, &p, errp);
367 g_free(p);
368 }
369
set_audiodev(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)370 static void set_audiodev(Object *obj, Visitor *v, const char* name,
371 void *opaque, Error **errp)
372 {
373 DeviceState *dev = DEVICE(obj);
374 Property *prop = opaque;
375 QEMUSoundCard *card = qdev_get_prop_ptr(dev, prop);
376 AudioState *state;
377 Error *local_err = NULL;
378 int err = 0;
379 char *str;
380
381 if (dev->realized) {
382 qdev_prop_set_after_realize(dev, name, errp);
383 return;
384 }
385
386 visit_type_str(v, name, &str, &local_err);
387 if (local_err) {
388 error_propagate(errp, local_err);
389 return;
390 }
391
392 state = audio_state_by_name(str);
393
394 if (!state) {
395 err = -ENOENT;
396 goto out;
397 }
398 card->state = state;
399
400 out:
401 error_set_from_qdev_prop_error(errp, err, dev, prop, str);
402 g_free(str);
403 }
404
405 const PropertyInfo qdev_prop_audiodev = {
406 .name = "str",
407 .description = "ID of an audiodev to use as a backend",
408 /* release done on shutdown */
409 .get = get_audiodev,
410 .set = set_audiodev,
411 };
412
qdev_prop_set_drive(DeviceState * dev,const char * name,BlockBackend * value,Error ** errp)413 void qdev_prop_set_drive(DeviceState *dev, const char *name,
414 BlockBackend *value, Error **errp)
415 {
416 const char *ref = "";
417
418 if (value) {
419 ref = blk_name(value);
420 if (!*ref) {
421 const BlockDriverState *bs = blk_bs(value);
422 if (bs) {
423 ref = bdrv_get_node_name(bs);
424 }
425 }
426 }
427
428 object_property_set_str(OBJECT(dev), ref, name, errp);
429 }
430
qdev_prop_set_chr(DeviceState * dev,const char * name,Chardev * value)431 void qdev_prop_set_chr(DeviceState *dev, const char *name,
432 Chardev *value)
433 {
434 assert(!value || value->label);
435 object_property_set_str(OBJECT(dev),
436 value ? value->label : "", name, &error_abort);
437 }
438
qdev_prop_set_netdev(DeviceState * dev,const char * name,NetClientState * value)439 void qdev_prop_set_netdev(DeviceState *dev, const char *name,
440 NetClientState *value)
441 {
442 assert(!value || value->name);
443 object_property_set_str(OBJECT(dev),
444 value ? value->name : "", name, &error_abort);
445 }
446
qdev_set_nic_properties(DeviceState * dev,NICInfo * nd)447 void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd)
448 {
449 qdev_prop_set_macaddr(dev, "mac", nd->macaddr.a);
450 if (nd->netdev) {
451 qdev_prop_set_netdev(dev, "netdev", nd->netdev);
452 }
453 if (nd->nvectors != DEV_NVECTORS_UNSPECIFIED &&
454 object_property_find(OBJECT(dev), "vectors", NULL)) {
455 qdev_prop_set_uint32(dev, "vectors", nd->nvectors);
456 }
457 nd->instantiated = 1;
458 }
459