piix: fix up/down races
RFC PATCH 1/1 piix: fix up/down races
piix acpi interface suffers from the following 2 issues:
1.
- delete device a
- quickly add device b in another slot
if we do this before guest reads the down register, the down event is discarded and device will never be deleted.
2.
- delete device a
- quickly reset before guest can respond
interrupt is reset and guest will never eject the device.
To fix this, we implement two changes:
1. Add two new registers:CLR_UPCLR_DOWN
bios will now write to these the value it read from UP/DOWN
2. on reset, remove all devices which have DOWN bit set
For compatibility with old guests, we also clear
the DOWN bit on write to EJ0 for a device.
Warning: untested.
Posting for early feedback/flames.
--- hw/acpi_piix4.c | 72 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 files changed, 58 insertions(+), 14 deletions(-)
hw/acpi_piix4.c +58 -14
@@ -43,6 +43,8 @@#define PCI_BASE 0xae00#define PCI_EJ_BASE 0xae08#define PCI_RMV_BASE 0xae0c +#define PCI_CLR_UP_BASE 0xae10 +#define PCI_CLR_DOWN_BASE 0xae14#define PIIX4_PCI_HOTPLUG_STATUS 2
@@ -287,6 +289,25 @@ static void piix4_update_hotplug(PIIX4PMState *s) } } +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 ; + +/* Clear down register here too - this is good for +* compatibility with old guests which do not have CLR_DOWN. */ + 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) { +qdev_free (qdev); + } + } +} +static void piix4_reset (void *opaque) {PIIX4PMState *s = opaque;
@@ -302,6 +323,19 @@ static void piix4_reset(void *opaque) pci_conf[0x5B ] =0x02 ; }piix4_update_hotplug (s); +/* +* Guest lost remove events if any. +* As it's being reset it's safe to remove the device now. +*/ +while (s->pci0_status.down) { +acpi_piix_eject_slot (s, s->pci0_status.down); + } +/* +* Guest lost add events if any. +* As it's being reset and will rescan the bus we cann discard +* past events now. +*/ + s->pci0_status.up =0 ; }static void piix4_powerdown (void *opaque,int irq,int power_failing)
@@ -490,22 +524,31 @@ 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 ; +PIIX4PMState *s = opaque; - 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); - } +if (val) { +acpi_piix_eject_slot (s, val); } -PIIX4_DPRINTF ("pciej write %x <== %d\n" , addr, val); } +static void pci_clr_up_write (void *opaque,uint32_t addr,uint32_t val) +{ +PIIX4PMState *s = opaque; + s->pci0_status.up &= ~val; + +PIIX4_DPRINTF ("pci_clr_up write %x <== %d\n" , addr, val); +} + +static void pci_clr_down_write (void *opaque,uint32_t addr,uint32_t val) +{ +PIIX4PMState *s = opaque; + s->pci0_status.down &= ~val; + +PIIX4_DPRINTF ("pci_clr_down write %x <== %d\n" , addr, val); +} +static uint32_t pcirmv_read (void *opaque,uint32_t addr) {PIIX4PMState *s = opaque;
@@ -532,12 +575,15 @@ static void piix4_acpi_system_hot_add_init(PCIBus *bus, PIIX4PMState *s)register_ioport_write (PCI_BASE ,8 ,4 , pcihotplug_write, pci0_status);register_ioport_read (PCI_BASE ,8 ,4 , pcihotplug_read, pci0_status); -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); +register_ioport_write (PCI_CLR_UP_BASE ,4 ,4 , pci_clr_up_write, s); +register_ioport_write (PCI_CLR_DOWN_BASE ,4 ,4 , pci_clr_down_write, s); +pci_bus_hotplug (bus, piix4_device_hotplug, &s->dev.qdev); }
@@ -567,8 +613,6 @@ static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev,return 0 ; } - s->pci0_status.up =0 ; - s->pci0_status.down =0 ;if (state ==PCI_HOTPLUG_ENABLED ) {enable_device (s, slot); }else {
Signature · 1 line
1.7.9.111.gf3fb0
Quoted 25 lines · Michael S. Tsirkin
piix acpi interface suffers from the following 2 issues:
1.
- delete device a
- quickly add device b in another slotif we do this before guest reads the down register, the down event is discarded and device will never be deleted.
2.
- delete device a
- quickly reset before guest can respondinterrupt is reset and guest will never eject the device.
To fix this, we implement two changes:
1. Add two new registers:
CLR_UPCLR_DOWN
bios will now write to these the value it read from UP/DOWN2. on reset, remove all devices which have DOWN bit set
Quoted 7 lines
For compatibility with old guests, we also clear
the DOWN bit on write to EJ0 for a device.Signed-off-by Michael S. Tsirkin <[email protected]>Warning: untested.
Posting for early feedback/flames.
OK, I've now tested this with a windows and linux guests. seabios patch posted on the seabios mailing list. I'll apply this if no one objects ...
Quoted 134 lines
--- hw/acpi_piix4.c | 72 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 files changed, 58 insertions(+), 14 deletions(-)hw/acpi_piix4.c +58
-14@@ -43,6 +43,8 @@#define PCI_BASE 0xae00#define PCI_EJ_BASE 0xae08#define PCI_RMV_BASE 0xae0c +#define PCI_CLR_UP_BASE 0xae10 +#define PCI_CLR_DOWN_BASE 0xae14#define PIIX4_PCI_HOTPLUG_STATUS 2@@ -287,6 +289,25 @@ static void piix4_update_hotplug(PIIX4PMState *s) } } +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 ; + +/* Clear down register here too - this is good for +* compatibility with old guests which do not have CLR_DOWN. */ + 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) { +qdev_free (qdev); + } + } +} +static void piix4_reset (void *opaque) {PIIX4PMState *s = opaque;@@ -302,6 +323,19 @@ static void piix4_reset(void *opaque) pci_conf[0x5B ] =0x02 ; }piix4_update_hotplug (s); +/* +* Guest lost remove events if any. +* As it's being reset it's safe to remove the device now. +*/ +while (s->pci0_status.down) { +acpi_piix_eject_slot (s, s->pci0_status.down); + } +/* +* Guest lost add events if any. +* As it's being reset and will rescan the bus we cann discard +* past events now. +*/ + s->pci0_status.up =0 ; }static void piix4_powerdown (void *opaque,int irq,int power_failing)@@ -490,22 +524,31 @@ 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 ; +PIIX4PMState *s = opaque; - 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); - } +if (val) { +acpi_piix_eject_slot (s, val); } -PIIX4_DPRINTF ("pciej write %x <== %d\n" , addr, val); } +static void pci_clr_up_write (void *opaque,uint32_t addr,uint32_t val) +{ +PIIX4PMState *s = opaque; + s->pci0_status.up &= ~val; + +PIIX4_DPRINTF ("pci_clr_up write %x <== %d\n" , addr, val); +} + +static void pci_clr_down_write (void *opaque,uint32_t addr,uint32_t val) +{ +PIIX4PMState *s = opaque; + s->pci0_status.down &= ~val; + +PIIX4_DPRINTF ("pci_clr_down write %x <== %d\n" , addr, val); +} +static uint32_t pcirmv_read (void *opaque,uint32_t addr) {PIIX4PMState *s = opaque;@@ -532,12 +575,15 @@ static void piix4_acpi_system_hot_add_init(PCIBus *bus, PIIX4PMState *s)register_ioport_write (PCI_BASE ,8 ,4 , pcihotplug_write, pci0_status);register_ioport_read (PCI_BASE ,8 ,4 , pcihotplug_read, pci0_status); -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); +register_ioport_write (PCI_CLR_UP_BASE ,4 ,4 , pci_clr_up_write, s); +register_ioport_write (PCI_CLR_DOWN_BASE ,4 ,4 , pci_clr_down_write, s); +pci_bus_hotplug (bus, piix4_device_hotplug, &s->dev.qdev); }@@ -567,8 +613,6 @@ static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev,return 0 ; } - s->pci0_status.up =0 ; - s->pci0_status.down =0 ;if (state ==PCI_HOTPLUG_ENABLED ) {enable_device (s, slot); }else {Signature · 1 line
1.7.9.111.gf3fb0
Quoted 28 lines · Michael S. Tsirkin
piix acpi interface suffers from the following 2 issues:
1.
- delete device a
- quickly add device b in another slotif we do this before guest reads the down register, the down event is discarded and device will never be deleted.
2.
- delete device a
- quickly reset before guest can respondinterrupt is reset and guest will never eject the device.
To fix this, we implement two changes:
1. Add two new registers:
CLR_UPCLR_DOWN
bios will now write to these the value it read from UP/DOWN2. on reset, remove all devices which have DOWN bit set
For compatibility with old guests, we also clear
the DOWN bit on write to EJ0 for a device.
Quoted 7 lines
Signed-off-by Michael S. Tsirkin <[email protected]>Warning: untested.
Posting for early feedback/flames.--- hw/acpi_piix4.c | 72 ++++++++++++++++++++++++++++++++++++++++++++----------
docs/specs/acpi_pci_hotplug.txt +++++++
Quoted 16 lines
1 files changed, 58 insertions(+), 14 deletions(-)hw/acpi_piix4.c +2
-0@@ -43,6 +43,8 @@#define PCI_BASE 0xae00#define PCI_EJ_BASE 0xae08#define PCI_RMV_BASE 0xae0c +#define PCI_CLR_UP_BASE 0xae10 +#define PCI_CLR_DOWN_BASE 0xae14#define PIIX4_PCI_HOTPLUG_STATUS 2
hw/acpi_piix4.c static void piix4_update_hotplug(PIIX4PMState *s) · line 297
Quoted 9 lines
hw/acpi_piix4.c +6
-0@@ -287,6 +289,25 @@ static void piix4_update_hotplug(PIIX4PMState *s) } } +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 ; +
Funny how AML code makes bitmap from a slot number and here we do the opposite. I wish we could fix it.
hw/acpi_piix4.c static void piix4_update_hotplug(PIIX4PMState *s) · line 300
Quoted 3 lines
hw/acpi_piix4.c +3
-0+/* Clear down register here too - this is good for +* compatibility with old guests which do not have CLR_DOWN. */ + s->pci0_status.down &= ~(1U << slot);
Strictly speaking the comment is incorrect since reset handler below relies on this too.
Quoted 104 lines
hw/acpi_piix4.c +47
-14+ + 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); + } + } +} +static void piix4_reset (void *opaque) {PIIX4PMState *s = opaque;@@ -302,6 +323,19 @@ static void piix4_reset(void *opaque) pci_conf[0x5B ] =0x02 ; }piix4_update_hotplug (s); +/* +* Guest lost remove events if any. +* As it's being reset it's safe to remove the device now. +*/ +while (s->pci0_status.down) { +acpi_piix_eject_slot (s, s->pci0_status.down); + } +/* +* Guest lost add events if any. +* As it's being reset and will rescan the bus we cann discard +* past events now. +*/ + s->pci0_status.up =0 ; }static void piix4_powerdown (void *opaque,int irq,int power_failing)@@ -490,22 +524,31 @@ 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 ; +PIIX4PMState *s = opaque; - 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); - } +if (val) { +acpi_piix_eject_slot (s, val); } -PIIX4_DPRINTF ("pciej write %x <== %d\n" , addr, val); } +static void pci_clr_up_write (void *opaque,uint32_t addr,uint32_t val) +{ +PIIX4PMState *s = opaque; + s->pci0_status.up &= ~val; + +PIIX4_DPRINTF ("pci_clr_up write %x <== %d\n" , addr, val); +} + +static void pci_clr_down_write (void *opaque,uint32_t addr,uint32_t val) +{ +PIIX4PMState *s = opaque; + s->pci0_status.down &= ~val; + +PIIX4_DPRINTF ("pci_clr_down write %x <== %d\n" , addr, val); +} +static uint32_t pcirmv_read (void *opaque,uint32_t addr) {PIIX4PMState *s = opaque;@@ -532,12 +575,15 @@ static void piix4_acpi_system_hot_add_init(PCIBus *bus, PIIX4PMState *s)register_ioport_write (PCI_BASE ,8 ,4 , pcihotplug_write, pci0_status);register_ioport_read (PCI_BASE ,8 ,4 , pcihotplug_read, pci0_status); -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); +register_ioport_write (PCI_CLR_UP_BASE ,4 ,4 , pci_clr_up_write, s); +register_ioport_write (PCI_CLR_DOWN_BASE ,4 ,4 , pci_clr_down_write, s); +pci_bus_hotplug (bus, piix4_device_hotplug, &s->dev.qdev); }@@ -567,8 +613,6 @@ static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev,return 0 ; } - s->pci0_status.up =0 ; - s->pci0_status.down =0 ;if (state ==PCI_HOTPLUG_ENABLED ) {enable_device (s, slot); }else {Signature · 1 line
1.7.9.111.gf3fb0
--
Gleb.