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