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 int (*parse)(DeviceState *dev, const char *str, 39 void **ptr), 40 const char *name, Error **errp) 41 { 42 DeviceState *dev = DEVICE(obj); 43 Error *local_err = NULL; 44 void **ptr = qdev_get_prop_ptr(dev, prop); 45 char *str; 46 int ret; 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 ret = parse(dev, str, ptr); 64 error_set_from_qdev_prop_error(errp, ret, dev, prop, str); 65 g_free(str); 66 } 67 68 /* --- drive --- */ 69 70 static int parse_drive(DeviceState *dev, const char *str, void **ptr) 71 { 72 BlockBackend *blk; 73 74 blk = blk_by_name(str); 75 if (!blk) { 76 return -ENOENT; 77 } 78 if (blk_attach_dev(blk, dev) < 0) { 79 return -EEXIST; 80 } 81 *ptr = blk; 82 return 0; 83 } 84 85 static void release_drive(Object *obj, const char *name, void *opaque) 86 { 87 DeviceState *dev = DEVICE(obj); 88 Property *prop = opaque; 89 BlockBackend **ptr = qdev_get_prop_ptr(dev, prop); 90 91 if (*ptr) { 92 blk_detach_dev(*ptr, dev); 93 blockdev_auto_del(*ptr); 94 } 95 } 96 97 static char *print_drive(void *ptr) 98 { 99 return g_strdup(blk_name(ptr)); 100 } 101 102 static void get_drive(Object *obj, Visitor *v, void *opaque, 103 const char *name, Error **errp) 104 { 105 get_pointer(obj, v, opaque, print_drive, name, errp); 106 } 107 108 static void set_drive(Object *obj, Visitor *v, void *opaque, 109 const char *name, Error **errp) 110 { 111 set_pointer(obj, v, opaque, parse_drive, name, errp); 112 } 113 114 PropertyInfo qdev_prop_drive = { 115 .name = "str", 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 .description = "ID of a chardev to use as a backend", 174 .get = get_chr, 175 .set = set_chr, 176 .release = release_chr, 177 }; 178 179 /* --- netdev device --- */ 180 static void get_netdev(Object *obj, Visitor *v, void *opaque, 181 const char *name, Error **errp) 182 { 183 DeviceState *dev = DEVICE(obj); 184 Property *prop = opaque; 185 NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop); 186 char *p = g_strdup(peers_ptr->ncs[0] ? peers_ptr->ncs[0]->name : ""); 187 188 visit_type_str(v, &p, name, errp); 189 g_free(p); 190 } 191 192 static void set_netdev(Object *obj, Visitor *v, void *opaque, 193 const char *name, Error **errp) 194 { 195 DeviceState *dev = DEVICE(obj); 196 Property *prop = opaque; 197 NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop); 198 NetClientState **ncs = peers_ptr->ncs; 199 NetClientState *peers[MAX_QUEUE_NUM]; 200 Error *local_err = NULL; 201 int queues, err = 0, i = 0; 202 char *str; 203 204 if (dev->realized) { 205 qdev_prop_set_after_realize(dev, name, errp); 206 return; 207 } 208 209 visit_type_str(v, &str, name, &local_err); 210 if (local_err) { 211 error_propagate(errp, local_err); 212 return; 213 } 214 215 queues = qemu_find_net_clients_except(str, peers, 216 NET_CLIENT_OPTIONS_KIND_NIC, 217 MAX_QUEUE_NUM); 218 if (queues == 0) { 219 err = -ENOENT; 220 goto out; 221 } 222 223 if (queues > MAX_QUEUE_NUM) { 224 error_setg(errp, "queues of backend '%s'(%d) exceeds QEMU limitation(%d)", 225 str, queues, MAX_QUEUE_NUM); 226 goto out; 227 } 228 229 for (i = 0; i < queues; i++) { 230 if (peers[i] == NULL) { 231 err = -ENOENT; 232 goto out; 233 } 234 235 if (peers[i]->peer) { 236 err = -EEXIST; 237 goto out; 238 } 239 240 if (ncs[i]) { 241 err = -EINVAL; 242 goto out; 243 } 244 245 ncs[i] = peers[i]; 246 ncs[i]->queue_index = i; 247 } 248 249 peers_ptr->queues = queues; 250 251 out: 252 error_set_from_qdev_prop_error(errp, err, dev, prop, str); 253 g_free(str); 254 } 255 256 PropertyInfo qdev_prop_netdev = { 257 .name = "str", 258 .description = "ID of a netdev to use as a backend", 259 .get = get_netdev, 260 .set = set_netdev, 261 }; 262 263 /* --- vlan --- */ 264 265 static int print_vlan(DeviceState *dev, Property *prop, char *dest, size_t len) 266 { 267 NetClientState **ptr = qdev_get_prop_ptr(dev, prop); 268 269 if (*ptr) { 270 int id; 271 if (!net_hub_id_for_client(*ptr, &id)) { 272 return snprintf(dest, len, "%d", id); 273 } 274 } 275 276 return snprintf(dest, len, "<null>"); 277 } 278 279 static void get_vlan(Object *obj, Visitor *v, void *opaque, 280 const char *name, Error **errp) 281 { 282 DeviceState *dev = DEVICE(obj); 283 Property *prop = opaque; 284 NetClientState **ptr = qdev_get_prop_ptr(dev, prop); 285 int32_t id = -1; 286 287 if (*ptr) { 288 int hub_id; 289 if (!net_hub_id_for_client(*ptr, &hub_id)) { 290 id = hub_id; 291 } 292 } 293 294 visit_type_int32(v, &id, name, errp); 295 } 296 297 static void set_vlan(Object *obj, Visitor *v, void *opaque, 298 const char *name, Error **errp) 299 { 300 DeviceState *dev = DEVICE(obj); 301 Property *prop = opaque; 302 NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop); 303 NetClientState **ptr = &peers_ptr->ncs[0]; 304 Error *local_err = NULL; 305 int32_t id; 306 NetClientState *hubport; 307 308 if (dev->realized) { 309 qdev_prop_set_after_realize(dev, name, errp); 310 return; 311 } 312 313 visit_type_int32(v, &id, name, &local_err); 314 if (local_err) { 315 error_propagate(errp, local_err); 316 return; 317 } 318 if (id == -1) { 319 *ptr = NULL; 320 return; 321 } 322 if (*ptr) { 323 error_set_from_qdev_prop_error(errp, -EINVAL, dev, prop, name); 324 return; 325 } 326 327 hubport = net_hub_port_find(id); 328 if (!hubport) { 329 error_set(errp, QERR_INVALID_PARAMETER_VALUE, 330 name, prop->info->name); 331 return; 332 } 333 *ptr = hubport; 334 } 335 336 PropertyInfo qdev_prop_vlan = { 337 .name = "int32", 338 .description = "Integer VLAN id to connect to", 339 .print = print_vlan, 340 .get = get_vlan, 341 .set = set_vlan, 342 }; 343 344 void qdev_prop_set_drive(DeviceState *dev, const char *name, 345 BlockBackend *value, Error **errp) 346 { 347 object_property_set_str(OBJECT(dev), value ? blk_name(value) : "", 348 name, errp); 349 } 350 351 void qdev_prop_set_drive_nofail(DeviceState *dev, const char *name, 352 BlockBackend *value) 353 { 354 Error *err = NULL; 355 356 qdev_prop_set_drive(dev, name, value, &err); 357 if (err) { 358 error_report_err(err); 359 exit(1); 360 } 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