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