xref: /openbmc/qemu/hw/core/qdev-properties-system.c (revision c0578de60fcc3a07a881e9c4b7b8262faf6abbc7)
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 }
514