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