2011-12-23 05:24:20 +08:00
|
|
|
/*
|
|
|
|
* 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/>.
|
|
|
|
*/
|
|
|
|
|
2013-02-04 18:37:52 +08:00
|
|
|
#include "hw/qdev.h"
|
2013-04-16 09:50:21 +08:00
|
|
|
#include "hw/sysbus.h"
|
2012-12-18 01:19:49 +08:00
|
|
|
#include "monitor/monitor.h"
|
2013-02-04 18:37:52 +08:00
|
|
|
#include "monitor/qdev.h"
|
2012-03-29 23:38:50 +08:00
|
|
|
#include "qmp-commands.h"
|
2012-12-18 01:20:04 +08:00
|
|
|
#include "sysemu/arch_init.h"
|
2015-03-18 00:22:46 +08:00
|
|
|
#include "qapi/qmp/qerror.h"
|
2012-12-18 01:20:00 +08:00
|
|
|
#include "qemu/config-file.h"
|
2015-03-18 01:29:20 +08:00
|
|
|
#include "qemu/error-report.h"
|
2011-12-23 05:24:20 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Aliases were a bad idea from the start. Let's keep them
|
|
|
|
* from spreading further.
|
|
|
|
*/
|
|
|
|
typedef struct QDevAlias
|
|
|
|
{
|
|
|
|
const char *typename;
|
|
|
|
const char *alias;
|
2012-05-18 08:36:26 +08:00
|
|
|
uint32_t arch_mask;
|
2011-12-23 05:24:20 +08:00
|
|
|
} QDevAlias;
|
|
|
|
|
|
|
|
static const QDevAlias qdev_alias_table[] = {
|
2012-05-18 08:36:26 +08:00
|
|
|
{ "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 },
|
2015-06-17 05:06:33 +08:00
|
|
|
{ "virtio-blk-ccw", "virtio-blk", QEMU_ARCH_S390X },
|
|
|
|
{ "virtio-net-ccw", "virtio-net", QEMU_ARCH_S390X },
|
|
|
|
{ "virtio-serial-ccw", "virtio-serial", QEMU_ARCH_S390X },
|
2011-12-23 05:24:20 +08:00
|
|
|
{ "lsi53c895a", "lsi" },
|
|
|
|
{ "ich9-ahci", "ahci" },
|
2012-08-31 02:30:00 +08:00
|
|
|
{ "kvm-pci-assign", "pci-assign" },
|
2015-09-28 13:37:26 +08:00
|
|
|
{ "e1000", "e1000-82540em" },
|
2011-12-23 05:24:20 +08:00
|
|
|
{ }
|
|
|
|
};
|
|
|
|
|
|
|
|
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++) {
|
2012-05-18 08:36:26 +08:00
|
|
|
if (qdev_alias_table[i].arch_mask &&
|
|
|
|
!(qdev_alias_table[i].arch_mask & arch_type)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2011-12-23 05:24:20 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-10-10 21:00:21 +08:00
|
|
|
static void qdev_print_devinfo(DeviceClass *dc)
|
2011-12-23 05:24:20 +08:00
|
|
|
{
|
2013-10-10 21:00:21 +08:00
|
|
|
error_printf("name \"%s\"", object_class_get_name(OBJECT_CLASS(dc)));
|
2012-05-02 15:00:20 +08:00
|
|
|
if (dc->bus_type) {
|
|
|
|
error_printf(", bus %s", dc->bus_type);
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
|
|
|
if (qdev_class_has_alias(dc)) {
|
|
|
|
error_printf(", alias \"%s\"", qdev_class_get_alias(dc));
|
|
|
|
}
|
|
|
|
if (dc->desc) {
|
|
|
|
error_printf(", desc \"%s\"", dc->desc);
|
|
|
|
}
|
qdev: Replace no_user by cannot_instantiate_with_device_add_yet
In an ideal world, machines can be built by wiring devices together
with configuration, not code. Unfortunately, that's not the world we
live in right now. We still have quite a few devices that need to be
wired up by code. If you try to device_add such a device, it'll fail
in sometimes mysterious ways. If you're lucky, you get an
unmysterious immediate crash.
To protect users from such badness, DeviceClass member no_user used to
make device models unavailable with -device / device_add, but that
regressed in commit 18b6dad. The device model is still omitted from
help, but is available anyway.
Attempts to fix the regression have been rejected with the argument
that the purpose of no_user isn't clear, and it's prone to misuse.
This commit clarifies no_user's purpose. Anthony suggested to rename
it cannot_instantiate_with_device_add_yet_due_to_internal_bugs, which
I shorten somewhat to keep checkpatch happy. While there, make it
bool.
Every use of cannot_instantiate_with_device_add_yet gets a FIXME
comment asking for rationale. The next few commits will clean them
all up, either by providing a rationale, or by getting rid of the use.
With that done, the regression fix is hopefully acceptable.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Marcel Apfelbaum <marcel.a@redhat.com>
Signed-off-by: Andreas Färber <afaerber@suse.de>
2013-11-29 00:26:54 +08:00
|
|
|
if (dc->cannot_instantiate_with_device_add_yet) {
|
2011-12-23 05:24:20 +08:00
|
|
|
error_printf(", no-user");
|
|
|
|
}
|
|
|
|
error_printf("\n");
|
|
|
|
}
|
|
|
|
|
2013-10-10 21:00:21 +08:00
|
|
|
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))
|
qdev: Replace no_user by cannot_instantiate_with_device_add_yet
In an ideal world, machines can be built by wiring devices together
with configuration, not code. Unfortunately, that's not the world we
live in right now. We still have quite a few devices that need to be
wired up by code. If you try to device_add such a device, it'll fail
in sometimes mysterious ways. If you're lucky, you get an
unmysterious immediate crash.
To protect users from such badness, DeviceClass member no_user used to
make device models unavailable with -device / device_add, but that
regressed in commit 18b6dad. The device model is still omitted from
help, but is available anyway.
Attempts to fix the regression have been rejected with the argument
that the purpose of no_user isn't clear, and it's prone to misuse.
This commit clarifies no_user's purpose. Anthony suggested to rename
it cannot_instantiate_with_device_add_yet_due_to_internal_bugs, which
I shorten somewhat to keep checkpatch happy. While there, make it
bool.
Every use of cannot_instantiate_with_device_add_yet gets a FIXME
comment asking for rationale. The next few commits will clean them
all up, either by providing a rationale, or by getting rid of the use.
With that done, the regression fix is hopefully acceptable.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Marcel Apfelbaum <marcel.a@redhat.com>
Signed-off-by: Andreas Färber <afaerber@suse.de>
2013-11-29 00:26:54 +08:00
|
|
|
|| (!show_no_user
|
|
|
|
&& dc->cannot_instantiate_with_device_add_yet)) {
|
2013-10-10 21:00:21 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2015-03-12 15:40:25 +08:00
|
|
|
static int set_property(void *opaque, const char *name, const char *value,
|
|
|
|
Error **errp)
|
2011-12-23 05:24:20 +08:00
|
|
|
{
|
2014-02-08 18:01:49 +08:00
|
|
|
Object *obj = opaque;
|
2013-05-01 22:10:24 +08:00
|
|
|
Error *err = NULL;
|
2011-12-23 05:24:20 +08:00
|
|
|
|
|
|
|
if (strcmp(name, "driver") == 0)
|
|
|
|
return 0;
|
|
|
|
if (strcmp(name, "bus") == 0)
|
|
|
|
return 0;
|
|
|
|
|
2014-02-08 18:01:49 +08:00
|
|
|
object_property_parse(obj, value, name, &err);
|
2013-05-01 22:10:24 +08:00
|
|
|
if (err != NULL) {
|
2015-03-12 20:58:02 +08:00
|
|
|
error_propagate(errp, err);
|
2011-12-23 05:24:20 +08:00
|
|
|
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++) {
|
2012-05-18 08:36:26 +08:00
|
|
|
if (qdev_alias_table[i].arch_mask &&
|
|
|
|
!(qdev_alias_table[i].arch_mask & arch_type)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2011-12-23 05:24:20 +08:00
|
|
|
if (strcmp(qdev_alias_table[i].alias, alias) == 0) {
|
|
|
|
return qdev_alias_table[i].typename;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-11-01 23:56:09 +08:00
|
|
|
static DeviceClass *qdev_get_device_class(const char **driver, Error **errp)
|
|
|
|
{
|
|
|
|
ObjectClass *oc;
|
|
|
|
DeviceClass *dc;
|
|
|
|
|
|
|
|
oc = object_class_by_name(*driver);
|
|
|
|
if (!oc) {
|
|
|
|
const char *typename = find_typename_by_alias(*driver);
|
|
|
|
|
|
|
|
if (typename) {
|
|
|
|
*driver = typename;
|
|
|
|
oc = object_class_by_name(*driver);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!object_class_dynamic_cast(oc, TYPE_DEVICE)) {
|
|
|
|
error_setg(errp, "'%s' is not a valid device model name", *driver);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (object_class_is_abstract(oc)) {
|
2015-03-17 18:54:50 +08:00
|
|
|
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "driver",
|
|
|
|
"non-abstract device type");
|
2014-11-01 23:56:09 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
dc = DEVICE_CLASS(oc);
|
|
|
|
if (dc->cannot_instantiate_with_device_add_yet ||
|
|
|
|
(qdev_hotplug && !dc->hotpluggable)) {
|
2015-03-17 18:54:50 +08:00
|
|
|
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "driver",
|
|
|
|
"pluggable device type");
|
2014-11-01 23:56:09 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return dc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-12-23 05:24:20 +08:00
|
|
|
int qdev_device_help(QemuOpts *opts)
|
|
|
|
{
|
2014-07-09 20:01:32 +08:00
|
|
|
Error *local_err = NULL;
|
2011-12-23 05:24:20 +08:00
|
|
|
const char *driver;
|
2014-07-09 20:01:32 +08:00
|
|
|
DevicePropertyInfoList *prop_list;
|
|
|
|
DevicePropertyInfoList *prop;
|
2011-12-23 05:24:20 +08:00
|
|
|
|
|
|
|
driver = qemu_opt_get(opts, "driver");
|
2012-08-02 20:45:54 +08:00
|
|
|
if (driver && is_help_option(driver)) {
|
2013-10-10 21:00:21 +08:00
|
|
|
qdev_print_devinfos(false);
|
2011-12-23 05:24:20 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-08-02 20:45:54 +08:00
|
|
|
if (!driver || !qemu_opt_has_help_opt(opts)) {
|
2011-12-23 05:24:20 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
Revert "qdev: Use qdev_get_device_class() for -device <type>,help"
This reverts commit 31bed5509dfcbdfc293154ce81086a4dbd7a80b6.
The reverted commit changed qdev_device_help() to reject abstract
devices and devices that have cannot_instantiate_with_device_add_yet
set, to fix crash bugs like -device x86_64-cpu,help.
Rejecting abstract devices makes sense: they're purely internal, and
the implementation of the help feature can't cope with them.
Rejecting non-pluggable devices makes less sense: even though you
can't use them with -device, the help may still be useful elsewhere,
for instance with -global. This is a regression: -device FOO,help
used to help even for FOO that aren't pluggable.
The previous two commits fixed the crash bug at a lower layer, so
reverting this one is now safe. Fixes the -device FOO,help
regression, except for the broken devices marked
cannot_even_create_with_object_new_yet. For those, the error message
is improved.
Example of a device where the regression is fixed:
$ qemu-system-x86_64 -device PIIX4_PM,help
PIIX4_PM.command_serr_enable=bool (on/off)
PIIX4_PM.multifunction=bool (on/off)
PIIX4_PM.rombar=uint32
PIIX4_PM.romfile=str
PIIX4_PM.addr=int32 (Slot and optional function number, example: 06.0 or 06)
PIIX4_PM.memory-hotplug-support=bool
PIIX4_PM.acpi-pci-hotplug-with-bridge-support=bool
PIIX4_PM.s4_val=uint8
PIIX4_PM.disable_s4=uint8
PIIX4_PM.disable_s3=uint8
PIIX4_PM.smb_io_base=uint32
Example of a device where it isn't fixed:
$ qemu-system-x86_64 -device host-x86_64-cpu,help
Can't list properties of device 'host-x86_64-cpu'
Both failed with "Parameter 'driver' expects pluggable device type"
before.
Cc: qemu-stable@nongnu.org
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Eduardo Habkost <ehabkost@redhat.com>
Message-Id: <1443689999-12182-11-git-send-email-armbru@redhat.com>
2015-10-01 16:59:59 +08:00
|
|
|
if (!object_class_by_name(driver)) {
|
|
|
|
const char *typename = find_typename_by_alias(driver);
|
|
|
|
|
|
|
|
if (typename) {
|
|
|
|
driver = typename;
|
|
|
|
}
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
|
|
|
|
2014-07-09 20:01:32 +08:00
|
|
|
prop_list = qmp_device_list_properties(driver, &local_err);
|
2014-09-16 10:19:33 +08:00
|
|
|
if (local_err) {
|
2014-11-01 23:56:10 +08:00
|
|
|
goto error;
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
2014-07-09 20:01:32 +08:00
|
|
|
|
|
|
|
for (prop = prop_list; prop; prop = prop->next) {
|
2014-10-07 14:33:23 +08:00
|
|
|
error_printf("%s.%s=%s", driver,
|
2014-07-09 20:01:32 +08:00
|
|
|
prop->value->name,
|
|
|
|
prop->value->type);
|
2014-10-07 14:33:23 +08:00
|
|
|
if (prop->value->has_description) {
|
|
|
|
error_printf(" (%s)\n", prop->value->description);
|
|
|
|
} else {
|
|
|
|
error_printf("\n");
|
|
|
|
}
|
2014-07-09 20:01:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
qapi_free_DevicePropertyInfoList(prop_list);
|
2011-12-23 05:24:20 +08:00
|
|
|
return 1;
|
2014-11-01 23:56:10 +08:00
|
|
|
|
|
|
|
error:
|
2015-12-18 23:35:07 +08:00
|
|
|
error_report_err(local_err);
|
2014-11-01 23:56:10 +08:00
|
|
|
return 1;
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
|
|
|
|
2012-01-30 22:55:55 +08:00
|
|
|
static Object *qdev_get_peripheral(void)
|
2011-12-23 05:24:20 +08:00
|
|
|
{
|
2011-12-23 23:08:05 +08:00
|
|
|
static Object *dev;
|
2011-12-23 05:24:20 +08:00
|
|
|
|
|
|
|
if (dev == NULL) {
|
2012-04-05 19:21:46 +08:00
|
|
|
dev = container_get(qdev_get_machine(), "/peripheral");
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
|
|
|
|
2011-12-23 23:08:05 +08:00
|
|
|
return dev;
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
|
|
|
|
2012-01-30 22:55:55 +08:00
|
|
|
static Object *qdev_get_peripheral_anon(void)
|
2011-12-23 05:24:20 +08:00
|
|
|
{
|
2011-12-23 23:08:05 +08:00
|
|
|
static Object *dev;
|
2011-12-23 05:24:20 +08:00
|
|
|
|
|
|
|
if (dev == NULL) {
|
2012-04-05 19:21:46 +08:00
|
|
|
dev = container_get(qdev_get_machine(), "/peripheral-anon");
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
|
|
|
|
2011-12-23 23:08:05 +08:00
|
|
|
return dev;
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
|
|
|
|
hmp: Allow for error message hints on HMP
Commits 7216ae3d and d2828429 disabled some error message hints,
all because a change to use modern error reporting meant that the
hint would be output prior to the actual error. Fix this by making
hints a first-class member of Error.
For example, we are now back to the pleasant:
$ qemu-system-x86_64 --nodefaults -S --vnc :0 --chardev null,id=,
qemu-system-x86_64: --chardev null,id=,: Parameter 'id' expects an identifier
Identifiers consist of letters, digits, '-', '.', '_', starting with a letter.
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Message-Id: <1441901956-21991-1-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2015-09-11 00:19:16 +08:00
|
|
|
static void qbus_list_bus(DeviceState *dev, Error **errp)
|
2011-12-23 05:24:20 +08:00
|
|
|
{
|
|
|
|
BusState *child;
|
|
|
|
const char *sep = " ";
|
|
|
|
|
hmp: Allow for error message hints on HMP
Commits 7216ae3d and d2828429 disabled some error message hints,
all because a change to use modern error reporting meant that the
hint would be output prior to the actual error. Fix this by making
hints a first-class member of Error.
For example, we are now back to the pleasant:
$ qemu-system-x86_64 --nodefaults -S --vnc :0 --chardev null,id=,
qemu-system-x86_64: --chardev null,id=,: Parameter 'id' expects an identifier
Identifiers consist of letters, digits, '-', '.', '_', starting with a letter.
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Message-Id: <1441901956-21991-1-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2015-09-11 00:19:16 +08:00
|
|
|
error_append_hint(errp, "child buses at \"%s\":",
|
|
|
|
dev->id ? dev->id : object_get_typename(OBJECT(dev)));
|
2011-12-23 05:24:20 +08:00
|
|
|
QLIST_FOREACH(child, &dev->child_bus, sibling) {
|
hmp: Allow for error message hints on HMP
Commits 7216ae3d and d2828429 disabled some error message hints,
all because a change to use modern error reporting meant that the
hint would be output prior to the actual error. Fix this by making
hints a first-class member of Error.
For example, we are now back to the pleasant:
$ qemu-system-x86_64 --nodefaults -S --vnc :0 --chardev null,id=,
qemu-system-x86_64: --chardev null,id=,: Parameter 'id' expects an identifier
Identifiers consist of letters, digits, '-', '.', '_', starting with a letter.
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Message-Id: <1441901956-21991-1-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2015-09-11 00:19:16 +08:00
|
|
|
error_append_hint(errp, "%s\"%s\"", sep, child->name);
|
2011-12-23 05:24:20 +08:00
|
|
|
sep = ", ";
|
|
|
|
}
|
2015-12-18 00:35:14 +08:00
|
|
|
error_append_hint(errp, "\n");
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
|
|
|
|
hmp: Allow for error message hints on HMP
Commits 7216ae3d and d2828429 disabled some error message hints,
all because a change to use modern error reporting meant that the
hint would be output prior to the actual error. Fix this by making
hints a first-class member of Error.
For example, we are now back to the pleasant:
$ qemu-system-x86_64 --nodefaults -S --vnc :0 --chardev null,id=,
qemu-system-x86_64: --chardev null,id=,: Parameter 'id' expects an identifier
Identifiers consist of letters, digits, '-', '.', '_', starting with a letter.
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Message-Id: <1441901956-21991-1-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2015-09-11 00:19:16 +08:00
|
|
|
static void qbus_list_dev(BusState *bus, Error **errp)
|
2011-12-23 05:24:20 +08:00
|
|
|
{
|
2011-12-24 05:34:39 +08:00
|
|
|
BusChild *kid;
|
2011-12-23 05:24:20 +08:00
|
|
|
const char *sep = " ";
|
|
|
|
|
hmp: Allow for error message hints on HMP
Commits 7216ae3d and d2828429 disabled some error message hints,
all because a change to use modern error reporting meant that the
hint would be output prior to the actual error. Fix this by making
hints a first-class member of Error.
For example, we are now back to the pleasant:
$ qemu-system-x86_64 --nodefaults -S --vnc :0 --chardev null,id=,
qemu-system-x86_64: --chardev null,id=,: Parameter 'id' expects an identifier
Identifiers consist of letters, digits, '-', '.', '_', starting with a letter.
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Message-Id: <1441901956-21991-1-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2015-09-11 00:19:16 +08:00
|
|
|
error_append_hint(errp, "devices at \"%s\":", bus->name);
|
2011-12-24 05:34:39 +08:00
|
|
|
QTAILQ_FOREACH(kid, &bus->children, sibling) {
|
|
|
|
DeviceState *dev = kid->child;
|
hmp: Allow for error message hints on HMP
Commits 7216ae3d and d2828429 disabled some error message hints,
all because a change to use modern error reporting meant that the
hint would be output prior to the actual error. Fix this by making
hints a first-class member of Error.
For example, we are now back to the pleasant:
$ qemu-system-x86_64 --nodefaults -S --vnc :0 --chardev null,id=,
qemu-system-x86_64: --chardev null,id=,: Parameter 'id' expects an identifier
Identifiers consist of letters, digits, '-', '.', '_', starting with a letter.
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Message-Id: <1441901956-21991-1-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2015-09-11 00:19:16 +08:00
|
|
|
error_append_hint(errp, "%s\"%s\"", sep,
|
|
|
|
object_get_typename(OBJECT(dev)));
|
|
|
|
if (dev->id) {
|
|
|
|
error_append_hint(errp, "/\"%s\"", dev->id);
|
|
|
|
}
|
2011-12-23 05:24:20 +08:00
|
|
|
sep = ", ";
|
|
|
|
}
|
2015-12-18 00:35:14 +08:00
|
|
|
error_append_hint(errp, "\n");
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2011-12-24 05:34:39 +08:00
|
|
|
BusChild *kid;
|
2011-12-23 05:24:20 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* try to match in order:
|
|
|
|
* (1) instance id, if present
|
|
|
|
* (2) driver name
|
|
|
|
* (3) driver alias, if present
|
|
|
|
*/
|
2011-12-24 05:34:39 +08:00
|
|
|
QTAILQ_FOREACH(kid, &bus->children, sibling) {
|
|
|
|
DeviceState *dev = kid->child;
|
2011-12-23 05:24:20 +08:00
|
|
|
if (dev->id && strcmp(dev->id, elem) == 0) {
|
|
|
|
return dev;
|
|
|
|
}
|
|
|
|
}
|
2011-12-24 05:34:39 +08:00
|
|
|
QTAILQ_FOREACH(kid, &bus->children, sibling) {
|
|
|
|
DeviceState *dev = kid->child;
|
2011-12-23 05:24:20 +08:00
|
|
|
if (strcmp(object_get_typename(OBJECT(dev)), elem) == 0) {
|
|
|
|
return dev;
|
|
|
|
}
|
|
|
|
}
|
2011-12-24 05:34:39 +08:00
|
|
|
QTAILQ_FOREACH(kid, &bus->children, sibling) {
|
|
|
|
DeviceState *dev = kid->child;
|
2011-12-23 05:24:20 +08:00
|
|
|
DeviceClass *dc = DEVICE_GET_CLASS(dev);
|
|
|
|
|
|
|
|
if (qdev_class_has_alias(dc) &&
|
|
|
|
strcmp(qdev_class_get_alias(dc), elem) == 0) {
|
|
|
|
return dev;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-03-12 00:26:31 +08:00
|
|
|
static inline bool qbus_is_full(BusState *bus)
|
|
|
|
{
|
|
|
|
BusClass *bus_class = BUS_GET_CLASS(bus);
|
|
|
|
return bus_class->max_dev && bus->max_index >= bus_class->max_dev;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Search the tree rooted at @bus for a bus.
|
|
|
|
* If @name, search for a bus with that name. Note that bus names
|
|
|
|
* need not be unique. Yes, that's screwed up.
|
|
|
|
* Else search for a bus that is a subtype of @bus_typename.
|
|
|
|
* If more than one exists, prefer one that can take another device.
|
|
|
|
* Return the bus if found, else %NULL.
|
|
|
|
*/
|
2011-12-23 05:24:20 +08:00
|
|
|
static BusState *qbus_find_recursive(BusState *bus, const char *name,
|
2012-05-02 15:00:20 +08:00
|
|
|
const char *bus_typename)
|
2011-12-23 05:24:20 +08:00
|
|
|
{
|
2011-12-24 05:34:39 +08:00
|
|
|
BusChild *kid;
|
2015-03-12 00:26:31 +08:00
|
|
|
BusState *pick, *child, *ret;
|
|
|
|
bool match;
|
|
|
|
|
|
|
|
assert(name || bus_typename);
|
|
|
|
if (name) {
|
|
|
|
match = !strcmp(bus->name, name);
|
|
|
|
} else {
|
|
|
|
match = !!object_dynamic_cast(OBJECT(bus), bus_typename);
|
2013-01-15 07:08:00 +08:00
|
|
|
}
|
2015-03-12 00:26:31 +08:00
|
|
|
|
|
|
|
if (match && !qbus_is_full(bus)) {
|
|
|
|
return bus; /* root matches and isn't full */
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
|
|
|
|
2015-03-12 00:26:31 +08:00
|
|
|
pick = match ? bus : NULL;
|
|
|
|
|
2011-12-24 05:34:39 +08:00
|
|
|
QTAILQ_FOREACH(kid, &bus->children, sibling) {
|
|
|
|
DeviceState *dev = kid->child;
|
2011-12-23 05:24:20 +08:00
|
|
|
QLIST_FOREACH(child, &dev->child_bus, sibling) {
|
2012-05-02 15:00:20 +08:00
|
|
|
ret = qbus_find_recursive(child, name, bus_typename);
|
2015-03-12 00:26:31 +08:00
|
|
|
if (ret && !qbus_is_full(ret)) {
|
|
|
|
return ret; /* a descendant matches and isn't full */
|
|
|
|
}
|
|
|
|
if (ret && !pick) {
|
|
|
|
pick = ret;
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-03-12 00:26:31 +08:00
|
|
|
|
|
|
|
/* root or a descendant matches, but is full */
|
|
|
|
return pick;
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
|
|
|
|
2015-03-12 02:16:04 +08:00
|
|
|
static BusState *qbus_find(const char *path, Error **errp)
|
2011-12-23 05:24:20 +08:00
|
|
|
{
|
|
|
|
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) {
|
2015-03-12 02:16:04 +08:00
|
|
|
error_setg(errp, "Bus '%s' not found", elem);
|
2011-12-23 05:24:20 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
pos = len;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
assert(path[pos] == '/' || !path[pos]);
|
|
|
|
while (path[pos] == '/') {
|
|
|
|
pos++;
|
|
|
|
}
|
|
|
|
if (path[pos] == '\0') {
|
2015-03-12 01:39:16 +08:00
|
|
|
break;
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* find device */
|
|
|
|
if (sscanf(path+pos, "%127[^/]%n", elem, &len) != 1) {
|
2013-07-26 00:21:28 +08:00
|
|
|
g_assert_not_reached();
|
2011-12-23 05:24:20 +08:00
|
|
|
elem[0] = len = 0;
|
|
|
|
}
|
|
|
|
pos += len;
|
|
|
|
dev = qbus_find_dev(bus, elem);
|
|
|
|
if (!dev) {
|
2015-03-16 15:57:47 +08:00
|
|
|
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
|
|
|
|
"Device '%s' not found", elem);
|
hmp: Allow for error message hints on HMP
Commits 7216ae3d and d2828429 disabled some error message hints,
all because a change to use modern error reporting meant that the
hint would be output prior to the actual error. Fix this by making
hints a first-class member of Error.
For example, we are now back to the pleasant:
$ qemu-system-x86_64 --nodefaults -S --vnc :0 --chardev null,id=,
qemu-system-x86_64: --chardev null,id=,: Parameter 'id' expects an identifier
Identifiers consist of letters, digits, '-', '.', '_', starting with a letter.
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Message-Id: <1441901956-21991-1-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2015-09-11 00:19:16 +08:00
|
|
|
qbus_list_dev(bus, errp);
|
2011-12-23 05:24:20 +08:00
|
|
|
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 */
|
2015-03-12 01:39:16 +08:00
|
|
|
if (dev->num_child_bus == 1) {
|
|
|
|
bus = QLIST_FIRST(&dev->child_bus);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (dev->num_child_bus) {
|
2015-03-12 02:16:04 +08:00
|
|
|
error_setg(errp, "Device '%s' has multiple child buses",
|
|
|
|
elem);
|
hmp: Allow for error message hints on HMP
Commits 7216ae3d and d2828429 disabled some error message hints,
all because a change to use modern error reporting meant that the
hint would be output prior to the actual error. Fix this by making
hints a first-class member of Error.
For example, we are now back to the pleasant:
$ qemu-system-x86_64 --nodefaults -S --vnc :0 --chardev null,id=,
qemu-system-x86_64: --chardev null,id=,: Parameter 'id' expects an identifier
Identifiers consist of letters, digits, '-', '.', '_', starting with a letter.
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Message-Id: <1441901956-21991-1-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2015-09-11 00:19:16 +08:00
|
|
|
qbus_list_bus(dev, errp);
|
2015-03-12 01:39:16 +08:00
|
|
|
} else {
|
2015-03-12 02:16:04 +08:00
|
|
|
error_setg(errp, "Device '%s' has no child bus", elem);
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
2015-03-12 01:39:16 +08:00
|
|
|
return NULL;
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* find bus */
|
|
|
|
if (sscanf(path+pos, "%127[^/]%n", elem, &len) != 1) {
|
2013-07-26 00:21:28 +08:00
|
|
|
g_assert_not_reached();
|
2011-12-23 05:24:20 +08:00
|
|
|
elem[0] = len = 0;
|
|
|
|
}
|
|
|
|
pos += len;
|
|
|
|
bus = qbus_find_bus(dev, elem);
|
|
|
|
if (!bus) {
|
2015-03-12 02:16:04 +08:00
|
|
|
error_setg(errp, "Bus '%s' not found", elem);
|
hmp: Allow for error message hints on HMP
Commits 7216ae3d and d2828429 disabled some error message hints,
all because a change to use modern error reporting meant that the
hint would be output prior to the actual error. Fix this by making
hints a first-class member of Error.
For example, we are now back to the pleasant:
$ qemu-system-x86_64 --nodefaults -S --vnc :0 --chardev null,id=,
qemu-system-x86_64: --chardev null,id=,: Parameter 'id' expects an identifier
Identifiers consist of letters, digits, '-', '.', '_', starting with a letter.
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Message-Id: <1441901956-21991-1-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2015-09-11 00:19:16 +08:00
|
|
|
qbus_list_bus(dev, errp);
|
2011-12-23 05:24:20 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
2015-03-12 01:39:16 +08:00
|
|
|
|
|
|
|
if (qbus_is_full(bus)) {
|
2015-03-12 02:16:04 +08:00
|
|
|
error_setg(errp, "Bus '%s' is full", path);
|
2015-03-12 01:39:16 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return bus;
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
|
|
|
|
2015-03-12 21:00:41 +08:00
|
|
|
DeviceState *qdev_device_add(QemuOpts *opts, Error **errp)
|
2011-12-23 05:24:20 +08:00
|
|
|
{
|
2013-08-24 07:21:22 +08:00
|
|
|
DeviceClass *dc;
|
2011-12-23 05:24:20 +08:00
|
|
|
const char *driver, *path, *id;
|
2013-10-07 22:17:54 +08:00
|
|
|
DeviceState *dev;
|
2013-04-16 09:50:21 +08:00
|
|
|
BusState *bus = NULL;
|
2013-10-07 22:42:34 +08:00
|
|
|
Error *err = NULL;
|
2011-12-23 05:24:20 +08:00
|
|
|
|
|
|
|
driver = qemu_opt_get(opts, "driver");
|
|
|
|
if (!driver) {
|
2015-03-17 18:54:50 +08:00
|
|
|
error_setg(errp, QERR_MISSING_PARAMETER, "driver");
|
2011-12-23 05:24:20 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* find driver */
|
2015-03-12 21:00:41 +08:00
|
|
|
dc = qdev_get_device_class(&driver, errp);
|
|
|
|
if (!dc) {
|
2013-11-29 00:27:03 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2011-12-23 05:24:20 +08:00
|
|
|
|
|
|
|
/* find bus */
|
|
|
|
path = qemu_opt_get(opts, "bus");
|
|
|
|
if (path != NULL) {
|
2015-03-12 21:00:41 +08:00
|
|
|
bus = qbus_find(path, errp);
|
2011-12-23 05:24:20 +08:00
|
|
|
if (!bus) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2013-08-24 07:21:22 +08:00
|
|
|
if (!object_dynamic_cast(OBJECT(bus), dc->bus_type)) {
|
2015-03-12 21:00:41 +08:00
|
|
|
error_setg(errp, "Device '%s' can't go on %s bus",
|
|
|
|
driver, object_get_typename(OBJECT(bus)));
|
2011-12-23 05:24:20 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2013-08-24 07:21:22 +08:00
|
|
|
} else if (dc->bus_type != NULL) {
|
|
|
|
bus = qbus_find_recursive(sysbus_get_default(), NULL, dc->bus_type);
|
2015-03-12 00:26:31 +08:00
|
|
|
if (!bus || qbus_is_full(bus)) {
|
2015-03-12 21:00:41 +08:00
|
|
|
error_setg(errp, "No '%s' bus found for device '%s'",
|
|
|
|
dc->bus_type, driver);
|
2011-12-23 05:24:20 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
2014-09-26 17:28:17 +08:00
|
|
|
if (qdev_hotplug && bus && !qbus_is_hotpluggable(bus)) {
|
2015-03-17 18:54:50 +08:00
|
|
|
error_setg(errp, QERR_BUS_NO_HOTPLUG, bus->name);
|
2011-12-23 05:24:20 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-03-03 15:57:55 +08:00
|
|
|
/* create device */
|
2013-10-07 22:17:54 +08:00
|
|
|
dev = DEVICE(object_new(driver));
|
2013-04-16 09:50:21 +08:00
|
|
|
|
|
|
|
if (bus) {
|
2013-10-07 22:17:54 +08:00
|
|
|
qdev_set_parent_bus(dev, bus);
|
2013-04-16 09:50:21 +08:00
|
|
|
}
|
2011-12-23 05:24:20 +08:00
|
|
|
|
|
|
|
id = qemu_opts_id(opts);
|
|
|
|
if (id) {
|
2013-10-07 22:17:54 +08:00
|
|
|
dev->id = id;
|
2012-02-13 01:36:24 +08:00
|
|
|
}
|
2014-03-03 15:57:55 +08:00
|
|
|
|
2013-10-07 22:17:54 +08:00
|
|
|
if (dev->id) {
|
|
|
|
object_property_add_child(qdev_get_peripheral(), dev->id,
|
|
|
|
OBJECT(dev), NULL);
|
2011-12-23 05:24:20 +08:00
|
|
|
} else {
|
|
|
|
static int anon_count;
|
|
|
|
gchar *name = g_strdup_printf("device[%d]", anon_count++);
|
2012-01-30 22:55:55 +08:00
|
|
|
object_property_add_child(qdev_get_peripheral_anon(), name,
|
2013-10-07 22:17:54 +08:00
|
|
|
OBJECT(dev), NULL);
|
2011-12-23 05:24:20 +08:00
|
|
|
g_free(name);
|
2013-10-07 22:17:54 +08:00
|
|
|
}
|
2014-02-27 01:32:40 +08:00
|
|
|
|
2014-03-03 15:57:55 +08:00
|
|
|
/* set properties */
|
2015-03-12 20:58:02 +08:00
|
|
|
if (qemu_opt_foreach(opts, set_property, dev, &err)) {
|
2015-03-12 21:00:41 +08:00
|
|
|
error_propagate(errp, err);
|
2014-03-03 15:57:55 +08:00
|
|
|
object_unparent(OBJECT(dev));
|
|
|
|
object_unref(OBJECT(dev));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-02-27 01:32:40 +08:00
|
|
|
dev->opts = opts;
|
2013-10-07 22:42:34 +08:00
|
|
|
object_property_set_bool(OBJECT(dev), true, "realized", &err);
|
|
|
|
if (err != NULL) {
|
2015-03-12 21:00:41 +08:00
|
|
|
error_propagate(errp, err);
|
2014-02-27 01:32:40 +08:00
|
|
|
dev->opts = NULL;
|
2013-10-07 22:42:34 +08:00
|
|
|
object_unparent(OBJECT(dev));
|
2013-10-07 22:17:54 +08:00
|
|
|
object_unref(OBJECT(dev));
|
2012-03-28 00:38:46 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2013-10-07 22:17:54 +08:00
|
|
|
return dev;
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#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,
|
2012-03-29 00:12:47 +08:00
|
|
|
int indent)
|
2011-12-23 05:24:20 +08:00
|
|
|
{
|
|
|
|
if (!props)
|
|
|
|
return;
|
2012-02-02 16:47:13 +08:00
|
|
|
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 {
|
2014-02-08 18:01:51 +08:00
|
|
|
value = object_property_print(OBJECT(dev), props->name, true, &err);
|
2012-02-02 16:47:13 +08:00
|
|
|
}
|
|
|
|
g_free(legacy_name);
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
error_free(err);
|
|
|
|
continue;
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
2012-03-29 00:12:47 +08:00
|
|
|
qdev_printf("%s = %s\n", props->name,
|
2012-02-02 16:47:13 +08:00
|
|
|
value && *value ? value : "<null>");
|
|
|
|
g_free(value);
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-02 15:00:20 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-23 05:24:20 +08:00
|
|
|
static void qdev_print(Monitor *mon, DeviceState *dev, int indent)
|
|
|
|
{
|
2012-03-29 00:12:47 +08:00
|
|
|
ObjectClass *class;
|
2011-12-23 05:24:20 +08:00
|
|
|
BusState *child;
|
2014-05-20 14:30:58 +08:00
|
|
|
NamedGPIOList *ngl;
|
|
|
|
|
2011-12-23 05:24:20 +08:00
|
|
|
qdev_printf("dev: %s, id \"%s\"\n", object_get_typename(OBJECT(dev)),
|
|
|
|
dev->id ? dev->id : "");
|
|
|
|
indent += 2;
|
2014-05-20 14:30:58 +08:00
|
|
|
QLIST_FOREACH(ngl, &dev->gpios, node) {
|
|
|
|
if (ngl->num_in) {
|
|
|
|
qdev_printf("gpio-in \"%s\" %d\n", ngl->name ? ngl->name : "",
|
|
|
|
ngl->num_in);
|
|
|
|
}
|
|
|
|
if (ngl->num_out) {
|
|
|
|
qdev_printf("gpio-out \"%s\" %d\n", ngl->name ? ngl->name : "",
|
|
|
|
ngl->num_out);
|
|
|
|
}
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
2012-03-29 00:12:47 +08:00
|
|
|
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));
|
2012-07-11 18:21:23 +08:00
|
|
|
bus_print_dev(dev->parent_bus, mon, dev, indent);
|
2011-12-23 05:24:20 +08:00
|
|
|
QLIST_FOREACH(child, &dev->child_bus, sibling) {
|
|
|
|
qbus_print(mon, child, indent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qbus_print(Monitor *mon, BusState *bus, int indent)
|
|
|
|
{
|
2011-12-24 05:34:39 +08:00
|
|
|
BusChild *kid;
|
2011-12-23 05:24:20 +08:00
|
|
|
|
|
|
|
qdev_printf("bus: %s\n", bus->name);
|
|
|
|
indent += 2;
|
2012-05-02 15:00:20 +08:00
|
|
|
qdev_printf("type %s\n", object_get_typename(OBJECT(bus)));
|
2011-12-24 05:34:39 +08:00
|
|
|
QTAILQ_FOREACH(kid, &bus->children, sibling) {
|
|
|
|
DeviceState *dev = kid->child;
|
2011-12-23 05:24:20 +08:00
|
|
|
qdev_print(mon, dev, indent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#undef qdev_printf
|
|
|
|
|
2015-02-06 21:18:24 +08:00
|
|
|
void hmp_info_qtree(Monitor *mon, const QDict *qdict)
|
2011-12-23 05:24:20 +08:00
|
|
|
{
|
|
|
|
if (sysbus_get_default())
|
|
|
|
qbus_print(mon, sysbus_get_default(), 0);
|
|
|
|
}
|
|
|
|
|
2015-02-06 21:18:24 +08:00
|
|
|
void hmp_info_qdm(Monitor *mon, const QDict *qdict)
|
2011-12-23 05:24:20 +08:00
|
|
|
{
|
2013-10-10 21:00:21 +08:00
|
|
|
qdev_print_devinfos(true);
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
|
|
|
|
2014-05-07 23:03:18 +08:00
|
|
|
typedef struct QOMCompositionState {
|
|
|
|
Monitor *mon;
|
|
|
|
int indent;
|
|
|
|
} QOMCompositionState;
|
|
|
|
|
|
|
|
static void print_qom_composition(Monitor *mon, Object *obj, int indent);
|
|
|
|
|
|
|
|
static int print_qom_composition_child(Object *obj, void *opaque)
|
|
|
|
{
|
|
|
|
QOMCompositionState *s = opaque;
|
|
|
|
|
|
|
|
print_qom_composition(s->mon, obj, s->indent);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void print_qom_composition(Monitor *mon, Object *obj, int indent)
|
|
|
|
{
|
|
|
|
QOMCompositionState s = {
|
|
|
|
.mon = mon,
|
|
|
|
.indent = indent + 2,
|
|
|
|
};
|
|
|
|
char *name;
|
|
|
|
|
|
|
|
if (obj == object_get_root()) {
|
|
|
|
name = g_strdup("");
|
|
|
|
} else {
|
|
|
|
name = object_get_canonical_path_component(obj);
|
|
|
|
}
|
|
|
|
monitor_printf(mon, "%*s/%s (%s)\n", indent, "", name,
|
|
|
|
object_get_typename(obj));
|
|
|
|
g_free(name);
|
|
|
|
object_child_foreach(obj, print_qom_composition_child, &s);
|
|
|
|
}
|
|
|
|
|
|
|
|
void hmp_info_qom_tree(Monitor *mon, const QDict *dict)
|
|
|
|
{
|
|
|
|
const char *path = qdict_get_try_str(dict, "path");
|
|
|
|
Object *obj;
|
|
|
|
bool ambiguous = false;
|
|
|
|
|
|
|
|
if (path) {
|
|
|
|
obj = object_resolve_path(path, &ambiguous);
|
|
|
|
if (!obj) {
|
|
|
|
monitor_printf(mon, "Path '%s' could not be resolved.\n", path);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (ambiguous) {
|
|
|
|
monitor_printf(mon, "Warning: Path '%s' is ambiguous.\n", path);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
obj = qdev_get_machine();
|
|
|
|
}
|
|
|
|
print_qom_composition(mon, obj, 0);
|
|
|
|
}
|
|
|
|
|
qmp: Wean off qerror_report()
The traditional QMP command handler interface
int qmp_FOO(Monitor *mon, const QDict *params, QObject **ret_data);
doesn't provide for returning an Error object. Instead, the handler
is expected to stash it in the monitor with qerror_report().
When we rebased QMP on top of QAPI, we didn't change this interface.
Instead, commit 776574d introduced "middle mode" as a temporary aid
for converting existing QMP commands to QAPI one by one. More than
three years later, we're still using it.
Middle mode has two effects:
* Instead of the native input marshallers
static void qmp_marshal_input_FOO(QDict *, QObject **, Error **)
it generates input marshallers conforming to the traditional QMP
command handler interface.
* It suppresses generation of code to register them with
qmp_register_command()
This permits giving them internal linkage.
As long as we need qmp-commands.hx, we can't use the registry behind
qmp_register_command(), so the latter has to stay for now.
The former has to go to get rid of qerror_report(). Changing all QMP
commands to fit the QAPI mold in one go was impractical back when we
started, but by now there are just a few stragglers left:
do_qmp_capabilities(), qmp_qom_set(), qmp_qom_get(), qmp_object_add(),
qmp_netdev_add(), do_device_add().
Switch middle mode to generate native input marshallers, and adapt the
stragglers. Simplifies both the monitor code and the stragglers.
Rename do_qmp_capabilities() to qmp_capabilities(), and
do_device_add() to qmp_device_add, because that's how QMP command
handlers are named today.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Luiz Capitulino <lcapitulino@redhat.com>
2015-03-14 00:25:50 +08:00
|
|
|
void qmp_device_add(QDict *qdict, QObject **ret_data, Error **errp)
|
2011-12-23 05:24:20 +08:00
|
|
|
{
|
2012-04-19 04:24:01 +08:00
|
|
|
Error *local_err = NULL;
|
2011-12-23 05:24:20 +08:00
|
|
|
QemuOpts *opts;
|
2013-01-25 21:12:37 +08:00
|
|
|
DeviceState *dev;
|
2011-12-23 05:24:20 +08:00
|
|
|
|
2012-04-19 04:24:01 +08:00
|
|
|
opts = qemu_opts_from_qdict(qemu_find_opts("device"), qdict, &local_err);
|
2014-01-30 22:07:28 +08:00
|
|
|
if (local_err) {
|
qmp: Wean off qerror_report()
The traditional QMP command handler interface
int qmp_FOO(Monitor *mon, const QDict *params, QObject **ret_data);
doesn't provide for returning an Error object. Instead, the handler
is expected to stash it in the monitor with qerror_report().
When we rebased QMP on top of QAPI, we didn't change this interface.
Instead, commit 776574d introduced "middle mode" as a temporary aid
for converting existing QMP commands to QAPI one by one. More than
three years later, we're still using it.
Middle mode has two effects:
* Instead of the native input marshallers
static void qmp_marshal_input_FOO(QDict *, QObject **, Error **)
it generates input marshallers conforming to the traditional QMP
command handler interface.
* It suppresses generation of code to register them with
qmp_register_command()
This permits giving them internal linkage.
As long as we need qmp-commands.hx, we can't use the registry behind
qmp_register_command(), so the latter has to stay for now.
The former has to go to get rid of qerror_report(). Changing all QMP
commands to fit the QAPI mold in one go was impractical back when we
started, but by now there are just a few stragglers left:
do_qmp_capabilities(), qmp_qom_set(), qmp_qom_get(), qmp_object_add(),
qmp_netdev_add(), do_device_add().
Switch middle mode to generate native input marshallers, and adapt the
stragglers. Simplifies both the monitor code and the stragglers.
Rename do_qmp_capabilities() to qmp_capabilities(), and
do_device_add() to qmp_device_add, because that's how QMP command
handlers are named today.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Luiz Capitulino <lcapitulino@redhat.com>
2015-03-14 00:25:50 +08:00
|
|
|
error_propagate(errp, local_err);
|
|
|
|
return;
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
|
|
|
if (!monitor_cur_is_qmp() && qdev_device_help(opts)) {
|
|
|
|
qemu_opts_del(opts);
|
qmp: Wean off qerror_report()
The traditional QMP command handler interface
int qmp_FOO(Monitor *mon, const QDict *params, QObject **ret_data);
doesn't provide for returning an Error object. Instead, the handler
is expected to stash it in the monitor with qerror_report().
When we rebased QMP on top of QAPI, we didn't change this interface.
Instead, commit 776574d introduced "middle mode" as a temporary aid
for converting existing QMP commands to QAPI one by one. More than
three years later, we're still using it.
Middle mode has two effects:
* Instead of the native input marshallers
static void qmp_marshal_input_FOO(QDict *, QObject **, Error **)
it generates input marshallers conforming to the traditional QMP
command handler interface.
* It suppresses generation of code to register them with
qmp_register_command()
This permits giving them internal linkage.
As long as we need qmp-commands.hx, we can't use the registry behind
qmp_register_command(), so the latter has to stay for now.
The former has to go to get rid of qerror_report(). Changing all QMP
commands to fit the QAPI mold in one go was impractical back when we
started, but by now there are just a few stragglers left:
do_qmp_capabilities(), qmp_qom_set(), qmp_qom_get(), qmp_object_add(),
qmp_netdev_add(), do_device_add().
Switch middle mode to generate native input marshallers, and adapt the
stragglers. Simplifies both the monitor code and the stragglers.
Rename do_qmp_capabilities() to qmp_capabilities(), and
do_device_add() to qmp_device_add, because that's how QMP command
handlers are named today.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Luiz Capitulino <lcapitulino@redhat.com>
2015-03-14 00:25:50 +08:00
|
|
|
return;
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
2015-03-12 21:00:41 +08:00
|
|
|
dev = qdev_device_add(opts, &local_err);
|
2013-01-25 21:12:37 +08:00
|
|
|
if (!dev) {
|
qmp: Wean off qerror_report()
The traditional QMP command handler interface
int qmp_FOO(Monitor *mon, const QDict *params, QObject **ret_data);
doesn't provide for returning an Error object. Instead, the handler
is expected to stash it in the monitor with qerror_report().
When we rebased QMP on top of QAPI, we didn't change this interface.
Instead, commit 776574d introduced "middle mode" as a temporary aid
for converting existing QMP commands to QAPI one by one. More than
three years later, we're still using it.
Middle mode has two effects:
* Instead of the native input marshallers
static void qmp_marshal_input_FOO(QDict *, QObject **, Error **)
it generates input marshallers conforming to the traditional QMP
command handler interface.
* It suppresses generation of code to register them with
qmp_register_command()
This permits giving them internal linkage.
As long as we need qmp-commands.hx, we can't use the registry behind
qmp_register_command(), so the latter has to stay for now.
The former has to go to get rid of qerror_report(). Changing all QMP
commands to fit the QAPI mold in one go was impractical back when we
started, but by now there are just a few stragglers left:
do_qmp_capabilities(), qmp_qom_set(), qmp_qom_get(), qmp_object_add(),
qmp_netdev_add(), do_device_add().
Switch middle mode to generate native input marshallers, and adapt the
stragglers. Simplifies both the monitor code and the stragglers.
Rename do_qmp_capabilities() to qmp_capabilities(), and
do_device_add() to qmp_device_add, because that's how QMP command
handlers are named today.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Luiz Capitulino <lcapitulino@redhat.com>
2015-03-14 00:25:50 +08:00
|
|
|
error_propagate(errp, local_err);
|
2011-12-23 05:24:20 +08:00
|
|
|
qemu_opts_del(opts);
|
qmp: Wean off qerror_report()
The traditional QMP command handler interface
int qmp_FOO(Monitor *mon, const QDict *params, QObject **ret_data);
doesn't provide for returning an Error object. Instead, the handler
is expected to stash it in the monitor with qerror_report().
When we rebased QMP on top of QAPI, we didn't change this interface.
Instead, commit 776574d introduced "middle mode" as a temporary aid
for converting existing QMP commands to QAPI one by one. More than
three years later, we're still using it.
Middle mode has two effects:
* Instead of the native input marshallers
static void qmp_marshal_input_FOO(QDict *, QObject **, Error **)
it generates input marshallers conforming to the traditional QMP
command handler interface.
* It suppresses generation of code to register them with
qmp_register_command()
This permits giving them internal linkage.
As long as we need qmp-commands.hx, we can't use the registry behind
qmp_register_command(), so the latter has to stay for now.
The former has to go to get rid of qerror_report(). Changing all QMP
commands to fit the QAPI mold in one go was impractical back when we
started, but by now there are just a few stragglers left:
do_qmp_capabilities(), qmp_qom_set(), qmp_qom_get(), qmp_object_add(),
qmp_netdev_add(), do_device_add().
Switch middle mode to generate native input marshallers, and adapt the
stragglers. Simplifies both the monitor code and the stragglers.
Rename do_qmp_capabilities() to qmp_capabilities(), and
do_device_add() to qmp_device_add, because that's how QMP command
handlers are named today.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Luiz Capitulino <lcapitulino@redhat.com>
2015-03-14 00:25:50 +08:00
|
|
|
return;
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
2013-01-25 21:12:37 +08:00
|
|
|
object_unref(OBJECT(dev));
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
|
|
|
|
2012-03-29 23:38:50 +08:00
|
|
|
void qmp_device_del(const char *id, Error **errp)
|
2011-12-23 05:24:20 +08:00
|
|
|
{
|
2014-10-02 18:08:45 +08:00
|
|
|
Object *obj;
|
2011-12-23 05:24:20 +08:00
|
|
|
|
2015-09-11 20:33:56 +08:00
|
|
|
if (id[0] == '/') {
|
|
|
|
obj = object_resolve_path(id, NULL);
|
|
|
|
} else {
|
|
|
|
char *root_path = object_get_canonical_path(qdev_get_peripheral());
|
|
|
|
char *path = g_strdup_printf("%s/%s", root_path, id);
|
|
|
|
|
|
|
|
g_free(root_path);
|
|
|
|
obj = object_resolve_path_type(path, TYPE_DEVICE, NULL);
|
|
|
|
g_free(path);
|
|
|
|
}
|
2014-10-02 18:08:45 +08:00
|
|
|
|
|
|
|
if (!obj) {
|
2015-03-16 15:57:47 +08:00
|
|
|
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
|
|
|
|
"Device '%s' not found", id);
|
2012-03-29 23:38:50 +08:00
|
|
|
return;
|
2012-03-15 04:37:38 +08:00
|
|
|
}
|
|
|
|
|
2015-09-11 20:33:56 +08:00
|
|
|
if (!object_dynamic_cast(obj, TYPE_DEVICE)) {
|
|
|
|
error_setg(errp, "%s is not a hotpluggable device", id);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-10-02 18:08:45 +08:00
|
|
|
qdev_unplug(DEVICE(obj), errp);
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void qdev_machine_init(void)
|
|
|
|
{
|
|
|
|
qdev_get_peripheral_anon();
|
|
|
|
qdev_get_peripheral();
|
|
|
|
}
|
2012-11-26 23:03:42 +08:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2015-04-09 20:16:19 +08:00
|
|
|
rc = sscanf(str, "%63[^.=].%63[^=]%n", driver, property, &offset);
|
|
|
|
if (rc == 2 && str[offset] == '=') {
|
|
|
|
opts = qemu_opts_create(&qemu_global_opts, NULL, 0, &error_abort);
|
|
|
|
qemu_opt_set(opts, "driver", driver, &error_abort);
|
|
|
|
qemu_opt_set(opts, "property", property, &error_abort);
|
|
|
|
qemu_opt_set(opts, "value", str + offset + 1, &error_abort);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
QemuOpts: Wean off qerror_report_err()
qerror_report_err() is a transitional interface to help with
converting existing monitor commands to QMP. It should not be used
elsewhere.
The only remaining user in qemu-option.c is qemu_opts_parse(). Is it
used in QMP context? If not, we can simply replace
qerror_report_err() by error_report_err().
The uses in qemu-img.c, qemu-io.c, qemu-nbd.c and under tests/ are
clearly not in QMP context.
The uses in vl.c aren't either, because the only QMP command handlers
there are qmp_query_status() and qmp_query_machines(), and they don't
call it.
Remaining uses:
* drive_def(): Command line -drive and such, HMP drive_add and pci_add
* hmp_chardev_add(): HMP chardev-add
* monitor_parse_command(): HMP core
* tmp_config_parse(): Command line -tpmdev
* net_host_device_add(): HMP host_net_add
* net_client_parse(): Command line -net and -netdev
* qemu_global_option(): Command line -global
* vnc_parse_func(): Command line -display, -vnc, default display, HMP
change, QMP change. Bummer.
* qemu_pci_hot_add_nic(): HMP pci_add
* usb_net_init(): Command line -usbdevice, HMP usb_add
Propagate errors through qemu_opts_parse(). Create a convenience
function qemu_opts_parse_noisily() that passes errors to
error_report_err(). Switch all non-QMP users outside tests to it.
That leaves vnc_parse_func(). Propagate errors through it. Since I'm
touching it anyway, rename it to vnc_parse().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Luiz Capitulino <lcapitulino@redhat.com>
2015-02-13 19:50:26 +08:00
|
|
|
opts = qemu_opts_parse_noisily(&qemu_global_opts, str, false);
|
2015-04-09 20:16:19 +08:00
|
|
|
if (!opts) {
|
2012-11-26 23:03:42 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|