qdev-monitor.c 19.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
 *  Dynamic device configuration and creation.
 *
 *  Copyright (c) 2009 CodeSourcery
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
 */

20
#include "hw/qdev.h"
21
#include "hw/sysbus.h"
22
#include "monitor/monitor.h"
23
#include "monitor/qdev.h"
L
Luiz Capitulino 已提交
24
#include "qmp-commands.h"
25
#include "sysemu/arch_init.h"
26
#include "qemu/config-file.h"
27 28 29 30 31 32 33 34 35

/*
 * Aliases were a bad idea from the start.  Let's keep them
 * from spreading further.
 */
typedef struct QDevAlias
{
    const char *typename;
    const char *alias;
A
Alexander Graf 已提交
36
    uint32_t arch_mask;
37 38 39
} QDevAlias;

static const QDevAlias qdev_alias_table[] = {
A
Alexander Graf 已提交
40 41 42 43 44 45 46 47
    { "virtio-blk-pci", "virtio-blk", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
    { "virtio-net-pci", "virtio-net", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
    { "virtio-serial-pci", "virtio-serial", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
    { "virtio-balloon-pci", "virtio-balloon",
            QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
    { "virtio-blk-s390", "virtio-blk", QEMU_ARCH_S390X },
    { "virtio-net-s390", "virtio-net", QEMU_ARCH_S390X },
    { "virtio-serial-s390", "virtio-serial", QEMU_ARCH_S390X },
48 49
    { "lsi53c895a", "lsi" },
    { "ich9-ahci", "ahci" },
50
    { "kvm-pci-assign", "pci-assign" },
51 52 53 54 55 56 57 58 59
    { }
};

static const char *qdev_class_get_alias(DeviceClass *dc)
{
    const char *typename = object_class_get_name(OBJECT_CLASS(dc));
    int i;

    for (i = 0; qdev_alias_table[i].typename; i++) {
A
Alexander Graf 已提交
60 61 62 63 64
        if (qdev_alias_table[i].arch_mask &&
            !(qdev_alias_table[i].arch_mask & arch_type)) {
            continue;
        }

65 66 67 68 69 70 71 72 73 74 75 76 77
        if (strcmp(qdev_alias_table[i].typename, typename) == 0) {
            return qdev_alias_table[i].alias;
        }
    }

    return NULL;
}

static bool qdev_class_has_alias(DeviceClass *dc)
{
    return (qdev_class_get_alias(dc) != NULL);
}

78
static void qdev_print_devinfo(DeviceClass *dc)
79
{
80
    error_printf("name \"%s\"", object_class_get_name(OBJECT_CLASS(dc)));
81 82
    if (dc->bus_type) {
        error_printf(", bus %s", dc->bus_type);
83 84 85 86 87 88 89 90 91 92 93 94 95
    }
    if (qdev_class_has_alias(dc)) {
        error_printf(", alias \"%s\"", qdev_class_get_alias(dc));
    }
    if (dc->desc) {
        error_printf(", desc \"%s\"", dc->desc);
    }
    if (dc->no_user) {
        error_printf(", no-user");
    }
    error_printf("\n");
}

96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
static gint devinfo_cmp(gconstpointer a, gconstpointer b)
{
    return strcasecmp(object_class_get_name((ObjectClass *)a),
                      object_class_get_name((ObjectClass *)b));
}

static void qdev_print_devinfos(bool show_no_user)
{
    static const char *cat_name[DEVICE_CATEGORY_MAX + 1] = {
        [DEVICE_CATEGORY_BRIDGE]  = "Controller/Bridge/Hub",
        [DEVICE_CATEGORY_USB]     = "USB",
        [DEVICE_CATEGORY_STORAGE] = "Storage",
        [DEVICE_CATEGORY_NETWORK] = "Network",
        [DEVICE_CATEGORY_INPUT]   = "Input",
        [DEVICE_CATEGORY_DISPLAY] = "Display",
        [DEVICE_CATEGORY_SOUND]   = "Sound",
        [DEVICE_CATEGORY_MISC]    = "Misc",
        [DEVICE_CATEGORY_MAX]     = "Uncategorized",
    };
    GSList *list, *elt;
    int i;
    bool cat_printed;

    list = g_slist_sort(object_class_get_list(TYPE_DEVICE, false),
                        devinfo_cmp);

    for (i = 0; i <= DEVICE_CATEGORY_MAX; i++) {
        cat_printed = false;
        for (elt = list; elt; elt = elt->next) {
            DeviceClass *dc = OBJECT_CLASS_CHECK(DeviceClass, elt->data,
                                                 TYPE_DEVICE);
            if ((i < DEVICE_CATEGORY_MAX
                 ? !test_bit(i, dc->categories)
                 : !bitmap_empty(dc->categories, DEVICE_CATEGORY_MAX))
                || (!show_no_user && dc->no_user)) {
                continue;
            }
            if (!cat_printed) {
                error_printf("%s%s devices:\n", i ? "\n" : "",
                             cat_name[i]);
                cat_printed = true;
            }
            qdev_print_devinfo(dc);
        }
    }

    g_slist_free(list);
}

145 146 147
static int set_property(const char *name, const char *value, void *opaque)
{
    DeviceState *dev = opaque;
148
    Error *err = NULL;
149 150 151 152 153 154

    if (strcmp(name, "driver") == 0)
        return 0;
    if (strcmp(name, "bus") == 0)
        return 0;

155 156 157 158
    qdev_prop_parse(dev, name, value, &err);
    if (err != NULL) {
        qerror_report_err(err);
        error_free(err);
159 160 161 162 163 164 165 166 167 168
        return -1;
    }
    return 0;
}

static const char *find_typename_by_alias(const char *alias)
{
    int i;

    for (i = 0; qdev_alias_table[i].alias; i++) {
A
Alexander Graf 已提交
169 170 171 172 173
        if (qdev_alias_table[i].arch_mask &&
            !(qdev_alias_table[i].arch_mask & arch_type)) {
            continue;
        }

174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
        if (strcmp(qdev_alias_table[i].alias, alias) == 0) {
            return qdev_alias_table[i].typename;
        }
    }

    return NULL;
}

int qdev_device_help(QemuOpts *opts)
{
    const char *driver;
    Property *prop;
    ObjectClass *klass;

    driver = qemu_opt_get(opts, "driver");
189
    if (driver && is_help_option(driver)) {
190
        qdev_print_devinfos(false);
191 192 193
        return 1;
    }

194
    if (!driver || !qemu_opt_has_help_opt(opts)) {
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
        return 0;
    }

    klass = object_class_by_name(driver);
    if (!klass) {
        const char *typename = find_typename_by_alias(driver);

        if (typename) {
            driver = typename;
            klass = object_class_by_name(driver);
        }
    }

    if (!klass) {
        return 0;
    }
211 212 213 214 215 216 217 218
    do {
        for (prop = DEVICE_CLASS(klass)->props; prop && prop->name; prop++) {
            /*
             * TODO Properties without a parser are just for dirty hacks.
             * qdev_prop_ptr is the only such PropertyInfo.  It's marked
             * for removal.  This conditional should be removed along with
             * it.
             */
P
Paolo Bonzini 已提交
219
            if (!prop->info->set) {
220 221 222 223
                continue;           /* no way to set it, don't show */
            }
            error_printf("%s.%s=%s\n", driver, prop->name,
                         prop->info->legacy_name ?: prop->info->name);
224
        }
225 226
        klass = object_class_get_parent(klass);
    } while (klass != object_class_by_name(TYPE_DEVICE));
227 228 229
    return 1;
}

230
static Object *qdev_get_peripheral(void)
231
{
232
    static Object *dev;
233 234

    if (dev == NULL) {
235
        dev = container_get(qdev_get_machine(), "/peripheral");
236 237
    }

238
    return dev;
239 240
}

241
static Object *qdev_get_peripheral_anon(void)
242
{
243
    static Object *dev;
244 245

    if (dev == NULL) {
246
        dev = container_get(qdev_get_machine(), "/peripheral-anon");
247 248
    }

249
    return dev;
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
}

static void qbus_list_bus(DeviceState *dev)
{
    BusState *child;
    const char *sep = " ";

    error_printf("child busses at \"%s\":",
                 dev->id ? dev->id : object_get_typename(OBJECT(dev)));
    QLIST_FOREACH(child, &dev->child_bus, sibling) {
        error_printf("%s\"%s\"", sep, child->name);
        sep = ", ";
    }
    error_printf("\n");
}

static void qbus_list_dev(BusState *bus)
{
A
Anthony Liguori 已提交
268
    BusChild *kid;
269 270 271
    const char *sep = " ";

    error_printf("devices at \"%s\":", bus->name);
A
Anthony Liguori 已提交
272 273
    QTAILQ_FOREACH(kid, &bus->children, sibling) {
        DeviceState *dev = kid->child;
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
        error_printf("%s\"%s\"", sep, object_get_typename(OBJECT(dev)));
        if (dev->id)
            error_printf("/\"%s\"", dev->id);
        sep = ", ";
    }
    error_printf("\n");
}

static BusState *qbus_find_bus(DeviceState *dev, char *elem)
{
    BusState *child;

    QLIST_FOREACH(child, &dev->child_bus, sibling) {
        if (strcmp(child->name, elem) == 0) {
            return child;
        }
    }
    return NULL;
}

static DeviceState *qbus_find_dev(BusState *bus, char *elem)
{
A
Anthony Liguori 已提交
296
    BusChild *kid;
297 298 299 300 301 302 303

    /*
     * try to match in order:
     *   (1) instance id, if present
     *   (2) driver name
     *   (3) driver alias, if present
     */
A
Anthony Liguori 已提交
304 305
    QTAILQ_FOREACH(kid, &bus->children, sibling) {
        DeviceState *dev = kid->child;
306 307 308 309
        if (dev->id  &&  strcmp(dev->id, elem) == 0) {
            return dev;
        }
    }
A
Anthony Liguori 已提交
310 311
    QTAILQ_FOREACH(kid, &bus->children, sibling) {
        DeviceState *dev = kid->child;
312 313 314 315
        if (strcmp(object_get_typename(OBJECT(dev)), elem) == 0) {
            return dev;
        }
    }
A
Anthony Liguori 已提交
316 317
    QTAILQ_FOREACH(kid, &bus->children, sibling) {
        DeviceState *dev = kid->child;
318 319 320 321 322 323 324 325 326 327 328
        DeviceClass *dc = DEVICE_GET_CLASS(dev);

        if (qdev_class_has_alias(dc) &&
            strcmp(qdev_class_get_alias(dc), elem) == 0) {
            return dev;
        }
    }
    return NULL;
}

static BusState *qbus_find_recursive(BusState *bus, const char *name,
329
                                     const char *bus_typename)
330
{
331
    BusClass *bus_class = BUS_GET_CLASS(bus);
A
Anthony Liguori 已提交
332
    BusChild *kid;
333 334 335 336 337
    BusState *child, *ret;
    int match = 1;

    if (name && (strcmp(bus->name, name) != 0)) {
        match = 0;
338
    } else if (bus_typename && !object_dynamic_cast(OBJECT(bus), bus_typename)) {
339
        match = 0;
340
    } else if ((bus_class->max_dev != 0) && (bus_class->max_dev <= bus->max_index)) {
341 342 343 344 345 346 347 348 349 350
        if (name != NULL) {
            /* bus was explicitly specified: return an error. */
            qerror_report(ERROR_CLASS_GENERIC_ERROR, "Bus '%s' is full",
                          bus->name);
            return NULL;
        } else {
            /* bus was not specified: try to find another one. */
            match = 0;
        }
    }
351 352 353 354
    if (match) {
        return bus;
    }

A
Anthony Liguori 已提交
355 356
    QTAILQ_FOREACH(kid, &bus->children, sibling) {
        DeviceState *dev = kid->child;
357
        QLIST_FOREACH(child, &dev->child_bus, sibling) {
358
            ret = qbus_find_recursive(child, name, bus_typename);
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401
            if (ret) {
                return ret;
            }
        }
    }
    return NULL;
}

static BusState *qbus_find(const char *path)
{
    DeviceState *dev;
    BusState *bus;
    char elem[128];
    int pos, len;

    /* find start element */
    if (path[0] == '/') {
        bus = sysbus_get_default();
        pos = 0;
    } else {
        if (sscanf(path, "%127[^/]%n", elem, &len) != 1) {
            assert(!path[0]);
            elem[0] = len = 0;
        }
        bus = qbus_find_recursive(sysbus_get_default(), elem, NULL);
        if (!bus) {
            qerror_report(QERR_BUS_NOT_FOUND, elem);
            return NULL;
        }
        pos = len;
    }

    for (;;) {
        assert(path[pos] == '/' || !path[pos]);
        while (path[pos] == '/') {
            pos++;
        }
        if (path[pos] == '\0') {
            return bus;
        }

        /* find device */
        if (sscanf(path+pos, "%127[^/]%n", elem, &len) != 1) {
402
            g_assert_not_reached();
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
            elem[0] = len = 0;
        }
        pos += len;
        dev = qbus_find_dev(bus, elem);
        if (!dev) {
            qerror_report(QERR_DEVICE_NOT_FOUND, elem);
            if (!monitor_cur_is_qmp()) {
                qbus_list_dev(bus);
            }
            return NULL;
        }

        assert(path[pos] == '/' || !path[pos]);
        while (path[pos] == '/') {
            pos++;
        }
        if (path[pos] == '\0') {
            /* last specified element is a device.  If it has exactly
             * one child bus accept it nevertheless */
            switch (dev->num_child_bus) {
            case 0:
                qerror_report(QERR_DEVICE_NO_BUS, elem);
                return NULL;
            case 1:
                return QLIST_FIRST(&dev->child_bus);
            default:
                qerror_report(QERR_DEVICE_MULTIPLE_BUSSES, elem);
                if (!monitor_cur_is_qmp()) {
                    qbus_list_bus(dev);
                }
                return NULL;
            }
        }

        /* find bus */
        if (sscanf(path+pos, "%127[^/]%n", elem, &len) != 1) {
439
            g_assert_not_reached();
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455
            elem[0] = len = 0;
        }
        pos += len;
        bus = qbus_find_bus(dev, elem);
        if (!bus) {
            qerror_report(QERR_BUS_NOT_FOUND, elem);
            if (!monitor_cur_is_qmp()) {
                qbus_list_bus(dev);
            }
            return NULL;
        }
    }
}

DeviceState *qdev_device_add(QemuOpts *opts)
{
456 457
    ObjectClass *oc;
    DeviceClass *dc;
458 459
    const char *driver, *path, *id;
    DeviceState *qdev;
460
    BusState *bus = NULL;
461 462 463 464 465 466 467 468

    driver = qemu_opt_get(opts, "driver");
    if (!driver) {
        qerror_report(QERR_MISSING_PARAMETER, "driver");
        return NULL;
    }

    /* find driver */
469 470
    oc = object_class_by_name(driver);
    if (!oc) {
471 472 473 474
        const char *typename = find_typename_by_alias(driver);

        if (typename) {
            driver = typename;
475
            oc = object_class_by_name(driver);
476 477 478
        }
    }

479
    if (!oc) {
480 481 482 483
        qerror_report(QERR_INVALID_PARAMETER_VALUE, "driver", "device type");
        return NULL;
    }

484 485 486 487 488 489
    if (object_class_is_abstract(oc)) {
        qerror_report(QERR_INVALID_PARAMETER_VALUE, "driver",
                      "non-abstract device type");
        return NULL;
    }

490
    dc = DEVICE_CLASS(oc);
491 492 493 494 495 496 497 498

    /* find bus */
    path = qemu_opt_get(opts, "bus");
    if (path != NULL) {
        bus = qbus_find(path);
        if (!bus) {
            return NULL;
        }
499
        if (!object_dynamic_cast(OBJECT(bus), dc->bus_type)) {
500
            qerror_report(QERR_BAD_BUS_FOR_DEVICE,
501
                          driver, object_get_typename(OBJECT(bus)));
502 503
            return NULL;
        }
504 505
    } else if (dc->bus_type != NULL) {
        bus = qbus_find_recursive(sysbus_get_default(), NULL, dc->bus_type);
506 507
        if (!bus) {
            qerror_report(QERR_NO_BUS_FOR_DEVICE,
508
                          dc->bus_type, driver);
509 510 511
            return NULL;
        }
    }
512
    if (qdev_hotplug && bus && !bus->allow_hotplug) {
513 514 515 516 517 518
        qerror_report(QERR_BUS_NO_HOTPLUG, bus->name);
        return NULL;
    }

    /* create device, set properties */
    qdev = DEVICE(object_new(driver));
519 520 521 522

    if (bus) {
        qdev_set_parent_bus(qdev, bus);
    }
523 524 525 526

    id = qemu_opts_id(opts);
    if (id) {
        qdev->id = id;
527 528 529
    }
    if (qemu_opt_foreach(opts, set_property, qdev, 1) != 0) {
        qdev_free(qdev);
530
        object_unref(OBJECT(qdev));
531 532 533
        return NULL;
    }
    if (qdev->id) {
534 535
        object_property_add_child(qdev_get_peripheral(), qdev->id,
                                  OBJECT(qdev), NULL);
536 537 538
    } else {
        static int anon_count;
        gchar *name = g_strdup_printf("device[%d]", anon_count++);
539 540
        object_property_add_child(qdev_get_peripheral_anon(), name,
                                  OBJECT(qdev), NULL);
541 542
        g_free(name);
    }        
543
    if (qdev_init(qdev) < 0) {
544
        object_unref(OBJECT(qdev));
545 546 547
        qerror_report(QERR_DEVICE_INIT_FAILED, driver);
        return NULL;
    }
548 549 550 551 552 553 554 555 556
    qdev->opts = opts;
    return qdev;
}


#define qdev_printf(fmt, ...) monitor_printf(mon, "%*s" fmt, indent, "", ## __VA_ARGS__)
static void qbus_print(Monitor *mon, BusState *bus, int indent);

static void qdev_print_props(Monitor *mon, DeviceState *dev, Property *props,
557
                             int indent)
558 559 560
{
    if (!props)
        return;
561 562 563 564 565 566 567
    for (; props->name; props++) {
        Error *err = NULL;
        char *value;
        char *legacy_name = g_strdup_printf("legacy-%s", props->name);
        if (object_property_get_type(OBJECT(dev), legacy_name, NULL)) {
            value = object_property_get_str(OBJECT(dev), legacy_name, &err);
        } else {
568
            value = object_property_print(OBJECT(dev), props->name, &err);
569 570 571 572 573 574
        }
        g_free(legacy_name);

        if (err) {
            error_free(err);
            continue;
575
        }
576
        qdev_printf("%s = %s\n", props->name,
577 578
                    value && *value ? value : "<null>");
        g_free(value);
579 580 581
    }
}

582 583 584 585 586 587 588 589 590
static void bus_print_dev(BusState *bus, Monitor *mon, DeviceState *dev, int indent)
{
    BusClass *bc = BUS_GET_CLASS(bus);

    if (bc->print_dev) {
        bc->print_dev(mon, dev, indent);
    }
}

591 592
static void qdev_print(Monitor *mon, DeviceState *dev, int indent)
{
593
    ObjectClass *class;
594 595 596 597 598 599 600 601 602 603
    BusState *child;
    qdev_printf("dev: %s, id \"%s\"\n", object_get_typename(OBJECT(dev)),
                dev->id ? dev->id : "");
    indent += 2;
    if (dev->num_gpio_in) {
        qdev_printf("gpio-in %d\n", dev->num_gpio_in);
    }
    if (dev->num_gpio_out) {
        qdev_printf("gpio-out %d\n", dev->num_gpio_out);
    }
604 605 606 607 608
    class = object_get_class(OBJECT(dev));
    do {
        qdev_print_props(mon, dev, DEVICE_CLASS(class)->props, indent);
        class = object_class_get_parent(class);
    } while (class != object_class_by_name(TYPE_DEVICE));
G
Gerd Hoffmann 已提交
609
    bus_print_dev(dev->parent_bus, mon, dev, indent);
610 611 612 613 614 615 616
    QLIST_FOREACH(child, &dev->child_bus, sibling) {
        qbus_print(mon, child, indent);
    }
}

static void qbus_print(Monitor *mon, BusState *bus, int indent)
{
A
Anthony Liguori 已提交
617
    BusChild *kid;
618 619 620

    qdev_printf("bus: %s\n", bus->name);
    indent += 2;
621
    qdev_printf("type %s\n", object_get_typename(OBJECT(bus)));
A
Anthony Liguori 已提交
622 623
    QTAILQ_FOREACH(kid, &bus->children, sibling) {
        DeviceState *dev = kid->child;
624 625 626 627 628
        qdev_print(mon, dev, indent);
    }
}
#undef qdev_printf

629
void do_info_qtree(Monitor *mon, const QDict *qdict)
630 631 632 633 634
{
    if (sysbus_get_default())
        qbus_print(mon, sysbus_get_default(), 0);
}

635
void do_info_qdm(Monitor *mon, const QDict *qdict)
636
{
637
    qdev_print_devinfos(true);
638 639 640 641
}

int do_device_add(Monitor *mon, const QDict *qdict, QObject **ret_data)
{
642
    Error *local_err = NULL;
643
    QemuOpts *opts;
644
    DeviceState *dev;
645

646 647 648 649
    opts = qemu_opts_from_qdict(qemu_find_opts("device"), qdict, &local_err);
    if (error_is_set(&local_err)) {
        qerror_report_err(local_err);
        error_free(local_err);
650 651 652 653 654 655
        return -1;
    }
    if (!monitor_cur_is_qmp() && qdev_device_help(opts)) {
        qemu_opts_del(opts);
        return 0;
    }
656 657
    dev = qdev_device_add(opts);
    if (!dev) {
658 659 660
        qemu_opts_del(opts);
        return -1;
    }
661
    object_unref(OBJECT(dev));
662 663 664
    return 0;
}

L
Luiz Capitulino 已提交
665
void qmp_device_del(const char *id, Error **errp)
666 667 668 669 670
{
    DeviceState *dev;

    dev = qdev_find_recursive(sysbus_get_default(), id);
    if (NULL == dev) {
L
Luiz Capitulino 已提交
671 672
        error_set(errp, QERR_DEVICE_NOT_FOUND, id);
        return;
673 674
    }

L
Luiz Capitulino 已提交
675
    qdev_unplug(dev, errp);
676 677 678 679 680 681 682
}

void qdev_machine_init(void)
{
    qdev_get_peripheral_anon();
    qdev_get_peripheral();
}
683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733

QemuOptsList qemu_device_opts = {
    .name = "device",
    .implied_opt_name = "driver",
    .head = QTAILQ_HEAD_INITIALIZER(qemu_device_opts.head),
    .desc = {
        /*
         * no elements => accept any
         * sanity checking will happen later
         * when setting device properties
         */
        { /* end of list */ }
    },
};

QemuOptsList qemu_global_opts = {
    .name = "global",
    .head = QTAILQ_HEAD_INITIALIZER(qemu_global_opts.head),
    .desc = {
        {
            .name = "driver",
            .type = QEMU_OPT_STRING,
        },{
            .name = "property",
            .type = QEMU_OPT_STRING,
        },{
            .name = "value",
            .type = QEMU_OPT_STRING,
        },
        { /* end of list */ }
    },
};

int qemu_global_option(const char *str)
{
    char driver[64], property[64];
    QemuOpts *opts;
    int rc, offset;

    rc = sscanf(str, "%63[^.].%63[^=]%n", driver, property, &offset);
    if (rc < 2 || str[offset] != '=') {
        error_report("can't parse: \"%s\"", str);
        return -1;
    }

    opts = qemu_opts_create_nofail(&qemu_global_opts);
    qemu_opt_set(opts, "driver", driver);
    qemu_opt_set(opts, "property", property);
    qemu_opt_set(opts, "value", str+offset+1);
    return 0;
}