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 /* BlockBackends of devices start in the main context and are only 84 * later moved into another context if the device supports that. */ 85 blk = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL); 86 blk_created = true; 87 88 ret = blk_insert_bs(blk, bs, errp); 89 if (ret < 0) { 90 goto fail; 91 } 92 } 93 } 94 if (!blk) { 95 error_setg(errp, "Property '%s.%s' can't find value '%s'", 96 object_get_typename(OBJECT(dev)), propname, str); 97 goto fail; 98 } 99 if (blk_attach_dev(blk, dev) < 0) { 100 DriveInfo *dinfo = blk_legacy_dinfo(blk); 101 102 if (dinfo && dinfo->type != IF_NONE) { 103 error_setg(errp, "Drive '%s' is already in use because " 104 "it has been automatically connected to another " 105 "device (did you need 'if=none' in the drive options?)", 106 str); 107 } else { 108 error_setg(errp, "Drive '%s' is already in use by another device", 109 str); 110 } 111 goto fail; 112 } 113 114 *ptr = blk; 115 116 fail: 117 if (blk_created) { 118 /* If we need to keep a reference, blk_attach_dev() took it */ 119 blk_unref(blk); 120 } 121 } 122 123 static void release_drive(Object *obj, const char *name, void *opaque) 124 { 125 DeviceState *dev = DEVICE(obj); 126 Property *prop = opaque; 127 BlockBackend **ptr = qdev_get_prop_ptr(dev, prop); 128 129 if (*ptr) { 130 AioContext *ctx = blk_get_aio_context(*ptr); 131 132 aio_context_acquire(ctx); 133 blockdev_auto_del(*ptr); 134 blk_detach_dev(*ptr, dev); 135 aio_context_release(ctx); 136 } 137 } 138 139 static char *print_drive(void *ptr) 140 { 141 const char *name; 142 143 name = blk_name(ptr); 144 if (!*name) { 145 BlockDriverState *bs = blk_bs(ptr); 146 if (bs) { 147 name = bdrv_get_node_name(bs); 148 } 149 } 150 return g_strdup(name); 151 } 152 153 static void get_drive(Object *obj, Visitor *v, const char *name, void *opaque, 154 Error **errp) 155 { 156 get_pointer(obj, v, opaque, print_drive, name, errp); 157 } 158 159 static void set_drive(Object *obj, Visitor *v, const char *name, void *opaque, 160 Error **errp) 161 { 162 set_pointer(obj, v, opaque, parse_drive, name, errp); 163 } 164 165 const PropertyInfo qdev_prop_drive = { 166 .name = "str", 167 .description = "Node name or ID of a block device to use as a backend", 168 .get = get_drive, 169 .set = set_drive, 170 .release = release_drive, 171 }; 172 173 /* --- character device --- */ 174 175 static void get_chr(Object *obj, Visitor *v, const char *name, void *opaque, 176 Error **errp) 177 { 178 DeviceState *dev = DEVICE(obj); 179 CharBackend *be = qdev_get_prop_ptr(dev, opaque); 180 char *p; 181 182 p = g_strdup(be->chr && be->chr->label ? be->chr->label : ""); 183 visit_type_str(v, name, &p, errp); 184 g_free(p); 185 } 186 187 static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque, 188 Error **errp) 189 { 190 DeviceState *dev = DEVICE(obj); 191 Error *local_err = NULL; 192 Property *prop = opaque; 193 CharBackend *be = qdev_get_prop_ptr(dev, prop); 194 Chardev *s; 195 char *str; 196 197 if (dev->realized) { 198 qdev_prop_set_after_realize(dev, name, errp); 199 return; 200 } 201 202 visit_type_str(v, name, &str, &local_err); 203 if (local_err) { 204 error_propagate(errp, local_err); 205 return; 206 } 207 208 if (!*str) { 209 g_free(str); 210 be->chr = NULL; 211 return; 212 } 213 214 s = qemu_chr_find(str); 215 if (s == NULL) { 216 error_setg(errp, "Property '%s.%s' can't find value '%s'", 217 object_get_typename(obj), prop->name, str); 218 } else if (!qemu_chr_fe_init(be, s, errp)) { 219 error_prepend(errp, "Property '%s.%s' can't take value '%s': ", 220 object_get_typename(obj), prop->name, str); 221 } 222 g_free(str); 223 } 224 225 static void release_chr(Object *obj, const char *name, void *opaque) 226 { 227 DeviceState *dev = DEVICE(obj); 228 Property *prop = opaque; 229 CharBackend *be = qdev_get_prop_ptr(dev, prop); 230 231 qemu_chr_fe_deinit(be, false); 232 } 233 234 const PropertyInfo qdev_prop_chr = { 235 .name = "str", 236 .description = "ID of a chardev to use as a backend", 237 .get = get_chr, 238 .set = set_chr, 239 .release = release_chr, 240 }; 241 242 /* --- netdev device --- */ 243 static void get_netdev(Object *obj, Visitor *v, const char *name, 244 void *opaque, Error **errp) 245 { 246 DeviceState *dev = DEVICE(obj); 247 Property *prop = opaque; 248 NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop); 249 char *p = g_strdup(peers_ptr->ncs[0] ? peers_ptr->ncs[0]->name : ""); 250 251 visit_type_str(v, name, &p, errp); 252 g_free(p); 253 } 254 255 static void set_netdev(Object *obj, Visitor *v, const char *name, 256 void *opaque, Error **errp) 257 { 258 DeviceState *dev = DEVICE(obj); 259 Property *prop = opaque; 260 NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop); 261 NetClientState **ncs = peers_ptr->ncs; 262 NetClientState *peers[MAX_QUEUE_NUM]; 263 Error *local_err = NULL; 264 int queues, err = 0, i = 0; 265 char *str; 266 267 if (dev->realized) { 268 qdev_prop_set_after_realize(dev, name, errp); 269 return; 270 } 271 272 visit_type_str(v, name, &str, &local_err); 273 if (local_err) { 274 error_propagate(errp, local_err); 275 return; 276 } 277 278 queues = qemu_find_net_clients_except(str, peers, 279 NET_CLIENT_DRIVER_NIC, 280 MAX_QUEUE_NUM); 281 if (queues == 0) { 282 err = -ENOENT; 283 goto out; 284 } 285 286 if (queues > MAX_QUEUE_NUM) { 287 error_setg(errp, "queues of backend '%s'(%d) exceeds QEMU limitation(%d)", 288 str, queues, MAX_QUEUE_NUM); 289 goto out; 290 } 291 292 for (i = 0; i < queues; i++) { 293 294 if (peers[i]->peer) { 295 err = -EEXIST; 296 goto out; 297 } 298 299 if (ncs[i]) { 300 err = -EINVAL; 301 goto out; 302 } 303 304 ncs[i] = peers[i]; 305 ncs[i]->queue_index = i; 306 } 307 308 peers_ptr->queues = queues; 309 310 out: 311 error_set_from_qdev_prop_error(errp, err, dev, prop, str); 312 g_free(str); 313 } 314 315 const PropertyInfo qdev_prop_netdev = { 316 .name = "str", 317 .description = "ID of a netdev to use as a backend", 318 .get = get_netdev, 319 .set = set_netdev, 320 }; 321 322 323 void qdev_prop_set_drive(DeviceState *dev, const char *name, 324 BlockBackend *value, Error **errp) 325 { 326 const char *ref = ""; 327 328 if (value) { 329 ref = blk_name(value); 330 if (!*ref) { 331 const BlockDriverState *bs = blk_bs(value); 332 if (bs) { 333 ref = bdrv_get_node_name(bs); 334 } 335 } 336 } 337 338 object_property_set_str(OBJECT(dev), ref, name, errp); 339 } 340 341 void qdev_prop_set_chr(DeviceState *dev, const char *name, 342 Chardev *value) 343 { 344 assert(!value || value->label); 345 object_property_set_str(OBJECT(dev), 346 value ? value->label : "", name, &error_abort); 347 } 348 349 void qdev_prop_set_netdev(DeviceState *dev, const char *name, 350 NetClientState *value) 351 { 352 assert(!value || value->name); 353 object_property_set_str(OBJECT(dev), 354 value ? value->name : "", name, &error_abort); 355 } 356 357 void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd) 358 { 359 qdev_prop_set_macaddr(dev, "mac", nd->macaddr.a); 360 if (nd->netdev) { 361 qdev_prop_set_netdev(dev, "netdev", nd->netdev); 362 } 363 if (nd->nvectors != DEV_NVECTORS_UNSPECIFIED && 364 object_property_find(OBJECT(dev), "vectors", NULL)) { 365 qdev_prop_set_uint32(dev, "vectors", nd->nvectors); 366 } 367 nd->instantiated = 1; 368 } 369