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