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
28static 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
41static 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
73static 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
124fail:
125 if (blk_created) {
126 /* If we need to keep a reference, blk_attach_dev() took it */
127 blk_unref(blk);
128 }
129}
130
131static 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
137static 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
143static 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
159static 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
173static 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
179static 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
185static 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
191const 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
199const 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
209static 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
221static 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
259static 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
268const 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 --- */
277static 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
289static 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
344out:
345 error_set_from_qdev_prop_error(errp, err, dev, prop, str);
346 g_free(str);
347}
348
349const 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 --- */
358static 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
370static 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
400out:
401 error_set_from_qdev_prop_error(errp, err, dev, prop, str);
402 g_free(str);
403}
404
405const 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
413void 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
431void 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
439void 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
447void 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