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