summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/acpi_piix4.c74
1 files changed, 53 insertions, 21 deletions
diff --git a/hw/acpi_piix4.c b/hw/acpi_piix4.c
index 5e8b261b74..0e7af51063 100644
--- a/hw/acpi_piix4.c
+++ b/hw/acpi_piix4.c
@@ -48,7 +48,7 @@
#define PIIX4_PCI_HOTPLUG_STATUS 2
struct pci_status {
- uint32_t up;
+ uint32_t up; /* deprecated, maintained for migration compatibility */
uint32_t down;
};
@@ -70,6 +70,7 @@ typedef struct PIIX4PMState {
/* for pci hotplug */
struct pci_status pci0_status;
uint32_t pci0_hotplug_enable;
+ uint32_t pci0_slot_device_present;
} PIIX4PMState;
static void piix4_acpi_system_hot_add_init(PCIBus *bus, PIIX4PMState *s);
@@ -205,6 +206,17 @@ static void pm_write_config(PCIDevice *d,
pm_io_space_update((PIIX4PMState *)d);
}
+static void vmstate_pci_status_pre_save(void *opaque)
+{
+ struct pci_status *pci0_status = opaque;
+ PIIX4PMState *s = container_of(pci0_status, PIIX4PMState, pci0_status);
+
+ /* We no longer track up, so build a safe value for migrating
+ * to a version that still does... of course these might get lost
+ * by an old buggy implementation, but we try. */
+ pci0_status->up = s->pci0_slot_device_present & s->pci0_hotplug_enable;
+}
+
static int vmstate_acpi_post_load(void *opaque, int version_id)
{
PIIX4PMState *s = opaque;
@@ -241,6 +253,7 @@ static const VMStateDescription vmstate_pci_status = {
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
+ .pre_save = vmstate_pci_status_pre_save,
.fields = (VMStateField []) {
VMSTATE_UINT32(up, struct pci_status),
VMSTATE_UINT32(down, struct pci_status),
@@ -269,13 +282,38 @@ static const VMStateDescription vmstate_acpi = {
}
};
+static void acpi_piix_eject_slot(PIIX4PMState *s, unsigned slots)
+{
+ DeviceState *qdev, *next;
+ BusState *bus = qdev_get_parent_bus(&s->dev.qdev);
+ int slot = ffs(slots) - 1;
+
+ /* Mark request as complete */
+ s->pci0_status.down &= ~(1U << slot);
+
+ QTAILQ_FOREACH_SAFE(qdev, &bus->children, sibling, next) {
+ PCIDevice *dev = PCI_DEVICE(qdev);
+ PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev);
+ if (PCI_SLOT(dev->devfn) == slot && !pc->no_hotplug) {
+ s->pci0_slot_device_present &= ~(1U << slot);
+ qdev_free(qdev);
+ }
+ }
+}
+
static void piix4_update_hotplug(PIIX4PMState *s)
{
PCIDevice *dev = &s->dev;
BusState *bus = qdev_get_parent_bus(&dev->qdev);
DeviceState *qdev, *next;
+ /* Execute any pending removes during reset */
+ while (s->pci0_status.down) {
+ acpi_piix_eject_slot(s, s->pci0_status.down);
+ }
+
s->pci0_hotplug_enable = ~0;
+ s->pci0_slot_device_present = 0;
QTAILQ_FOREACH_SAFE(qdev, &bus->children, sibling, next) {
PCIDevice *pdev = PCI_DEVICE(qdev);
@@ -283,8 +321,10 @@ static void piix4_update_hotplug(PIIX4PMState *s)
int slot = PCI_SLOT(pdev->devfn);
if (pc->no_hotplug) {
- s->pci0_hotplug_enable &= ~(1 << slot);
+ s->pci0_hotplug_enable &= ~(1U << slot);
}
+
+ s->pci0_slot_device_present |= (1U << slot);
}
}
@@ -452,7 +492,11 @@ static void gpe_writeb(void *opaque, uint32_t addr, uint32_t val)
static uint32_t pci_up_read(void *opaque, uint32_t addr)
{
PIIX4PMState *s = opaque;
- uint32_t val = s->pci0_status.up;
+ uint32_t val;
+
+ /* Manufacture an "up" value to cause a device check on any hotplug
+ * slot with a device. Extra device checks are harmless. */
+ val = s->pci0_slot_device_present & s->pci0_hotplug_enable;
PIIX4_DPRINTF("pci_up_read %x\n", val);
return val;
@@ -475,18 +519,7 @@ static uint32_t pciej_read(void *opaque, uint32_t addr)
static void pciej_write(void *opaque, uint32_t addr, uint32_t val)
{
- BusState *bus = opaque;
- DeviceState *qdev, *next;
- int slot = ffs(val) - 1;
-
- QTAILQ_FOREACH_SAFE(qdev, &bus->children, sibling, next) {
- PCIDevice *dev = PCI_DEVICE(qdev);
- PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev);
- if (PCI_SLOT(dev->devfn) == slot && !pc->no_hotplug) {
- qdev_free(qdev);
- }
- }
-
+ acpi_piix_eject_slot(opaque, val);
PIIX4_DPRINTF("pciej write %x <== %d\n", addr, val);
}
@@ -516,8 +549,8 @@ static void piix4_acpi_system_hot_add_init(PCIBus *bus, PIIX4PMState *s)
register_ioport_read(PCI_UP_BASE, 4, 4, pci_up_read, s);
register_ioport_read(PCI_DOWN_BASE, 4, 4, pci_down_read, s);
- register_ioport_write(PCI_EJ_BASE, 4, 4, pciej_write, bus);
- register_ioport_read(PCI_EJ_BASE, 4, 4, pciej_read, bus);
+ register_ioport_write(PCI_EJ_BASE, 4, 4, pciej_write, s);
+ register_ioport_read(PCI_EJ_BASE, 4, 4, pciej_read, s);
register_ioport_write(PCI_RMV_BASE, 4, 4, pcirmv_write, s);
register_ioport_read(PCI_RMV_BASE, 4, 4, pcirmv_read, s);
@@ -528,13 +561,13 @@ static void piix4_acpi_system_hot_add_init(PCIBus *bus, PIIX4PMState *s)
static void enable_device(PIIX4PMState *s, int slot)
{
s->ar.gpe.sts[0] |= PIIX4_PCI_HOTPLUG_STATUS;
- s->pci0_status.up |= (1 << slot);
+ s->pci0_slot_device_present |= (1U << slot);
}
static void disable_device(PIIX4PMState *s, int slot)
{
s->ar.gpe.sts[0] |= PIIX4_PCI_HOTPLUG_STATUS;
- s->pci0_status.down |= (1 << slot);
+ s->pci0_status.down |= (1U << slot);
}
static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev,
@@ -548,11 +581,10 @@ static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev,
* it is present on boot, no hotplug event is necessary. We do send an
* event when the device is disabled later. */
if (state == PCI_COLDPLUG_ENABLED) {
+ s->pci0_slot_device_present |= (1U << slot);
return 0;
}
- s->pci0_status.up = 0;
- s->pci0_status.down = 0;
if (state == PCI_HOTPLUG_ENABLED) {
enable_device(s, slot);
} else {