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