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 "audio/audio.h" 15 #include "net/net.h" 16 #include "hw/qdev-properties.h" 17 #include "qapi/error.h" 18 #include "qapi/qmp/qerror.h" 19 #include "sysemu/block-backend.h" 20 #include "sysemu/blockdev.h" 21 #include "hw/block/block.h" 22 #include "net/hub.h" 23 #include "qapi/visitor.h" 24 #include "chardev/char-fe.h" 25 #include "sysemu/iothread.h" 26 #include "sysemu/tpm_backend.h" 27 28 static bool check_prop_still_unset(DeviceState *dev, const char *name, 29 const void *old_val, const char *new_val, 30 Error **errp) 31 { 32 const GlobalProperty *prop = qdev_find_global_prop(dev, name); 33 34 if (!old_val) { 35 return true; 36 } 37 38 if (prop) { 39 error_setg(errp, "-global %s.%s=... conflicts with %s=%s", 40 prop->driver, prop->property, name, new_val); 41 } else { 42 /* Error message is vague, but a better one would be hard */ 43 error_setg(errp, "%s=%s conflicts, and override is not implemented", 44 name, new_val); 45 } 46 return false; 47 } 48 49 50 /* --- drive --- */ 51 52 static void get_drive(Object *obj, Visitor *v, const char *name, void *opaque, 53 Error **errp) 54 { 55 DeviceState *dev = DEVICE(obj); 56 Property *prop = opaque; 57 void **ptr = qdev_get_prop_ptr(dev, prop); 58 const char *value; 59 char *p; 60 61 if (*ptr) { 62 value = blk_name(*ptr); 63 if (!*value) { 64 BlockDriverState *bs = blk_bs(*ptr); 65 if (bs) { 66 value = bdrv_get_node_name(bs); 67 } 68 } 69 } else { 70 value = ""; 71 } 72 73 p = g_strdup(value); 74 visit_type_str(v, name, &p, errp); 75 g_free(p); 76 } 77 78 static void set_drive_helper(Object *obj, Visitor *v, const char *name, 79 void *opaque, bool iothread, Error **errp) 80 { 81 DeviceState *dev = DEVICE(obj); 82 Property *prop = opaque; 83 Error *local_err = NULL; 84 void **ptr = qdev_get_prop_ptr(dev, prop); 85 char *str; 86 BlockBackend *blk; 87 bool blk_created = false; 88 int ret; 89 90 if (dev->realized) { 91 qdev_prop_set_after_realize(dev, name, errp); 92 return; 93 } 94 95 visit_type_str(v, name, &str, &local_err); 96 if (local_err) { 97 error_propagate(errp, local_err); 98 return; 99 } 100 101 /* 102 * TODO Should this really be an error? If no, the old value 103 * needs to be released before we store the new one. 104 */ 105 if (!check_prop_still_unset(dev, name, *ptr, str, errp)) { 106 return; 107 } 108 109 if (!*str) { 110 g_free(str); 111 *ptr = NULL; 112 return; 113 } 114 115 blk = blk_by_name(str); 116 if (!blk) { 117 BlockDriverState *bs = bdrv_lookup_bs(NULL, str, NULL); 118 if (bs) { 119 /* 120 * If the device supports iothreads, it will make sure to move the 121 * block node to the right AioContext if necessary (or fail if this 122 * isn't possible because of other users). Devices that are not 123 * aware of iothreads require their BlockBackends to be in the main 124 * AioContext. 125 */ 126 AioContext *ctx = iothread ? bdrv_get_aio_context(bs) : 127 qemu_get_aio_context(); 128 blk = blk_new(ctx, 0, BLK_PERM_ALL); 129 blk_created = true; 130 131 ret = blk_insert_bs(blk, bs, errp); 132 if (ret < 0) { 133 goto fail; 134 } 135 } 136 } 137 if (!blk) { 138 error_setg(errp, "Property '%s.%s' can't find value '%s'", 139 object_get_typename(OBJECT(dev)), prop->name, str); 140 goto fail; 141 } 142 if (blk_attach_dev(blk, dev) < 0) { 143 DriveInfo *dinfo = blk_legacy_dinfo(blk); 144 145 if (dinfo && dinfo->type != IF_NONE) { 146 error_setg(errp, "Drive '%s' is already in use because " 147 "it has been automatically connected to another " 148 "device (did you need 'if=none' in the drive options?)", 149 str); 150 } else { 151 error_setg(errp, "Drive '%s' is already in use by another device", 152 str); 153 } 154 goto fail; 155 } 156 157 *ptr = blk; 158 159 fail: 160 if (blk_created) { 161 /* If we need to keep a reference, blk_attach_dev() took it */ 162 blk_unref(blk); 163 } 164 165 g_free(str); 166 } 167 168 static void set_drive(Object *obj, Visitor *v, const char *name, void *opaque, 169 Error **errp) 170 { 171 set_drive_helper(obj, v, name, opaque, false, errp); 172 } 173 174 static void set_drive_iothread(Object *obj, Visitor *v, const char *name, 175 void *opaque, Error **errp) 176 { 177 set_drive_helper(obj, v, name, opaque, true, errp); 178 } 179 180 static void release_drive(Object *obj, const char *name, void *opaque) 181 { 182 DeviceState *dev = DEVICE(obj); 183 Property *prop = opaque; 184 BlockBackend **ptr = qdev_get_prop_ptr(dev, prop); 185 186 if (*ptr) { 187 AioContext *ctx = blk_get_aio_context(*ptr); 188 189 aio_context_acquire(ctx); 190 blockdev_auto_del(*ptr); 191 blk_detach_dev(*ptr, dev); 192 aio_context_release(ctx); 193 } 194 } 195 196 const PropertyInfo qdev_prop_drive = { 197 .name = "str", 198 .description = "Node name or ID of a block device to use as a backend", 199 .get = get_drive, 200 .set = set_drive, 201 .release = release_drive, 202 }; 203 204 const PropertyInfo qdev_prop_drive_iothread = { 205 .name = "str", 206 .description = "Node name or ID of a block device to use as a backend", 207 .get = get_drive, 208 .set = set_drive_iothread, 209 .release = release_drive, 210 }; 211 212 /* --- character device --- */ 213 214 static void get_chr(Object *obj, Visitor *v, const char *name, void *opaque, 215 Error **errp) 216 { 217 DeviceState *dev = DEVICE(obj); 218 CharBackend *be = qdev_get_prop_ptr(dev, opaque); 219 char *p; 220 221 p = g_strdup(be->chr && be->chr->label ? be->chr->label : ""); 222 visit_type_str(v, name, &p, errp); 223 g_free(p); 224 } 225 226 static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque, 227 Error **errp) 228 { 229 DeviceState *dev = DEVICE(obj); 230 Error *local_err = NULL; 231 Property *prop = opaque; 232 CharBackend *be = qdev_get_prop_ptr(dev, prop); 233 Chardev *s; 234 char *str; 235 236 if (dev->realized) { 237 qdev_prop_set_after_realize(dev, name, errp); 238 return; 239 } 240 241 visit_type_str(v, name, &str, &local_err); 242 if (local_err) { 243 error_propagate(errp, local_err); 244 return; 245 } 246 247 /* 248 * TODO Should this really be an error? If no, the old value 249 * needs to be released before we store the new one. 250 */ 251 if (!check_prop_still_unset(dev, name, be->chr, str, errp)) { 252 return; 253 } 254 255 if (!*str) { 256 g_free(str); 257 be->chr = NULL; 258 return; 259 } 260 261 s = qemu_chr_find(str); 262 if (s == NULL) { 263 error_setg(errp, "Property '%s.%s' can't find value '%s'", 264 object_get_typename(obj), prop->name, str); 265 } else if (!qemu_chr_fe_init(be, s, errp)) { 266 error_prepend(errp, "Property '%s.%s' can't take value '%s': ", 267 object_get_typename(obj), prop->name, str); 268 } 269 g_free(str); 270 } 271 272 static void release_chr(Object *obj, const char *name, void *opaque) 273 { 274 DeviceState *dev = DEVICE(obj); 275 Property *prop = opaque; 276 CharBackend *be = qdev_get_prop_ptr(dev, prop); 277 278 qemu_chr_fe_deinit(be, false); 279 } 280 281 const PropertyInfo qdev_prop_chr = { 282 .name = "str", 283 .description = "ID of a chardev to use as a backend", 284 .get = get_chr, 285 .set = set_chr, 286 .release = release_chr, 287 }; 288 289 /* --- netdev device --- */ 290 static void get_netdev(Object *obj, Visitor *v, const char *name, 291 void *opaque, Error **errp) 292 { 293 DeviceState *dev = DEVICE(obj); 294 Property *prop = opaque; 295 NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop); 296 char *p = g_strdup(peers_ptr->ncs[0] ? peers_ptr->ncs[0]->name : ""); 297 298 visit_type_str(v, name, &p, errp); 299 g_free(p); 300 } 301 302 static void set_netdev(Object *obj, Visitor *v, const char *name, 303 void *opaque, Error **errp) 304 { 305 DeviceState *dev = DEVICE(obj); 306 Property *prop = opaque; 307 NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop); 308 NetClientState **ncs = peers_ptr->ncs; 309 NetClientState *peers[MAX_QUEUE_NUM]; 310 Error *local_err = NULL; 311 int queues, err = 0, i = 0; 312 char *str; 313 314 if (dev->realized) { 315 qdev_prop_set_after_realize(dev, name, errp); 316 return; 317 } 318 319 visit_type_str(v, name, &str, &local_err); 320 if (local_err) { 321 error_propagate(errp, local_err); 322 return; 323 } 324 325 queues = qemu_find_net_clients_except(str, peers, 326 NET_CLIENT_DRIVER_NIC, 327 MAX_QUEUE_NUM); 328 if (queues == 0) { 329 err = -ENOENT; 330 goto out; 331 } 332 333 if (queues > MAX_QUEUE_NUM) { 334 error_setg(errp, "queues of backend '%s'(%d) exceeds QEMU limitation(%d)", 335 str, queues, MAX_QUEUE_NUM); 336 goto out; 337 } 338 339 for (i = 0; i < queues; i++) { 340 if (peers[i]->peer) { 341 err = -EEXIST; 342 goto out; 343 } 344 345 /* 346 * TODO Should this really be an error? If no, the old value 347 * needs to be released before we store the new one. 348 */ 349 if (!check_prop_still_unset(dev, name, ncs[i], str, errp)) { 350 goto out; 351 } 352 353 ncs[i] = peers[i]; 354 ncs[i]->queue_index = i; 355 } 356 357 peers_ptr->queues = queues; 358 359 out: 360 error_set_from_qdev_prop_error(errp, err, dev, prop, str); 361 g_free(str); 362 } 363 364 const PropertyInfo qdev_prop_netdev = { 365 .name = "str", 366 .description = "ID of a netdev to use as a backend", 367 .get = get_netdev, 368 .set = set_netdev, 369 }; 370 371 372 /* --- audiodev --- */ 373 static void get_audiodev(Object *obj, Visitor *v, const char* name, 374 void *opaque, Error **errp) 375 { 376 DeviceState *dev = DEVICE(obj); 377 Property *prop = opaque; 378 QEMUSoundCard *card = qdev_get_prop_ptr(dev, prop); 379 char *p = g_strdup(audio_get_id(card)); 380 381 visit_type_str(v, name, &p, errp); 382 g_free(p); 383 } 384 385 static void set_audiodev(Object *obj, Visitor *v, const char* name, 386 void *opaque, Error **errp) 387 { 388 DeviceState *dev = DEVICE(obj); 389 Property *prop = opaque; 390 QEMUSoundCard *card = qdev_get_prop_ptr(dev, prop); 391 AudioState *state; 392 Error *local_err = NULL; 393 int err = 0; 394 char *str; 395 396 if (dev->realized) { 397 qdev_prop_set_after_realize(dev, name, errp); 398 return; 399 } 400 401 visit_type_str(v, name, &str, &local_err); 402 if (local_err) { 403 error_propagate(errp, local_err); 404 return; 405 } 406 407 state = audio_state_by_name(str); 408 409 if (!state) { 410 err = -ENOENT; 411 goto out; 412 } 413 card->state = state; 414 415 out: 416 error_set_from_qdev_prop_error(errp, err, dev, prop, str); 417 g_free(str); 418 } 419 420 const PropertyInfo qdev_prop_audiodev = { 421 .name = "str", 422 .description = "ID of an audiodev to use as a backend", 423 /* release done on shutdown */ 424 .get = get_audiodev, 425 .set = set_audiodev, 426 }; 427 428 void qdev_prop_set_drive_err(DeviceState *dev, const char *name, 429 BlockBackend *value, Error **errp) 430 { 431 const char *ref = ""; 432 433 if (value) { 434 ref = blk_name(value); 435 if (!*ref) { 436 const BlockDriverState *bs = blk_bs(value); 437 if (bs) { 438 ref = bdrv_get_node_name(bs); 439 } 440 } 441 } 442 443 object_property_set_str(OBJECT(dev), ref, name, errp); 444 } 445 446 void qdev_prop_set_drive(DeviceState *dev, const char *name, 447 BlockBackend *value) 448 { 449 qdev_prop_set_drive_err(dev, name, value, &error_abort); 450 } 451 452 void qdev_prop_set_chr(DeviceState *dev, const char *name, 453 Chardev *value) 454 { 455 assert(!value || value->label); 456 object_property_set_str(OBJECT(dev), 457 value ? value->label : "", name, &error_abort); 458 } 459 460 void qdev_prop_set_netdev(DeviceState *dev, const char *name, 461 NetClientState *value) 462 { 463 assert(!value || value->name); 464 object_property_set_str(OBJECT(dev), 465 value ? value->name : "", name, &error_abort); 466 } 467 468 void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd) 469 { 470 qdev_prop_set_macaddr(dev, "mac", nd->macaddr.a); 471 if (nd->netdev) { 472 qdev_prop_set_netdev(dev, "netdev", nd->netdev); 473 } 474 if (nd->nvectors != DEV_NVECTORS_UNSPECIFIED && 475 object_property_find(OBJECT(dev), "vectors", NULL)) { 476 qdev_prop_set_uint32(dev, "vectors", nd->nvectors); 477 } 478 nd->instantiated = 1; 479 } 480