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