1 /* 2 * qdev property parsing and global properties 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/qmp/qerror.h" 17 #include "sysemu/block-backend.h" 18 #include "sysemu/blockdev.h" 19 #include "hw/block/block.h" 20 #include "net/hub.h" 21 #include "qapi/visitor.h" 22 #include "sysemu/char.h" 23 #include "sysemu/iothread.h" 24 25 static void get_pointer(Object *obj, Visitor *v, Property *prop, 26 char *(*print)(void *ptr), 27 const char *name, Error **errp) 28 { 29 DeviceState *dev = DEVICE(obj); 30 void **ptr = qdev_get_prop_ptr(dev, prop); 31 char *p; 32 33 p = *ptr ? print(*ptr) : g_strdup(""); 34 visit_type_str(v, &p, name, errp); 35 g_free(p); 36 } 37 38 static void set_pointer(Object *obj, Visitor *v, Property *prop, 39 void (*parse)(DeviceState *dev, const char *str, 40 void **ptr, const char *propname, 41 Error **errp), 42 const char *name, Error **errp) 43 { 44 DeviceState *dev = DEVICE(obj); 45 Error *local_err = NULL; 46 void **ptr = qdev_get_prop_ptr(dev, prop); 47 char *str; 48 49 if (dev->realized) { 50 qdev_prop_set_after_realize(dev, name, errp); 51 return; 52 } 53 54 visit_type_str(v, &str, name, &local_err); 55 if (local_err) { 56 error_propagate(errp, local_err); 57 return; 58 } 59 if (!*str) { 60 g_free(str); 61 *ptr = NULL; 62 return; 63 } 64 parse(dev, str, ptr, prop->name, errp); 65 g_free(str); 66 } 67 68 /* --- drive --- */ 69 70 static void parse_drive(DeviceState *dev, const char *str, void **ptr, 71 const char *propname, Error **errp) 72 { 73 BlockBackend *blk; 74 75 blk = blk_by_name(str); 76 if (!blk) { 77 error_setg(errp, "Property '%s.%s' can't find value '%s'", 78 object_get_typename(OBJECT(dev)), propname, str); 79 return; 80 } 81 if (blk_attach_dev(blk, dev) < 0) { 82 DriveInfo *dinfo = blk_legacy_dinfo(blk); 83 84 if (dinfo->type != IF_NONE) { 85 error_setg(errp, "Drive '%s' is already in use because " 86 "it has been automatically connected to another " 87 "device (did you need 'if=none' in the drive options?)", 88 str); 89 } else { 90 error_setg(errp, "Drive '%s' is already in use by another device", 91 str); 92 } 93 return; 94 } 95 *ptr = blk; 96 } 97 98 static void release_drive(Object *obj, const char *name, void *opaque) 99 { 100 DeviceState *dev = DEVICE(obj); 101 Property *prop = opaque; 102 BlockBackend **ptr = qdev_get_prop_ptr(dev, prop); 103 104 if (*ptr) { 105 blk_detach_dev(*ptr, dev); 106 blockdev_auto_del(*ptr); 107 } 108 } 109 110 static char *print_drive(void *ptr) 111 { 112 return g_strdup(blk_name(ptr)); 113 } 114 115 static void get_drive(Object *obj, Visitor *v, void *opaque, 116 const char *name, Error **errp) 117 { 118 get_pointer(obj, v, opaque, print_drive, name, errp); 119 } 120 121 static void set_drive(Object *obj, Visitor *v, void *opaque, 122 const char *name, Error **errp) 123 { 124 set_pointer(obj, v, opaque, parse_drive, name, errp); 125 } 126 127 PropertyInfo qdev_prop_drive = { 128 .name = "str", 129 .description = "ID of a drive to use as a backend", 130 .get = get_drive, 131 .set = set_drive, 132 .release = release_drive, 133 }; 134 135 /* --- character device --- */ 136 137 static void parse_chr(DeviceState *dev, const char *str, void **ptr, 138 const char *propname, Error **errp) 139 { 140 CharDriverState *chr = qemu_chr_find(str); 141 if (chr == NULL) { 142 error_setg(errp, "Property '%s.%s' can't find value '%s'", 143 object_get_typename(OBJECT(dev)), propname, str); 144 return; 145 } 146 if (qemu_chr_fe_claim(chr) != 0) { 147 error_setg(errp, "Property '%s.%s' can't take value '%s', it's in use", 148 object_get_typename(OBJECT(dev)), propname, str); 149 return; 150 } 151 *ptr = chr; 152 } 153 154 static void release_chr(Object *obj, const char *name, void *opaque) 155 { 156 DeviceState *dev = DEVICE(obj); 157 Property *prop = opaque; 158 CharDriverState **ptr = qdev_get_prop_ptr(dev, prop); 159 CharDriverState *chr = *ptr; 160 161 if (chr) { 162 qemu_chr_add_handlers(chr, NULL, NULL, NULL, NULL); 163 qemu_chr_fe_release(chr); 164 } 165 } 166 167 168 static char *print_chr(void *ptr) 169 { 170 CharDriverState *chr = ptr; 171 const char *val = chr->label ? chr->label : ""; 172 173 return g_strdup(val); 174 } 175 176 static void get_chr(Object *obj, Visitor *v, void *opaque, 177 const char *name, Error **errp) 178 { 179 get_pointer(obj, v, opaque, print_chr, name, errp); 180 } 181 182 static void set_chr(Object *obj, Visitor *v, void *opaque, 183 const char *name, Error **errp) 184 { 185 set_pointer(obj, v, opaque, parse_chr, name, errp); 186 } 187 188 PropertyInfo qdev_prop_chr = { 189 .name = "str", 190 .description = "ID of a chardev to use as a backend", 191 .get = get_chr, 192 .set = set_chr, 193 .release = release_chr, 194 }; 195 196 /* --- netdev device --- */ 197 static void get_netdev(Object *obj, Visitor *v, void *opaque, 198 const char *name, Error **errp) 199 { 200 DeviceState *dev = DEVICE(obj); 201 Property *prop = opaque; 202 NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop); 203 char *p = g_strdup(peers_ptr->ncs[0] ? peers_ptr->ncs[0]->name : ""); 204 205 visit_type_str(v, &p, name, errp); 206 g_free(p); 207 } 208 209 static void set_netdev(Object *obj, Visitor *v, void *opaque, 210 const char *name, Error **errp) 211 { 212 DeviceState *dev = DEVICE(obj); 213 Property *prop = opaque; 214 NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop); 215 NetClientState **ncs = peers_ptr->ncs; 216 NetClientState *peers[MAX_QUEUE_NUM]; 217 Error *local_err = NULL; 218 int queues, err = 0, i = 0; 219 char *str; 220 221 if (dev->realized) { 222 qdev_prop_set_after_realize(dev, name, errp); 223 return; 224 } 225 226 visit_type_str(v, &str, name, &local_err); 227 if (local_err) { 228 error_propagate(errp, local_err); 229 return; 230 } 231 232 queues = qemu_find_net_clients_except(str, peers, 233 NET_CLIENT_OPTIONS_KIND_NIC, 234 MAX_QUEUE_NUM); 235 if (queues == 0) { 236 err = -ENOENT; 237 goto out; 238 } 239 240 if (queues > MAX_QUEUE_NUM) { 241 error_setg(errp, "queues of backend '%s'(%d) exceeds QEMU limitation(%d)", 242 str, queues, MAX_QUEUE_NUM); 243 goto out; 244 } 245 246 for (i = 0; i < queues; i++) { 247 if (peers[i] == NULL) { 248 err = -ENOENT; 249 goto out; 250 } 251 252 if (peers[i]->peer) { 253 err = -EEXIST; 254 goto out; 255 } 256 257 if (ncs[i]) { 258 err = -EINVAL; 259 goto out; 260 } 261 262 ncs[i] = peers[i]; 263 ncs[i]->queue_index = i; 264 } 265 266 peers_ptr->queues = queues; 267 268 out: 269 error_set_from_qdev_prop_error(errp, err, dev, prop, str); 270 g_free(str); 271 } 272 273 PropertyInfo qdev_prop_netdev = { 274 .name = "str", 275 .description = "ID of a netdev to use as a backend", 276 .get = get_netdev, 277 .set = set_netdev, 278 }; 279 280 /* --- vlan --- */ 281 282 static int print_vlan(DeviceState *dev, Property *prop, char *dest, size_t len) 283 { 284 NetClientState **ptr = qdev_get_prop_ptr(dev, prop); 285 286 if (*ptr) { 287 int id; 288 if (!net_hub_id_for_client(*ptr, &id)) { 289 return snprintf(dest, len, "%d", id); 290 } 291 } 292 293 return snprintf(dest, len, "<null>"); 294 } 295 296 static void get_vlan(Object *obj, Visitor *v, void *opaque, 297 const char *name, Error **errp) 298 { 299 DeviceState *dev = DEVICE(obj); 300 Property *prop = opaque; 301 NetClientState **ptr = qdev_get_prop_ptr(dev, prop); 302 int32_t id = -1; 303 304 if (*ptr) { 305 int hub_id; 306 if (!net_hub_id_for_client(*ptr, &hub_id)) { 307 id = hub_id; 308 } 309 } 310 311 visit_type_int32(v, &id, name, errp); 312 } 313 314 static void set_vlan(Object *obj, Visitor *v, void *opaque, 315 const char *name, Error **errp) 316 { 317 DeviceState *dev = DEVICE(obj); 318 Property *prop = opaque; 319 NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop); 320 NetClientState **ptr = &peers_ptr->ncs[0]; 321 Error *local_err = NULL; 322 int32_t id; 323 NetClientState *hubport; 324 325 if (dev->realized) { 326 qdev_prop_set_after_realize(dev, name, errp); 327 return; 328 } 329 330 visit_type_int32(v, &id, name, &local_err); 331 if (local_err) { 332 error_propagate(errp, local_err); 333 return; 334 } 335 if (id == -1) { 336 *ptr = NULL; 337 return; 338 } 339 if (*ptr) { 340 error_set_from_qdev_prop_error(errp, -EINVAL, dev, prop, name); 341 return; 342 } 343 344 hubport = net_hub_port_find(id); 345 if (!hubport) { 346 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, 347 name, prop->info->name); 348 return; 349 } 350 *ptr = hubport; 351 } 352 353 PropertyInfo qdev_prop_vlan = { 354 .name = "int32", 355 .description = "Integer VLAN id to connect to", 356 .print = print_vlan, 357 .get = get_vlan, 358 .set = set_vlan, 359 }; 360 361 void qdev_prop_set_drive(DeviceState *dev, const char *name, 362 BlockBackend *value, Error **errp) 363 { 364 object_property_set_str(OBJECT(dev), value ? blk_name(value) : "", 365 name, errp); 366 } 367 368 void qdev_prop_set_chr(DeviceState *dev, const char *name, 369 CharDriverState *value) 370 { 371 assert(!value || value->label); 372 object_property_set_str(OBJECT(dev), 373 value ? value->label : "", name, &error_abort); 374 } 375 376 void qdev_prop_set_netdev(DeviceState *dev, const char *name, 377 NetClientState *value) 378 { 379 assert(!value || value->name); 380 object_property_set_str(OBJECT(dev), 381 value ? value->name : "", name, &error_abort); 382 } 383 384 void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd) 385 { 386 qdev_prop_set_macaddr(dev, "mac", nd->macaddr.a); 387 if (nd->netdev) { 388 qdev_prop_set_netdev(dev, "netdev", nd->netdev); 389 } 390 if (nd->nvectors != DEV_NVECTORS_UNSPECIFIED && 391 object_property_find(OBJECT(dev), "vectors", NULL)) { 392 qdev_prop_set_uint32(dev, "vectors", nd->nvectors); 393 } 394 nd->instantiated = 1; 395 } 396 397 static int qdev_add_one_global(void *opaque, QemuOpts *opts, Error **errp) 398 { 399 GlobalProperty *g; 400 401 g = g_malloc0(sizeof(*g)); 402 g->driver = qemu_opt_get(opts, "driver"); 403 g->property = qemu_opt_get(opts, "property"); 404 g->value = qemu_opt_get(opts, "value"); 405 g->user_provided = true; 406 qdev_prop_register_global(g); 407 return 0; 408 } 409 410 void qemu_add_globals(void) 411 { 412 qemu_opts_foreach(qemu_find_opts("global"), 413 qdev_add_one_global, NULL, NULL); 414 } 415