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