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/iothread.h"
25 #include "sysemu/tpm_backend.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 /* --- netdev device --- */
241 static void get_netdev(Object *obj, Visitor *v, const char *name,
242                        void *opaque, Error **errp)
243 {
244     DeviceState *dev = DEVICE(obj);
245     Property *prop = opaque;
246     NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop);
247     char *p = g_strdup(peers_ptr->ncs[0] ? peers_ptr->ncs[0]->name : "");
248 
249     visit_type_str(v, name, &p, errp);
250     g_free(p);
251 }
252 
253 static void set_netdev(Object *obj, Visitor *v, const char *name,
254                        void *opaque, Error **errp)
255 {
256     DeviceState *dev = DEVICE(obj);
257     Property *prop = opaque;
258     NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop);
259     NetClientState **ncs = peers_ptr->ncs;
260     NetClientState *peers[MAX_QUEUE_NUM];
261     Error *local_err = NULL;
262     int queues, err = 0, i = 0;
263     char *str;
264 
265     if (dev->realized) {
266         qdev_prop_set_after_realize(dev, name, errp);
267         return;
268     }
269 
270     visit_type_str(v, name, &str, &local_err);
271     if (local_err) {
272         error_propagate(errp, local_err);
273         return;
274     }
275 
276     queues = qemu_find_net_clients_except(str, peers,
277                                           NET_CLIENT_DRIVER_NIC,
278                                           MAX_QUEUE_NUM);
279     if (queues == 0) {
280         err = -ENOENT;
281         goto out;
282     }
283 
284     if (queues > MAX_QUEUE_NUM) {
285         error_setg(errp, "queues of backend '%s'(%d) exceeds QEMU limitation(%d)",
286                    str, queues, MAX_QUEUE_NUM);
287         goto out;
288     }
289 
290     for (i = 0; i < queues; i++) {
291         if (peers[i] == NULL) {
292             err = -ENOENT;
293             goto out;
294         }
295 
296         if (peers[i]->peer) {
297             err = -EEXIST;
298             goto out;
299         }
300 
301         if (ncs[i]) {
302             err = -EINVAL;
303             goto out;
304         }
305 
306         ncs[i] = peers[i];
307         ncs[i]->queue_index = i;
308     }
309 
310     peers_ptr->queues = queues;
311 
312 out:
313     error_set_from_qdev_prop_error(errp, err, dev, prop, str);
314     g_free(str);
315 }
316 
317 const PropertyInfo qdev_prop_netdev = {
318     .name  = "str",
319     .description = "ID of a netdev to use as a backend",
320     .get   = get_netdev,
321     .set   = set_netdev,
322 };
323 
324 /* --- vlan --- */
325 
326 static int print_vlan(DeviceState *dev, Property *prop, char *dest, size_t len)
327 {
328     NetClientState **ptr = qdev_get_prop_ptr(dev, prop);
329 
330     if (*ptr) {
331         int id;
332         if (!net_hub_id_for_client(*ptr, &id)) {
333             return snprintf(dest, len, "%d", id);
334         }
335     }
336 
337     return snprintf(dest, len, "<null>");
338 }
339 
340 static void get_vlan(Object *obj, Visitor *v, const char *name, void *opaque,
341                      Error **errp)
342 {
343     DeviceState *dev = DEVICE(obj);
344     Property *prop = opaque;
345     NetClientState **ptr = qdev_get_prop_ptr(dev, prop);
346     int32_t id = -1;
347 
348     if (*ptr) {
349         int hub_id;
350         if (!net_hub_id_for_client(*ptr, &hub_id)) {
351             id = hub_id;
352         }
353     }
354 
355     visit_type_int32(v, name, &id, errp);
356 }
357 
358 static void set_vlan(Object *obj, Visitor *v, const char *name, void *opaque,
359                      Error **errp)
360 {
361     DeviceState *dev = DEVICE(obj);
362     Property *prop = opaque;
363     NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop);
364     NetClientState **ptr = &peers_ptr->ncs[0];
365     Error *local_err = NULL;
366     int32_t id;
367     NetClientState *hubport;
368 
369     if (dev->realized) {
370         qdev_prop_set_after_realize(dev, name, errp);
371         return;
372     }
373 
374     visit_type_int32(v, name, &id, &local_err);
375     if (local_err) {
376         error_propagate(errp, local_err);
377         return;
378     }
379     if (id == -1) {
380         *ptr = NULL;
381         return;
382     }
383     if (*ptr) {
384         error_set_from_qdev_prop_error(errp, -EINVAL, dev, prop, name);
385         return;
386     }
387 
388     hubport = net_hub_port_find(id);
389     if (!hubport) {
390         error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
391                    name, prop->info->name);
392         return;
393     }
394     *ptr = hubport;
395 }
396 
397 const PropertyInfo qdev_prop_vlan = {
398     .name  = "int32",
399     .description = "Integer VLAN id to connect to",
400     .print = print_vlan,
401     .get   = get_vlan,
402     .set   = set_vlan,
403 };
404 
405 void qdev_prop_set_drive(DeviceState *dev, const char *name,
406                          BlockBackend *value, Error **errp)
407 {
408     const char *ref = "";
409 
410     if (value) {
411         ref = blk_name(value);
412         if (!*ref) {
413             const BlockDriverState *bs = blk_bs(value);
414             if (bs) {
415                 ref = bdrv_get_node_name(bs);
416             }
417         }
418     }
419 
420     object_property_set_str(OBJECT(dev), ref, name, errp);
421 }
422 
423 void qdev_prop_set_chr(DeviceState *dev, const char *name,
424                        Chardev *value)
425 {
426     assert(!value || value->label);
427     object_property_set_str(OBJECT(dev),
428                             value ? value->label : "", name, &error_abort);
429 }
430 
431 void qdev_prop_set_netdev(DeviceState *dev, const char *name,
432                           NetClientState *value)
433 {
434     assert(!value || value->name);
435     object_property_set_str(OBJECT(dev),
436                             value ? value->name : "", name, &error_abort);
437 }
438 
439 void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd)
440 {
441     qdev_prop_set_macaddr(dev, "mac", nd->macaddr.a);
442     if (nd->netdev) {
443         qdev_prop_set_netdev(dev, "netdev", nd->netdev);
444     }
445     if (nd->nvectors != DEV_NVECTORS_UNSPECIFIED &&
446         object_property_find(OBJECT(dev), "vectors", NULL)) {
447         qdev_prop_set_uint32(dev, "vectors", nd->nvectors);
448     }
449     nd->instantiated = 1;
450 }
451