]> git.proxmox.com Git - mirror_qemu.git/blob - hw/core/qdev-properties-system.c
qdev: add DEFINE_PROP_TPMBE
[mirror_qemu.git] / hw / core / qdev-properties-system.c
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 "net/net.h"
15 #include "hw/qdev.h"
16 #include "qapi/error.h"
17 #include "qapi/qmp/qerror.h"
18 #include "sysemu/block-backend.h"
19 #include "sysemu/blockdev.h"
20 #include "hw/block/block.h"
21 #include "net/hub.h"
22 #include "qapi/visitor.h"
23 #include "chardev/char-fe.h"
24 #include "sysemu/tpm_backend.h"
25 #include "sysemu/iothread.h"
26
27 static void get_pointer(Object *obj, Visitor *v, Property *prop,
28 char *(*print)(void *ptr),
29 const char *name, Error **errp)
30 {
31 DeviceState *dev = DEVICE(obj);
32 void **ptr = qdev_get_prop_ptr(dev, prop);
33 char *p;
34
35 p = *ptr ? print(*ptr) : g_strdup("");
36 visit_type_str(v, name, &p, errp);
37 g_free(p);
38 }
39
40 static void set_pointer(Object *obj, Visitor *v, Property *prop,
41 void (*parse)(DeviceState *dev, const char *str,
42 void **ptr, const char *propname,
43 Error **errp),
44 const char *name, Error **errp)
45 {
46 DeviceState *dev = DEVICE(obj);
47 Error *local_err = NULL;
48 void **ptr = qdev_get_prop_ptr(dev, prop);
49 char *str;
50
51 if (dev->realized) {
52 qdev_prop_set_after_realize(dev, name, errp);
53 return;
54 }
55
56 visit_type_str(v, name, &str, &local_err);
57 if (local_err) {
58 error_propagate(errp, local_err);
59 return;
60 }
61 if (!*str) {
62 g_free(str);
63 *ptr = NULL;
64 return;
65 }
66 parse(dev, str, ptr, prop->name, errp);
67 g_free(str);
68 }
69
70 /* --- drive --- */
71
72 static void parse_drive(DeviceState *dev, const char *str, void **ptr,
73 const char *propname, Error **errp)
74 {
75 BlockBackend *blk;
76 bool blk_created = false;
77 int ret;
78
79 blk = blk_by_name(str);
80 if (!blk) {
81 BlockDriverState *bs = bdrv_lookup_bs(NULL, str, NULL);
82 if (bs) {
83 blk = blk_new(0, BLK_PERM_ALL);
84 blk_created = true;
85
86 ret = blk_insert_bs(blk, bs, errp);
87 if (ret < 0) {
88 goto fail;
89 }
90 }
91 }
92 if (!blk) {
93 error_setg(errp, "Property '%s.%s' can't find value '%s'",
94 object_get_typename(OBJECT(dev)), propname, str);
95 goto fail;
96 }
97 if (blk_attach_dev(blk, dev) < 0) {
98 DriveInfo *dinfo = blk_legacy_dinfo(blk);
99
100 if (dinfo && dinfo->type != IF_NONE) {
101 error_setg(errp, "Drive '%s' is already in use because "
102 "it has been automatically connected to another "
103 "device (did you need 'if=none' in the drive options?)",
104 str);
105 } else {
106 error_setg(errp, "Drive '%s' is already in use by another device",
107 str);
108 }
109 goto fail;
110 }
111
112 *ptr = blk;
113
114 fail:
115 if (blk_created) {
116 /* If we need to keep a reference, blk_attach_dev() took it */
117 blk_unref(blk);
118 }
119 }
120
121 static void release_drive(Object *obj, const char *name, void *opaque)
122 {
123 DeviceState *dev = DEVICE(obj);
124 Property *prop = opaque;
125 BlockBackend **ptr = qdev_get_prop_ptr(dev, prop);
126
127 if (*ptr) {
128 AioContext *ctx = blk_get_aio_context(*ptr);
129
130 aio_context_acquire(ctx);
131 blockdev_auto_del(*ptr);
132 blk_detach_dev(*ptr, dev);
133 aio_context_release(ctx);
134 }
135 }
136
137 static char *print_drive(void *ptr)
138 {
139 const char *name;
140
141 name = blk_name(ptr);
142 if (!*name) {
143 BlockDriverState *bs = blk_bs(ptr);
144 if (bs) {
145 name = bdrv_get_node_name(bs);
146 }
147 }
148 return g_strdup(name);
149 }
150
151 static void get_drive(Object *obj, Visitor *v, const char *name, void *opaque,
152 Error **errp)
153 {
154 get_pointer(obj, v, opaque, print_drive, name, errp);
155 }
156
157 static void set_drive(Object *obj, Visitor *v, const char *name, void *opaque,
158 Error **errp)
159 {
160 set_pointer(obj, v, opaque, parse_drive, name, errp);
161 }
162
163 const PropertyInfo qdev_prop_drive = {
164 .name = "str",
165 .description = "Node name or ID of a block device to use as a backend",
166 .get = get_drive,
167 .set = set_drive,
168 .release = release_drive,
169 };
170
171 /* --- character device --- */
172
173 static void get_chr(Object *obj, Visitor *v, const char *name, void *opaque,
174 Error **errp)
175 {
176 DeviceState *dev = DEVICE(obj);
177 CharBackend *be = qdev_get_prop_ptr(dev, opaque);
178 char *p;
179
180 p = g_strdup(be->chr && be->chr->label ? be->chr->label : "");
181 visit_type_str(v, name, &p, errp);
182 g_free(p);
183 }
184
185 static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque,
186 Error **errp)
187 {
188 DeviceState *dev = DEVICE(obj);
189 Error *local_err = NULL;
190 Property *prop = opaque;
191 CharBackend *be = qdev_get_prop_ptr(dev, prop);
192 Chardev *s;
193 char *str;
194
195 if (dev->realized) {
196 qdev_prop_set_after_realize(dev, name, errp);
197 return;
198 }
199
200 visit_type_str(v, name, &str, &local_err);
201 if (local_err) {
202 error_propagate(errp, local_err);
203 return;
204 }
205
206 if (!*str) {
207 g_free(str);
208 be->chr = NULL;
209 return;
210 }
211
212 s = qemu_chr_find(str);
213 if (s == NULL) {
214 error_setg(errp, "Property '%s.%s' can't find value '%s'",
215 object_get_typename(obj), prop->name, str);
216 } else if (!qemu_chr_fe_init(be, s, errp)) {
217 error_prepend(errp, "Property '%s.%s' can't take value '%s': ",
218 object_get_typename(obj), prop->name, str);
219 }
220 g_free(str);
221 }
222
223 static void release_chr(Object *obj, const char *name, void *opaque)
224 {
225 DeviceState *dev = DEVICE(obj);
226 Property *prop = opaque;
227 CharBackend *be = qdev_get_prop_ptr(dev, prop);
228
229 qemu_chr_fe_deinit(be, false);
230 }
231
232 const PropertyInfo qdev_prop_chr = {
233 .name = "str",
234 .description = "ID of a chardev to use as a backend",
235 .get = get_chr,
236 .set = set_chr,
237 .release = release_chr,
238 };
239
240 /* --- character device --- */
241
242 static void get_tpm(Object *obj, Visitor *v, const char *name, void *opaque,
243 Error **errp)
244 {
245 DeviceState *dev = DEVICE(obj);
246 TPMBackend **be = qdev_get_prop_ptr(dev, opaque);
247 char *p;
248
249 p = g_strdup(*be ? (*be)->id : "");
250 visit_type_str(v, name, &p, errp);
251 g_free(p);
252 }
253
254 static void set_tpm(Object *obj, Visitor *v, const char *name, void *opaque,
255 Error **errp)
256 {
257 DeviceState *dev = DEVICE(obj);
258 Error *local_err = NULL;
259 Property *prop = opaque;
260 TPMBackend *s, **be = qdev_get_prop_ptr(dev, prop);
261 char *str;
262
263 if (dev->realized) {
264 qdev_prop_set_after_realize(dev, name, errp);
265 return;
266 }
267
268 visit_type_str(v, name, &str, &local_err);
269 if (local_err) {
270 error_propagate(errp, local_err);
271 return;
272 }
273
274 s = qemu_find_tpm_be(str);
275 if (s == NULL) {
276 error_setg(errp, "Property '%s.%s' can't find value '%s'",
277 object_get_typename(obj), prop->name, str);
278 } else if (tpm_backend_init(s, TPM_IF(obj), errp) == 0) {
279 *be = s; /* weak reference, avoid cyclic ref */
280 }
281 g_free(str);
282 }
283
284 static void release_tpm(Object *obj, const char *name, void *opaque)
285 {
286 DeviceState *dev = DEVICE(obj);
287 Property *prop = opaque;
288 TPMBackend **be = qdev_get_prop_ptr(dev, prop);
289
290 if (*be) {
291 tpm_backend_reset(*be);
292 }
293 }
294
295 const PropertyInfo qdev_prop_tpm = {
296 .name = "str",
297 .description = "ID of a tpm to use as a backend",
298 .get = get_tpm,
299 .set = set_tpm,
300 .release = release_tpm,
301 };
302
303 /* --- netdev device --- */
304 static void get_netdev(Object *obj, Visitor *v, const char *name,
305 void *opaque, Error **errp)
306 {
307 DeviceState *dev = DEVICE(obj);
308 Property *prop = opaque;
309 NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop);
310 char *p = g_strdup(peers_ptr->ncs[0] ? peers_ptr->ncs[0]->name : "");
311
312 visit_type_str(v, name, &p, errp);
313 g_free(p);
314 }
315
316 static void set_netdev(Object *obj, Visitor *v, const char *name,
317 void *opaque, Error **errp)
318 {
319 DeviceState *dev = DEVICE(obj);
320 Property *prop = opaque;
321 NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop);
322 NetClientState **ncs = peers_ptr->ncs;
323 NetClientState *peers[MAX_QUEUE_NUM];
324 Error *local_err = NULL;
325 int queues, err = 0, i = 0;
326 char *str;
327
328 if (dev->realized) {
329 qdev_prop_set_after_realize(dev, name, errp);
330 return;
331 }
332
333 visit_type_str(v, name, &str, &local_err);
334 if (local_err) {
335 error_propagate(errp, local_err);
336 return;
337 }
338
339 queues = qemu_find_net_clients_except(str, peers,
340 NET_CLIENT_DRIVER_NIC,
341 MAX_QUEUE_NUM);
342 if (queues == 0) {
343 err = -ENOENT;
344 goto out;
345 }
346
347 if (queues > MAX_QUEUE_NUM) {
348 error_setg(errp, "queues of backend '%s'(%d) exceeds QEMU limitation(%d)",
349 str, queues, MAX_QUEUE_NUM);
350 goto out;
351 }
352
353 for (i = 0; i < queues; i++) {
354 if (peers[i] == NULL) {
355 err = -ENOENT;
356 goto out;
357 }
358
359 if (peers[i]->peer) {
360 err = -EEXIST;
361 goto out;
362 }
363
364 if (ncs[i]) {
365 err = -EINVAL;
366 goto out;
367 }
368
369 ncs[i] = peers[i];
370 ncs[i]->queue_index = i;
371 }
372
373 peers_ptr->queues = queues;
374
375 out:
376 error_set_from_qdev_prop_error(errp, err, dev, prop, str);
377 g_free(str);
378 }
379
380 const PropertyInfo qdev_prop_netdev = {
381 .name = "str",
382 .description = "ID of a netdev to use as a backend",
383 .get = get_netdev,
384 .set = set_netdev,
385 };
386
387 /* --- vlan --- */
388
389 static int print_vlan(DeviceState *dev, Property *prop, char *dest, size_t len)
390 {
391 NetClientState **ptr = qdev_get_prop_ptr(dev, prop);
392
393 if (*ptr) {
394 int id;
395 if (!net_hub_id_for_client(*ptr, &id)) {
396 return snprintf(dest, len, "%d", id);
397 }
398 }
399
400 return snprintf(dest, len, "<null>");
401 }
402
403 static void get_vlan(Object *obj, Visitor *v, const char *name, void *opaque,
404 Error **errp)
405 {
406 DeviceState *dev = DEVICE(obj);
407 Property *prop = opaque;
408 NetClientState **ptr = qdev_get_prop_ptr(dev, prop);
409 int32_t id = -1;
410
411 if (*ptr) {
412 int hub_id;
413 if (!net_hub_id_for_client(*ptr, &hub_id)) {
414 id = hub_id;
415 }
416 }
417
418 visit_type_int32(v, name, &id, errp);
419 }
420
421 static void set_vlan(Object *obj, Visitor *v, const char *name, void *opaque,
422 Error **errp)
423 {
424 DeviceState *dev = DEVICE(obj);
425 Property *prop = opaque;
426 NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop);
427 NetClientState **ptr = &peers_ptr->ncs[0];
428 Error *local_err = NULL;
429 int32_t id;
430 NetClientState *hubport;
431
432 if (dev->realized) {
433 qdev_prop_set_after_realize(dev, name, errp);
434 return;
435 }
436
437 visit_type_int32(v, name, &id, &local_err);
438 if (local_err) {
439 error_propagate(errp, local_err);
440 return;
441 }
442 if (id == -1) {
443 *ptr = NULL;
444 return;
445 }
446 if (*ptr) {
447 error_set_from_qdev_prop_error(errp, -EINVAL, dev, prop, name);
448 return;
449 }
450
451 hubport = net_hub_port_find(id);
452 if (!hubport) {
453 error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
454 name, prop->info->name);
455 return;
456 }
457 *ptr = hubport;
458 }
459
460 const PropertyInfo qdev_prop_vlan = {
461 .name = "int32",
462 .description = "Integer VLAN id to connect to",
463 .print = print_vlan,
464 .get = get_vlan,
465 .set = set_vlan,
466 };
467
468 void qdev_prop_set_drive(DeviceState *dev, const char *name,
469 BlockBackend *value, Error **errp)
470 {
471 const char *ref = "";
472
473 if (value) {
474 ref = blk_name(value);
475 if (!*ref) {
476 const BlockDriverState *bs = blk_bs(value);
477 if (bs) {
478 ref = bdrv_get_node_name(bs);
479 }
480 }
481 }
482
483 object_property_set_str(OBJECT(dev), ref, name, errp);
484 }
485
486 void qdev_prop_set_chr(DeviceState *dev, const char *name,
487 Chardev *value)
488 {
489 assert(!value || value->label);
490 object_property_set_str(OBJECT(dev),
491 value ? value->label : "", name, &error_abort);
492 }
493
494 void qdev_prop_set_netdev(DeviceState *dev, const char *name,
495 NetClientState *value)
496 {
497 assert(!value || value->name);
498 object_property_set_str(OBJECT(dev),
499 value ? value->name : "", name, &error_abort);
500 }
501
502 void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd)
503 {
504 qdev_prop_set_macaddr(dev, "mac", nd->macaddr.a);
505 if (nd->netdev) {
506 qdev_prop_set_netdev(dev, "netdev", nd->netdev);
507 }
508 if (nd->nvectors != DEV_NVECTORS_UNSPECIFIED &&
509 object_property_find(OBJECT(dev), "vectors", NULL)) {
510 qdev_prop_set_uint32(dev, "vectors", nd->nvectors);
511 }
512 nd->instantiated = 1;
513 }