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