aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/acpi/piix4.c6
-rw-r--r--hw/char/virtio-serial-bus.c20
-rw-r--r--hw/core/hotplug.c11
-rw-r--r--hw/core/qdev-properties-system.c8
-rw-r--r--hw/core/qdev-properties.c14
-rw-r--r--hw/core/qdev.c113
-rw-r--r--hw/cpu/icc_bus.c8
-rw-r--r--hw/i386/acpi-build.c2
-rw-r--r--hw/isa/lpc_ich9.c6
-rw-r--r--hw/pci-bridge/pci_bridge_dev.c2
-rw-r--r--hw/pci/pci-hotplug-old.c4
-rw-r--r--hw/pci/pcie.c4
-rw-r--r--hw/pci/pcie_port.c2
-rw-r--r--hw/pci/shpc.c4
-rw-r--r--hw/s390x/event-facility.c2
-rw-r--r--hw/s390x/s390-virtio-bus.c10
-rw-r--r--hw/s390x/virtio-ccw.c17
-rw-r--r--hw/scsi/scsi-bus.c24
-rw-r--r--hw/scsi/virtio-scsi.c30
-rw-r--r--hw/scsi/vmw_pvscsi.c26
-rw-r--r--hw/usb/bus.c9
-rw-r--r--hw/usb/dev-smartcard-reader.c8
-rw-r--r--hw/usb/dev-storage.c4
-rw-r--r--hw/virtio/virtio-mmio.c17
-rw-r--r--hw/virtio/virtio-pci.c3
-rw-r--r--include/hw/hotplug.h16
-rw-r--r--include/hw/pci/pcie.h4
-rw-r--r--include/hw/pci/shpc.h4
-rw-r--r--include/hw/qdev-core.h19
-rw-r--r--include/hw/scsi/scsi.h2
-rw-r--r--include/qom/object.h14
-rw-r--r--qapi-schema.json4
-rw-r--r--qdev-monitor.c25
-rw-r--r--qmp.c13
-rw-r--r--qom/object.c41
-rw-r--r--target-ppc/translate_init.c2
-rw-r--r--tests/Makefile13
-rw-r--r--tests/libqos/pci-pc.c49
-rw-r--r--tests/libqos/pci.h3
-rw-r--r--tests/libqos/usb.c71
-rw-r--r--tests/libqos/usb.h17
-rw-r--r--tests/usb-hcd-ehci-test.c50
-rw-r--r--tests/usb-hcd-ohci-test.c10
-rw-r--r--tests/usb-hcd-uhci-test.c65
-rw-r--r--tests/usb-hcd-xhci-test.c68
-rw-r--r--tests/virtio-blk-test.c49
-rw-r--r--tests/virtio-net-test.c10
-rw-r--r--tests/virtio-rng-test.c10
-rw-r--r--tests/virtio-scsi-test.c29
-rw-r--r--tests/virtio-serial-test.c27
50 files changed, 744 insertions, 225 deletions
diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c
index b72b34e..0bfa814 100644
--- a/hw/acpi/piix4.c
+++ b/hw/acpi/piix4.c
@@ -354,8 +354,8 @@ static void piix4_device_plug_cb(HotplugHandler *hotplug_dev,
}
}
-static void piix4_device_unplug_cb(HotplugHandler *hotplug_dev,
- DeviceState *dev, Error **errp)
+static void piix4_device_unplug_request_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
{
PIIX4PMState *s = PIIX4_PM(hotplug_dev);
@@ -615,7 +615,7 @@ static void piix4_pm_class_init(ObjectClass *klass, void *data)
dc->cannot_instantiate_with_device_add_yet = true;
dc->hotpluggable = false;
hc->plug = piix4_device_plug_cb;
- hc->unplug = piix4_device_unplug_cb;
+ hc->unplug_request = piix4_device_unplug_request_cb;
adevc->ospm_status = piix4_ospm_status;
}
diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c
index 3931085..c6870f1 100644
--- a/hw/char/virtio-serial-bus.c
+++ b/hw/char/virtio-serial-bus.c
@@ -904,6 +904,12 @@ static void virtser_port_device_realize(DeviceState *dev, Error **errp)
}
port->elem.out_num = 0;
+}
+
+static void virtser_port_device_plug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(dev);
QTAILQ_INSERT_TAIL(&port->vser->ports, port, next);
port->ivq = port->vser->ivqs[port->id];
@@ -912,7 +918,7 @@ static void virtser_port_device_realize(DeviceState *dev, Error **errp)
add_port(port->vser, port->id);
/* Send an update to the guest about this new port added */
- virtio_notify_config(vdev);
+ virtio_notify_config(VIRTIO_DEVICE(hotplug_dev));
}
static void virtser_port_device_unrealize(DeviceState *dev, Error **errp)
@@ -935,7 +941,6 @@ static void virtio_serial_device_realize(DeviceState *dev, Error **errp)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VirtIOSerial *vser = VIRTIO_SERIAL(dev);
- BusState *bus;
uint32_t i, max_supported_ports;
if (!vser->serial.max_virtserial_ports) {
@@ -957,8 +962,7 @@ static void virtio_serial_device_realize(DeviceState *dev, Error **errp)
/* Spawn a new virtio-serial bus on which the ports will ride as devices */
qbus_create_inplace(&vser->bus, sizeof(vser->bus), TYPE_VIRTIO_SERIAL_BUS,
dev, vdev->bus_name);
- bus = BUS(&vser->bus);
- bus->allow_hotplug = 1;
+ qbus_set_hotplug_handler(BUS(&vser->bus), DEVICE(vser), errp);
vser->bus.vser = vser;
QTAILQ_INIT(&vser->ports);
@@ -1021,7 +1025,6 @@ static void virtio_serial_port_class_init(ObjectClass *klass, void *data)
k->bus_type = TYPE_VIRTIO_SERIAL_BUS;
k->realize = virtser_port_device_realize;
k->unrealize = virtser_port_device_unrealize;
- k->unplug = qdev_simple_unplug_cb;
k->props = virtser_props;
}
@@ -1064,6 +1067,7 @@ static void virtio_serial_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
+ HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
QLIST_INIT(&vserdevices.devices);
@@ -1077,6 +1081,8 @@ static void virtio_serial_class_init(ObjectClass *klass, void *data)
vdc->reset = vser_reset;
vdc->save = virtio_serial_save_device;
vdc->load = virtio_serial_load_device;
+ hc->plug = virtser_port_device_plug;
+ hc->unplug = qdev_simple_device_unplug_cb;
}
static const TypeInfo virtio_device_info = {
@@ -1084,6 +1090,10 @@ static const TypeInfo virtio_device_info = {
.parent = TYPE_VIRTIO_DEVICE,
.instance_size = sizeof(VirtIOSerial),
.class_init = virtio_serial_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_HOTPLUG_HANDLER },
+ { }
+ }
};
static void virtio_serial_register_types(void)
diff --git a/hw/core/hotplug.c b/hw/core/hotplug.c
index 5573d9d..4e01074 100644
--- a/hw/core/hotplug.c
+++ b/hw/core/hotplug.c
@@ -23,6 +23,17 @@ void hotplug_handler_plug(HotplugHandler *plug_handler,
}
}
+void hotplug_handler_unplug_request(HotplugHandler *plug_handler,
+ DeviceState *plugged_dev,
+ Error **errp)
+{
+ HotplugHandlerClass *hdc = HOTPLUG_HANDLER_GET_CLASS(plug_handler);
+
+ if (hdc->unplug_request) {
+ hdc->unplug_request(plug_handler, plugged_dev, errp);
+ }
+}
+
void hotplug_handler_unplug(HotplugHandler *plug_handler,
DeviceState *plugged_dev,
Error **errp)
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
index 84caa1d..f2bd954 100644
--- a/hw/core/qdev-properties-system.c
+++ b/hw/core/qdev-properties-system.c
@@ -112,7 +112,7 @@ static void set_drive(Object *obj, Visitor *v, void *opaque,
PropertyInfo qdev_prop_drive = {
.name = "str",
- .legacy_name = "drive",
+ .description = "ID of a drive to use as a backend",
.get = get_drive,
.set = set_drive,
.release = release_drive,
@@ -169,7 +169,7 @@ static void set_chr(Object *obj, Visitor *v, void *opaque,
PropertyInfo qdev_prop_chr = {
.name = "str",
- .legacy_name = "chr",
+ .description = "ID of a chardev to use as a backend",
.get = get_chr,
.set = set_chr,
.release = release_chr,
@@ -248,7 +248,7 @@ static void set_netdev(Object *obj, Visitor *v, void *opaque,
PropertyInfo qdev_prop_netdev = {
.name = "str",
- .legacy_name = "netdev",
+ .description = "ID of a netdev to use as a backend",
.get = get_netdev,
.set = set_netdev,
};
@@ -328,7 +328,7 @@ static void set_vlan(Object *obj, Visitor *v, void *opaque,
PropertyInfo qdev_prop_vlan = {
.name = "int32",
- .legacy_name = "vlan",
+ .description = "Integer VLAN id to connect to",
.print = print_vlan,
.get = get_vlan,
.set = set_vlan,
diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c
index 66556d3..2ed995f 100644
--- a/hw/core/qdev-properties.c
+++ b/hw/core/qdev-properties.c
@@ -120,7 +120,7 @@ static void prop_set_bit(Object *obj, Visitor *v, void *opaque,
PropertyInfo qdev_prop_bit = {
.name = "bool",
- .legacy_name = "on/off",
+ .description = "on/off",
.get = prop_get_bit,
.set = prop_set_bit,
};
@@ -455,7 +455,7 @@ inval:
PropertyInfo qdev_prop_macaddr = {
.name = "str",
- .legacy_name = "macaddr",
+ .description = "Ethernet 6-byte MAC Address, example: 52:54:00:12:34:56",
.get = get_mac,
.set = set_mac,
};
@@ -477,7 +477,8 @@ QEMU_BUILD_BUG_ON(sizeof(BiosAtaTranslation) != sizeof(int));
PropertyInfo qdev_prop_bios_chs_trans = {
.name = "BiosAtaTranslation",
- .legacy_name = "bios-chs-trans",
+ .description = "Logical CHS translation algorithm, "
+ "auto/none/lba/large/rechs",
.enum_table = BiosAtaTranslation_lookup,
.get = get_enum,
.set = set_enum,
@@ -551,7 +552,7 @@ static int print_pci_devfn(DeviceState *dev, Property *prop, char *dest,
PropertyInfo qdev_prop_pci_devfn = {
.name = "int32",
- .legacy_name = "pci-devfn",
+ .description = "Slot and optional function number, example: 06.0 or 06",
.print = print_pci_devfn,
.get = get_int32,
.set = set_pci_devfn,
@@ -598,7 +599,7 @@ static void set_blocksize(Object *obj, Visitor *v, void *opaque,
PropertyInfo qdev_prop_blocksize = {
.name = "uint16",
- .legacy_name = "blocksize",
+ .description = "A power of two between 512 and 32768",
.get = get_uint16,
.set = set_blocksize,
};
@@ -706,7 +707,8 @@ inval:
PropertyInfo qdev_prop_pci_host_devaddr = {
.name = "str",
- .legacy_name = "pci-host-devaddr",
+ .description = "Address (bus/device/function) of "
+ "the host device, example: 04:10.0",
.get = get_pci_host_devaddr,
.set = set_pci_host_devaddr,
};
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index fcb1638..a1e9247 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -85,10 +85,6 @@ static void bus_add_child(BusState *bus, DeviceState *child)
char name[32];
BusChild *kid = g_malloc0(sizeof(*kid));
- if (qdev_hotplug) {
- assert(bus->allow_hotplug);
- }
-
kid->index = bus->max_index++;
kid->child = child;
object_ref(OBJECT(kid->child));
@@ -112,6 +108,24 @@ void qdev_set_parent_bus(DeviceState *dev, BusState *bus)
bus_add_child(bus, dev);
}
+static void qbus_set_hotplug_handler_internal(BusState *bus, Object *handler,
+ Error **errp)
+{
+
+ object_property_set_link(OBJECT(bus), OBJECT(handler),
+ QDEV_HOTPLUG_HANDLER_PROPERTY, errp);
+}
+
+void qbus_set_hotplug_handler(BusState *bus, DeviceState *handler, Error **errp)
+{
+ qbus_set_hotplug_handler_internal(bus, OBJECT(handler), errp);
+}
+
+void qbus_set_bus_hotplug_handler(BusState *bus, Error **errp)
+{
+ qbus_set_hotplug_handler_internal(bus, OBJECT(bus), errp);
+}
+
/* Create a new device. This only initializes the device state structure
and allows properties to be set. qdev_init should be called to
initialize the actual device emulation. */
@@ -209,11 +223,30 @@ void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id,
dev->alias_required_for_version = required_for_version;
}
+static HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev)
+{
+ HotplugHandler *hotplug_ctrl = NULL;
+
+ if (dev->parent_bus && dev->parent_bus->hotplug_handler) {
+ hotplug_ctrl = dev->parent_bus->hotplug_handler;
+ } else if (object_dynamic_cast(qdev_get_machine(), TYPE_MACHINE)) {
+ MachineState *machine = MACHINE(qdev_get_machine());
+ MachineClass *mc = MACHINE_GET_CLASS(machine);
+
+ if (mc->get_hotplug_handler) {
+ hotplug_ctrl = mc->get_hotplug_handler(machine, dev);
+ }
+ }
+ return hotplug_ctrl;
+}
+
void qdev_unplug(DeviceState *dev, Error **errp)
{
DeviceClass *dc = DEVICE_GET_CLASS(dev);
+ HotplugHandler *hotplug_ctrl;
+ HotplugHandlerClass *hdc;
- if (dev->parent_bus && !dev->parent_bus->allow_hotplug) {
+ if (dev->parent_bus && !qbus_is_hotpluggable(dev->parent_bus)) {
error_set(errp, QERR_BUS_NO_HOTPLUG, dev->parent_bus->name);
return;
}
@@ -226,13 +259,18 @@ void qdev_unplug(DeviceState *dev, Error **errp)
qdev_hot_removed = true;
- if (dev->parent_bus && dev->parent_bus->hotplug_handler) {
- hotplug_handler_unplug(dev->parent_bus->hotplug_handler, dev, errp);
+ 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) {
+ hotplug_handler_unplug_request(hotplug_ctrl, dev, errp);
} else {
- assert(dc->unplug != NULL);
- if (dc->unplug(dev) < 0) { /* legacy handler */
- error_set(errp, QERR_UNDEFINED_ERROR);
- }
+ hotplug_handler_unplug(hotplug_ctrl, dev, errp);
}
}
@@ -269,14 +307,13 @@ void qbus_reset_all_fn(void *opaque)
}
/* can be used as ->unplug() callback for the simple cases */
-int qdev_simple_unplug_cb(DeviceState *dev)
+void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
{
/* just zap it */
object_unparent(OBJECT(dev));
- return 0;
}
-
/* Like qdev_init(), but terminate program via error_report() instead of
returning an error value. This is okay during machine creation.
Don't use for hotplug, because there callers need to recover from
@@ -337,10 +374,20 @@ static NamedGPIOList *qdev_get_named_gpio_list(DeviceState *dev,
void qdev_init_gpio_in_named(DeviceState *dev, qemu_irq_handler handler,
const char *name, int n)
{
+ int i;
NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name);
+ char *propname = g_strdup_printf("%s[*]", name ? name : "unnamed-gpio-in");
+ assert(gpio_list->num_out == 0 || !name);
gpio_list->in = qemu_extend_irqs(gpio_list->in, gpio_list->num_in, handler,
dev, n);
+
+ for (i = gpio_list->num_in; i < gpio_list->num_in + n; i++) {
+ object_property_add_child(OBJECT(dev), propname,
+ OBJECT(gpio_list->in[i]), &error_abort);
+ }
+ g_free(propname);
+
gpio_list->num_in += n;
}
@@ -352,11 +399,24 @@ void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n)
void qdev_init_gpio_out_named(DeviceState *dev, qemu_irq *pins,
const char *name, int n)
{
+ int i;
NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name);
+ char *propname = g_strdup_printf("%s[*]", name ? name : "unnamed-gpio-out");
+ assert(gpio_list->num_in == 0 || !name);
assert(gpio_list->num_out == 0);
gpio_list->num_out = n;
gpio_list->out = pins;
+
+ for (i = 0; i < n; ++i) {
+ memset(&pins[i], 0, sizeof(*pins));
+ object_property_add_link(OBJECT(dev), propname, TYPE_IRQ,
+ (Object **)&pins[i],
+ object_property_allow_set_link,
+ OBJ_PROP_LINK_UNREF_ON_RELEASE,
+ &error_abort);
+ }
+ g_free(propname);
}
void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n)
@@ -766,6 +826,11 @@ void qdev_property_add_static(DeviceState *dev, Property *prop,
error_propagate(errp, local_err);
return;
}
+
+ object_property_set_description(obj, prop->name,
+ prop->info->description,
+ &error_abort);
+
if (prop->qtype == QTYPE_NONE) {
return;
}
@@ -811,6 +876,7 @@ static void device_set_realized(Object *obj, bool value, Error **errp)
{
DeviceState *dev = DEVICE(obj);
DeviceClass *dc = DEVICE_GET_CLASS(dev);
+ HotplugHandler *hotplug_ctrl;
BusState *bus;
Error *local_err = NULL;
@@ -838,20 +904,9 @@ static void device_set_realized(Object *obj, bool value, Error **errp)
goto fail;
}
- if (dev->parent_bus && dev->parent_bus->hotplug_handler) {
- hotplug_handler_plug(dev->parent_bus->hotplug_handler,
- dev, &local_err);
- } else if (object_dynamic_cast(qdev_get_machine(), TYPE_MACHINE)) {
- HotplugHandler *hotplug_ctrl;
- MachineState *machine = MACHINE(qdev_get_machine());
- MachineClass *mc = MACHINE_GET_CLASS(machine);
-
- if (mc->get_hotplug_handler) {
- hotplug_ctrl = mc->get_hotplug_handler(machine, dev);
- if (hotplug_ctrl) {
- hotplug_handler_plug(hotplug_ctrl, dev, &local_err);
- }
- }
+ hotplug_ctrl = qdev_get_hotplug_handler(dev);
+ if (hotplug_ctrl) {
+ hotplug_handler_plug(hotplug_ctrl, dev, &local_err);
}
if (local_err != NULL) {
@@ -925,7 +980,7 @@ static bool device_get_hotpluggable(Object *obj, Error **errp)
DeviceState *dev = DEVICE(obj);
return dc->hotpluggable && (dev->parent_bus == NULL ||
- dev->parent_bus->allow_hotplug);
+ qbus_is_hotpluggable(dev->parent_bus));
}
static bool device_get_hotplugged(Object *obj, Error **err)
diff --git a/hw/cpu/icc_bus.c b/hw/cpu/icc_bus.c
index 7f44c59..9575fd6 100644
--- a/hw/cpu/icc_bus.c
+++ b/hw/cpu/icc_bus.c
@@ -24,18 +24,10 @@
/* icc-bridge implementation */
-static void icc_bus_init(Object *obj)
-{
- BusState *b = BUS(obj);
-
- b->allow_hotplug = true;
-}
-
static const TypeInfo icc_bus_info = {
.name = TYPE_ICC_BUS,
.parent = TYPE_BUS,
.instance_size = sizeof(ICCBus),
- .instance_init = icc_bus_init,
};
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index a313321..00be4bb 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -774,7 +774,7 @@ static void *acpi_set_bsel(PCIBus *bus, void *opaque)
unsigned *bsel_alloc = opaque;
unsigned *bus_bsel;
- if (bus->qbus.allow_hotplug) {
+ if (qbus_is_hotpluggable(BUS(bus))) {
bus_bsel = g_malloc(sizeof *bus_bsel);
*bus_bsel = (*bsel_alloc)++;
diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
index 177023b..530b074 100644
--- a/hw/isa/lpc_ich9.c
+++ b/hw/isa/lpc_ich9.c
@@ -607,8 +607,8 @@ static void ich9_device_plug_cb(HotplugHandler *hotplug_dev,
ich9_pm_device_plug_cb(&lpc->pm, dev, errp);
}
-static void ich9_device_unplug_cb(HotplugHandler *hotplug_dev,
- DeviceState *dev, Error **errp)
+static void ich9_device_unplug_request_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
{
error_setg(errp, "acpi: device unplug request for not supported device"
" type: %s", object_get_typename(OBJECT(dev)));
@@ -676,7 +676,7 @@ static void ich9_lpc_class_init(ObjectClass *klass, void *data)
*/
dc->cannot_instantiate_with_device_add_yet = true;
hc->plug = ich9_device_plug_cb;
- hc->unplug = ich9_device_unplug_cb;
+ hc->unplug_request = ich9_device_unplug_request_cb;
adevc->ospm_status = ich9_pm_ospm_status;
}
diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c
index 92799d0..252ea5e 100644
--- a/hw/pci-bridge/pci_bridge_dev.c
+++ b/hw/pci-bridge/pci_bridge_dev.c
@@ -150,7 +150,7 @@ static void pci_bridge_dev_class_init(ObjectClass *klass, void *data)
dc->vmsd = &pci_bridge_dev_vmstate;
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
hc->plug = shpc_device_hotplug_cb;
- hc->unplug = shpc_device_hot_unplug_cb;
+ hc->unplug_request = shpc_device_hot_unplug_request_cb;
}
static const TypeInfo pci_bridge_dev_info = {
diff --git a/hw/pci/pci-hotplug-old.c b/hw/pci/pci-hotplug-old.c
index d87c469..6ab28b7 100644
--- a/hw/pci/pci-hotplug-old.c
+++ b/hw/pci/pci-hotplug-old.c
@@ -77,7 +77,7 @@ static PCIDevice *qemu_pci_hot_add_nic(Monitor *mon,
monitor_printf(mon, "Invalid PCI device address %s\n", devaddr);
return NULL;
}
- if (!((BusState*)bus)->allow_hotplug) {
+ if (!qbus_is_hotpluggable(BUS(bus))) {
monitor_printf(mon, "PCI bus doesn't support hotplug\n");
return NULL;
}
@@ -227,7 +227,7 @@ static PCIDevice *qemu_pci_hot_add_storage(Monitor *mon,
monitor_printf(mon, "Invalid PCI device address %s\n", devaddr);
return NULL;
}
- if (!((BusState*)bus)->allow_hotplug) {
+ if (!qbus_is_hotpluggable(BUS(bus))) {
monitor_printf(mon, "PCI bus doesn't support hotplug\n");
return NULL;
}
diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c
index 1babddf..b64a004 100644
--- a/hw/pci/pcie.c
+++ b/hw/pci/pcie.c
@@ -262,8 +262,8 @@ void pcie_cap_slot_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
PCI_EXP_HP_EV_PDC | PCI_EXP_HP_EV_ABP);
}
-void pcie_cap_slot_hot_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
- Error **errp)
+void pcie_cap_slot_hot_unplug_request_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
{
uint8_t *exp_cap;
diff --git a/hw/pci/pcie_port.c b/hw/pci/pcie_port.c
index fa24877..40ca8d5 100644
--- a/hw/pci/pcie_port.c
+++ b/hw/pci/pcie_port.c
@@ -154,7 +154,7 @@ static void pcie_slot_class_init(ObjectClass *oc, void *data)
dc->props = pcie_slot_props;
hc->plug = pcie_cap_slot_hotplug_cb;
- hc->unplug = pcie_cap_slot_hot_unplug_cb;
+ hc->unplug_request = pcie_cap_slot_hot_unplug_request_cb;
}
static const TypeInfo pcie_slot_type_info = {
diff --git a/hw/pci/shpc.c b/hw/pci/shpc.c
index 1fcb8c4..65b2f51 100644
--- a/hw/pci/shpc.c
+++ b/hw/pci/shpc.c
@@ -549,8 +549,8 @@ void shpc_device_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
shpc_interrupt_update(pci_hotplug_dev);
}
-void shpc_device_hot_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
- Error **errp)
+void shpc_device_hot_unplug_request_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
{
Error *local_err = NULL;
PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev);
diff --git a/hw/s390x/event-facility.c b/hw/s390x/event-facility.c
index 597db34..78da718 100644
--- a/hw/s390x/event-facility.c
+++ b/hw/s390x/event-facility.c
@@ -333,7 +333,6 @@ static int init_event_facility(SCLPEventFacility *event_facility)
/* Spawn a new bus for SCLP events */
qbus_create_inplace(&event_facility->sbus, sizeof(event_facility->sbus),
TYPE_SCLP_EVENTS_BUS, sdev, NULL);
- event_facility->sbus.qbus.allow_hotplug = 0;
quiesce = qdev_create(&event_facility->sbus.qbus, "sclpquiesce");
if (!quiesce) {
@@ -408,7 +407,6 @@ static void event_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
dc->bus_type = TYPE_SCLP_EVENTS_BUS;
- dc->unplug = qdev_simple_unplug_cb;
dc->realize = event_realize;
dc->unrealize = event_unrealize;
}
diff --git a/hw/s390x/s390-virtio-bus.c b/hw/s390x/s390-virtio-bus.c
index f451ca1..5a9e52b 100644
--- a/hw/s390x/s390-virtio-bus.c
+++ b/hw/s390x/s390-virtio-bus.c
@@ -102,7 +102,7 @@ VirtIOS390Bus *s390_virtio_bus_init(ram_addr_t *ram_size)
bus->next_ring = bus->dev_page + TARGET_PAGE_SIZE;
/* Enable hotplugging */
- _bus->allow_hotplug = 1;
+ qbus_set_hotplug_handler(_bus, dev, &error_abort);
/* Allocate RAM for VirtIO device pages (descriptors, queues, rings) */
*ram_size += S390_DEVICE_PAGES * TARGET_PAGE_SIZE;
@@ -600,7 +600,6 @@ static void virtio_s390_device_class_init(ObjectClass *klass, void *data)
dc->init = s390_virtio_busdev_init;
dc->bus_type = TYPE_S390_VIRTIO_BUS;
- dc->unplug = qdev_simple_unplug_cb;
dc->reset = s390_virtio_busdev_reset;
}
@@ -681,6 +680,10 @@ static const TypeInfo s390_virtio_bridge_info = {
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(SysBusDevice),
.class_init = s390_virtio_bridge_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_HOTPLUG_HANDLER },
+ { }
+ }
};
/* virtio-s390-bus */
@@ -689,13 +692,10 @@ static void virtio_s390_bus_new(VirtioBusState *bus, size_t bus_size,
VirtIOS390Device *dev)
{
DeviceState *qdev = DEVICE(dev);
- BusState *qbus;
char virtio_bus_name[] = "virtio-bus";
qbus_create_inplace(bus, bus_size, TYPE_VIRTIO_S390_BUS,
qdev, virtio_bus_name);
- qbus = BUS(bus);
- qbus->allow_hotplug = 1;
}
static void virtio_s390_bus_class_init(ObjectClass *klass, void *data)
diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c
index 18ba29f..4438b6f 100644
--- a/hw/s390x/virtio-ccw.c
+++ b/hw/s390x/virtio-ccw.c
@@ -230,7 +230,7 @@ VirtualCssBus *virtual_css_bus_init(void)
cbus = VIRTUAL_CSS_BUS(bus);
/* Enable hotplugging */
- bus->allow_hotplug = 1;
+ qbus_set_hotplug_handler(bus, dev, &error_abort);
return cbus;
}
@@ -1590,7 +1590,8 @@ static int virtio_ccw_busdev_exit(DeviceState *dev)
return _info->exit(_dev);
}
-static int virtio_ccw_busdev_unplug(DeviceState *dev)
+static void virtio_ccw_busdev_unplug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
{
VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev;
SubchDev *sch = _dev->sch;
@@ -1609,7 +1610,6 @@ static int virtio_ccw_busdev_unplug(DeviceState *dev)
css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, 1, 0);
object_unparent(OBJECT(dev));
- return 0;
}
static Property virtio_ccw_properties[] = {
@@ -1624,9 +1624,7 @@ static void virtio_ccw_device_class_init(ObjectClass *klass, void *data)
dc->props = virtio_ccw_properties;
dc->init = virtio_ccw_busdev_init;
dc->exit = virtio_ccw_busdev_exit;
- dc->unplug = virtio_ccw_busdev_unplug;
dc->bus_type = TYPE_VIRTUAL_CSS_BUS;
-
}
static const TypeInfo virtio_ccw_device_info = {
@@ -1650,8 +1648,10 @@ static int virtual_css_bridge_init(SysBusDevice *dev)
static void virtual_css_bridge_class_init(ObjectClass *klass, void *data)
{
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+ HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
k->init = virtual_css_bridge_init;
+ hc->unplug = virtio_ccw_busdev_unplug;
}
static const TypeInfo virtual_css_bridge_info = {
@@ -1659,6 +1659,10 @@ static const TypeInfo virtual_css_bridge_info = {
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(SysBusDevice),
.class_init = virtual_css_bridge_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_HOTPLUG_HANDLER },
+ { }
+ }
};
/* virtio-ccw-bus */
@@ -1667,13 +1671,10 @@ static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size,
VirtioCcwDevice *dev)
{
DeviceState *qdev = DEVICE(dev);
- BusState *qbus;
char virtio_bus_name[] = "virtio-bus";
qbus_create_inplace(bus, bus_size, TYPE_VIRTIO_CCW_BUS,
qdev, virtio_bus_name);
- qbus = BUS(bus);
- qbus->allow_hotplug = 1;
}
static void virtio_ccw_bus_class_init(ObjectClass *klass, void *data)
diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c
index 0f3e039..d33030e 100644
--- a/hw/scsi/scsi-bus.c
+++ b/hw/scsi/scsi-bus.c
@@ -23,9 +23,11 @@ static Property scsi_props[] = {
static void scsi_bus_class_init(ObjectClass *klass, void *data)
{
BusClass *k = BUS_CLASS(klass);
+ HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
k->get_dev_path = scsibus_get_dev_path;
k->get_fw_dev_path = scsibus_get_fw_dev_path;
+ hc->unplug = qdev_simple_device_unplug_cb;
}
static const TypeInfo scsi_bus_info = {
@@ -33,6 +35,10 @@ static const TypeInfo scsi_bus_info = {
.parent = TYPE_BUS,
.instance_size = sizeof(SCSIBus),
.class_init = scsi_bus_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_HOTPLUG_HANDLER },
+ { }
+ }
};
static int next_scsi_bus;
@@ -92,7 +98,7 @@ void scsi_bus_new(SCSIBus *bus, size_t bus_size, DeviceState *host,
qbus_create_inplace(bus, bus_size, TYPE_SCSI_BUS, host, bus_name);
bus->busnr = next_scsi_bus++;
bus->info = info;
- bus->qbus.allow_hotplug = 1;
+ qbus_set_bus_hotplug_handler(BUS(bus), &error_abort);
}
static void scsi_dma_restart_bh(void *opaque)
@@ -202,10 +208,6 @@ static void scsi_qdev_realize(DeviceState *qdev, Error **errp)
}
dev->vmsentry = qemu_add_vm_change_state_handler(scsi_dma_restart_cb,
dev);
-
- if (bus->info->hotplug) {
- bus->info->hotplug(bus, dev);
- }
}
static void scsi_qdev_unrealize(DeviceState *qdev, Error **errp)
@@ -1937,17 +1939,6 @@ static int get_scsi_requests(QEMUFile *f, void *pv, size_t size)
return 0;
}
-static int scsi_qdev_unplug(DeviceState *qdev)
-{
- SCSIDevice *dev = SCSI_DEVICE(qdev);
- SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus);
-
- if (bus->info->hot_unplug) {
- bus->info->hot_unplug(bus, dev);
- }
- return qdev_simple_unplug_cb(qdev);
-}
-
static const VMStateInfo vmstate_info_scsi_requests = {
.name = "scsi-requests",
.get = get_scsi_requests,
@@ -2011,7 +2002,6 @@ static void scsi_device_class_init(ObjectClass *klass, void *data)
set_bit(DEVICE_CATEGORY_STORAGE, k->categories);
k->bus_type = TYPE_SCSI_BUS;
k->realize = scsi_qdev_realize;
- k->unplug = scsi_qdev_unplug;
k->unrealize = scsi_qdev_unrealize;
k->props = scsi_props;
}
diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c
index 6c02fe2..8547ea0 100644
--- a/hw/scsi/virtio-scsi.c
+++ b/hw/scsi/virtio-scsi.c
@@ -737,26 +737,29 @@ static void virtio_scsi_change(SCSIBus *bus, SCSIDevice *dev, SCSISense sense)
}
}
-static void virtio_scsi_hotplug(SCSIBus *bus, SCSIDevice *dev)
+static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev,
+ Error **errp)
{
- VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus);
- VirtIODevice *vdev = VIRTIO_DEVICE(s);
+ VirtIODevice *vdev = VIRTIO_DEVICE(hotplug_dev);
if ((vdev->guest_features >> VIRTIO_SCSI_F_HOTPLUG) & 1) {
- virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_TRANSPORT_RESET,
+ virtio_scsi_push_event(VIRTIO_SCSI(hotplug_dev), SCSI_DEVICE(dev),
+ VIRTIO_SCSI_T_TRANSPORT_RESET,
VIRTIO_SCSI_EVT_RESET_RESCAN);
}
}
-static void virtio_scsi_hot_unplug(SCSIBus *bus, SCSIDevice *dev)
+static void virtio_scsi_hotunplug(HotplugHandler *hotplug_dev, DeviceState *dev,
+ Error **errp)
{
- VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus);
- VirtIODevice *vdev = VIRTIO_DEVICE(s);
+ VirtIODevice *vdev = VIRTIO_DEVICE(hotplug_dev);
if ((vdev->guest_features >> VIRTIO_SCSI_F_HOTPLUG) & 1) {
- virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_TRANSPORT_RESET,
+ virtio_scsi_push_event(VIRTIO_SCSI(hotplug_dev), SCSI_DEVICE(dev),
+ VIRTIO_SCSI_T_TRANSPORT_RESET,
VIRTIO_SCSI_EVT_RESET_REMOVED);
}
+ qdev_simple_device_unplug_cb(hotplug_dev, dev, errp);
}
static struct SCSIBusInfo virtio_scsi_scsi_info = {
@@ -768,8 +771,6 @@ static struct SCSIBusInfo virtio_scsi_scsi_info = {
.complete = virtio_scsi_command_complete,
.cancel = virtio_scsi_request_cancelled,
.change = virtio_scsi_change,
- .hotplug = virtio_scsi_hotplug,
- .hot_unplug = virtio_scsi_hot_unplug,
.parse_cdb = virtio_scsi_parse_cdb,
.get_sg_list = virtio_scsi_get_sg_list,
.save_request = virtio_scsi_save_request,
@@ -853,6 +854,8 @@ static void virtio_scsi_device_realize(DeviceState *dev, Error **errp)
scsi_bus_new(&s->bus, sizeof(s->bus), dev,
&virtio_scsi_scsi_info, vdev->bus_name);
+ /* override default SCSI bus hotplug-handler, with virtio-scsi's one */
+ qbus_set_hotplug_handler(BUS(&s->bus), dev, &error_abort);
if (!dev->hotplugged) {
scsi_bus_legacy_handle_cmdline(&s->bus, &err);
@@ -915,6 +918,7 @@ static void virtio_scsi_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
+ HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
dc->props = virtio_scsi_properties;
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
@@ -923,6 +927,8 @@ static void virtio_scsi_class_init(ObjectClass *klass, void *data)
vdc->set_config = virtio_scsi_set_config;
vdc->get_features = virtio_scsi_get_features;
vdc->reset = virtio_scsi_reset;
+ hc->plug = virtio_scsi_hotplug;
+ hc->unplug = virtio_scsi_hotunplug;
}
static const TypeInfo virtio_scsi_common_info = {
@@ -939,6 +945,10 @@ static const TypeInfo virtio_scsi_info = {
.instance_size = sizeof(VirtIOSCSI),
.instance_init = virtio_scsi_instance_init,
.class_init = virtio_scsi_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_HOTPLUG_HANDLER },
+ { }
+ }
};
static void virtio_register_types(void)
diff --git a/hw/scsi/vmw_pvscsi.c b/hw/scsi/vmw_pvscsi.c
index 5734d19..d3a92fb 100644
--- a/hw/scsi/vmw_pvscsi.c
+++ b/hw/scsi/vmw_pvscsi.c
@@ -524,17 +524,20 @@ pvscsi_send_msg(PVSCSIState *s, SCSIDevice *dev, uint32_t msg_type)
}
static void
-pvscsi_hotplug(SCSIBus *bus, SCSIDevice *dev)
+pvscsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp)
{
- PVSCSIState *s = container_of(bus, PVSCSIState, bus);
- pvscsi_send_msg(s, dev, PVSCSI_MSG_DEV_ADDED);
+ PVSCSIState *s = PVSCSI(hotplug_dev);
+
+ pvscsi_send_msg(s, SCSI_DEVICE(dev), PVSCSI_MSG_DEV_ADDED);
}
static void
-pvscsi_hot_unplug(SCSIBus *bus, SCSIDevice *dev)
+pvscsi_hot_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp)
{
- PVSCSIState *s = container_of(bus, PVSCSIState, bus);
- pvscsi_send_msg(s, dev, PVSCSI_MSG_DEV_REMOVED);
+ PVSCSIState *s = PVSCSI(hotplug_dev);
+
+ pvscsi_send_msg(s, SCSI_DEVICE(dev), PVSCSI_MSG_DEV_REMOVED);
+ qdev_simple_device_unplug_cb(hotplug_dev, dev, errp);
}
static void
@@ -1057,8 +1060,6 @@ static const struct SCSIBusInfo pvscsi_scsi_info = {
.get_sg_list = pvscsi_get_sg_list,
.complete = pvscsi_command_complete,
.cancel = pvscsi_request_cancelled,
- .hotplug = pvscsi_hotplug,
- .hot_unplug = pvscsi_hot_unplug,
};
static int
@@ -1092,6 +1093,8 @@ pvscsi_init(PCIDevice *pci_dev)
scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(pci_dev),
&pvscsi_scsi_info, NULL);
+ /* override default SCSI bus hotplug-handler, with pvscsi's one */
+ qbus_set_hotplug_handler(BUS(&s->bus), DEVICE(s), &error_abort);
pvscsi_reset_state(s);
return 0;
@@ -1187,6 +1190,7 @@ static void pvscsi_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
k->init = pvscsi_init;
k->exit = pvscsi_uninit;
@@ -1199,6 +1203,8 @@ static void pvscsi_class_init(ObjectClass *klass, void *data)
dc->props = pvscsi_properties;
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
k->config_write = pvscsi_write_config;
+ hc->unplug = pvscsi_hot_unplug;
+ hc->plug = pvscsi_hotplug;
}
static const TypeInfo pvscsi_info = {
@@ -1206,6 +1212,10 @@ static const TypeInfo pvscsi_info = {
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(PVSCSIState),
.class_init = pvscsi_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_HOTPLUG_HANDLER },
+ { }
+ }
};
static void
diff --git a/hw/usb/bus.c b/hw/usb/bus.c
index da1eba9..986b2d8 100644
--- a/hw/usb/bus.c
+++ b/hw/usb/bus.c
@@ -24,10 +24,12 @@ static Property usb_props[] = {
static void usb_bus_class_init(ObjectClass *klass, void *data)
{
BusClass *k = BUS_CLASS(klass);
+ HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
k->print_dev = usb_bus_dev_print;
k->get_dev_path = usb_get_dev_path;
k->get_fw_dev_path = usb_get_fw_dev_path;
+ hc->unplug = qdev_simple_device_unplug_cb;
}
static const TypeInfo usb_bus_info = {
@@ -35,6 +37,10 @@ static const TypeInfo usb_bus_info = {
.parent = TYPE_BUS,
.instance_size = sizeof(USBBus),
.class_init = usb_bus_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_HOTPLUG_HANDLER },
+ { }
+ }
};
static int next_usb_bus = 0;
@@ -79,9 +85,9 @@ void usb_bus_new(USBBus *bus, size_t bus_size,
USBBusOps *ops, DeviceState *host)
{
qbus_create_inplace(bus, bus_size, TYPE_USB_BUS, host, NULL);
+ qbus_set_bus_hotplug_handler(BUS(bus), &error_abort);
bus->ops = ops;
bus->busnr = next_usb_bus++;
- bus->qbus.allow_hotplug = 1; /* Yes, we can */
QTAILQ_INIT(&bus->free);
QTAILQ_INIT(&bus->used);
QTAILQ_INSERT_TAIL(&busses, bus, next);
@@ -701,7 +707,6 @@ static void usb_device_class_init(ObjectClass *klass, void *data)
{
DeviceClass *k = DEVICE_CLASS(klass);
k->bus_type = TYPE_USB_BUS;
- k->unplug = qdev_simple_unplug_cb;
k->realize = usb_qdev_realize;
k->unrealize = usb_qdev_unrealize;
k->props = usb_props;
diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c
index d37ed02..78ce681 100644
--- a/hw/usb/dev-smartcard-reader.c
+++ b/hw/usb/dev-smartcard-reader.c
@@ -1312,8 +1312,8 @@ static void ccid_realize(USBDevice *dev, Error **errp)
usb_desc_init(dev);
qbus_create_inplace(&s->bus, sizeof(s->bus), TYPE_CCID_BUS, DEVICE(dev),
NULL);
+ qbus_set_hotplug_handler(BUS(&s->bus), DEVICE(dev), &error_abort);
s->intr = usb_ep_get(dev, USB_TOKEN_IN, CCID_INT_IN_EP);
- s->bus.qbus.allow_hotplug = 1;
s->card = NULL;
s->migration_state = MIGRATION_NONE;
s->migration_target_ip = 0;
@@ -1439,6 +1439,7 @@ static void ccid_class_initfn(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
+ HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
uc->realize = ccid_realize;
uc->product_desc = "QEMU USB CCID";
@@ -1451,6 +1452,7 @@ static void ccid_class_initfn(ObjectClass *klass, void *data)
dc->vmsd = &ccid_vmstate;
dc->props = ccid_properties;
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
+ hc->unplug = qdev_simple_device_unplug_cb;
}
static const TypeInfo ccid_info = {
@@ -1458,6 +1460,10 @@ static const TypeInfo ccid_info = {
.parent = TYPE_USB_DEVICE,
.instance_size = sizeof(USBCCIDState),
.class_init = ccid_class_initfn,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_HOTPLUG_HANDLER },
+ { }
+ }
};
static void ccid_card_class_init(ObjectClass *klass, void *data)
diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c
index bd7cc53..b005783 100644
--- a/hw/usb/dev-storage.c
+++ b/hw/usb/dev-storage.c
@@ -632,7 +632,6 @@ static void usb_msd_realize_storage(USBDevice *dev, Error **errp)
error_propagate(errp, err);
return;
}
- s->bus.qbus.allow_hotplug = 0;
usb_msd_handle_reset(dev);
if (bdrv_key_required(bs)) {
@@ -653,7 +652,6 @@ static void usb_msd_realize_bot(USBDevice *dev, Error **errp)
usb_desc_init(dev);
scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev),
&usb_msd_scsi_info_bot, NULL);
- s->bus.qbus.allow_hotplug = 0;
usb_msd_handle_reset(dev);
}
@@ -770,9 +768,11 @@ static void usb_msd_class_initfn_storage(ObjectClass *klass, void *data)
static void usb_msd_class_initfn_bot(ObjectClass *klass, void *data)
{
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
uc->realize = usb_msd_realize_bot;
usb_msd_class_initfn_common(klass);
+ dc->hotpluggable = false;
}
static const TypeInfo msd_info = {
diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c
index 18c6e5b..2450c13 100644
--- a/hw/virtio/virtio-mmio.c
+++ b/hw/virtio/virtio-mmio.c
@@ -89,9 +89,6 @@ typedef struct {
VirtioBusState bus;
} VirtIOMMIOProxy;
-static void virtio_mmio_bus_new(VirtioBusState *bus, size_t bus_size,
- VirtIOMMIOProxy *dev);
-
static uint64_t virtio_mmio_read(void *opaque, hwaddr offset, unsigned size)
{
VirtIOMMIOProxy *proxy = (VirtIOMMIOProxy *)opaque;
@@ -362,7 +359,8 @@ static void virtio_mmio_realizefn(DeviceState *d, Error **errp)
VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d);
SysBusDevice *sbd = SYS_BUS_DEVICE(d);
- virtio_mmio_bus_new(&proxy->bus, sizeof(proxy->bus), proxy);
+ qbus_create_inplace(&proxy->bus, sizeof(proxy->bus), TYPE_VIRTIO_MMIO_BUS,
+ d, NULL);
sysbus_init_irq(sbd, &proxy->irq);
memory_region_init_io(&proxy->iomem, OBJECT(d), &virtio_mem_ops, proxy,
TYPE_VIRTIO_MMIO, 0x200);
@@ -393,17 +391,6 @@ static const TypeInfo virtio_mmio_info = {
/* virtio-mmio-bus. */
-static void virtio_mmio_bus_new(VirtioBusState *bus, size_t bus_size,
- VirtIOMMIOProxy *dev)
-{
- DeviceState *qdev = DEVICE(dev);
- BusState *qbus;
-
- qbus_create_inplace(bus, bus_size, TYPE_VIRTIO_MMIO_BUS, qdev, NULL);
- qbus = BUS(bus);
- qbus->allow_hotplug = 0;
-}
-
static void virtio_mmio_bus_class_init(ObjectClass *klass, void *data)
{
BusClass *bus_class = BUS_CLASS(klass);
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index 390f824..10abd65 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -1542,13 +1542,10 @@ static void virtio_pci_bus_new(VirtioBusState *bus, size_t bus_size,
VirtIOPCIProxy *dev)
{
DeviceState *qdev = DEVICE(dev);
- BusState *qbus;
char virtio_bus_name[] = "virtio-bus";
qbus_create_inplace(bus, bus_size, TYPE_VIRTIO_PCI_BUS, qdev,
virtio_bus_name);
- qbus = BUS(bus);
- qbus->allow_hotplug = 1;
}
static void virtio_pci_bus_class_init(ObjectClass *klass, void *data)
diff --git a/include/hw/hotplug.h b/include/hw/hotplug.h
index a6533cb..050d2f0 100644
--- a/include/hw/hotplug.h
+++ b/include/hw/hotplug.h
@@ -47,7 +47,12 @@ typedef void (*hotplug_fn)(HotplugHandler *plug_handler,
*
* @parent: Opaque parent interface.
* @plug: plug callback.
+ * @unplug_request: unplug request callback.
+ * Used as a means to initiate device unplug for devices that
+ * require asynchronous unplug handling.
* @unplug: unplug callback.
+ * Used for device removal with devices that implement
+ * asynchronous and synchronous (suprise) removal.
*/
typedef struct HotplugHandlerClass {
/* <private> */
@@ -55,6 +60,7 @@ typedef struct HotplugHandlerClass {
/* <public> */
hotplug_fn plug;
+ hotplug_fn unplug_request;
hotplug_fn unplug;
} HotplugHandlerClass;
@@ -68,9 +74,17 @@ void hotplug_handler_plug(HotplugHandler *plug_handler,
Error **errp);
/**
+ * hotplug_handler_unplug_request:
+ *
+ * Calls #HotplugHandlerClass.unplug_request callback of @plug_handler.
+ */
+void hotplug_handler_unplug_request(HotplugHandler *plug_handler,
+ DeviceState *plugged_dev,
+ Error **errp);
+/**
* hotplug_handler_unplug:
*
- * Call #HotplugHandlerClass.unplug callback of @plug_handler.
+ * Calls #HotplugHandlerClass.unplug callback of @plug_handler.
*/
void hotplug_handler_unplug(HotplugHandler *plug_handler,
DeviceState *plugged_dev,
diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h
index d139d58..b48a7a2 100644
--- a/include/hw/pci/pcie.h
+++ b/include/hw/pci/pcie.h
@@ -128,6 +128,6 @@ extern const VMStateDescription vmstate_pcie_device;
void pcie_cap_slot_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
Error **errp);
-void pcie_cap_slot_hot_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
- Error **errp);
+void pcie_cap_slot_hot_unplug_request_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp);
#endif /* QEMU_PCIE_H */
diff --git a/include/hw/pci/shpc.h b/include/hw/pci/shpc.h
index eef1a1a..025bc5b 100644
--- a/include/hw/pci/shpc.h
+++ b/include/hw/pci/shpc.h
@@ -46,8 +46,8 @@ void shpc_cap_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int len);
void shpc_device_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
Error **errp);
-void shpc_device_hot_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
- Error **errp);
+void shpc_device_hot_unplug_request_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp);
extern VMStateInfo shpc_vmstate_info;
#define SHPC_VMSTATE(_field, _type) \
diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
index 178fee2..1fca75c 100644
--- a/include/hw/qdev-core.h
+++ b/include/hw/qdev-core.h
@@ -126,7 +126,6 @@ typedef struct DeviceClass {
/* Private to qdev / bus. */
qdev_initfn init; /* TODO remove, once users are converted to realize */
- qdev_event unplug;
qdev_event exit; /* TODO remove, once users are converted to unrealize */
const char *bus_type;
} DeviceClass;
@@ -210,7 +209,6 @@ struct BusState {
Object obj;
DeviceState *parent;
const char *name;
- int allow_hotplug;
HotplugHandler *hotplug_handler;
int max_index;
bool realized;
@@ -232,7 +230,7 @@ struct Property {
struct PropertyInfo {
const char *name;
- const char *legacy_name;
+ const char *description;
const char **enum_table;
int (*print)(DeviceState *dev, Property *prop, char *dest, size_t len);
ObjectPropertyAccessor *get;
@@ -264,7 +262,8 @@ void qdev_init_nofail(DeviceState *dev);
void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id,
int required_for_version);
void qdev_unplug(DeviceState *dev, Error **errp);
-int qdev_simple_unplug_cb(DeviceState *dev);
+void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp);
void qdev_machine_creation_done(void);
bool qdev_machine_modified(void);
@@ -361,11 +360,13 @@ extern int qdev_hotplug;
char *qdev_get_dev_path(DeviceState *dev);
-static inline void qbus_set_hotplug_handler(BusState *bus, DeviceState *handler,
- Error **errp)
+void qbus_set_hotplug_handler(BusState *bus, DeviceState *handler,
+ Error **errp);
+
+void qbus_set_bus_hotplug_handler(BusState *bus, Error **errp);
+
+static inline bool qbus_is_hotpluggable(BusState *bus)
{
- object_property_set_link(OBJECT(bus), OBJECT(handler),
- QDEV_HOTPLUG_HANDLER_PROPERTY, errp);
- bus->allow_hotplug = 1;
+ return bus->hotplug_handler;
}
#endif
diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h
index b61bedb..caaa320 100644
--- a/include/hw/scsi/scsi.h
+++ b/include/hw/scsi/scsi.h
@@ -146,8 +146,6 @@ struct SCSIBusInfo {
void (*transfer_data)(SCSIRequest *req, uint32_t arg);
void (*complete)(SCSIRequest *req, uint32_t arg, size_t resid);
void (*cancel)(SCSIRequest *req);
- void (*hotplug)(SCSIBus *bus, SCSIDevice *dev);
- void (*hot_unplug)(SCSIBus *bus, SCSIDevice *dev);
void (*change)(SCSIBus *bus, SCSIDevice *dev, SCSISense sense);
QEMUSGList *(*get_sg_list)(SCSIRequest *req);
diff --git a/include/qom/object.h b/include/qom/object.h
index 8a05a81..89c3092 100644
--- a/include/qom/object.h
+++ b/include/qom/object.h
@@ -338,6 +338,7 @@ typedef struct ObjectProperty
{
gchar *name;
gchar *type;
+ gchar *description;
ObjectPropertyAccessor *get;
ObjectPropertyAccessor *set;
ObjectPropertyResolve *resolve;
@@ -1275,6 +1276,19 @@ void object_property_add_alias(Object *obj, const char *name,
Error **errp);
/**
+ * object_property_set_description:
+ * @obj: the object owning the property
+ * @name: the name of the property
+ * @description: the description of the property on the object
+ * @errp: if an error occurs, a pointer to an area to store the error
+ *
+ * Set an object property's description.
+ *
+ */
+void object_property_set_description(Object *obj, const char *name,
+ const char *description, Error **errp);
+
+/**
* object_child_foreach:
* @obj: the object whose children will be navigated
* @fn: the iterator function to be called
diff --git a/qapi-schema.json b/qapi-schema.json
index 4f0d7e3..24379ab 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1615,11 +1615,13 @@
#
# @name: the name of the property
# @type: the typename of the property
+# @description: #optional if specified, the description of the property.
+# (since 2.2)
#
# Since: 1.2
##
{ 'type': 'DevicePropertyInfo',
- 'data': { 'name': 'str', 'type': 'str' } }
+ 'data': { 'name': 'str', 'type': 'str', '*description': 'str' } }
##
# @device-list-properties:
diff --git a/qdev-monitor.c b/qdev-monitor.c
index 5ec6606..fac7d17 100644
--- a/qdev-monitor.c
+++ b/qdev-monitor.c
@@ -213,9 +213,14 @@ int qdev_device_help(QemuOpts *opts)
}
for (prop = prop_list; prop; prop = prop->next) {
- error_printf("%s.%s=%s\n", driver,
+ error_printf("%s.%s=%s", driver,
prop->value->name,
prop->value->type);
+ if (prop->value->has_description) {
+ error_printf(" (%s)\n", prop->value->description);
+ } else {
+ error_printf("\n");
+ }
}
qapi_free_DevicePropertyInfoList(prop_list);
@@ -487,7 +492,8 @@ DeviceState *qdev_device_add(QemuOpts *opts)
}
dc = DEVICE_CLASS(oc);
- if (dc->cannot_instantiate_with_device_add_yet) {
+ if (dc->cannot_instantiate_with_device_add_yet ||
+ (qdev_hotplug && !dc->hotpluggable)) {
qerror_report(QERR_INVALID_PARAMETER_VALUE, "driver",
"pluggable device type");
return NULL;
@@ -515,7 +521,7 @@ DeviceState *qdev_device_add(QemuOpts *opts)
return NULL;
}
}
- if (qdev_hotplug && bus && !bus->allow_hotplug) {
+ if (qdev_hotplug && bus && !qbus_is_hotpluggable(bus)) {
qerror_report(QERR_BUS_NO_HOTPLUG, bus->name);
return NULL;
}
@@ -685,15 +691,20 @@ int do_device_add(Monitor *mon, const QDict *qdict, QObject **ret_data)
void qmp_device_del(const char *id, Error **errp)
{
- DeviceState *dev;
+ Object *obj;
+ char *root_path = object_get_canonical_path(qdev_get_peripheral());
+ char *path = g_strdup_printf("%s/%s", root_path, id);
- dev = qdev_find_recursive(sysbus_get_default(), id);
- if (!dev) {
+ g_free(root_path);
+ obj = object_resolve_path_type(path, TYPE_DEVICE, NULL);
+ g_free(path);
+
+ if (!obj) {
error_set(errp, QERR_DEVICE_NOT_FOUND, id);
return;
}
- qdev_unplug(dev, errp);
+ qdev_unplug(DEVICE(obj), errp);
}
void qdev_machine_init(void)
diff --git a/qmp.c b/qmp.c
index c6767c4..0b4f131 100644
--- a/qmp.c
+++ b/qmp.c
@@ -442,7 +442,8 @@ ObjectTypeInfoList *qmp_qom_list_types(bool has_implements,
*/
static DevicePropertyInfo *make_device_property_info(ObjectClass *klass,
const char *name,
- const char *default_type)
+ const char *default_type,
+ const char *description)
{
DevicePropertyInfo *info;
Property *prop;
@@ -465,7 +466,9 @@ static DevicePropertyInfo *make_device_property_info(ObjectClass *klass,
info = g_malloc0(sizeof(*info));
info->name = g_strdup(prop->name);
- info->type = g_strdup(prop->info->legacy_name ?: prop->info->name);
+ info->type = g_strdup(prop->info->name);
+ info->has_description = !!prop->info->description;
+ info->description = g_strdup(prop->info->description);
return info;
}
klass = object_class_get_parent(klass);
@@ -475,6 +478,9 @@ static DevicePropertyInfo *make_device_property_info(ObjectClass *klass,
info = g_malloc0(sizeof(*info));
info->name = g_strdup(name);
info->type = g_strdup(default_type);
+ info->has_description = !!description;
+ info->description = g_strdup(description);
+
return info;
}
@@ -521,7 +527,8 @@ DevicePropertyInfoList *qmp_device_list_properties(const char *typename,
continue;
}
- info = make_device_property_info(klass, prop->name, prop->type);
+ info = make_device_property_info(klass, prop->name, prop->type,
+ prop->description);
if (!info) {
continue;
}
diff --git a/qom/object.c b/qom/object.c
index da0919a..a751367 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -369,6 +369,7 @@ static void object_property_del_all(Object *obj)
g_free(prop->name);
g_free(prop->type);
+ g_free(prop->description);
g_free(prop);
}
}
@@ -803,6 +804,7 @@ void object_property_del(Object *obj, const char *name, Error **errp)
g_free(prop->name);
g_free(prop->type);
+ g_free(prop->description);
g_free(prop);
}
@@ -1010,11 +1012,19 @@ char *object_property_print(Object *obj, const char *name, bool human,
Error **errp)
{
StringOutputVisitor *mo;
- char *string;
+ char *string = NULL;
+ Error *local_err = NULL;
mo = string_output_visitor_new(human);
- object_property_get(obj, string_output_get_visitor(mo), name, errp);
+ object_property_get(obj, string_output_get_visitor(mo), name, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto out;
+ }
+
string = string_output_get_string(mo);
+
+out:
string_output_visitor_cleanup(mo);
return string;
}
@@ -1634,6 +1644,7 @@ void object_property_add_alias(Object *obj, const char *name,
ObjectProperty *op;
ObjectProperty *target_prop;
gchar *prop_type;
+ Error *local_err = NULL;
target_prop = object_property_find(target_obj, target_name, errp);
if (!target_prop) {
@@ -1655,12 +1666,36 @@ void object_property_add_alias(Object *obj, const char *name,
property_get_alias,
property_set_alias,
property_release_alias,
- prop, errp);
+ prop, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ g_free(prop);
+ goto out;
+ }
op->resolve = property_resolve_alias;
+ object_property_set_description(obj, name,
+ target_prop->description,
+ &error_abort);
+
+out:
g_free(prop_type);
}
+void object_property_set_description(Object *obj, const char *name,
+ const char *description, Error **errp)
+{
+ ObjectProperty *op;
+
+ op = object_property_find(obj, name, errp);
+ if (!op) {
+ return;
+ }
+
+ g_free(op->description);
+ op->description = g_strdup(description);
+}
+
static void object_instance_init(Object *obj)
{
object_property_add_str(obj, "type", qdev_get_type, NULL, NULL);
diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c
index 65b840d..33fb4cc 100644
--- a/target-ppc/translate_init.c
+++ b/target-ppc/translate_init.c
@@ -8044,7 +8044,7 @@ static void powerpc_set_compat(Object *obj, Visitor *v,
static PropertyInfo powerpc_compat_propinfo = {
.name = "str",
- .legacy_name = "powerpc-server-compat",
+ .description = "compatibility mode, power6/power7/power8",
.get = powerpc_get_compat,
.set = powerpc_set_compat,
};
diff --git a/tests/Makefile b/tests/Makefile
index ffa8312..95deff0 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -301,6 +301,7 @@ libqos-pc-obj-y = $(libqos-obj-y) tests/libqos/pci-pc.o
libqos-pc-obj-y += tests/libqos/malloc-pc.o
libqos-omap-obj-y = $(libqos-obj-y) tests/libqos/i2c-omap.o
libqos-virtio-obj-y = $(libqos-obj-y) $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o
+libqos-usb-obj-y = $(libqos-pc-obj-y) tests/libqos/usb.o
tests/rtc-test$(EXESUF): tests/rtc-test.o
tests/m48t59-test$(EXESUF): tests/m48t59-test.o
@@ -324,8 +325,8 @@ tests/ne2000-test$(EXESUF): tests/ne2000-test.o
tests/wdt_ib700-test$(EXESUF): tests/wdt_ib700-test.o
tests/virtio-balloon-test$(EXESUF): tests/virtio-balloon-test.o
tests/virtio-blk-test$(EXESUF): tests/virtio-blk-test.o $(libqos-virtio-obj-y)
-tests/virtio-net-test$(EXESUF): tests/virtio-net-test.o
-tests/virtio-rng-test$(EXESUF): tests/virtio-rng-test.o
+tests/virtio-net-test$(EXESUF): tests/virtio-net-test.o $(libqos-pc-obj-y)
+tests/virtio-rng-test$(EXESUF): tests/virtio-rng-test.o $(libqos-pc-obj-y)
tests/virtio-scsi-test$(EXESUF): tests/virtio-scsi-test.o
tests/virtio-9p-test$(EXESUF): tests/virtio-9p-test.o
tests/virtio-serial-test$(EXESUF): tests/virtio-serial-test.o
@@ -343,10 +344,10 @@ tests/ac97-test$(EXESUF): tests/ac97-test.o
tests/es1370-test$(EXESUF): tests/es1370-test.o
tests/intel-hda-test$(EXESUF): tests/intel-hda-test.o
tests/ioh3420-test$(EXESUF): tests/ioh3420-test.o
-tests/usb-hcd-ohci-test$(EXESUF): tests/usb-hcd-ohci-test.o
-tests/usb-hcd-uhci-test$(EXESUF): tests/usb-hcd-uhci-test.o
-tests/usb-hcd-ehci-test$(EXESUF): tests/usb-hcd-ehci-test.o $(libqos-pc-obj-y)
-tests/usb-hcd-xhci-test$(EXESUF): tests/usb-hcd-xhci-test.o
+tests/usb-hcd-ohci-test$(EXESUF): tests/usb-hcd-ohci-test.o $(libqos-usb-obj-y)
+tests/usb-hcd-uhci-test$(EXESUF): tests/usb-hcd-uhci-test.o $(libqos-usb-obj-y)
+tests/usb-hcd-ehci-test$(EXESUF): tests/usb-hcd-ehci-test.o $(libqos-usb-obj-y)
+tests/usb-hcd-xhci-test$(EXESUF): tests/usb-hcd-xhci-test.o $(libqos-usb-obj-y)
tests/vhost-user-test$(EXESUF): tests/vhost-user-test.o qemu-char.o qemu-timer.o $(qtest-obj-y)
tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o
tests/test-qemu-opts$(EXESUF): tests/test-qemu-opts.o libqemuutil.a libqemustub.a
diff --git a/tests/libqos/pci-pc.c b/tests/libqos/pci-pc.c
index 0609294..6dba0db 100644
--- a/tests/libqos/pci-pc.c
+++ b/tests/libqos/pci-pc.c
@@ -20,6 +20,9 @@
#include <glib.h>
+#define ACPI_PCIHP_ADDR 0xae00
+#define PCI_EJ_BASE 0x0008
+
typedef struct QPCIBusPC
{
QPCIBus bus;
@@ -247,3 +250,49 @@ void qpci_free_pc(QPCIBus *bus)
g_free(s);
}
+
+void qpci_plug_device_test(const char *driver, const char *id,
+ uint8_t slot, const char *opts)
+{
+ QDict *response;
+ char *cmd;
+
+ cmd = g_strdup_printf("{'execute': 'device_add',"
+ " 'arguments': {"
+ " 'driver': '%s',"
+ " 'addr': '%d',"
+ " %s%s"
+ " 'id': '%s'"
+ "}}", driver, slot,
+ opts ? opts : "", opts ? "," : "",
+ id);
+ response = qmp(cmd);
+ g_free(cmd);
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+}
+
+void qpci_unplug_acpi_device_test(const char *id, uint8_t slot)
+{
+ QDict *response;
+ char *cmd;
+
+ cmd = g_strdup_printf("{'execute': 'device_del',"
+ " 'arguments': {"
+ " 'id': '%s'"
+ "}}", id);
+ response = qmp(cmd);
+ g_free(cmd);
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+
+ outb(ACPI_PCIHP_ADDR + PCI_EJ_BASE, 1 << slot);
+
+ response = qmp("");
+ g_assert(response);
+ g_assert(qdict_haskey(response, "event"));
+ g_assert(!strcmp(qdict_get_str(response, "event"), "DEVICE_DELETED"));
+ QDECREF(response);
+}
diff --git a/tests/libqos/pci.h b/tests/libqos/pci.h
index d51eb9e..dfaee9e 100644
--- a/tests/libqos/pci.h
+++ b/tests/libqos/pci.h
@@ -87,4 +87,7 @@ void qpci_io_writel(QPCIDevice *dev, void *data, uint32_t value);
void *qpci_iomap(QPCIDevice *dev, int barno, uint64_t *sizeptr);
void qpci_iounmap(QPCIDevice *dev, void *data);
+void qpci_plug_device_test(const char *driver, const char *id,
+ uint8_t slot, const char *opts);
+void qpci_unplug_acpi_device_test(const char *id, uint8_t slot);
#endif
diff --git a/tests/libqos/usb.c b/tests/libqos/usb.c
new file mode 100644
index 0000000..41d89b8
--- /dev/null
+++ b/tests/libqos/usb.c
@@ -0,0 +1,71 @@
+/*
+ * common code shared by usb tests
+ *
+ * Copyright (c) 2014 Red Hat, Inc
+ *
+ * Authors:
+ * Gerd Hoffmann <kraxel@redhat.com>
+ * John Snow <jsnow@redhat.com>
+ * Igor Mammedov <imammedo@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#include <glib.h>
+#include <string.h>
+#include "libqtest.h"
+#include "qemu/osdep.h"
+#include "hw/usb/uhci-regs.h"
+#include "libqos/usb.h"
+
+void qusb_pci_init_one(QPCIBus *pcibus, struct qhc *hc, uint32_t devfn, int bar)
+{
+ hc->dev = qpci_device_find(pcibus, devfn);
+ g_assert(hc->dev != NULL);
+ qpci_device_enable(hc->dev);
+ hc->base = qpci_iomap(hc->dev, bar, NULL);
+ g_assert(hc->base != NULL);
+}
+
+void uhci_port_test(struct qhc *hc, int port, uint16_t expect)
+{
+ void *addr = hc->base + 0x10 + 2 * port;
+ uint16_t value = qpci_io_readw(hc->dev, addr);
+ uint16_t mask = ~(UHCI_PORT_WRITE_CLEAR | UHCI_PORT_RSVD1);
+
+ g_assert((value & mask) == (expect & mask));
+}
+
+void usb_test_hotplug(const char *hcd_id, const int port,
+ void (*port_check)(void))
+{
+ QDict *response;
+ char *cmd;
+
+ cmd = g_strdup_printf("{'execute': 'device_add',"
+ " 'arguments': {"
+ " 'driver': 'usb-tablet',"
+ " 'port': '%d',"
+ " 'bus': '%s.0',"
+ " 'id': 'usbdev%d'"
+ "}}", port, hcd_id, port);
+ response = qmp(cmd);
+ g_free(cmd);
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+
+ if (port_check) {
+ port_check();
+ }
+
+ cmd = g_strdup_printf("{'execute': 'device_del',"
+ " 'arguments': {"
+ " 'id': 'usbdev%d'"
+ "}}", port);
+ response = qmp(cmd);
+ g_free(cmd);
+ g_assert(response);
+ g_assert(qdict_haskey(response, "event"));
+ g_assert(!strcmp(qdict_get_str(response, "event"), "DEVICE_DELETED"));
+}
diff --git a/tests/libqos/usb.h b/tests/libqos/usb.h
new file mode 100644
index 0000000..8fe5687
--- /dev/null
+++ b/tests/libqos/usb.h
@@ -0,0 +1,17 @@
+#ifndef LIBQOS_USB_H
+#define LIBQOS_USB_H
+
+#include "libqos/pci-pc.h"
+
+struct qhc {
+ QPCIDevice *dev;
+ void *base;
+};
+
+void qusb_pci_init_one(QPCIBus *pcibus, struct qhc *hc,
+ uint32_t devfn, int bar);
+void uhci_port_test(struct qhc *hc, int port, uint16_t expect);
+
+void usb_test_hotplug(const char *bus_name, const int port,
+ void (*port_check)(void));
+#endif
diff --git a/tests/usb-hcd-ehci-test.c b/tests/usb-hcd-ehci-test.c
index c990492..75073bf 100644
--- a/tests/usb-hcd-ehci-test.c
+++ b/tests/usb-hcd-ehci-test.c
@@ -15,11 +15,7 @@
#include "qemu/osdep.h"
#include "hw/usb/uhci-regs.h"
#include "hw/usb/ehci-regs.h"
-
-struct qhc {
- QPCIDevice *dev;
- void *base;
-};
+#include "libqos/usb.h"
static QPCIBus *pcibus;
static struct qhc uhci1;
@@ -29,15 +25,6 @@ static struct qhc ehci1;
/* helpers */
-static void pci_init_one(struct qhc *hc, uint32_t devfn, int bar)
-{
- hc->dev = qpci_device_find(pcibus, devfn);
- g_assert(hc->dev != NULL);
- qpci_device_enable(hc->dev);
- hc->base = qpci_iomap(hc->dev, bar, NULL);
- g_assert(hc->base != NULL);
-}
-
#if 0
static void uhci_port_update(struct qhc *hc, int port,
uint16_t set, uint16_t clear)
@@ -52,19 +39,6 @@ static void uhci_port_update(struct qhc *hc, int port,
}
#endif
-static void uhci_port_test(struct qhc *hc, int port, uint16_t expect)
-{
- void *addr = hc->base + 0x10 + 2 * port;
- uint16_t value = qpci_io_readw(hc->dev, addr);
- uint16_t mask = ~(UHCI_PORT_WRITE_CLEAR | UHCI_PORT_RSVD1);
-
-#if 0
- fprintf(stderr, "%s: %d, have 0x%04x, want 0x%04x\n",
- __func__, port, value & mask, expect & mask);
-#endif
- g_assert((value & mask) == (expect & mask));
-}
-
static void ehci_port_test(struct qhc *hc, int port, uint32_t expect)
{
void *addr = hc->base + 0x64 + 4 * port;
@@ -88,10 +62,10 @@ static void pci_init(void)
pcibus = qpci_init_pc();
g_assert(pcibus != NULL);
- pci_init_one(&uhci1, QPCI_DEVFN(0x1d, 0), 4);
- pci_init_one(&uhci2, QPCI_DEVFN(0x1d, 1), 4);
- pci_init_one(&uhci3, QPCI_DEVFN(0x1d, 2), 4);
- pci_init_one(&ehci1, QPCI_DEVFN(0x1d, 7), 0);
+ qusb_pci_init_one(pcibus, &uhci1, QPCI_DEVFN(0x1d, 0), 4);
+ qusb_pci_init_one(pcibus, &uhci2, QPCI_DEVFN(0x1d, 1), 4);
+ qusb_pci_init_one(pcibus, &uhci3, QPCI_DEVFN(0x1d, 2), 4);
+ qusb_pci_init_one(pcibus, &ehci1, QPCI_DEVFN(0x1d, 7), 0);
}
static void pci_uhci_port_1(void)
@@ -154,6 +128,19 @@ static void pci_ehci_port_2(void)
}
}
+static void pci_ehci_port_3_hotplug(void)
+{
+ /* check for presence of hotplugged usb-tablet */
+ g_assert(pcibus != NULL);
+ ehci_port_test(&ehci1, 2, PORTSC_PPOWER | PORTSC_CONNECT);
+}
+
+static void pci_ehci_port_hotplug(void)
+{
+ usb_test_hotplug("ich9-ehci-1", 3, pci_ehci_port_3_hotplug);
+}
+
+
int main(int argc, char **argv)
{
int ret;
@@ -165,6 +152,7 @@ int main(int argc, char **argv)
qtest_add_func("/ehci/pci/ehci-config", pci_ehci_config);
qtest_add_func("/ehci/pci/uhci-port-2", pci_uhci_port_2);
qtest_add_func("/ehci/pci/ehci-port-2", pci_ehci_port_2);
+ qtest_add_func("/ehci/pci/ehci-port-3-hotplug", pci_ehci_port_hotplug);
qtest_start("-machine q35 -device ich9-usb-ehci1,bus=pcie.0,addr=1d.7,"
"multifunction=on,id=ich9-ehci-1 "
diff --git a/tests/usb-hcd-ohci-test.c b/tests/usb-hcd-ohci-test.c
index fbc3ffe..1160bde 100644
--- a/tests/usb-hcd-ohci-test.c
+++ b/tests/usb-hcd-ohci-test.c
@@ -11,15 +11,18 @@
#include <string.h>
#include "libqtest.h"
#include "qemu/osdep.h"
+#include "libqos/usb.h"
static void test_ohci_init(void)
{
- qtest_start("-device pci-ohci,id=ohci");
- qtest_end();
}
+static void test_ohci_hotplug(void)
+{
+ usb_test_hotplug("ohci", 1, NULL);
+}
int main(int argc, char **argv)
{
@@ -28,8 +31,11 @@ int main(int argc, char **argv)
g_test_init(&argc, &argv, NULL);
qtest_add_func("/ohci/pci/init", test_ohci_init);
+ qtest_add_func("/ohci/pci/hotplug", test_ohci_hotplug);
+ qtest_start("-device pci-ohci,id=ohci");
ret = g_test_run();
+ qtest_end();
return ret;
}
diff --git a/tests/usb-hcd-uhci-test.c b/tests/usb-hcd-uhci-test.c
index 94e858f..8cf2c5b 100644
--- a/tests/usb-hcd-uhci-test.c
+++ b/tests/usb-hcd-uhci-test.c
@@ -11,15 +11,69 @@
#include <string.h>
#include "libqtest.h"
#include "qemu/osdep.h"
+#include "libqos/usb.h"
+#include "hw/usb/uhci-regs.h"
static void test_uhci_init(void)
{
- qtest_start("-device piix3-usb-uhci,id=uhci");
+}
- qtest_end();
+static void test_port(int port)
+{
+ QPCIBus *pcibus;
+ struct qhc uhci;
+
+ g_assert(port > 0);
+ pcibus = qpci_init_pc();
+ g_assert(pcibus != NULL);
+ qusb_pci_init_one(pcibus, &uhci, QPCI_DEVFN(0x1d, 0), 4);
+ uhci_port_test(&uhci, port - 1, UHCI_PORT_CCS);
+}
+
+static void test_port_1(void)
+{
+ test_port(1);
}
+static void test_port_2(void)
+{
+ test_port(2);
+}
+
+static void test_uhci_hotplug(void)
+{
+ usb_test_hotplug("uhci", 2, test_port_2);
+}
+
+static void test_usb_storage_hotplug(void)
+{
+ QDict *response;
+
+ response = qmp("{'execute': 'device_add',"
+ " 'arguments': {"
+ " 'driver': 'usb-storage',"
+ " 'drive': 'drive0',"
+ " 'id': 'usbdev0'"
+ "}}");
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+
+ response = qmp("{'execute': 'device_del',"
+ " 'arguments': {"
+ " 'id': 'usbdev0'"
+ "}}");
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+
+ response = qmp("");
+ g_assert(response);
+ g_assert(qdict_haskey(response, "event"));
+ g_assert(!strcmp(qdict_get_str(response, "event"), "DEVICE_DELETED"));
+ QDECREF(response);
+}
int main(int argc, char **argv)
{
@@ -28,8 +82,15 @@ int main(int argc, char **argv)
g_test_init(&argc, &argv, NULL);
qtest_add_func("/uhci/pci/init", test_uhci_init);
+ qtest_add_func("/uhci/pci/port1", test_port_1);
+ qtest_add_func("/uhci/pci/hotplug", test_uhci_hotplug);
+ qtest_add_func("/uhci/pci/hotplug/usb-storage", test_usb_storage_hotplug);
+ qtest_start("-device piix3-usb-uhci,id=uhci,addr=1d.0"
+ " -drive id=drive0,if=none,file=/dev/null"
+ " -device usb-tablet,bus=uhci.0,port=1");
ret = g_test_run();
+ qtest_end();
return ret;
}
diff --git a/tests/usb-hcd-xhci-test.c b/tests/usb-hcd-xhci-test.c
index 743e979..b1a7dec 100644
--- a/tests/usb-hcd-xhci-test.c
+++ b/tests/usb-hcd-xhci-test.c
@@ -11,15 +11,74 @@
#include <string.h>
#include "libqtest.h"
#include "qemu/osdep.h"
+#include "libqos/usb.h"
static void test_xhci_init(void)
{
- qtest_start("-device nec-usb-xhci,id=xhci");
+}
- qtest_end();
+static void test_xhci_hotplug(void)
+{
+ usb_test_hotplug("xhci", 1, NULL);
}
+static void test_usb_uas_hotplug(void)
+{
+ QDict *response;
+
+ response = qmp("{'execute': 'device_add',"
+ " 'arguments': {"
+ " 'driver': 'usb-uas',"
+ " 'id': 'uas'"
+ "}}");
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+
+ response = qmp("{'execute': 'device_add',"
+ " 'arguments': {"
+ " 'driver': 'scsi-hd',"
+ " 'drive': 'drive0',"
+ " 'id': 'scsi-hd'"
+ "}}");
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+
+ /* TODO:
+ UAS HBA driver in libqos, to check that
+ added disk is visible after BUS rescan
+ */
+
+ response = qmp("{'execute': 'device_del',"
+ " 'arguments': {"
+ " 'id': 'scsi-hd'"
+ "}}");
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+
+ response = qmp("");
+ g_assert(qdict_haskey(response, "event"));
+ g_assert(!strcmp(qdict_get_str(response, "event"), "DEVICE_DELETED"));
+ QDECREF(response);
+
+
+ response = qmp("{'execute': 'device_del',"
+ " 'arguments': {"
+ " 'id': 'uas'"
+ "}}");
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+
+ response = qmp("");
+ g_assert(response);
+ g_assert(qdict_haskey(response, "event"));
+ g_assert(!strcmp(qdict_get_str(response, "event"), "DEVICE_DELETED"));
+ QDECREF(response);
+}
int main(int argc, char **argv)
{
@@ -28,8 +87,13 @@ int main(int argc, char **argv)
g_test_init(&argc, &argv, NULL);
qtest_add_func("/xhci/pci/init", test_xhci_init);
+ qtest_add_func("/xhci/pci/hotplug", test_xhci_hotplug);
+ qtest_add_func("/xhci/pci/hotplug/usb-uas", test_usb_uas_hotplug);
+ qtest_start("-device nec-usb-xhci,id=xhci"
+ " -drive id=drive0,if=none,file=/dev/null");
ret = g_test_run();
+ qtest_end();
return ret;
}
diff --git a/tests/virtio-blk-test.c b/tests/virtio-blk-test.c
index 5ce6e79..ead3911 100644
--- a/tests/virtio-blk-test.c
+++ b/tests/virtio-blk-test.c
@@ -45,6 +45,8 @@
#define PCI_SLOT 0x04
#define PCI_FN 0x00
+#define PCI_SLOT_HP 0x06
+
typedef struct QVirtioBlkReq {
uint32_t type;
uint32_t ioprio;
@@ -55,7 +57,7 @@ typedef struct QVirtioBlkReq {
static QPCIBus *test_start(void)
{
- char cmdline[100];
+ char *cmdline;
char tmp_path[] = "/tmp/qtest.XXXXXX";
int fd, ret;
@@ -66,11 +68,14 @@ static QPCIBus *test_start(void)
g_assert_cmpint(ret, ==, 0);
close(fd);
- snprintf(cmdline, 100, "-drive if=none,id=drive0,file=%s "
- "-device virtio-blk-pci,drive=drive0,addr=%x.%x",
- tmp_path, PCI_SLOT, PCI_FN);
+ cmdline = g_strdup_printf("-drive if=none,id=drive0,file=%s "
+ "-drive if=none,id=drive1,file=/dev/null "
+ "-device virtio-blk-pci,id=drv0,drive=drive0,"
+ "addr=%x.%x",
+ tmp_path, PCI_SLOT, PCI_FN);
qtest_start(cmdline);
unlink(tmp_path);
+ g_free(cmdline);
return qpci_init_pc();
}
@@ -80,14 +85,14 @@ static void test_end(void)
qtest_end();
}
-static QVirtioPCIDevice *virtio_blk_init(QPCIBus *bus)
+static QVirtioPCIDevice *virtio_blk_init(QPCIBus *bus, int slot)
{
QVirtioPCIDevice *dev;
dev = qvirtio_pci_device_find(bus, QVIRTIO_BLK_DEVICE_ID);
g_assert(dev != NULL);
g_assert_cmphex(dev->vdev.device_type, ==, QVIRTIO_BLK_DEVICE_ID);
- g_assert_cmphex(dev->pdev->devfn, ==, ((PCI_SLOT << 3) | PCI_FN));
+ g_assert_cmphex(dev->pdev->devfn, ==, ((slot << 3) | PCI_FN));
qvirtio_pci_device_enable(dev);
qvirtio_reset(&qvirtio_pci, &dev->vdev);
@@ -147,7 +152,7 @@ static void pci_basic(void)
bus = test_start();
- dev = virtio_blk_init(bus);
+ dev = virtio_blk_init(bus, PCI_SLOT);
/* MSI-X is not enabled */
addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_NO_MSIX;
@@ -293,7 +298,7 @@ static void pci_indirect(void)
bus = test_start();
- dev = virtio_blk_init(bus);
+ dev = virtio_blk_init(bus, PCI_SLOT);
/* MSI-X is not enabled */
addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_NO_MSIX;
@@ -384,7 +389,7 @@ static void pci_config(void)
bus = test_start();
- dev = virtio_blk_init(bus);
+ dev = virtio_blk_init(bus, PCI_SLOT);
/* MSI-X is not enabled */
addr = dev->addr + QVIRTIO_DEVICE_SPECIFIC_NO_MSIX;
@@ -425,7 +430,7 @@ static void pci_msix(void)
bus = test_start();
alloc = pc_alloc_init();
- dev = virtio_blk_init(bus);
+ dev = virtio_blk_init(bus, PCI_SLOT);
qpci_msix_enable(dev->pdev);
qvirtio_pci_set_msix_configuration_vector(dev, alloc, 0);
@@ -534,7 +539,7 @@ static void pci_idx(void)
bus = test_start();
alloc = pc_alloc_init();
- dev = virtio_blk_init(bus);
+ dev = virtio_blk_init(bus, PCI_SLOT);
qpci_msix_enable(dev->pdev);
qvirtio_pci_set_msix_configuration_vector(dev, alloc, 0);
@@ -637,6 +642,27 @@ static void pci_idx(void)
test_end();
}
+static void hotplug(void)
+{
+ QPCIBus *bus;
+ QVirtioPCIDevice *dev;
+
+ bus = test_start();
+
+ /* plug secondary disk */
+ qpci_plug_device_test("virtio-blk-pci", "drv1", PCI_SLOT_HP,
+ "'drive': 'drive1'");
+
+ dev = virtio_blk_init(bus, PCI_SLOT_HP);
+ g_assert(dev);
+ qvirtio_pci_device_disable(dev);
+ g_free(dev);
+
+ /* unplug secondary disk */
+ qpci_unplug_acpi_device_test("drv1", PCI_SLOT_HP);
+ test_end();
+}
+
int main(int argc, char **argv)
{
int ret;
@@ -648,6 +674,7 @@ int main(int argc, char **argv)
g_test_add_func("/virtio/blk/pci/config", pci_config);
g_test_add_func("/virtio/blk/pci/msix", pci_msix);
g_test_add_func("/virtio/blk/pci/idx", pci_idx);
+ g_test_add_func("/virtio/blk/pci/hotplug", hotplug);
ret = g_test_run();
diff --git a/tests/virtio-net-test.c b/tests/virtio-net-test.c
index df99343..ea7478c 100644
--- a/tests/virtio-net-test.c
+++ b/tests/virtio-net-test.c
@@ -11,18 +11,28 @@
#include <string.h>
#include "libqtest.h"
#include "qemu/osdep.h"
+#include "libqos/pci.h"
+
+#define PCI_SLOT_HP 0x06
/* Tests only initialization so far. TODO: Replace with functional tests */
static void pci_nop(void)
{
}
+static void hotplug(void)
+{
+ qpci_plug_device_test("virtio-net-pci", "net1", PCI_SLOT_HP, NULL);
+ qpci_unplug_acpi_device_test("net1", PCI_SLOT_HP);
+}
+
int main(int argc, char **argv)
{
int ret;
g_test_init(&argc, &argv, NULL);
qtest_add_func("/virtio/net/pci/nop", pci_nop);
+ qtest_add_func("/virtio/net/pci/hotplug", hotplug);
qtest_start("-device virtio-net-pci");
ret = g_test_run();
diff --git a/tests/virtio-rng-test.c b/tests/virtio-rng-test.c
index 402c206..41c1cdb 100644
--- a/tests/virtio-rng-test.c
+++ b/tests/virtio-rng-test.c
@@ -11,18 +11,28 @@
#include <string.h>
#include "libqtest.h"
#include "qemu/osdep.h"
+#include "libqos/pci.h"
+
+#define PCI_SLOT_HP 0x06
/* Tests only initialization so far. TODO: Replace with functional tests */
static void pci_nop(void)
{
}
+static void hotplug(void)
+{
+ qpci_plug_device_test("virtio-rng-pci", "rng1", PCI_SLOT_HP, NULL);
+ qpci_unplug_acpi_device_test("rng1", PCI_SLOT_HP);
+}
+
int main(int argc, char **argv)
{
int ret;
g_test_init(&argc, &argv, NULL);
qtest_add_func("/virtio/rng/pci/nop", pci_nop);
+ qtest_add_func("/virtio/rng/pci/hotplug", hotplug);
qtest_start("-device virtio-rng-pci");
ret = g_test_run();
diff --git a/tests/virtio-scsi-test.c b/tests/virtio-scsi-test.c
index 3230908..41f9602 100644
--- a/tests/virtio-scsi-test.c
+++ b/tests/virtio-scsi-test.c
@@ -17,14 +17,43 @@ static void pci_nop(void)
{
}
+static void hotplug(void)
+{
+ QDict *response;
+
+ response = qmp("{\"execute\": \"device_add\","
+ " \"arguments\": {"
+ " \"driver\": \"scsi-hd\","
+ " \"id\": \"scsi-hd\","
+ " \"drive\": \"drv1\""
+ "}}");
+
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+
+ response = qmp("{\"execute\": \"device_del\","
+ " \"arguments\": {"
+ " \"id\": \"scsi-hd\""
+ "}}");
+
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ g_assert(qdict_haskey(response, "event"));
+ g_assert(!strcmp(qdict_get_str(response, "event"), "DEVICE_DELETED"));
+ QDECREF(response);
+}
+
int main(int argc, char **argv)
{
int ret;
g_test_init(&argc, &argv, NULL);
qtest_add_func("/virtio/scsi/pci/nop", pci_nop);
+ qtest_add_func("/virtio/scsi/pci/hotplug", hotplug);
qtest_start("-drive id=drv0,if=none,file=/dev/null "
+ "-drive id=drv1,if=none,file=/dev/null "
"-device virtio-scsi-pci,id=vscsi0 "
"-device scsi-hd,bus=vscsi0.0,drive=drv0");
ret = g_test_run();
diff --git a/tests/virtio-serial-test.c b/tests/virtio-serial-test.c
index e743875..bf030a6 100644
--- a/tests/virtio-serial-test.c
+++ b/tests/virtio-serial-test.c
@@ -17,12 +17,39 @@ static void pci_nop(void)
{
}
+static void hotplug(void)
+{
+ QDict *response;
+
+ response = qmp("{\"execute\": \"device_add\","
+ " \"arguments\": {"
+ " \"driver\": \"virtserialport\","
+ " \"id\": \"hp-port\""
+ "}}");
+
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+
+ response = qmp("{\"execute\": \"device_del\","
+ " \"arguments\": {"
+ " \"id\": \"hp-port\""
+ "}}");
+
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ g_assert(qdict_haskey(response, "event"));
+ g_assert(!strcmp(qdict_get_str(response, "event"), "DEVICE_DELETED"));
+ QDECREF(response);
+}
+
int main(int argc, char **argv)
{
int ret;
g_test_init(&argc, &argv, NULL);
qtest_add_func("/virtio/serial/pci/nop", pci_nop);
+ qtest_add_func("/virtio/serial/pci/hotplug", hotplug);
qtest_start("-device virtio-serial-pci");
ret = g_test_run();