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