* 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-host/i440fx.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 "migration/vmstate.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
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);
}
}
}
+/* 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;
static void acpi_pcihp_eject_slot(AcpiPciHpState *s, unsigned bsel, unsigned slots)
{
+ HotplugHandler *hotplug_ctrl;
BusChild *kid, *next;
int slot = ctz32(slots);
PCIBus *bus = acpi_pcihp_find_hotplug_bus(s, bsel);
+ trace_acpi_pci_eject_slot(bsel, slot);
+
if (!bus) {
return;
}
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));
}
}
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");
}
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)
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;
}
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;
}
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);