---
Notes:
v6:
- new in v6 (replaces "hw/pci-bridge: create interrupt-less,
hotplug-less bridge for PXB" from v5) [Michael]
- I tested migration-to-file and migration-from-file, with a single PXB
behind which a virtio-scsi HBA lived
- No idea how to test the hotplug callbacks. I tried the device_add and
device_del HMP commands with a virtio-scsi HBA behind the PXB, and the
monitor reports no error at all; the callbacks don't seem to be
reached.
- Retested with windows 2012; it works
hw/pci-bridge/pci_bridge_dev.c | 90 ++++++++++++++++++++++++++++++++++++------
1 file changed, 78 insertions(+), 12 deletions(-)
diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c
index d697c7b..a0a1ce9 100644
--- a/hw/pci-bridge/pci_bridge_dev.c
+++ b/hw/pci-bridge/pci_bridge_dev.c
@@ -40,10 +40,16 @@ struct PCIBridgeDev {
MemoryRegion bar;
uint8_t chassis_nr;
#define PCI_BRIDGE_DEV_F_MSI_REQ 0
+#define PCI_BRIDGE_DEV_F_HOTPLUG 1
uint32_t flags;
};
typedef struct PCIBridgeDev PCIBridgeDev;
+static inline bool hotplug_enabled(const PCIBridgeDev *bridge_dev)
+{
+ return !!(bridge_dev->flags & (1u << PCI_BRIDGE_DEV_F_HOTPLUG));
+}
+
static int pci_bridge_dev_initfn(PCIDevice *dev)
{
PCIBridge *br = PCI_BRIDGE(dev);
@@ -54,16 +60,26 @@ static int pci_bridge_dev_initfn(PCIDevice *dev)
if (err) {
goto bridge_error;
}
- dev->config[PCI_INTERRUPT_PIN] = 0x1;
- memory_region_init(&bridge_dev->bar, OBJECT(dev), "shpc-bar",
shpc_bar_size(dev));
- err = shpc_init(dev, &br->sec_bus, &bridge_dev->bar, 0);
- if (err) {
- goto shpc_error;
+
+ if (hotplug_enabled(bridge_dev)) {
+ dev->config[PCI_INTERRUPT_PIN] = 0x1;
+ memory_region_init(&bridge_dev->bar, OBJECT(dev), "shpc-bar",
+ shpc_bar_size(dev));
+ err = shpc_init(dev, &br->sec_bus, &bridge_dev->bar, 0);
+ if (err) {
+ goto shpc_error;
+ }
}
+
err = slotid_cap_init(dev, 0, bridge_dev->chassis_nr, 0);
if (err) {
goto slotid_error;
}
+
+ if (!hotplug_enabled(bridge_dev)) {
+ return 0;
+ }
+
if ((bridge_dev->flags & (1 << PCI_BRIDGE_DEV_F_MSI_REQ)) &&
msi_supported) {
err = msi_init(dev, 0, 1, true, true);
@@ -79,7 +95,9 @@ static int pci_bridge_dev_initfn(PCIDevice *dev)
msi_error:
slotid_cap_cleanup(dev);
slotid_error:
- shpc_cleanup(dev, &bridge_dev->bar);
+ if (hotplug_enabled(bridge_dev)) {
+ shpc_cleanup(dev, &bridge_dev->bar);
+ }
shpc_error:
pci_bridge_exitfn(dev);
bridge_error:
@@ -89,53 +107,101 @@ bridge_error:
static void pci_bridge_dev_exitfn(PCIDevice *dev)
{
PCIBridgeDev *bridge_dev = PCI_BRIDGE_DEV(dev);
+
if (msi_present(dev)) {
msi_uninit(dev);
}
slotid_cap_cleanup(dev);
- shpc_cleanup(dev, &bridge_dev->bar);
+ if (hotplug_enabled(bridge_dev)) {
+ shpc_cleanup(dev, &bridge_dev->bar);
+ }
pci_bridge_exitfn(dev);
}
static void pci_bridge_dev_instance_finalize(Object *obj)
{
+ /* this function is idempotent and handles (PCIDevice.shpc == NULL) */
shpc_free(PCI_DEVICE(obj));
}
static void pci_bridge_dev_write_config(PCIDevice *d,
uint32_t address, uint32_t val, int
len)
{
+ PCIBridgeDev *bridge_dev = PCI_BRIDGE_DEV(d);
+
pci_bridge_write_config(d, address, val, len);
if (msi_present(d)) {
msi_write_config(d, address, val, len);
}
- shpc_cap_write_config(d, address, val, len);
+ if (hotplug_enabled(bridge_dev)) {
+ shpc_cap_write_config(d, address, val, len);
+ }
}
static void qdev_pci_bridge_dev_reset(DeviceState *qdev)
{
PCIDevice *dev = PCI_DEVICE(qdev);
+ PCIBridgeDev *bridge_dev = PCI_BRIDGE_DEV(dev);
pci_bridge_reset(qdev);
- shpc_reset(dev);
+ if (hotplug_enabled(bridge_dev)) {
+ shpc_reset(dev);
+ }
}
static Property pci_bridge_dev_properties[] = {
/* Note: 0 is not a legal chassis number. */
DEFINE_PROP_UINT8("chassis_nr", PCIBridgeDev, chassis_nr, 0),
DEFINE_PROP_BIT("msi", PCIBridgeDev, flags, PCI_BRIDGE_DEV_F_MSI_REQ,
true),
+ DEFINE_PROP_BIT("hotplug", PCIBridgeDev, flags, PCI_BRIDGE_DEV_F_HOTPLUG,
+ true),
DEFINE_PROP_END_OF_LIST(),
};
+static bool pci_bridge_dev_shpc_exists(void *opaque, int version_id)
+{
+ PCIDevice *dev = opaque;
+ PCIBridgeDev *bridge_dev = PCI_BRIDGE_DEV(dev);
+
+ return hotplug_enabled(bridge_dev);
+}
+
static const VMStateDescription pci_bridge_dev_vmstate = {
.name = "pci_bridge",
.fields = (VMStateField[]) {
VMSTATE_PCI_DEVICE(parent_obj, PCIBridge),
- SHPC_VMSTATE(shpc, PCIDevice, NULL),
+ SHPC_VMSTATE(shpc, PCIDevice, pci_bridge_dev_shpc_exists),
VMSTATE_END_OF_LIST()
}
};
+static void pci_bridge_dev_hotplug_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ PCIBridgeDev *bridge_dev = PCI_BRIDGE_DEV(hotplug_dev);
+
+ if (!hotplug_enabled(bridge_dev)) {
+ error_setg(errp, "standard hotplug controller has been disabled for "
+ "this %s", TYPE_PCI_BRIDGE_DEV);
+ return;
+ }
+ shpc_device_hotplug_cb(hotplug_dev, dev, errp);
+}
+
+static void pci_bridge_dev_hot_unplug_request_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev,
+ Error **errp)
+{
+ PCIBridgeDev *bridge_dev = PCI_BRIDGE_DEV(hotplug_dev);
+
+ if (!hotplug_enabled(bridge_dev)) {
+ error_setg(errp, "standard hotplug controller has been disabled for "
+ "this %s", TYPE_PCI_BRIDGE_DEV);
+ return;
+ }
+ shpc_device_hot_unplug_request_cb(hotplug_dev, dev, errp);
+}
+
static void pci_bridge_dev_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -154,8 +220,8 @@ static void pci_bridge_dev_class_init(ObjectClass *klass,
void *data)
dc->props = pci_bridge_dev_properties;
dc->vmsd = &pci_bridge_dev_vmstate;
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
- hc->plug = shpc_device_hotplug_cb;
- hc->unplug_request = shpc_device_hot_unplug_request_cb;
+ hc->plug = pci_bridge_dev_hotplug_cb;
+ hc->unplug_request = pci_bridge_dev_hot_unplug_request_cb;
}
static const TypeInfo pci_bridge_dev_info = {
--
1.8.3.1