Also some minor code restructions along the way: * create a new struct for acpi pci hotplug state * rename functions so they no longer carry piix in the name
Signed-off-by: Gerd Hoffmann <kra...@redhat.com> --- hw/Makefile.objs | 2 +- hw/acpi_pci_hotplug.c | 214 +++++++++++++++++++++++++++++++++++++++++++++++++ hw/acpi_pci_hotplug.h | 26 ++++++ hw/acpi_piix4.c | 202 ++-------------------------------------------- 4 files changed, 250 insertions(+), 194 deletions(-) create mode 100644 hw/acpi_pci_hotplug.c create mode 100644 hw/acpi_pci_hotplug.h diff --git a/hw/Makefile.objs b/hw/Makefile.objs index 6b8a68c..dc1479f 100644 --- a/hw/Makefile.objs +++ b/hw/Makefile.objs @@ -31,7 +31,7 @@ common-obj-$(CONFIG_I8254) += i8254_common.o i8254.o common-obj-$(CONFIG_PCSPK) += pcspk.o common-obj-$(CONFIG_PCKBD) += pckbd.o common-obj-$(CONFIG_FDC) += fdc.o -common-obj-$(CONFIG_ACPI) += acpi.o acpi_piix4.o acpi_ich9.o smbus_ich9.o +common-obj-$(CONFIG_ACPI) += acpi.o acpi_pci_hotplug.o acpi_piix4.o acpi_ich9.o smbus_ich9.o common-obj-$(CONFIG_APM) += pm_smbus.o apm.o common-obj-$(CONFIG_DMA) += dma.o common-obj-$(CONFIG_I82374) += i82374.o diff --git a/hw/acpi_pci_hotplug.c b/hw/acpi_pci_hotplug.c new file mode 100644 index 0000000..cff21bd --- /dev/null +++ b/hw/acpi_pci_hotplug.c @@ -0,0 +1,214 @@ +/* + * ACPI implementation + * + * Copyright (c) 2006 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/> + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include "hw.h" +#include "pci/pci.h" + +#include "acpi_pci_hotplug.h" + +//#define DEBUG + +#ifdef DEBUG +# define DPRINTF(format, ...) printf(format, ## __VA_ARGS__) +#else +# define DPRINTF(format, ...) do { } while (0) +#endif + +static void acpi_pci_hotplug_eject_slot(ACPIPCI *s, unsigned slots) +{ + BusChild *kid, *next; + BusState *bus = s->bus; + int slot = ffs(slots) - 1; + bool slot_free = true; + + /* Mark request as complete */ + s->pci0_status.down &= ~(1U << slot); + + QTAILQ_FOREACH_SAFE(kid, &bus->children, sibling, next) { + DeviceState *qdev = kid->child; + PCIDevice *dev = PCI_DEVICE(qdev); + PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); + if (PCI_SLOT(dev->devfn) == slot) { + if (pc->no_hotplug) { + slot_free = false; + } else { + qdev_free(qdev); + } + } + } + if (slot_free) { + s->pci0_slot_device_present &= ~(1U << slot); + } +} + +static uint32_t pci_up_read(void *opaque, uint32_t addr) +{ + ACPIPCI *s = opaque; + 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; + + DPRINTF("pci_up_read %x\n", val); + return val; +} + +static uint32_t pci_down_read(void *opaque, uint32_t addr) +{ + ACPIPCI *s = opaque; + uint32_t val = s->pci0_status.down; + + DPRINTF("pci_down_read %x\n", val); + return val; +} + +static uint32_t pci_features_read(void *opaque, uint32_t addr) +{ + /* No feature defined yet */ + DPRINTF("pci_features_read %x\n", 0); + return 0; +} + +static void pciej_write(void *opaque, uint32_t addr, uint32_t val) +{ + acpi_pci_hotplug_eject_slot(opaque, val); + + DPRINTF("pciej write %x <== %d\n", addr, val); +} + +static uint32_t pcirmv_read(void *opaque, uint32_t addr) +{ + ACPIPCI *s = opaque; + + return s->pci0_hotplug_enable; +} + +static const MemoryRegionOps piix4_pci_ops = { + .old_portio = (MemoryRegionPortio[]) { + { + .offset = PCI_UP_BASE - PCI_HOTPLUG_ADDR, .len = 4, .size = 4, + .read = pci_up_read, + },{ + .offset = PCI_DOWN_BASE - PCI_HOTPLUG_ADDR, .len = 4, .size = 4, + .read = pci_down_read, + },{ + .offset = PCI_EJ_BASE - PCI_HOTPLUG_ADDR, .len = 4, .size = 4, + .read = pci_features_read, + .write = pciej_write, + },{ + .offset = PCI_RMV_BASE - PCI_HOTPLUG_ADDR, .len = 4, .size = 4, + .read = pcirmv_read, + }, + PORTIO_END_OF_LIST() + }, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void vmstate_pci_status_pre_save(void *opaque) +{ + struct pci_status *pci0_status = opaque; + ACPIPCI *s = container_of(pci0_status, ACPIPCI, 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; +} + +const VMStateDescription vmstate_pci_status = { + .name = "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), + VMSTATE_END_OF_LIST() + } +}; + +void acpi_pci_hotplug_update(ACPIPCI *s) +{ + BusState *bus = s->bus; + BusChild *kid, *next; + + /* Execute any pending removes during reset */ + while (s->pci0_status.down) { + acpi_pci_hotplug_eject_slot(s, s->pci0_status.down); + } + + s->pci0_hotplug_enable = ~0; + s->pci0_slot_device_present = 0; + + QTAILQ_FOREACH_SAFE(kid, &bus->children, sibling, next) { + DeviceState *qdev = kid->child; + PCIDevice *pdev = PCI_DEVICE(qdev); + PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(pdev); + int slot = PCI_SLOT(pdev->devfn); + + if (pc->no_hotplug) { + s->pci0_hotplug_enable &= ~(1U << slot); + } + + s->pci0_slot_device_present |= (1U << slot); + } +} + +void acpi_pci_hotplug_init(ACPIPCI *s, DeviceState *qdev) +{ + s->bus = qdev_get_parent_bus(qdev); + memory_region_init_io(&s->io, &piix4_pci_ops, s, "apci-pci-hotplug", + PCI_HOTPLUG_SIZE); +} + + +static void enable_device(ACPIPCI *s, int slot) +{ + s->pci0_slot_device_present |= (1U << slot); +} + +static void disable_device(ACPIPCI *s, int slot) +{ + s->pci0_status.down |= (1U << slot); +} + +int acpi_pci_hotplug_device(ACPIPCI *s, PCIDevice *dev, + PCIHotplugState state) +{ + int slot = PCI_SLOT(dev->devfn); + + /* Don't send event when device is enabled during qemu machine creation: + * 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; + } + + if (state == PCI_HOTPLUG_ENABLED) { + enable_device(s, slot); + } else { + disable_device(s, slot); + } + return 1; +} diff --git a/hw/acpi_pci_hotplug.h b/hw/acpi_pci_hotplug.h new file mode 100644 index 0000000..b97403d --- /dev/null +++ b/hw/acpi_pci_hotplug.h @@ -0,0 +1,26 @@ +#define PCI_HOTPLUG_ADDR 0xae00 +#define PCI_HOTPLUG_SIZE 0x000f +#define PCI_UP_BASE 0xae00 +#define PCI_DOWN_BASE 0xae04 +#define PCI_EJ_BASE 0xae08 +#define PCI_RMV_BASE 0xae0c + +struct pci_status { + uint32_t up; /* deprecated, maintained for migration compatibility */ + uint32_t down; +}; + +typedef struct ACPIPCI { + MemoryRegion io; + BusState *bus; + struct pci_status pci0_status; + uint32_t pci0_hotplug_enable; + uint32_t pci0_slot_device_present; +} ACPIPCI; + +extern const VMStateDescription vmstate_pci_status; + +void acpi_pci_hotplug_update(ACPIPCI *s); +void acpi_pci_hotplug_init(ACPIPCI *s, DeviceState *qdev); +int acpi_pci_hotplug_device(ACPIPCI *s, PCIDevice *dev, + PCIHotplugState state); diff --git a/hw/acpi_piix4.c b/hw/acpi_piix4.c index 06a8aca..1374116 100644 --- a/hw/acpi_piix4.c +++ b/hw/acpi_piix4.c @@ -29,6 +29,7 @@ #include "exec/ioport.h" #include "fw_cfg.h" #include "exec/address-spaces.h" +#include "acpi_pci_hotplug.h" //#define DEBUG @@ -41,27 +42,15 @@ #define GPE_BASE 0xafe0 #define GPE_LEN 4 -#define PCI_HOTPLUG_ADDR 0xae00 -#define PCI_HOTPLUG_SIZE 0x000f -#define PCI_UP_BASE 0xae00 -#define PCI_DOWN_BASE 0xae04 -#define PCI_EJ_BASE 0xae08 -#define PCI_RMV_BASE 0xae0c - #define PIIX4_PCI_HOTPLUG_STATUS 2 -struct pci_status { - uint32_t up; /* deprecated, maintained for migration compatibility */ - uint32_t down; -}; - typedef struct PIIX4PMState { PCIDevice dev; MemoryRegion io; MemoryRegion io_gpe; - MemoryRegion io_pci; ACPIREGS ar; + ACPIPCI pci; APMState apm; @@ -74,11 +63,6 @@ typedef struct PIIX4PMState { Notifier machine_ready; Notifier powerdown_notifier; - /* for pci hotplug */ - struct pci_status pci0_status; - uint32_t pci0_hotplug_enable; - uint32_t pci0_slot_device_present; - uint8_t disable_s3; uint8_t disable_s4; uint8_t s4_val; @@ -167,17 +151,6 @@ static void pm_write_config(PCIDevice *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; @@ -208,19 +181,6 @@ static const VMStateDescription vmstate_gpe = { } }; -static const VMStateDescription vmstate_pci_status = { - .name = "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), - VMSTATE_END_OF_LIST() - } -}; - static int acpi_load_old(QEMUFile *f, void *opaque, int version_id) { PIIX4PMState *s = opaque; @@ -279,67 +239,12 @@ static const VMStateDescription vmstate_acpi = { VMSTATE_TIMER(ar.tmr.timer, PIIX4PMState), VMSTATE_INT64(ar.tmr.overflow_time, PIIX4PMState), VMSTATE_STRUCT(ar.gpe, PIIX4PMState, 2, vmstate_gpe, ACPIGPE), - VMSTATE_STRUCT(pci0_status, PIIX4PMState, 2, vmstate_pci_status, + VMSTATE_STRUCT(pci.pci0_status, PIIX4PMState, 2, vmstate_pci_status, struct pci_status), VMSTATE_END_OF_LIST() } }; -static void acpi_piix_eject_slot(PIIX4PMState *s, unsigned slots) -{ - BusChild *kid, *next; - BusState *bus = qdev_get_parent_bus(&s->dev.qdev); - int slot = ffs(slots) - 1; - bool slot_free = true; - - /* Mark request as complete */ - s->pci0_status.down &= ~(1U << slot); - - QTAILQ_FOREACH_SAFE(kid, &bus->children, sibling, next) { - DeviceState *qdev = kid->child; - PCIDevice *dev = PCI_DEVICE(qdev); - PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); - if (PCI_SLOT(dev->devfn) == slot) { - if (pc->no_hotplug) { - slot_free = false; - } else { - qdev_free(qdev); - } - } - } - if (slot_free) { - s->pci0_slot_device_present &= ~(1U << slot); - } -} - -static void piix4_update_hotplug(PIIX4PMState *s) -{ - PCIDevice *dev = &s->dev; - BusState *bus = qdev_get_parent_bus(&dev->qdev); - BusChild *kid, *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(kid, &bus->children, sibling, next) { - DeviceState *qdev = kid->child; - PCIDevice *pdev = PCI_DEVICE(qdev); - PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(pdev); - int slot = PCI_SLOT(pdev->devfn); - - if (pc->no_hotplug) { - s->pci0_hotplug_enable &= ~(1U << slot); - } - - s->pci0_slot_device_present |= (1U << slot); - } -} - static void piix4_reset(void *opaque) { PIIX4PMState *s = opaque; @@ -357,7 +262,7 @@ static void piix4_reset(void *opaque) /* Mark SMM as already inited (until KVM supports SMM). */ pci_conf[0x5B] = 0x02; } - piix4_update_hotplug(s); + acpi_pci_hotplug_update(&s->pci); } static void piix4_pm_powerdown_req(Notifier *n, void *opaque) @@ -531,70 +436,6 @@ static const MemoryRegionOps piix4_gpe_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static uint32_t pci_up_read(void *opaque, uint32_t addr) -{ - PIIX4PMState *s = opaque; - 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; -} - -static uint32_t pci_down_read(void *opaque, uint32_t addr) -{ - PIIX4PMState *s = opaque; - uint32_t val = s->pci0_status.down; - - PIIX4_DPRINTF("pci_down_read %x\n", val); - return val; -} - -static uint32_t pci_features_read(void *opaque, uint32_t addr) -{ - /* No feature defined yet */ - PIIX4_DPRINTF("pci_features_read %x\n", 0); - return 0; -} - -static void pciej_write(void *opaque, uint32_t addr, uint32_t val) -{ - acpi_piix_eject_slot(opaque, val); - - PIIX4_DPRINTF("pciej write %x <== %d\n", addr, val); -} - -static uint32_t pcirmv_read(void *opaque, uint32_t addr) -{ - PIIX4PMState *s = opaque; - - return s->pci0_hotplug_enable; -} - -static const MemoryRegionOps piix4_pci_ops = { - .old_portio = (MemoryRegionPortio[]) { - { - .offset = PCI_UP_BASE - PCI_HOTPLUG_ADDR, .len = 4, .size = 4, - .read = pci_up_read, - },{ - .offset = PCI_DOWN_BASE - PCI_HOTPLUG_ADDR, .len = 4, .size = 4, - .read = pci_down_read, - },{ - .offset = PCI_EJ_BASE - PCI_HOTPLUG_ADDR, .len = 4, .size = 4, - .read = pci_features_read, - .write = pciej_write, - },{ - .offset = PCI_RMV_BASE - PCI_HOTPLUG_ADDR, .len = 4, .size = 4, - .read = pcirmv_read, - }, - PORTIO_END_OF_LIST() - }, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev, PCIHotplugState state); @@ -605,47 +446,22 @@ static void piix4_acpi_system_hot_add_init(MemoryRegion *parent, GPE_LEN); memory_region_add_subregion(parent, GPE_BASE, &s->io_gpe); - memory_region_init_io(&s->io_pci, &piix4_pci_ops, s, "apci-pci-hotplug", - PCI_HOTPLUG_SIZE); + acpi_pci_hotplug_init(&s->pci, &s->dev.qdev); memory_region_add_subregion(parent, PCI_HOTPLUG_ADDR, - &s->io_pci); + &s->pci.io); pci_bus_hotplug(bus, piix4_device_hotplug, &s->dev.qdev); } -static void enable_device(PIIX4PMState *s, int slot) -{ - s->ar.gpe.sts[0] |= PIIX4_PCI_HOTPLUG_STATUS; - 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 |= (1U << slot); -} - static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev, PCIHotplugState state) { - int slot = PCI_SLOT(dev->devfn); PIIX4PMState *s = DO_UPCAST(PIIX4PMState, dev, PCI_DEVICE(qdev)); - /* Don't send event when device is enabled during qemu machine creation: - * 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; + if (acpi_pci_hotplug_device(&s->pci, dev, state)) { + s->ar.gpe.sts[0] |= PIIX4_PCI_HOTPLUG_STATUS; + pm_update_sci(s); } - if (state == PCI_HOTPLUG_ENABLED) { - enable_device(s, slot); - } else { - disable_device(s, slot); - } - - pm_update_sci(s); - return 0; } -- 1.7.1