qemu/hw/core/qdev-properties-system.c
<<
>>
Prefs
   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 "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
  26static 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
  39static 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
  71static 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->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
  99static 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
 111static char *print_drive(void *ptr)
 112{
 113    return g_strdup(blk_name(ptr));
 114}
 115
 116static 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
 122static 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
 128PropertyInfo 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
 138static 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
 155static 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
 169static 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
 177static 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
 183static 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
 189PropertyInfo 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 --- */
 198static 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
 210static 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
 269out:
 270    error_set_from_qdev_prop_error(errp, err, dev, prop, str);
 271    g_free(str);
 272}
 273
 274PropertyInfo 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
 283static 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
 297static 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
 315static 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
 354PropertyInfo 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
 362void 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
 369void 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
 377void 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
 385void 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
 398static int qdev_add_one_global(void *opaque, QemuOpts *opts, Error **errp)
 399{
 400    GlobalProperty *g;
 401
 402    g = g_malloc0(sizeof(*g));
 403    g->driver   = qemu_opt_get(opts, "driver");
 404    g->property = qemu_opt_get(opts, "property");
 405    g->value    = qemu_opt_get(opts, "value");
 406    g->user_provided = true;
 407    qdev_prop_register_global(g);
 408    return 0;
 409}
 410
 411void qemu_add_globals(void)
 412{
 413    qemu_opts_foreach(qemu_find_opts("global"),
 414                      qdev_add_one_global, NULL, NULL);
 415}
 416