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 const char *name; 130 131 name = blk_name(ptr); 132 if (!*name) { 133 BlockDriverState *bs = blk_bs(ptr); 134 if (bs) { 135 name = bdrv_get_node_name(bs); 136 } 137 } 138 return g_strdup(name); 139 } 140 141 static void get_drive(Object *obj, Visitor *v, const char *name, void *opaque, 142 Error **errp) 143 { 144 get_pointer(obj, v, opaque, print_drive, name, errp); 145 } 146 147 static void set_drive(Object *obj, Visitor *v, const char *name, void *opaque, 148 Error **errp) 149 { 150 set_pointer(obj, v, opaque, parse_drive, name, errp); 151 } 152 153 PropertyInfo qdev_prop_drive = { 154 .name = "str", 155 .description = "Node name or ID of a block device to use as a backend", 156 .get = get_drive, 157 .set = set_drive, 158 .release = release_drive, 159 }; 160 161 /* --- character device --- */ 162 163 static void get_chr(Object *obj, Visitor *v, const char *name, void *opaque, 164 Error **errp) 165 { 166 DeviceState *dev = DEVICE(obj); 167 CharBackend *be = qdev_get_prop_ptr(dev, opaque); 168 char *p; 169 170 p = g_strdup(be->chr && be->chr->label ? be->chr->label : ""); 171 visit_type_str(v, name, &p, errp); 172 g_free(p); 173 } 174 175 static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque, 176 Error **errp) 177 { 178 DeviceState *dev = DEVICE(obj); 179 Error *local_err = NULL; 180 Property *prop = opaque; 181 CharBackend *be = qdev_get_prop_ptr(dev, prop); 182 CharDriverState *s; 183 char *str; 184 185 if (dev->realized) { 186 qdev_prop_set_after_realize(dev, name, errp); 187 return; 188 } 189 190 visit_type_str(v, name, &str, &local_err); 191 if (local_err) { 192 error_propagate(errp, local_err); 193 return; 194 } 195 196 if (!*str) { 197 g_free(str); 198 be->chr = NULL; 199 return; 200 } 201 202 s = qemu_chr_find(str); 203 g_free(str); 204 if (s == NULL) { 205 error_setg(errp, "Property '%s.%s' can't find value '%s'", 206 object_get_typename(obj), prop->name, str); 207 return; 208 } 209 if (qemu_chr_fe_claim(s) != 0) { 210 error_setg(errp, "Property '%s.%s' can't take value '%s', it's in use", 211 object_get_typename(obj), prop->name, str); 212 return; 213 } 214 215 qemu_chr_fe_init(be, s, errp); 216 } 217 218 static void release_chr(Object *obj, const char *name, void *opaque) 219 { 220 DeviceState *dev = DEVICE(obj); 221 Property *prop = opaque; 222 CharBackend *be = qdev_get_prop_ptr(dev, prop); 223 224 if (be->chr) { 225 qemu_chr_fe_set_handlers(be, NULL, NULL, NULL, NULL, NULL); 226 qemu_chr_fe_release(be->chr); 227 } 228 } 229 230 PropertyInfo qdev_prop_chr = { 231 .name = "str", 232 .description = "ID of a chardev to use as a backend", 233 .get = get_chr, 234 .set = set_chr, 235 .release = release_chr, 236 }; 237 238 /* --- netdev device --- */ 239 static void get_netdev(Object *obj, Visitor *v, const char *name, 240 void *opaque, Error **errp) 241 { 242 DeviceState *dev = DEVICE(obj); 243 Property *prop = opaque; 244 NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop); 245 char *p = g_strdup(peers_ptr->ncs[0] ? peers_ptr->ncs[0]->name : ""); 246 247 visit_type_str(v, name, &p, errp); 248 g_free(p); 249 } 250 251 static void set_netdev(Object *obj, Visitor *v, const char *name, 252 void *opaque, Error **errp) 253 { 254 DeviceState *dev = DEVICE(obj); 255 Property *prop = opaque; 256 NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop); 257 NetClientState **ncs = peers_ptr->ncs; 258 NetClientState *peers[MAX_QUEUE_NUM]; 259 Error *local_err = NULL; 260 int queues, err = 0, i = 0; 261 char *str; 262 263 if (dev->realized) { 264 qdev_prop_set_after_realize(dev, name, errp); 265 return; 266 } 267 268 visit_type_str(v, name, &str, &local_err); 269 if (local_err) { 270 error_propagate(errp, local_err); 271 return; 272 } 273 274 queues = qemu_find_net_clients_except(str, peers, 275 NET_CLIENT_DRIVER_NIC, 276 MAX_QUEUE_NUM); 277 if (queues == 0) { 278 err = -ENOENT; 279 goto out; 280 } 281 282 if (queues > MAX_QUEUE_NUM) { 283 error_setg(errp, "queues of backend '%s'(%d) exceeds QEMU limitation(%d)", 284 str, queues, MAX_QUEUE_NUM); 285 goto out; 286 } 287 288 for (i = 0; i < queues; i++) { 289 if (peers[i] == NULL) { 290 err = -ENOENT; 291 goto out; 292 } 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 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 /* --- vlan --- */ 323 324 static int print_vlan(DeviceState *dev, Property *prop, char *dest, size_t len) 325 { 326 NetClientState **ptr = qdev_get_prop_ptr(dev, prop); 327 328 if (*ptr) { 329 int id; 330 if (!net_hub_id_for_client(*ptr, &id)) { 331 return snprintf(dest, len, "%d", id); 332 } 333 } 334 335 return snprintf(dest, len, "<null>"); 336 } 337 338 static void get_vlan(Object *obj, Visitor *v, const char *name, void *opaque, 339 Error **errp) 340 { 341 DeviceState *dev = DEVICE(obj); 342 Property *prop = opaque; 343 NetClientState **ptr = qdev_get_prop_ptr(dev, prop); 344 int32_t id = -1; 345 346 if (*ptr) { 347 int hub_id; 348 if (!net_hub_id_for_client(*ptr, &hub_id)) { 349 id = hub_id; 350 } 351 } 352 353 visit_type_int32(v, name, &id, errp); 354 } 355 356 static void set_vlan(Object *obj, Visitor *v, const char *name, void *opaque, 357 Error **errp) 358 { 359 DeviceState *dev = DEVICE(obj); 360 Property *prop = opaque; 361 NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop); 362 NetClientState **ptr = &peers_ptr->ncs[0]; 363 Error *local_err = NULL; 364 int32_t id; 365 NetClientState *hubport; 366 367 if (dev->realized) { 368 qdev_prop_set_after_realize(dev, name, errp); 369 return; 370 } 371 372 visit_type_int32(v, name, &id, &local_err); 373 if (local_err) { 374 error_propagate(errp, local_err); 375 return; 376 } 377 if (id == -1) { 378 *ptr = NULL; 379 return; 380 } 381 if (*ptr) { 382 error_set_from_qdev_prop_error(errp, -EINVAL, dev, prop, name); 383 return; 384 } 385 386 hubport = net_hub_port_find(id); 387 if (!hubport) { 388 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, 389 name, prop->info->name); 390 return; 391 } 392 *ptr = hubport; 393 } 394 395 PropertyInfo qdev_prop_vlan = { 396 .name = "int32", 397 .description = "Integer VLAN id to connect to", 398 .print = print_vlan, 399 .get = get_vlan, 400 .set = set_vlan, 401 }; 402 403 void qdev_prop_set_drive(DeviceState *dev, const char *name, 404 BlockBackend *value, Error **errp) 405 { 406 const char *ref = ""; 407 408 if (value) { 409 ref = blk_name(value); 410 if (!*ref) { 411 BlockDriverState *bs = blk_bs(value); 412 if (bs) { 413 ref = bdrv_get_node_name(bs); 414 } 415 } 416 } 417 418 object_property_set_str(OBJECT(dev), ref, name, errp); 419 } 420 421 void qdev_prop_set_chr(DeviceState *dev, const char *name, 422 CharDriverState *value) 423 { 424 assert(!value || value->label); 425 object_property_set_str(OBJECT(dev), 426 value ? value->label : "", name, &error_abort); 427 } 428 429 void qdev_prop_set_netdev(DeviceState *dev, const char *name, 430 NetClientState *value) 431 { 432 assert(!value || value->name); 433 object_property_set_str(OBJECT(dev), 434 value ? value->name : "", name, &error_abort); 435 } 436 437 void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd) 438 { 439 qdev_prop_set_macaddr(dev, "mac", nd->macaddr.a); 440 if (nd->netdev) { 441 qdev_prop_set_netdev(dev, "netdev", nd->netdev); 442 } 443 if (nd->nvectors != DEV_NVECTORS_UNSPECIFIED && 444 object_property_find(OBJECT(dev), "vectors", NULL)) { 445 qdev_prop_set_uint32(dev, "vectors", nd->nvectors); 446 } 447 nd->instantiated = 1; 448 } 449