X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/1976058109890892db8ec88bfd3273f79c459f6b..a8cd64d488325f3be5c4ddec4bf07efb3b8c7330:/hw/acpi/pcihp.c diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c index 612fec03ee..613406d09b 100644 --- a/hw/acpi/pcihp.c +++ b/hw/acpi/pcihp.c @@ -24,32 +24,23 @@ * GNU GPL, version 2 or (at your option) any later version. */ +#include "qemu/osdep.h" #include "hw/acpi/pcihp.h" #include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/pci/pci.h" +#include "hw/pci/pci_bridge.h" #include "hw/acpi/acpi.h" #include "sysemu/sysemu.h" -#include "qemu/range.h" -#include "exec/ioport.h" #include "exec/address-spaces.h" #include "hw/pci/pci_bus.h" +#include "qapi/error.h" #include "qom/qom-qobject.h" -#include "qapi/qmp/qint.h" +#include "trace.h" -//#define DEBUG - -#ifdef DEBUG -# define ACPI_PCIHP_DPRINTF(format, ...) printf(format, ## __VA_ARGS__) -#else -# define ACPI_PCIHP_DPRINTF(format, ...) do { } while (0) -#endif - -#define ACPI_PCI_HOTPLUG_STATUS 2 #define ACPI_PCIHP_ADDR 0xae00 #define ACPI_PCIHP_SIZE 0x0014 -#define ACPI_PCIHP_LEGACY_SIZE 0x000f #define PCI_UP_BASE 0x0000 #define PCI_DOWN_BASE 0x0004 #define PCI_EJ_BASE 0x0008 @@ -64,10 +55,10 @@ typedef struct AcpiPciHpFind { static int acpi_pcihp_get_bsel(PCIBus *bus) { Error *local_err = NULL; - int64_t bsel = object_property_get_int(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, - &local_err); + uint64_t bsel = object_property_get_uint(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, + &local_err); - if (local_err || bsel < 0 || bsel >= ACPI_PCIHP_MAX_HOTPLUG_BUS) { + if (local_err || bsel >= ACPI_PCIHP_MAX_HOTPLUG_BUS) { if (local_err) { error_free(local_err); } @@ -77,6 +68,43 @@ static int acpi_pcihp_get_bsel(PCIBus *bus) } } +/* Assign BSEL property to all buses. In the future, this can be changed + * to only assign to buses that support hotplug. + */ +static void *acpi_set_bsel(PCIBus *bus, void *opaque) +{ + unsigned *bsel_alloc = opaque; + unsigned *bus_bsel; + + if (qbus_is_hotpluggable(BUS(bus))) { + bus_bsel = g_malloc(sizeof *bus_bsel); + + *bus_bsel = (*bsel_alloc)++; + object_property_add_uint32_ptr(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, + bus_bsel, &error_abort); + } + + return bsel_alloc; +} + +static void acpi_set_pci_info(void) +{ + static bool bsel_is_set; + PCIBus *bus; + unsigned bsel_alloc = ACPI_PCIHP_BSEL_DEFAULT; + + if (bsel_is_set) { + return; + } + bsel_is_set = true; + + bus = find_i440fx(); /* TODO: Q35 support */ + if (bus) { + /* Scan all PCI buses. Set property to enable acpi based hotplug. */ + pci_for_each_bus_depth_first(bus, acpi_set_bsel, NULL, &bsel_alloc); + } +} + static void acpi_pcihp_test_hotplug_bus(PCIBus *bus, void *opaque) { AcpiPciHpFind *find = opaque; @@ -119,10 +147,13 @@ static bool acpi_pcihp_pc_no_hotplug(AcpiPciHpState *s, PCIDevice *dev) static void acpi_pcihp_eject_slot(AcpiPciHpState *s, unsigned bsel, unsigned slots) { + HotplugHandler *hotplug_ctrl; BusChild *kid, *next; - int slot = ffs(slots) - 1; + int slot = ctz32(slots); PCIBus *bus = acpi_pcihp_find_hotplug_bus(s, bsel); + trace_acpi_pci_eject_slot(bsel, slot); + if (!bus) { return; } @@ -136,6 +167,8 @@ static void acpi_pcihp_eject_slot(AcpiPciHpState *s, unsigned bsel, unsigned slo PCIDevice *dev = PCI_DEVICE(qdev); if (PCI_SLOT(dev->devfn) == slot) { if (!acpi_pcihp_pc_no_hotplug(s, dev)) { + hotplug_ctrl = qdev_get_hotplug_handler(qdev); + hotplug_handler_unplug(hotplug_ctrl, qdev, &error_abort); object_unparent(OBJECT(qdev)); } } @@ -179,40 +212,74 @@ static void acpi_pcihp_update(AcpiPciHpState *s) void acpi_pcihp_reset(AcpiPciHpState *s) { + acpi_set_pci_info(); acpi_pcihp_update(s); } -void acpi_pcihp_device_plug_cb(ACPIREGS *ar, qemu_irq irq, AcpiPciHpState *s, - DeviceState *dev, Error **errp) +void acpi_pcihp_device_pre_plug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { - PCIDevice *pdev = PCI_DEVICE(dev); - int slot = PCI_SLOT(pdev->devfn); - int bsel = acpi_pcihp_get_bsel(pdev->bus); - if (bsel < 0) { + /* Only hotplugged devices need the hotplug capability. */ + if (dev->hotplugged && + acpi_pcihp_get_bsel(pci_get_bus(PCI_DEVICE(dev))) < 0) { error_setg(errp, "Unsupported bus. Bus doesn't have property '" ACPI_PCIHP_PROP_BSEL "' set"); return; } +} + +void acpi_pcihp_device_plug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s, + DeviceState *dev, Error **errp) +{ + PCIDevice *pdev = PCI_DEVICE(dev); + int slot = PCI_SLOT(pdev->devfn); + int bsel; /* 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 (!dev->hotplugged) { + /* + * Overwrite the default hotplug handler with the ACPI PCI one + * for cold plugged bridges only. + */ + if (!s->legacy_piix && + object_dynamic_cast(OBJECT(dev), TYPE_PCI_BRIDGE)) { + PCIBus *sec = pci_bridge_get_sec_bus(PCI_BRIDGE(pdev)); + + qbus_set_hotplug_handler(BUS(sec), OBJECT(hotplug_dev), + &error_abort); + /* We don't have to overwrite any other hotplug handler yet */ + assert(QLIST_EMPTY(&sec->child)); + } + return; } + bsel = acpi_pcihp_get_bsel(pci_get_bus(pdev)); + g_assert(bsel >= 0); s->acpi_pcihp_pci_status[bsel].up |= (1U << slot); - - ar->gpe.sts[0] |= ACPI_PCI_HOTPLUG_STATUS; - acpi_update_sci(ar, irq); + acpi_send_event(DEVICE(hotplug_dev), ACPI_PCI_HOTPLUG_STATUS); } -void acpi_pcihp_device_unplug_cb(ACPIREGS *ar, qemu_irq irq, AcpiPciHpState *s, +void acpi_pcihp_device_unplug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s, DeviceState *dev, Error **errp) +{ + trace_acpi_pci_unplug(PCI_SLOT(PCI_DEVICE(dev)->devfn), + acpi_pcihp_get_bsel(pci_get_bus(PCI_DEVICE(dev)))); + object_property_set_bool(OBJECT(dev), false, "realized", NULL); +} + +void acpi_pcihp_device_unplug_request_cb(HotplugHandler *hotplug_dev, + AcpiPciHpState *s, DeviceState *dev, + Error **errp) { PCIDevice *pdev = PCI_DEVICE(dev); int slot = PCI_SLOT(pdev->devfn); - int bsel = acpi_pcihp_get_bsel(pdev->bus); + int bsel = acpi_pcihp_get_bsel(pci_get_bus(pdev)); + + trace_acpi_pci_unplug_request(bsel, slot); + if (bsel < 0) { error_setg(errp, "Unsupported bus. Bus doesn't have property '" ACPI_PCIHP_PROP_BSEL "' set"); @@ -220,9 +287,7 @@ void acpi_pcihp_device_unplug_cb(ACPIREGS *ar, qemu_irq irq, AcpiPciHpState *s, } s->acpi_pcihp_pci_status[bsel].down |= (1U << slot); - - ar->gpe.sts[0] |= ACPI_PCI_HOTPLUG_STATUS; - acpi_update_sci(ar, irq); + acpi_send_event(DEVICE(hotplug_dev), ACPI_PCI_HOTPLUG_STATUS); } static uint64_t pci_read(void *opaque, hwaddr addr, unsigned int size) @@ -241,23 +306,23 @@ static uint64_t pci_read(void *opaque, hwaddr addr, unsigned int size) if (!s->legacy_piix) { s->acpi_pcihp_pci_status[bsel].up = 0; } - ACPI_PCIHP_DPRINTF("pci_up_read %" PRIu32 "\n", val); + trace_acpi_pci_up_read(val); break; case PCI_DOWN_BASE: val = s->acpi_pcihp_pci_status[bsel].down; - ACPI_PCIHP_DPRINTF("pci_down_read %" PRIu32 "\n", val); + trace_acpi_pci_down_read(val); break; case PCI_EJ_BASE: /* No feature defined yet */ - ACPI_PCIHP_DPRINTF("pci_features_read %" PRIu32 "\n", val); + trace_acpi_pci_features_read(val); break; case PCI_RMV_BASE: val = s->acpi_pcihp_pci_status[bsel].hotplug_enable; - ACPI_PCIHP_DPRINTF("pci_rmv_read %" PRIu32 "\n", val); + trace_acpi_pci_rmv_read(val); break; case PCI_SEL_BASE: val = s->hotplug_select; - ACPI_PCIHP_DPRINTF("pci_sel_read %" PRIu32 "\n", val); + trace_acpi_pci_sel_read(val); default: break; } @@ -275,13 +340,11 @@ static void pci_write(void *opaque, hwaddr addr, uint64_t data, break; } acpi_pcihp_eject_slot(s, s->hotplug_select, data); - ACPI_PCIHP_DPRINTF("pciej write %" HWADDR_PRIx " <== %" PRIu64 "\n", - addr, data); + trace_acpi_pci_ej_write(addr, data); break; case PCI_SEL_BASE: - s->hotplug_select = data; - ACPI_PCIHP_DPRINTF("pcisel write %" HWADDR_PRIx " <== %" PRIu64 "\n", - addr, data); + s->hotplug_select = s->legacy_piix ? ACPI_PCIHP_BSEL_DEFAULT : data; + trace_acpi_pci_sel_write(addr, data); default: break; } @@ -306,16 +369,6 @@ void acpi_pcihp_init(Object *owner, AcpiPciHpState *s, PCIBus *root_bus, s->root= root_bus; s->legacy_piix = !bridges_enabled; - if (s->legacy_piix) { - unsigned *bus_bsel = g_malloc(sizeof *bus_bsel); - - s->io_len = ACPI_PCIHP_LEGACY_SIZE; - - *bus_bsel = ACPI_PCIHP_BSEL_DEFAULT; - object_property_add_uint32_ptr(OBJECT(root_bus), ACPI_PCIHP_PROP_BSEL, - bus_bsel, NULL); - } - memory_region_init_io(&s->io, owner, &acpi_pcihp_io_ops, s, "acpi-pci-hotplug", s->io_len); memory_region_add_subregion(address_space_io, s->io_base, &s->io);