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