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
|
2020-10-23 20:44:24 +08:00
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
2011-12-23 05:24:20 +08:00
|
|
|
*
|
|
|
|
* 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/>.
|
|
|
|
*/
|
|
|
|
|
2016-01-30 01:50:05 +08:00
|
|
|
#include "qemu/osdep.h"
|
2013-04-16 09:50:21 +08:00
|
|
|
#include "hw/sysbus.h"
|
2019-07-10 02:59:36 +08:00
|
|
|
#include "monitor/hmp.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-12-18 01:20:04 +08:00
|
|
|
#include "sysemu/arch_init.h"
|
2018-02-01 19:18:31 +08:00
|
|
|
#include "qapi/error.h"
|
2019-06-20 04:10:37 +08:00
|
|
|
#include "qapi/qapi-commands-qdev.h"
|
2020-10-27 16:44:18 +08:00
|
|
|
#include "qapi/qmp/dispatch.h"
|
2018-02-01 19:18:39 +08:00
|
|
|
#include "qapi/qmp/qdict.h"
|
2015-03-18 00:22:46 +08:00
|
|
|
#include "qapi/qmp/qerror.h"
|
2021-10-08 21:34:34 +08:00
|
|
|
#include "qapi/qmp/qstring.h"
|
|
|
|
#include "qapi/qobject-input-visitor.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"
|
2016-03-21 01:16:19 +08:00
|
|
|
#include "qemu/help_option.h"
|
2018-02-01 19:18:46 +08:00
|
|
|
#include "qemu/option.h"
|
2019-04-18 03:06:41 +08:00
|
|
|
#include "qemu/qemu-print.h"
|
2019-10-29 19:48:55 +08:00
|
|
|
#include "qemu/option_int.h"
|
2016-09-20 19:38:42 +08:00
|
|
|
#include "sysemu/block-backend.h"
|
2017-04-25 01:02:44 +08:00
|
|
|
#include "migration/misc.h"
|
2019-10-29 19:48:55 +08:00
|
|
|
#include "migration/migration.h"
|
2020-01-10 23:30:39 +08:00
|
|
|
#include "qemu/cutils.h"
|
2020-12-12 06:05:00 +08:00
|
|
|
#include "hw/qdev-properties.h"
|
2020-04-06 21:52:51 +08:00
|
|
|
#include "hw/clock.h"
|
2021-10-29 22:22:57 +08:00
|
|
|
#include "hw/boards.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;
|
|
|
|
|
2021-07-30 18:59:45 +08:00
|
|
|
/* default virtio transport per architecture */
|
|
|
|
#define QEMU_ARCH_VIRTIO_PCI (QEMU_ARCH_ALPHA | QEMU_ARCH_ARM | \
|
|
|
|
QEMU_ARCH_HPPA | QEMU_ARCH_I386 | \
|
|
|
|
QEMU_ARCH_MIPS | QEMU_ARCH_PPC | \
|
|
|
|
QEMU_ARCH_RISCV | QEMU_ARCH_SH4 | \
|
|
|
|
QEMU_ARCH_SPARC | QEMU_ARCH_XTENSA)
|
|
|
|
#define QEMU_ARCH_VIRTIO_CCW (QEMU_ARCH_S390X)
|
|
|
|
#define QEMU_ARCH_VIRTIO_MMIO (QEMU_ARCH_M68K)
|
|
|
|
|
2016-02-19 05:44:13 +08:00
|
|
|
/* Please keep this table sorted by typename. */
|
2011-12-23 05:24:20 +08:00
|
|
|
static const QDevAlias qdev_alias_table[] = {
|
2020-07-02 21:25:09 +08:00
|
|
|
{ "AC97", "ac97" }, /* -soundhw name */
|
2016-02-19 05:44:13 +08:00
|
|
|
{ "e1000", "e1000-82540em" },
|
2020-07-02 21:25:10 +08:00
|
|
|
{ "ES1370", "es1370" }, /* -soundhw name */
|
2016-02-19 05:44:13 +08:00
|
|
|
{ "ich9-ahci", "ahci" },
|
|
|
|
{ "lsi53c895a", "lsi" },
|
2021-03-24 00:53:03 +08:00
|
|
|
{ "virtio-9p-device", "virtio-9p", QEMU_ARCH_VIRTIO_MMIO },
|
2021-03-24 00:53:02 +08:00
|
|
|
{ "virtio-9p-ccw", "virtio-9p", QEMU_ARCH_VIRTIO_CCW },
|
|
|
|
{ "virtio-9p-pci", "virtio-9p", QEMU_ARCH_VIRTIO_PCI },
|
2021-03-24 00:53:03 +08:00
|
|
|
{ "virtio-balloon-device", "virtio-balloon", QEMU_ARCH_VIRTIO_MMIO },
|
2021-03-24 00:53:02 +08:00
|
|
|
{ "virtio-balloon-ccw", "virtio-balloon", QEMU_ARCH_VIRTIO_CCW },
|
|
|
|
{ "virtio-balloon-pci", "virtio-balloon", QEMU_ARCH_VIRTIO_PCI },
|
2021-03-24 00:53:03 +08:00
|
|
|
{ "virtio-blk-device", "virtio-blk", QEMU_ARCH_VIRTIO_MMIO },
|
2021-03-24 00:53:02 +08:00
|
|
|
{ "virtio-blk-ccw", "virtio-blk", QEMU_ARCH_VIRTIO_CCW },
|
|
|
|
{ "virtio-blk-pci", "virtio-blk", QEMU_ARCH_VIRTIO_PCI },
|
2021-03-24 00:53:03 +08:00
|
|
|
{ "virtio-gpu-device", "virtio-gpu", QEMU_ARCH_VIRTIO_MMIO },
|
2021-03-24 00:53:02 +08:00
|
|
|
{ "virtio-gpu-ccw", "virtio-gpu", QEMU_ARCH_VIRTIO_CCW },
|
|
|
|
{ "virtio-gpu-pci", "virtio-gpu", QEMU_ARCH_VIRTIO_PCI },
|
2021-03-24 00:53:03 +08:00
|
|
|
{ "virtio-input-host-device", "virtio-input-host", QEMU_ARCH_VIRTIO_MMIO },
|
2021-03-24 00:53:02 +08:00
|
|
|
{ "virtio-input-host-ccw", "virtio-input-host", QEMU_ARCH_VIRTIO_CCW },
|
|
|
|
{ "virtio-input-host-pci", "virtio-input-host", QEMU_ARCH_VIRTIO_PCI },
|
|
|
|
{ "virtio-iommu-pci", "virtio-iommu", QEMU_ARCH_VIRTIO_PCI },
|
2021-03-24 00:53:03 +08:00
|
|
|
{ "virtio-keyboard-device", "virtio-keyboard", QEMU_ARCH_VIRTIO_MMIO },
|
2021-03-24 00:53:02 +08:00
|
|
|
{ "virtio-keyboard-ccw", "virtio-keyboard", QEMU_ARCH_VIRTIO_CCW },
|
|
|
|
{ "virtio-keyboard-pci", "virtio-keyboard", QEMU_ARCH_VIRTIO_PCI },
|
2021-03-24 00:53:03 +08:00
|
|
|
{ "virtio-mouse-device", "virtio-mouse", QEMU_ARCH_VIRTIO_MMIO },
|
2021-03-24 00:53:02 +08:00
|
|
|
{ "virtio-mouse-ccw", "virtio-mouse", QEMU_ARCH_VIRTIO_CCW },
|
|
|
|
{ "virtio-mouse-pci", "virtio-mouse", QEMU_ARCH_VIRTIO_PCI },
|
2021-03-24 00:53:03 +08:00
|
|
|
{ "virtio-net-device", "virtio-net", QEMU_ARCH_VIRTIO_MMIO },
|
2021-03-24 00:53:02 +08:00
|
|
|
{ "virtio-net-ccw", "virtio-net", QEMU_ARCH_VIRTIO_CCW },
|
|
|
|
{ "virtio-net-pci", "virtio-net", QEMU_ARCH_VIRTIO_PCI },
|
2021-03-24 00:53:03 +08:00
|
|
|
{ "virtio-rng-device", "virtio-rng", QEMU_ARCH_VIRTIO_MMIO },
|
2021-03-24 00:53:02 +08:00
|
|
|
{ "virtio-rng-ccw", "virtio-rng", QEMU_ARCH_VIRTIO_CCW },
|
|
|
|
{ "virtio-rng-pci", "virtio-rng", QEMU_ARCH_VIRTIO_PCI },
|
2021-03-24 00:53:03 +08:00
|
|
|
{ "virtio-scsi-device", "virtio-scsi", QEMU_ARCH_VIRTIO_MMIO },
|
2021-03-24 00:53:02 +08:00
|
|
|
{ "virtio-scsi-ccw", "virtio-scsi", QEMU_ARCH_VIRTIO_CCW },
|
|
|
|
{ "virtio-scsi-pci", "virtio-scsi", QEMU_ARCH_VIRTIO_PCI },
|
2021-03-24 00:53:03 +08:00
|
|
|
{ "virtio-serial-device", "virtio-serial", QEMU_ARCH_VIRTIO_MMIO },
|
2021-03-24 00:53:02 +08:00
|
|
|
{ "virtio-serial-ccw", "virtio-serial", QEMU_ARCH_VIRTIO_CCW },
|
|
|
|
{ "virtio-serial-pci", "virtio-serial", QEMU_ARCH_VIRTIO_PCI},
|
2021-03-24 00:53:03 +08:00
|
|
|
{ "virtio-tablet-device", "virtio-tablet", QEMU_ARCH_VIRTIO_MMIO },
|
2021-03-24 00:53:02 +08:00
|
|
|
{ "virtio-tablet-ccw", "virtio-tablet", QEMU_ARCH_VIRTIO_CCW },
|
|
|
|
{ "virtio-tablet-pci", "virtio-tablet", QEMU_ARCH_VIRTIO_PCI },
|
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
|
|
|
{
|
2019-04-18 03:06:41 +08:00
|
|
|
qemu_printf("name \"%s\"", object_class_get_name(OBJECT_CLASS(dc)));
|
2012-05-02 15:00:20 +08:00
|
|
|
if (dc->bus_type) {
|
2019-04-18 03:06:41 +08:00
|
|
|
qemu_printf(", bus %s", dc->bus_type);
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
|
|
|
if (qdev_class_has_alias(dc)) {
|
2019-04-18 03:06:41 +08:00
|
|
|
qemu_printf(", alias \"%s\"", qdev_class_get_alias(dc));
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
|
|
|
if (dc->desc) {
|
2019-04-18 03:06:41 +08:00
|
|
|
qemu_printf(", desc \"%s\"", dc->desc);
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
2017-05-04 04:35:44 +08:00
|
|
|
if (!dc->user_creatable) {
|
2019-04-18 03:06:41 +08:00
|
|
|
qemu_printf(", no-user");
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
2019-04-18 03:06:41 +08:00
|
|
|
qemu_printf("\n");
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
|
|
|
|
2013-10-10 21:00:21 +08:00
|
|
|
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",
|
2017-01-20 21:01:16 +08:00
|
|
|
[DEVICE_CATEGORY_CPU] = "CPU",
|
2021-10-27 20:34:53 +08:00
|
|
|
[DEVICE_CATEGORY_WATCHDOG]= "Watchdog",
|
2013-10-10 21:00:21 +08:00
|
|
|
[DEVICE_CATEGORY_MAX] = "Uncategorized",
|
|
|
|
};
|
|
|
|
GSList *list, *elt;
|
|
|
|
int i;
|
|
|
|
bool cat_printed;
|
|
|
|
|
2020-06-24 21:10:38 +08:00
|
|
|
module_load_qom_all();
|
2018-03-03 15:33:10 +08:00
|
|
|
list = object_class_get_list_sorted(TYPE_DEVICE, false);
|
2013-10-10 21:00:21 +08:00
|
|
|
|
|
|
|
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
|
2017-05-04 04:35:44 +08:00
|
|
|
&& !dc->user_creatable)) {
|
2013-10-10 21:00:21 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!cat_printed) {
|
2019-04-18 03:06:41 +08:00
|
|
|
qemu_printf("%s%s devices:\n", i ? "\n" : "", cat_name[i]);
|
2013-10-10 21:00:21 +08:00
|
|
|
cat_printed = true;
|
|
|
|
}
|
|
|
|
qdev_print_devinfo(dc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
g_slist_free(list);
|
|
|
|
}
|
|
|
|
|
2011-12-23 05:24:20 +08:00
|
|
|
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;
|
2016-02-19 05:44:12 +08:00
|
|
|
const char *original_name = *driver;
|
2014-11-01 23:56:09 +08:00
|
|
|
|
2020-06-24 21:10:38 +08:00
|
|
|
oc = module_object_class_by_name(*driver);
|
2014-11-01 23:56:09 +08:00
|
|
|
if (!oc) {
|
|
|
|
const char *typename = find_typename_by_alias(*driver);
|
|
|
|
|
|
|
|
if (typename) {
|
|
|
|
*driver = typename;
|
2020-06-24 21:10:38 +08:00
|
|
|
oc = module_object_class_by_name(*driver);
|
2014-11-01 23:56:09 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!object_class_dynamic_cast(oc, TYPE_DEVICE)) {
|
2016-02-19 05:44:12 +08:00
|
|
|
if (*driver != original_name) {
|
|
|
|
error_setg(errp, "'%s' (alias '%s') is not a valid device model"
|
|
|
|
" name", original_name, *driver);
|
|
|
|
} else {
|
|
|
|
error_setg(errp, "'%s' is not a valid device model name", *driver);
|
|
|
|
}
|
2014-11-01 23:56:09 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (object_class_is_abstract(oc)) {
|
2015-03-17 18:54:50 +08:00
|
|
|
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "driver",
|
2020-11-13 16:26:26 +08:00
|
|
|
"a non-abstract device type");
|
2014-11-01 23:56:09 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
dc = DEVICE_CLASS(oc);
|
2017-05-04 04:35:44 +08:00
|
|
|
if (!dc->user_creatable ||
|
2020-11-12 22:38:36 +08:00
|
|
|
(phase_check(PHASE_MACHINE_READY) && !dc->hotpluggable)) {
|
2015-03-17 18:54:50 +08:00
|
|
|
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "driver",
|
2020-11-13 16:26:26 +08:00
|
|
|
"a pluggable device type");
|
2014-11-01 23:56:09 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2021-10-29 22:22:57 +08:00
|
|
|
if (object_class_dynamic_cast(oc, TYPE_SYS_BUS_DEVICE)) {
|
|
|
|
/* sysbus devices need to be allowed by the machine */
|
|
|
|
MachineClass *mc = MACHINE_CLASS(object_get_class(qdev_get_machine()));
|
|
|
|
if (!device_type_is_dynamic_sysbus(mc, *driver)) {
|
|
|
|
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "driver",
|
|
|
|
"a dynamic sysbus device type for the machine");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-01 23:56:09 +08:00
|
|
|
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;
|
2018-03-01 21:09:38 +08:00
|
|
|
ObjectPropertyInfoList *prop_list;
|
|
|
|
ObjectPropertyInfoList *prop;
|
2020-01-10 23:30:39 +08:00
|
|
|
GPtrArray *array;
|
|
|
|
int i;
|
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
|
|
|
|
2018-10-20 00:49:27 +08:00
|
|
|
if (prop_list) {
|
2019-04-18 03:06:41 +08:00
|
|
|
qemu_printf("%s options:\n", driver);
|
2018-10-20 00:49:27 +08:00
|
|
|
} else {
|
2019-04-18 03:06:41 +08:00
|
|
|
qemu_printf("There are no options for %s.\n", driver);
|
2018-10-20 00:49:27 +08:00
|
|
|
}
|
2020-01-10 23:30:39 +08:00
|
|
|
array = g_ptr_array_new();
|
2014-07-09 20:01:32 +08:00
|
|
|
for (prop = prop_list; prop; prop = prop->next) {
|
2020-01-10 23:30:39 +08:00
|
|
|
g_ptr_array_add(array,
|
|
|
|
object_property_help(prop->value->name,
|
|
|
|
prop->value->type,
|
|
|
|
prop->value->default_value,
|
|
|
|
prop->value->description));
|
2014-07-09 20:01:32 +08:00
|
|
|
}
|
2020-01-10 23:30:39 +08:00
|
|
|
g_ptr_array_sort(array, (GCompareFunc)qemu_pstrcmp0);
|
|
|
|
for (i = 0; i < array->len; i++) {
|
2020-07-15 00:01:58 +08:00
|
|
|
qemu_printf("%s\n", (char *)array->pdata[i]);
|
2020-01-10 23:30:39 +08:00
|
|
|
}
|
|
|
|
g_ptr_array_set_free_func(array, g_free);
|
|
|
|
g_ptr_array_free(array, true);
|
2018-03-01 21:09:38 +08:00
|
|
|
qapi_free_ObjectPropertyInfoList(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
|
|
|
}
|
|
|
|
|
2019-12-06 01:46:20 +08:00
|
|
|
static void qbus_error_append_bus_list_hint(DeviceState *dev,
|
|
|
|
Error *const *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
|
|
|
}
|
|
|
|
|
2019-12-06 01:46:20 +08:00
|
|
|
static void qbus_error_append_dev_list_hint(BusState *bus,
|
|
|
|
Error *const *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)
|
|
|
|
{
|
2021-09-13 23:07:25 +08:00
|
|
|
BusClass *bus_class;
|
|
|
|
|
|
|
|
if (bus->full) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bus_class = BUS_GET_CLASS(bus);
|
2018-12-17 23:57:30 +08:00
|
|
|
return bus_class->max_dev && bus->num_children >= bus_class->max_dev;
|
2015-03-12 00:26:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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);
|
2019-12-06 01:46:20 +08:00
|
|
|
qbus_error_append_dev_list_hint(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);
|
2019-12-06 01:46:20 +08:00
|
|
|
qbus_error_append_bus_list_hint(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);
|
2019-12-06 01:46:20 +08:00
|
|
|
qbus_error_append_bus_list_hint(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
|
|
|
}
|
|
|
|
|
2021-10-08 21:34:35 +08:00
|
|
|
/* Takes ownership of @id, will be freed when deleting the device */
|
2021-10-08 21:34:36 +08:00
|
|
|
const char *qdev_set_id(DeviceState *dev, char *id, Error **errp)
|
2016-11-22 14:10:57 +08:00
|
|
|
{
|
2021-10-08 21:34:36 +08:00
|
|
|
ObjectProperty *prop;
|
2016-11-22 14:10:57 +08:00
|
|
|
|
2021-10-08 21:34:36 +08:00
|
|
|
assert(!dev->id && !dev->realized);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* object_property_[try_]add_child() below will assert the device
|
|
|
|
* has no parent
|
|
|
|
*/
|
|
|
|
if (id) {
|
|
|
|
prop = object_property_try_add_child(qdev_get_peripheral(), id,
|
|
|
|
OBJECT(dev), NULL);
|
|
|
|
if (prop) {
|
|
|
|
dev->id = id;
|
|
|
|
} else {
|
|
|
|
g_free(id);
|
|
|
|
error_setg(errp, "Duplicate device ID '%s'", id);
|
|
|
|
return NULL;
|
|
|
|
}
|
2016-11-22 14:10:57 +08:00
|
|
|
} else {
|
|
|
|
static int anon_count;
|
|
|
|
gchar *name = g_strdup_printf("device[%d]", anon_count++);
|
2021-10-08 21:34:36 +08:00
|
|
|
prop = object_property_add_child(qdev_get_peripheral_anon(), name,
|
|
|
|
OBJECT(dev));
|
2016-11-22 14:10:57 +08:00
|
|
|
g_free(name);
|
|
|
|
}
|
2021-10-08 21:34:36 +08:00
|
|
|
|
|
|
|
return prop->name;
|
2016-11-22 14:10:57 +08:00
|
|
|
}
|
|
|
|
|
2021-10-08 21:34:41 +08:00
|
|
|
DeviceState *qdev_device_add_from_qdict(const QDict *opts,
|
|
|
|
bool from_json, Error **errp)
|
2011-12-23 05:24:20 +08:00
|
|
|
{
|
2021-10-08 21:34:38 +08:00
|
|
|
ERRP_GUARD();
|
2013-08-24 07:21:22 +08:00
|
|
|
DeviceClass *dc;
|
2016-11-22 14:10:57 +08:00
|
|
|
const char *driver, *path;
|
2021-10-08 21:34:41 +08:00
|
|
|
char *id;
|
2019-10-29 19:48:55 +08:00
|
|
|
DeviceState *dev = NULL;
|
2013-04-16 09:50:21 +08:00
|
|
|
BusState *bus = NULL;
|
2011-12-23 05:24:20 +08:00
|
|
|
|
2021-10-08 21:34:41 +08:00
|
|
|
driver = qdict_get_try_str(opts, "driver");
|
2011-12-23 05:24:20 +08:00
|
|
|
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 */
|
2021-10-08 21:34:41 +08:00
|
|
|
path = qdict_get_try_str(opts, "bus");
|
2011-12-23 05:24:20 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2019-10-29 19:48:55 +08:00
|
|
|
|
2021-10-19 15:15:32 +08:00
|
|
|
if (qdev_should_hide_device(opts, from_json, errp)) {
|
|
|
|
if (bus && !qbus_is_hotpluggable(bus)) {
|
|
|
|
error_setg(errp, QERR_BUS_NO_HOTPLUG, bus->name);
|
2020-11-18 16:37:40 +08:00
|
|
|
}
|
2021-10-19 15:15:32 +08:00
|
|
|
return NULL;
|
|
|
|
} else if (*errp) {
|
|
|
|
return NULL;
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
|
|
|
|
2020-11-12 22:38:36 +08:00
|
|
|
if (phase_check(PHASE_MACHINE_READY) && bus && !qbus_is_hotpluggable(bus)) {
|
2020-11-18 16:37:40 +08:00
|
|
|
error_setg(errp, QERR_BUS_NO_HOTPLUG, bus->name);
|
2019-10-29 19:48:55 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-03-23 00:34:27 +08:00
|
|
|
if (!migration_is_idle()) {
|
|
|
|
error_setg(errp, "device_add not allowed while migrating");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-03-03 15:57:55 +08:00
|
|
|
/* create device */
|
2020-06-10 13:31:54 +08:00
|
|
|
dev = qdev_new(driver);
|
2013-04-16 09:50:21 +08:00
|
|
|
|
2019-09-16 16:07:16 +08:00
|
|
|
/* Check whether the hotplug is allowed by the machine */
|
2020-11-12 22:38:36 +08:00
|
|
|
if (phase_check(PHASE_MACHINE_READY)) {
|
|
|
|
if (!qdev_hotplug_allowed(dev, errp)) {
|
|
|
|
goto err_del_dev;
|
|
|
|
}
|
2019-09-16 16:07:16 +08:00
|
|
|
|
2020-11-12 22:38:36 +08:00
|
|
|
if (!bus && !qdev_get_machine_hotplug_handler(dev)) {
|
|
|
|
/* No bus, no machine hotplug handler --> device is not hotpluggable */
|
|
|
|
error_setg(errp, "Device '%s' can not be hotplugged on this machine",
|
|
|
|
driver);
|
|
|
|
goto err_del_dev;
|
|
|
|
}
|
2013-04-16 09:50:21 +08:00
|
|
|
}
|
2011-12-23 05:24:20 +08:00
|
|
|
|
2021-10-08 21:34:36 +08:00
|
|
|
/*
|
|
|
|
* set dev's parent and register its id.
|
|
|
|
* If it fails it means the id is already taken.
|
|
|
|
*/
|
2021-10-08 21:34:41 +08:00
|
|
|
id = g_strdup(qdict_get_try_str(opts, "id"));
|
|
|
|
if (!qdev_set_id(dev, id, errp)) {
|
2021-10-08 21:34:36 +08:00
|
|
|
goto err_del_dev;
|
|
|
|
}
|
2014-02-27 01:32:40 +08:00
|
|
|
|
2014-03-03 15:57:55 +08:00
|
|
|
/* set properties */
|
2021-10-08 21:34:41 +08:00
|
|
|
dev->opts = qdict_clone_shallow(opts);
|
|
|
|
qdict_del(dev->opts, "driver");
|
|
|
|
qdict_del(dev->opts, "bus");
|
|
|
|
qdict_del(dev->opts, "id");
|
|
|
|
|
|
|
|
object_set_properties_from_keyval(&dev->parent_obj, dev->opts, from_json,
|
|
|
|
errp);
|
|
|
|
if (*errp) {
|
2017-11-02 18:10:05 +08:00
|
|
|
goto err_del_dev;
|
2014-03-03 15:57:55 +08:00
|
|
|
}
|
|
|
|
|
2020-07-08 00:06:04 +08:00
|
|
|
if (!qdev_realize(DEVICE(dev), bus, errp)) {
|
2017-11-02 18:10:05 +08:00
|
|
|
goto err_del_dev;
|
2012-03-28 00:38:46 +08:00
|
|
|
}
|
2013-10-07 22:17:54 +08:00
|
|
|
return dev;
|
2017-11-02 18:10:05 +08:00
|
|
|
|
|
|
|
err_del_dev:
|
2019-10-29 19:48:55 +08:00
|
|
|
if (dev) {
|
|
|
|
object_unparent(OBJECT(dev));
|
|
|
|
object_unref(OBJECT(dev));
|
|
|
|
}
|
2017-11-02 18:10:05 +08:00
|
|
|
return NULL;
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
|
|
|
|
2021-10-08 21:34:41 +08:00
|
|
|
/* Takes ownership of @opts on success */
|
|
|
|
DeviceState *qdev_device_add(QemuOpts *opts, Error **errp)
|
|
|
|
{
|
|
|
|
QDict *qdict = qemu_opts_to_qdict(opts, NULL);
|
|
|
|
DeviceState *ret;
|
|
|
|
|
|
|
|
ret = qdev_device_add_from_qdict(qdict, false, errp);
|
|
|
|
if (ret) {
|
|
|
|
qemu_opts_del(opts);
|
|
|
|
}
|
|
|
|
qobject_unref(qdict);
|
|
|
|
return ret;
|
|
|
|
}
|
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++) {
|
|
|
|
char *value;
|
|
|
|
char *legacy_name = g_strdup_printf("legacy-%s", props->name);
|
2020-07-08 00:06:13 +08:00
|
|
|
|
2012-02-02 16:47:13 +08:00
|
|
|
if (object_property_get_type(OBJECT(dev), legacy_name, NULL)) {
|
2020-07-08 00:06:13 +08:00
|
|
|
value = object_property_get_str(OBJECT(dev), legacy_name, NULL);
|
2012-02-02 16:47:13 +08:00
|
|
|
} else {
|
2020-07-08 00:06:13 +08:00
|
|
|
value = object_property_print(OBJECT(dev), props->name, true,
|
|
|
|
NULL);
|
2012-02-02 16:47:13 +08:00
|
|
|
}
|
|
|
|
g_free(legacy_name);
|
|
|
|
|
2020-07-08 00:06:13 +08:00
|
|
|
if (!value) {
|
2012-02-02 16:47:13 +08:00
|
|
|
continue;
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
2012-03-29 00:12:47 +08:00
|
|
|
qdev_printf("%s = %s\n", props->name,
|
2020-07-08 00:06:13 +08:00
|
|
|
*value ? value : "<null>");
|
2012-02-02 16:47:13 +08:00
|
|
|
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;
|
2020-04-06 21:52:51 +08:00
|
|
|
NamedClockList *ncl;
|
2014-05-20 14:30:58 +08:00
|
|
|
|
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
|
|
|
}
|
2020-04-06 21:52:51 +08:00
|
|
|
QLIST_FOREACH(ncl, &dev->clocks, node) {
|
2020-12-15 23:09:29 +08:00
|
|
|
g_autofree char *freq_str = clock_display_freq(ncl->clock);
|
|
|
|
qdev_printf("clock-%s%s \"%s\" freq_hz=%s\n",
|
2020-04-06 21:52:51 +08:00
|
|
|
ncl->output ? "out" : "in",
|
|
|
|
ncl->alias ? " (alias)" : "",
|
2020-12-15 23:09:29 +08:00
|
|
|
ncl->name, freq_str);
|
2020-04-06 21:52:51 +08:00
|
|
|
}
|
2012-03-29 00:12:47 +08:00
|
|
|
class = object_get_class(OBJECT(dev));
|
|
|
|
do {
|
2020-01-23 19:11:38 +08:00
|
|
|
qdev_print_props(mon, dev, DEVICE_CLASS(class)->props_, indent);
|
2012-03-29 00:12:47 +08:00
|
|
|
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
|
|
|
}
|
|
|
|
|
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
|
|
|
{
|
|
|
|
QemuOpts *opts;
|
2013-01-25 21:12:37 +08:00
|
|
|
DeviceState *dev;
|
2011-12-23 05:24:20 +08:00
|
|
|
|
2020-07-08 00:05:35 +08:00
|
|
|
opts = qemu_opts_from_qdict(qemu_find_opts("device"), qdict, errp);
|
|
|
|
if (!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
|
|
|
}
|
|
|
|
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
|
|
|
}
|
error: Eliminate error_propagate() with Coccinelle, part 1
When all we do with an Error we receive into a local variable is
propagating to somewhere else, we can just as well receive it there
right away. Convert
if (!foo(..., &err)) {
...
error_propagate(errp, err);
...
return ...
}
to
if (!foo(..., errp)) {
...
...
return ...
}
where nothing else needs @err. Coccinelle script:
@rule1 forall@
identifier fun, err, errp, lbl;
expression list args, args2;
binary operator op;
constant c1, c2;
symbol false;
@@
if (
(
- fun(args, &err, args2)
+ fun(args, errp, args2)
|
- !fun(args, &err, args2)
+ !fun(args, errp, args2)
|
- fun(args, &err, args2) op c1
+ fun(args, errp, args2) op c1
)
)
{
... when != err
when != lbl:
when strict
- error_propagate(errp, err);
... when != err
(
return;
|
return c2;
|
return false;
)
}
@rule2 forall@
identifier fun, err, errp, lbl;
expression list args, args2;
expression var;
binary operator op;
constant c1, c2;
symbol false;
@@
- var = fun(args, &err, args2);
+ var = fun(args, errp, args2);
... when != err
if (
(
var
|
!var
|
var op c1
)
)
{
... when != err
when != lbl:
when strict
- error_propagate(errp, err);
... when != err
(
return;
|
return c2;
|
return false;
|
return var;
)
}
@depends on rule1 || rule2@
identifier err;
@@
- Error *err = NULL;
... when != err
Not exactly elegant, I'm afraid.
The "when != lbl:" is necessary to avoid transforming
if (fun(args, &err)) {
goto out
}
...
out:
error_propagate(errp, err);
even though other paths to label out still need the error_propagate().
For an actual example, see sclp_realize().
Without the "when strict", Coccinelle transforms vfio_msix_setup(),
incorrectly. I don't know what exactly "when strict" does, only that
it helps here.
The match of return is narrower than what I want, but I can't figure
out how to express "return where the operand doesn't use @err". For
an example where it's too narrow, see vfio_intx_enable().
Silently fails to convert hw/arm/armsse.c, because Coccinelle gets
confused by ARMSSE being used both as typedef and function-like macro
there. Converted manually.
Line breaks tidied up manually. One nested declaration of @local_err
deleted manually. Preexisting unwanted blank line dropped in
hw/riscv/sifive_e.c.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <20200707160613.848843-35-armbru@redhat.com>
2020-07-08 00:06:02 +08:00
|
|
|
dev = qdev_device_add(opts, errp);
|
2020-10-06 20:38:58 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Drain all pending RCU callbacks. This is done because
|
|
|
|
* some bus related operations can delay a device removal
|
|
|
|
* (in this case this can happen if device is added and then
|
|
|
|
* removed due to a configuration error)
|
|
|
|
* to a RCU callback, but user might expect that this interface
|
|
|
|
* will finish its job completely once qmp command returns result
|
|
|
|
* to the user
|
|
|
|
*/
|
|
|
|
drain_call_rcu();
|
|
|
|
|
2013-01-25 21:12:37 +08:00
|
|
|
if (!dev) {
|
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
|
|
|
}
|
|
|
|
|
2016-09-20 19:38:41 +08:00
|
|
|
static DeviceState *find_device_state(const char *id, Error **errp)
|
2011-12-23 05:24:20 +08:00
|
|
|
{
|
2014-10-02 18:08:45 +08:00
|
|
|
Object *obj;
|
monitor: Tidy up find_device_state()
Commit 6287d827d4 "monitor: allow device_del to accept QOM paths"
extended find_device_state() to accept QOM paths in addition to qdev
IDs. This added a checked conversion to TYPE_DEVICE at the end, which
duplicates the check done for the qdev ID case earlier, except it sets
a *different* error: GenericError "ID is not a hotpluggable device"
when passed a QOM path, and DeviceNotFound "Device 'ID' not found"
when passed a qdev ID. Fortunately, the latter won't happen as long
as we add only devices to /machine/peripheral/.
Earlier, commit b6cc36abb2 "qdev: device_del: Search for to be
unplugged device in 'peripheral' container" rewrote the lookup by qdev
ID to use QOM instead of qdev_find_recursive(), so it can handle
buss-less devices. It does so by constructing an absolute QOM path.
Works, but object_resolve_path_component() is easier. Switching to it
also gets rid of the unclean duplication described above.
While there, avoid converting to TYPE_DEVICE twice, first to check
whether it's possible, and then for real.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Damien Hedde <damien.hedde@greensocs.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Message-Id: <20210916111707.84999-1-armbru@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-09-16 19:17:07 +08:00
|
|
|
DeviceState *dev;
|
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 {
|
monitor: Tidy up find_device_state()
Commit 6287d827d4 "monitor: allow device_del to accept QOM paths"
extended find_device_state() to accept QOM paths in addition to qdev
IDs. This added a checked conversion to TYPE_DEVICE at the end, which
duplicates the check done for the qdev ID case earlier, except it sets
a *different* error: GenericError "ID is not a hotpluggable device"
when passed a QOM path, and DeviceNotFound "Device 'ID' not found"
when passed a qdev ID. Fortunately, the latter won't happen as long
as we add only devices to /machine/peripheral/.
Earlier, commit b6cc36abb2 "qdev: device_del: Search for to be
unplugged device in 'peripheral' container" rewrote the lookup by qdev
ID to use QOM instead of qdev_find_recursive(), so it can handle
buss-less devices. It does so by constructing an absolute QOM path.
Works, but object_resolve_path_component() is easier. Switching to it
also gets rid of the unclean duplication described above.
While there, avoid converting to TYPE_DEVICE twice, first to check
whether it's possible, and then for real.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Damien Hedde <damien.hedde@greensocs.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Message-Id: <20210916111707.84999-1-armbru@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-09-16 19:17:07 +08:00
|
|
|
obj = object_resolve_path_component(qdev_get_peripheral(), id);
|
2015-09-11 20:33:56 +08:00
|
|
|
}
|
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);
|
2016-09-20 19:38:41 +08:00
|
|
|
return NULL;
|
2012-03-15 04:37:38 +08:00
|
|
|
}
|
|
|
|
|
monitor: Tidy up find_device_state()
Commit 6287d827d4 "monitor: allow device_del to accept QOM paths"
extended find_device_state() to accept QOM paths in addition to qdev
IDs. This added a checked conversion to TYPE_DEVICE at the end, which
duplicates the check done for the qdev ID case earlier, except it sets
a *different* error: GenericError "ID is not a hotpluggable device"
when passed a QOM path, and DeviceNotFound "Device 'ID' not found"
when passed a qdev ID. Fortunately, the latter won't happen as long
as we add only devices to /machine/peripheral/.
Earlier, commit b6cc36abb2 "qdev: device_del: Search for to be
unplugged device in 'peripheral' container" rewrote the lookup by qdev
ID to use QOM instead of qdev_find_recursive(), so it can handle
buss-less devices. It does so by constructing an absolute QOM path.
Works, but object_resolve_path_component() is easier. Switching to it
also gets rid of the unclean duplication described above.
While there, avoid converting to TYPE_DEVICE twice, first to check
whether it's possible, and then for real.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Damien Hedde <damien.hedde@greensocs.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Message-Id: <20210916111707.84999-1-armbru@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-09-16 19:17:07 +08:00
|
|
|
dev = (DeviceState *)object_dynamic_cast(obj, TYPE_DEVICE);
|
|
|
|
if (!dev) {
|
2015-09-11 20:33:56 +08:00
|
|
|
error_setg(errp, "%s is not a hotpluggable device", id);
|
2016-09-20 19:38:41 +08:00
|
|
|
return NULL;
|
2015-09-11 20:33:56 +08:00
|
|
|
}
|
|
|
|
|
monitor: Tidy up find_device_state()
Commit 6287d827d4 "monitor: allow device_del to accept QOM paths"
extended find_device_state() to accept QOM paths in addition to qdev
IDs. This added a checked conversion to TYPE_DEVICE at the end, which
duplicates the check done for the qdev ID case earlier, except it sets
a *different* error: GenericError "ID is not a hotpluggable device"
when passed a QOM path, and DeviceNotFound "Device 'ID' not found"
when passed a qdev ID. Fortunately, the latter won't happen as long
as we add only devices to /machine/peripheral/.
Earlier, commit b6cc36abb2 "qdev: device_del: Search for to be
unplugged device in 'peripheral' container" rewrote the lookup by qdev
ID to use QOM instead of qdev_find_recursive(), so it can handle
buss-less devices. It does so by constructing an absolute QOM path.
Works, but object_resolve_path_component() is easier. Switching to it
also gets rid of the unclean duplication described above.
While there, avoid converting to TYPE_DEVICE twice, first to check
whether it's possible, and then for real.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Damien Hedde <damien.hedde@greensocs.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Message-Id: <20210916111707.84999-1-armbru@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-09-16 19:17:07 +08:00
|
|
|
return dev;
|
2016-09-20 19:38:41 +08:00
|
|
|
}
|
|
|
|
|
2017-03-28 17:22:51 +08:00
|
|
|
void qdev_unplug(DeviceState *dev, Error **errp)
|
|
|
|
{
|
|
|
|
DeviceClass *dc = DEVICE_GET_CLASS(dev);
|
|
|
|
HotplugHandler *hotplug_ctrl;
|
|
|
|
HotplugHandlerClass *hdc;
|
qdev: Let the hotplug_handler_unplug() caller delete the device
When unplugging a device, at one point the device will be destroyed
via object_unparent(). This will, one the one hand, unrealize the
removed device hierarchy, and on the other hand, destroy/free the
device hierarchy.
When chaining hotplug handlers, we want to overwrite a bus hotplug
handler by the machine hotplug handler, to be able to perform
some part of the plug/unplug and to forward the calls to the bus hotplug
handler.
For now, the bus hotplug handler would trigger an object_unparent(), not
allowing us to perform some unplug action on a device after we forwarded
the call to the bus hotplug handler. The device would be gone at that
point.
machine_unplug_handler(dev)
/* eventually do unplug stuff */
bus_unplug_handler(dev)
/* dev is gone, we can't do more unplug stuff */
So move the object_unparent() to the original caller of the unplug. For
now, keep the unrealize() at the original places of the
object_unparent(). For implicitly chained hotplug handlers (e.g. pc
code calling acpi hotplug handlers), the object_unparent() has to be
done by the outermost caller. So when calling hotplug_handler_unplug()
from inside an unplug handler, nothing is to be done.
hotplug_handler_unplug(dev) -> calls machine_unplug_handler()
machine_unplug_handler(dev) {
/* eventually do unplug stuff */
bus_unplug_handler(dev) -> calls unrealize(dev)
/* we can do more unplug stuff but device already unrealized */
}
object_unparent(dev)
In the long run, every unplug action should be factored out of the
unrealize() function into the unplug handler (especially for PCI). Then
we can get rid of the additonal unrealize() calls and object_unparent()
will properly unrealize the device hierarchy after the device has been
unplugged.
hotplug_handler_unplug(dev) -> calls machine_unplug_handler()
machine_unplug_handler(dev) {
/* eventually do unplug stuff */
bus_unplug_handler(dev) -> only unplugs, does not unrealize
/* we can do more unplug stuff */
}
object_unparent(dev) -> will unrealize
The original approach was suggested by Igor Mammedov for the PCI
part, but I extended it to all hotplug handlers. I consider this one
step into the right direction.
To summarize:
- object_unparent() on synchronous unplugs is done by common code
-- "Caller of hotplug_handler_unplug"
- object_unparent() on asynchronous unplugs ("unplug requests") has to
be done manually
-- "Caller of hotplug_handler_unplug"
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
Acked-by: Cornelia Huck <cohuck@redhat.com>
Signed-off-by: David Hildenbrand <david@redhat.com>
Message-Id: <20190228122849.4296-2-david@redhat.com>
Reviewed-by: Greg Kurz <groug@kaod.org>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
2019-02-28 20:28:47 +08:00
|
|
|
Error *local_err = NULL;
|
2017-03-28 17:22:51 +08:00
|
|
|
|
|
|
|
if (dev->parent_bus && !qbus_is_hotpluggable(dev->parent_bus)) {
|
|
|
|
error_setg(errp, QERR_BUS_NO_HOTPLUG, dev->parent_bus->name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!dc->hotpluggable) {
|
|
|
|
error_setg(errp, QERR_DEVICE_NO_HOTPLUG,
|
|
|
|
object_get_typename(OBJECT(dev)));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-10-29 19:49:01 +08:00
|
|
|
if (!migration_is_idle() && !dev->allow_unplug_during_migration) {
|
2017-03-23 00:34:27 +08:00
|
|
|
error_setg(errp, "device_del not allowed while migrating");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-03-28 17:22:51 +08:00
|
|
|
qdev_hot_removed = true;
|
|
|
|
|
|
|
|
hotplug_ctrl = qdev_get_hotplug_handler(dev);
|
|
|
|
/* hotpluggable device MUST have HotplugHandler, if it doesn't
|
|
|
|
* then something is very wrong with it */
|
|
|
|
g_assert(hotplug_ctrl);
|
|
|
|
|
|
|
|
/* If device supports async unplug just request it to be done,
|
|
|
|
* otherwise just remove it synchronously */
|
|
|
|
hdc = HOTPLUG_HANDLER_GET_CLASS(hotplug_ctrl);
|
|
|
|
if (hdc->unplug_request) {
|
qdev: Let the hotplug_handler_unplug() caller delete the device
When unplugging a device, at one point the device will be destroyed
via object_unparent(). This will, one the one hand, unrealize the
removed device hierarchy, and on the other hand, destroy/free the
device hierarchy.
When chaining hotplug handlers, we want to overwrite a bus hotplug
handler by the machine hotplug handler, to be able to perform
some part of the plug/unplug and to forward the calls to the bus hotplug
handler.
For now, the bus hotplug handler would trigger an object_unparent(), not
allowing us to perform some unplug action on a device after we forwarded
the call to the bus hotplug handler. The device would be gone at that
point.
machine_unplug_handler(dev)
/* eventually do unplug stuff */
bus_unplug_handler(dev)
/* dev is gone, we can't do more unplug stuff */
So move the object_unparent() to the original caller of the unplug. For
now, keep the unrealize() at the original places of the
object_unparent(). For implicitly chained hotplug handlers (e.g. pc
code calling acpi hotplug handlers), the object_unparent() has to be
done by the outermost caller. So when calling hotplug_handler_unplug()
from inside an unplug handler, nothing is to be done.
hotplug_handler_unplug(dev) -> calls machine_unplug_handler()
machine_unplug_handler(dev) {
/* eventually do unplug stuff */
bus_unplug_handler(dev) -> calls unrealize(dev)
/* we can do more unplug stuff but device already unrealized */
}
object_unparent(dev)
In the long run, every unplug action should be factored out of the
unrealize() function into the unplug handler (especially for PCI). Then
we can get rid of the additonal unrealize() calls and object_unparent()
will properly unrealize the device hierarchy after the device has been
unplugged.
hotplug_handler_unplug(dev) -> calls machine_unplug_handler()
machine_unplug_handler(dev) {
/* eventually do unplug stuff */
bus_unplug_handler(dev) -> only unplugs, does not unrealize
/* we can do more unplug stuff */
}
object_unparent(dev) -> will unrealize
The original approach was suggested by Igor Mammedov for the PCI
part, but I extended it to all hotplug handlers. I consider this one
step into the right direction.
To summarize:
- object_unparent() on synchronous unplugs is done by common code
-- "Caller of hotplug_handler_unplug"
- object_unparent() on asynchronous unplugs ("unplug requests") has to
be done manually
-- "Caller of hotplug_handler_unplug"
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
Acked-by: Cornelia Huck <cohuck@redhat.com>
Signed-off-by: David Hildenbrand <david@redhat.com>
Message-Id: <20190228122849.4296-2-david@redhat.com>
Reviewed-by: Greg Kurz <groug@kaod.org>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
2019-02-28 20:28:47 +08:00
|
|
|
hotplug_handler_unplug_request(hotplug_ctrl, dev, &local_err);
|
2017-03-28 17:22:51 +08:00
|
|
|
} else {
|
qdev: Let the hotplug_handler_unplug() caller delete the device
When unplugging a device, at one point the device will be destroyed
via object_unparent(). This will, one the one hand, unrealize the
removed device hierarchy, and on the other hand, destroy/free the
device hierarchy.
When chaining hotplug handlers, we want to overwrite a bus hotplug
handler by the machine hotplug handler, to be able to perform
some part of the plug/unplug and to forward the calls to the bus hotplug
handler.
For now, the bus hotplug handler would trigger an object_unparent(), not
allowing us to perform some unplug action on a device after we forwarded
the call to the bus hotplug handler. The device would be gone at that
point.
machine_unplug_handler(dev)
/* eventually do unplug stuff */
bus_unplug_handler(dev)
/* dev is gone, we can't do more unplug stuff */
So move the object_unparent() to the original caller of the unplug. For
now, keep the unrealize() at the original places of the
object_unparent(). For implicitly chained hotplug handlers (e.g. pc
code calling acpi hotplug handlers), the object_unparent() has to be
done by the outermost caller. So when calling hotplug_handler_unplug()
from inside an unplug handler, nothing is to be done.
hotplug_handler_unplug(dev) -> calls machine_unplug_handler()
machine_unplug_handler(dev) {
/* eventually do unplug stuff */
bus_unplug_handler(dev) -> calls unrealize(dev)
/* we can do more unplug stuff but device already unrealized */
}
object_unparent(dev)
In the long run, every unplug action should be factored out of the
unrealize() function into the unplug handler (especially for PCI). Then
we can get rid of the additonal unrealize() calls and object_unparent()
will properly unrealize the device hierarchy after the device has been
unplugged.
hotplug_handler_unplug(dev) -> calls machine_unplug_handler()
machine_unplug_handler(dev) {
/* eventually do unplug stuff */
bus_unplug_handler(dev) -> only unplugs, does not unrealize
/* we can do more unplug stuff */
}
object_unparent(dev) -> will unrealize
The original approach was suggested by Igor Mammedov for the PCI
part, but I extended it to all hotplug handlers. I consider this one
step into the right direction.
To summarize:
- object_unparent() on synchronous unplugs is done by common code
-- "Caller of hotplug_handler_unplug"
- object_unparent() on asynchronous unplugs ("unplug requests") has to
be done manually
-- "Caller of hotplug_handler_unplug"
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
Acked-by: Cornelia Huck <cohuck@redhat.com>
Signed-off-by: David Hildenbrand <david@redhat.com>
Message-Id: <20190228122849.4296-2-david@redhat.com>
Reviewed-by: Greg Kurz <groug@kaod.org>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
2019-02-28 20:28:47 +08:00
|
|
|
hotplug_handler_unplug(hotplug_ctrl, dev, &local_err);
|
|
|
|
if (!local_err) {
|
|
|
|
object_unparent(OBJECT(dev));
|
|
|
|
}
|
2017-03-28 17:22:51 +08:00
|
|
|
}
|
qdev: Let the hotplug_handler_unplug() caller delete the device
When unplugging a device, at one point the device will be destroyed
via object_unparent(). This will, one the one hand, unrealize the
removed device hierarchy, and on the other hand, destroy/free the
device hierarchy.
When chaining hotplug handlers, we want to overwrite a bus hotplug
handler by the machine hotplug handler, to be able to perform
some part of the plug/unplug and to forward the calls to the bus hotplug
handler.
For now, the bus hotplug handler would trigger an object_unparent(), not
allowing us to perform some unplug action on a device after we forwarded
the call to the bus hotplug handler. The device would be gone at that
point.
machine_unplug_handler(dev)
/* eventually do unplug stuff */
bus_unplug_handler(dev)
/* dev is gone, we can't do more unplug stuff */
So move the object_unparent() to the original caller of the unplug. For
now, keep the unrealize() at the original places of the
object_unparent(). For implicitly chained hotplug handlers (e.g. pc
code calling acpi hotplug handlers), the object_unparent() has to be
done by the outermost caller. So when calling hotplug_handler_unplug()
from inside an unplug handler, nothing is to be done.
hotplug_handler_unplug(dev) -> calls machine_unplug_handler()
machine_unplug_handler(dev) {
/* eventually do unplug stuff */
bus_unplug_handler(dev) -> calls unrealize(dev)
/* we can do more unplug stuff but device already unrealized */
}
object_unparent(dev)
In the long run, every unplug action should be factored out of the
unrealize() function into the unplug handler (especially for PCI). Then
we can get rid of the additonal unrealize() calls and object_unparent()
will properly unrealize the device hierarchy after the device has been
unplugged.
hotplug_handler_unplug(dev) -> calls machine_unplug_handler()
machine_unplug_handler(dev) {
/* eventually do unplug stuff */
bus_unplug_handler(dev) -> only unplugs, does not unrealize
/* we can do more unplug stuff */
}
object_unparent(dev) -> will unrealize
The original approach was suggested by Igor Mammedov for the PCI
part, but I extended it to all hotplug handlers. I consider this one
step into the right direction.
To summarize:
- object_unparent() on synchronous unplugs is done by common code
-- "Caller of hotplug_handler_unplug"
- object_unparent() on asynchronous unplugs ("unplug requests") has to
be done manually
-- "Caller of hotplug_handler_unplug"
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
Acked-by: Cornelia Huck <cohuck@redhat.com>
Signed-off-by: David Hildenbrand <david@redhat.com>
Message-Id: <20190228122849.4296-2-david@redhat.com>
Reviewed-by: Greg Kurz <groug@kaod.org>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
2019-02-28 20:28:47 +08:00
|
|
|
error_propagate(errp, local_err);
|
2017-03-28 17:22:51 +08:00
|
|
|
}
|
|
|
|
|
2016-09-20 19:38:41 +08:00
|
|
|
void qmp_device_del(const char *id, Error **errp)
|
|
|
|
{
|
|
|
|
DeviceState *dev = find_device_state(id, errp);
|
|
|
|
if (dev != NULL) {
|
2020-02-21 00:55:56 +08:00
|
|
|
if (dev->pending_deleted_event) {
|
|
|
|
error_setg(errp, "Device %s is already in the "
|
|
|
|
"process of unplug", id);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-09-20 19:38:41 +08:00
|
|
|
qdev_unplug(dev, errp);
|
|
|
|
}
|
2011-12-23 05:24:20 +08:00
|
|
|
}
|
|
|
|
|
2019-07-10 02:59:36 +08:00
|
|
|
void hmp_device_add(Monitor *mon, const QDict *qdict)
|
|
|
|
{
|
|
|
|
Error *err = NULL;
|
|
|
|
|
|
|
|
qmp_device_add((QDict *)qdict, NULL, &err);
|
2019-12-06 01:46:18 +08:00
|
|
|
hmp_handle_error(mon, err);
|
2019-07-10 02:59:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void hmp_device_del(Monitor *mon, const QDict *qdict)
|
|
|
|
{
|
|
|
|
const char *id = qdict_get_str(qdict, "id");
|
|
|
|
Error *err = NULL;
|
|
|
|
|
|
|
|
qmp_device_del(id, &err);
|
2019-12-06 01:46:18 +08:00
|
|
|
hmp_handle_error(mon, err);
|
2019-07-10 02:59:36 +08:00
|
|
|
}
|
|
|
|
|
2016-09-20 19:38:42 +08:00
|
|
|
BlockBackend *blk_by_qdev_id(const char *id, Error **errp)
|
|
|
|
{
|
|
|
|
DeviceState *dev;
|
|
|
|
BlockBackend *blk;
|
|
|
|
|
|
|
|
dev = find_device_state(id, errp);
|
|
|
|
if (dev == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
blk = blk_by_dev(dev);
|
|
|
|
if (!blk) {
|
|
|
|
error_setg(errp, "Device does not have a block device backend");
|
|
|
|
}
|
|
|
|
return blk;
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|
2020-10-27 16:44:18 +08:00
|
|
|
|
|
|
|
bool qmp_command_available(const QmpCommand *cmd, Error **errp)
|
|
|
|
{
|
2020-11-12 22:38:36 +08:00
|
|
|
if (!phase_check(PHASE_MACHINE_READY) &&
|
2020-10-27 16:44:18 +08:00
|
|
|
!(cmd->options & QCO_ALLOW_PRECONFIG)) {
|
|
|
|
error_setg(errp, "The command '%s' is permitted only after machine initialization has completed",
|
|
|
|
cmd->name);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|