qemu/hw/core/qdev-properties-system.c
<<
>>
Prefs
   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 "chardev/char-fe.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    bool blk_created = false;
  76    int ret;
  77
  78    blk = blk_by_name(str);
  79    if (!blk) {
  80        BlockDriverState *bs = bdrv_lookup_bs(NULL, str, NULL);
  81        if (bs) {
  82            blk = blk_new(0, BLK_PERM_ALL);
  83            blk_created = true;
  84
  85            ret = blk_insert_bs(blk, bs, errp);
  86            if (ret < 0) {
  87                goto fail;
  88            }
  89        }
  90    }
  91    if (!blk) {
  92        error_setg(errp, "Property '%s.%s' can't find value '%s'",
  93                   object_get_typename(OBJECT(dev)), propname, str);
  94        goto fail;
  95    }
  96    if (blk_attach_dev(blk, dev) < 0) {
  97        DriveInfo *dinfo = blk_legacy_dinfo(blk);
  98
  99        if (dinfo && dinfo->type != IF_NONE) {
 100            error_setg(errp, "Drive '%s' is already in use because "
 101                       "it has been automatically connected to another "
 102                       "device (did you need 'if=none' in the drive options?)",
 103                       str);
 104        } else {
 105            error_setg(errp, "Drive '%s' is already in use by another device",
 106                       str);
 107        }
 108        goto fail;
 109    }
 110
 111    *ptr = blk;
 112
 113fail:
 114    if (blk_created) {
 115        /* If we need to keep a reference, blk_attach_dev() took it */
 116        blk_unref(blk);
 117    }
 118}
 119
 120static void release_drive(Object *obj, const char *name, void *opaque)
 121{
 122    DeviceState *dev = DEVICE(obj);
 123    Property *prop = opaque;
 124    BlockBackend **ptr = qdev_get_prop_ptr(dev, prop);
 125
 126    if (*ptr) {
 127        AioContext *ctx = blk_get_aio_context(*ptr);
 128
 129        aio_context_acquire(ctx);
 130        blockdev_auto_del(*ptr);
 131        blk_detach_dev(*ptr, dev);
 132        aio_context_release(ctx);
 133    }
 134}
 135
 136static char *print_drive(void *ptr)
 137{
 138    const char *name;
 139
 140    name = blk_name(ptr);
 141    if (!*name) {
 142        BlockDriverState *bs = blk_bs(ptr);
 143        if (bs) {
 144            name = bdrv_get_node_name(bs);
 145        }
 146    }
 147    return g_strdup(name);
 148}
 149
 150static void get_drive(Object *obj, Visitor *v, const char *name, void *opaque,
 151                      Error **errp)
 152{
 153    get_pointer(obj, v, opaque, print_drive, name, errp);
 154}
 155
 156static void set_drive(Object *obj, Visitor *v, const char *name, void *opaque,
 157                      Error **errp)
 158{
 159    set_pointer(obj, v, opaque, parse_drive, name, errp);
 160}
 161
 162const PropertyInfo qdev_prop_drive = {
 163    .name  = "str",
 164    .description = "Node name or ID of a block device to use as a backend",
 165    .get   = get_drive,
 166    .set   = set_drive,
 167    .release = release_drive,
 168};
 169
 170/* --- character device --- */
 171
 172static void get_chr(Object *obj, Visitor *v, const char *name, void *opaque,
 173                    Error **errp)
 174{
 175    DeviceState *dev = DEVICE(obj);
 176    CharBackend *be = qdev_get_prop_ptr(dev, opaque);
 177    char *p;
 178
 179    p = g_strdup(be->chr && be->chr->label ? be->chr->label : "");
 180    visit_type_str(v, name, &p, errp);
 181    g_free(p);
 182}
 183
 184static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque,
 185                    Error **errp)
 186{
 187    DeviceState *dev = DEVICE(obj);
 188    Error *local_err = NULL;
 189    Property *prop = opaque;
 190    CharBackend *be = qdev_get_prop_ptr(dev, prop);
 191    Chardev *s;
 192    char *str;
 193
 194    if (dev->realized) {
 195        qdev_prop_set_after_realize(dev, name, errp);
 196        return;
 197    }
 198
 199    visit_type_str(v, name, &str, &local_err);
 200    if (local_err) {
 201        error_propagate(errp, local_err);
 202        return;
 203    }
 204
 205    if (!*str) {
 206        g_free(str);
 207        be->chr = NULL;
 208        return;
 209    }
 210
 211    s = qemu_chr_find(str);
 212    if (s == NULL) {
 213        error_setg(errp, "Property '%s.%s' can't find value '%s'",
 214                   object_get_typename(obj), prop->name, str);
 215    } else if (!qemu_chr_fe_init(be, s, errp)) {
 216        error_prepend(errp, "Property '%s.%s' can't take value '%s': ",
 217                      object_get_typename(obj), prop->name, str);
 218    }
 219    g_free(str);
 220}
 221
 222static void release_chr(Object *obj, const char *name, void *opaque)
 223{
 224    DeviceState *dev = DEVICE(obj);
 225    Property *prop = opaque;
 226    CharBackend *be = qdev_get_prop_ptr(dev, prop);
 227
 228    qemu_chr_fe_deinit(be, false);
 229}
 230
 231const PropertyInfo qdev_prop_chr = {
 232    .name  = "str",
 233    .description = "ID of a chardev to use as a backend",
 234    .get   = get_chr,
 235    .set   = set_chr,
 236    .release = release_chr,
 237};
 238
 239/* --- netdev device --- */
 240static void get_netdev(Object *obj, Visitor *v, const char *name,
 241                       void *opaque, Error **errp)
 242{
 243    DeviceState *dev = DEVICE(obj);
 244    Property *prop = opaque;
 245    NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop);
 246    char *p = g_strdup(peers_ptr->ncs[0] ? peers_ptr->ncs[0]->name : "");
 247
 248    visit_type_str(v, name, &p, errp);
 249    g_free(p);
 250}
 251
 252static void set_netdev(Object *obj, Visitor *v, const char *name,
 253                       void *opaque, Error **errp)
 254{
 255    DeviceState *dev = DEVICE(obj);
 256    Property *prop = opaque;
 257    NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop);
 258    NetClientState **ncs = peers_ptr->ncs;
 259    NetClientState *peers[MAX_QUEUE_NUM];
 260    Error *local_err = NULL;
 261    int queues, err = 0, i = 0;
 262    char *str;
 263
 264    if (dev->realized) {
 265        qdev_prop_set_after_realize(dev, name, errp);
 266        return;
 267    }
 268
 269    visit_type_str(v, name, &str, &local_err);
 270    if (local_err) {
 271        error_propagate(errp, local_err);
 272        return;
 273    }
 274
 275    queues = qemu_find_net_clients_except(str, peers,
 276                                          NET_CLIENT_DRIVER_NIC,
 277                                          MAX_QUEUE_NUM);
 278    if (queues == 0) {
 279        err = -ENOENT;
 280        goto out;
 281    }
 282
 283    if (queues > MAX_QUEUE_NUM) {
 284        error_setg(errp, "queues of backend '%s'(%d) exceeds QEMU limitation(%d)",
 285                   str, queues, MAX_QUEUE_NUM);
 286        goto out;
 287    }
 288
 289    for (i = 0; i < queues; i++) {
 290        if (peers[i] == NULL) {
 291            err = -ENOENT;
 292            goto out;
 293        }
 294
 295        if (peers[i]->peer) {
 296            err = -EEXIST;
 297            goto out;
 298        }
 299
 300        if (ncs[i]) {
 301            err = -EINVAL;
 302            goto out;
 303        }
 304
 305        ncs[i] = peers[i];
 306        ncs[i]->queue_index = i;
 307    }
 308
 309    peers_ptr->queues = queues;
 310
 311out:
 312    error_set_from_qdev_prop_error(errp, err, dev, prop, str);
 313    g_free(str);
 314}
 315
 316const PropertyInfo qdev_prop_netdev = {
 317    .name  = "str",
 318    .description = "ID of a netdev to use as a backend",
 319    .get   = get_netdev,
 320    .set   = set_netdev,
 321};
 322
 323/* --- vlan --- */
 324
 325static int print_vlan(DeviceState *dev, Property *prop, char *dest, size_t len)
 326{
 327    NetClientState **ptr = qdev_get_prop_ptr(dev, prop);
 328
 329    if (*ptr) {
 330        int id;
 331        if (!net_hub_id_for_client(*ptr, &id)) {
 332            return snprintf(dest, len, "%d", id);
 333        }
 334    }
 335
 336    return snprintf(dest, len, "<null>");
 337}
 338
 339static void get_vlan(Object *obj, Visitor *v, const char *name, void *opaque,
 340                     Error **errp)
 341{
 342    DeviceState *dev = DEVICE(obj);
 343    Property *prop = opaque;
 344    NetClientState **ptr = qdev_get_prop_ptr(dev, prop);
 345    int32_t id = -1;
 346
 347    if (*ptr) {
 348        int hub_id;
 349        if (!net_hub_id_for_client(*ptr, &hub_id)) {
 350            id = hub_id;
 351        }
 352    }
 353
 354    visit_type_int32(v, name, &id, errp);
 355}
 356
 357static void set_vlan(Object *obj, Visitor *v, const char *name, void *opaque,
 358                     Error **errp)
 359{
 360    DeviceState *dev = DEVICE(obj);
 361    Property *prop = opaque;
 362    NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop);
 363    NetClientState **ptr = &peers_ptr->ncs[0];
 364    Error *local_err = NULL;
 365    int32_t id;
 366    NetClientState *hubport;
 367
 368    if (dev->realized) {
 369        qdev_prop_set_after_realize(dev, name, errp);
 370        return;
 371    }
 372
 373    visit_type_int32(v, name, &id, &local_err);
 374    if (local_err) {
 375        error_propagate(errp, local_err);
 376        return;
 377    }
 378    if (id == -1) {
 379        *ptr = NULL;
 380        return;
 381    }
 382    if (*ptr) {
 383        error_set_from_qdev_prop_error(errp, -EINVAL, dev, prop, name);
 384        return;
 385    }
 386
 387    hubport = net_hub_port_find(id);
 388    if (!hubport) {
 389        error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
 390                   name, prop->info->name);
 391        return;
 392    }
 393    *ptr = hubport;
 394}
 395
 396const PropertyInfo qdev_prop_vlan = {
 397    .name  = "int32",
 398    .description = "Integer VLAN id to connect to",
 399    .print = print_vlan,
 400    .get   = get_vlan,
 401    .set   = set_vlan,
 402};
 403
 404void qdev_prop_set_drive(DeviceState *dev, const char *name,
 405                         BlockBackend *value, Error **errp)
 406{
 407    const char *ref = "";
 408
 409    if (value) {
 410        ref = blk_name(value);
 411        if (!*ref) {
 412            const BlockDriverState *bs = blk_bs(value);
 413            if (bs) {
 414                ref = bdrv_get_node_name(bs);
 415            }
 416        }
 417    }
 418
 419    object_property_set_str(OBJECT(dev), ref, name, errp);
 420}
 421
 422void qdev_prop_set_chr(DeviceState *dev, const char *name,
 423                       Chardev *value)
 424{
 425    assert(!value || value->label);
 426    object_property_set_str(OBJECT(dev),
 427                            value ? value->label : "", name, &error_abort);
 428}
 429
 430void qdev_prop_set_netdev(DeviceState *dev, const char *name,
 431                          NetClientState *value)
 432{
 433    assert(!value || value->name);
 434    object_property_set_str(OBJECT(dev),
 435                            value ? value->name : "", name, &error_abort);
 436}
 437
 438void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd)
 439{
 440    qdev_prop_set_macaddr(dev, "mac", nd->macaddr.a);
 441    if (nd->netdev) {
 442        qdev_prop_set_netdev(dev, "netdev", nd->netdev);
 443    }
 444    if (nd->nvectors != DEV_NVECTORS_UNSPECIFIED &&
 445        object_property_find(OBJECT(dev), "vectors", NULL)) {
 446        qdev_prop_set_uint32(dev, "vectors", nd->nvectors);
 447    }
 448    nd->instantiated = 1;
 449}
 450