qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v3 0/4] acpi hotplug of devices behind a pci bridge
@ 2013-10-07  9:21 Michael S. Tsirkin
  2013-10-07  9:21 ` [Qemu-devel] [PATCH v3 1/4] pci: add pci_for_each_bus_depth_first Michael S. Tsirkin
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Michael S. Tsirkin @ 2013-10-07  9:21 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Gerd Hoffmann, Anthony Liguori, Igor Mammedov,
	Laszlo Ersek, afaerber

This is on top of patchset generating ACPI tables in qemu.
Please review and consider for 1.7.

Changes from v2:
    - split new code out to a separate file: acpi/pcihp.c
      this way we can reuse it for q35 later
    - rebased on top of the latest acpi-build patchset

Changes from v1:
    - fixed some bugs
    - fixed migration and cross version compatibility
    - rebased to latest bits

This adds support for device hotplug behind pci bridges, by generating ACPI
tables describing the current hierarchy.
Bridge devices themselves need to be pre-configured on qemu command line.

How to use:
Describe buses on command line. E.g.:
    -device pci-bridge,id=bar,chassis_nr=1 -device
    virtio-net,netdev=bur,addr=0x1,id=far,bus=bar
use monitor command for hotplug:
    device_del far

We don't currently use QOM properties to generate this info since:
    - info about hotpluggability of devices is lacking
    - we need to list all devices and buses in a predictable order
        For now, add a pci core function to do the above predictable order
        walk.

This is on top of acpi patchset posted previously.
with a small patch adding a core function to walk all
pci buses, on top.
Can also be found in my git tree
    git://git.kernel.org/pub/scm/virt/kvm/mst/qemu.git acpi-bridges

Portability:
    - Non x86 (or any Linux) platforms don't need any of this code.
      They can keep happily using SHPC the way
      they always did.

Things to note:

    - Extensive use of glib completely removes
      pointer math: we use
      g_array_append_vals exclusively.

    - As was the case previously,
      systems that lack working iasl are detected at configure time,
      pre-generated hex files in source tree are used in this case.
      This addresses the concern with iasl/big-endian
      systems.

    - Cross version migration: when running with -M 1.6
      and older, new hotplug functionality is unavailable.
      All ACPI table generation is disabled.
      We present FW_CFG interface compatible with 1.6.

Michael S. Tsirkin (4):
  pci: add pci_for_each_bus_depth_first
  pcihp: generalization of piix4 acpi
  piix4: add acpi pci hotplug support
  acpi-build: enable hotplug for PCI bridges

 include/hw/acpi/pcihp.h |  72 ++++++++++
 include/hw/i386/pc.h    |   5 +
 include/hw/pci/pci.h    |  14 ++
 hw/acpi/pcihp.c         | 312 +++++++++++++++++++++++++++++++++++++++++++
 hw/acpi/piix4.c         |  75 +++++++++--
 hw/i386/acpi-build.c    | 346 ++++++++++++++++++++++++++++++++++++++----------
 hw/pci/pci.c            |  28 ++++
 hw/acpi/Makefile.objs   |   2 +-
 hw/i386/acpi-dsdt.dsl   |  34 +++--
 hw/i386/ssdt-pcihp.dsl  |  11 +-
 10 files changed, 794 insertions(+), 105 deletions(-)
 create mode 100644 include/hw/acpi/pcihp.h
 create mode 100644 hw/acpi/pcihp.c

-- 
MST

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [Qemu-devel] [PATCH v3 1/4] pci: add pci_for_each_bus_depth_first
  2013-10-07  9:21 [Qemu-devel] [PATCH v3 0/4] acpi hotplug of devices behind a pci bridge Michael S. Tsirkin
@ 2013-10-07  9:21 ` Michael S. Tsirkin
  2013-10-07  9:21 ` [Qemu-devel] [PATCH v3 2/4] pcihp: generalization of piix4 acpi Michael S. Tsirkin
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Michael S. Tsirkin @ 2013-10-07  9:21 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Gerd Hoffmann, Anthony Liguori, Igor Mammedov,
	Laszlo Ersek, afaerber

Add a method to scan pci buses in a predictable order.
Useful for ACPI hotplug.
Document that order is not guaranteed for pci_for_each_device,
and re-implement using that.

Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
---
 include/hw/pci/pci.h | 14 ++++++++++++++
 hw/pci/pci.c         | 28 ++++++++++++++++++++++++++++
 2 files changed, 42 insertions(+)

diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
index 4b90e5d..6259e23 100644
--- a/include/hw/pci/pci.h
+++ b/include/hw/pci/pci.h
@@ -391,6 +391,20 @@ int pci_bus_num(PCIBus *s);
 void pci_for_each_device(PCIBus *bus, int bus_num,
                          void (*fn)(PCIBus *bus, PCIDevice *d, void *opaque),
                          void *opaque);
+void pci_for_each_bus_depth_first(PCIBus *bus,
+                                  void *(*begin)(PCIBus *bus, void *parent_state),
+                                  void (*end)(PCIBus *bus, void *state),
+                                  void *parent_state);
+
+/* Use this wrapper when specific scan order is not required. */
+static inline
+void pci_for_each_bus(PCIBus *bus,
+                      void (*fn)(PCIBus *bus, void *opaque),
+                      void *opaque)
+{
+    pci_for_each_bus_depth_first(bus, NULL, fn, opaque);
+}
+
 PCIBus *pci_find_primary_bus(void);
 PCIBus *pci_device_root_bus(const PCIDevice *d);
 const char *pci_root_bus_path(PCIDevice *dev);
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index c3fdff4..728f7b9 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -1682,6 +1682,34 @@ static PCIBus *pci_find_bus_nr(PCIBus *bus, int bus_num)
     return NULL;
 }
 
+void pci_for_each_bus_depth_first(PCIBus *bus,
+                                  void *(*begin)(PCIBus *bus, void *parent_state),
+                                  void (*end)(PCIBus *bus, void *state),
+                                  void *parent_state)
+{
+    PCIBus *sec;
+    void *state;
+
+    if (!bus) {
+        return;
+    }
+
+    if (begin) {
+        state = begin(bus, parent_state);
+    } else {
+        state = parent_state;
+    }
+
+    QLIST_FOREACH(sec, &bus->child, sibling) {
+        pci_for_each_bus_depth_first(sec, begin, end, state);
+    }
+
+    if (end) {
+        end(bus, state);
+    }
+}
+
+
 PCIDevice *pci_find_device(PCIBus *bus, int bus_num, uint8_t devfn)
 {
     bus = pci_find_bus_nr(bus, bus_num);
-- 
MST

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [Qemu-devel] [PATCH v3 2/4] pcihp: generalization of piix4 acpi
  2013-10-07  9:21 [Qemu-devel] [PATCH v3 0/4] acpi hotplug of devices behind a pci bridge Michael S. Tsirkin
  2013-10-07  9:21 ` [Qemu-devel] [PATCH v3 1/4] pci: add pci_for_each_bus_depth_first Michael S. Tsirkin
@ 2013-10-07  9:21 ` Michael S. Tsirkin
  2013-10-07  9:21 ` [Qemu-devel] [PATCH v3 3/4] piix4: add acpi pci hotplug support Michael S. Tsirkin
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Michael S. Tsirkin @ 2013-10-07  9:21 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Gerd Hoffmann, Anthony Liguori, Igor Mammedov,
	Laszlo Ersek, afaerber

Add ACPI based PCI hotplug library with bridge hotplug
support.
Design
   - each bus gets assigned "bsel" property.
   - ACPI code writes this number
     to a new BNUM register, then uses existing
     UP/DOWN registers to probe slot status;
     to eject, write number to BNUM register,
     then slot into existing EJ.

The interface is actually backwards-compatible with
existing PIIX4 ACPI (though not migration compatible).

This is split out from PIIX4 codebase so we can
reuse it for Q35 as well.

Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
---
 include/hw/acpi/pcihp.h |  72 +++++++++++
 hw/acpi/pcihp.c         | 312 ++++++++++++++++++++++++++++++++++++++++++++++++
 hw/acpi/Makefile.objs   |   2 +-
 3 files changed, 385 insertions(+), 1 deletion(-)
 create mode 100644 include/hw/acpi/pcihp.h
 create mode 100644 hw/acpi/pcihp.c

diff --git a/include/hw/acpi/pcihp.h b/include/hw/acpi/pcihp.h
new file mode 100644
index 0000000..6230e60
--- /dev/null
+++ b/include/hw/acpi/pcihp.h
@@ -0,0 +1,72 @@
+/*
+ * QEMU<->ACPI BIOS PCI hotplug interface
+ *
+ * QEMU supports PCI hotplug via ACPI. This module
+ * implements the interface between QEMU and the ACPI BIOS.
+ * Interface specification - see docs/specs/acpi_pci_hotplug.txt
+ *
+ * Copyright (c) 2013, Red Hat Inc, Michael S. Tsirkin (mst@redhat.com)
+ * 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.
+ */
+
+#ifndef HW_ACPI_PCIHP_H
+#define HW_ACPI_PCIHP_H
+
+#include <inttypes.h>
+#include <qemu/typedefs.h>
+#include "hw/pci/pci.h" /* for PCIHotplugState */
+
+typedef struct AcpiPciHpPciStatus {
+    uint32_t up; /* deprecated, maintained for migration compatibility */
+    uint32_t down;
+    uint32_t hotplug_enable;
+    uint32_t device_present;
+} AcpiPciHpPciStatus;
+
+#define ACPI_PCIHP_PROP_BSEL "acpi-pcihp-bsel"
+#define ACPI_PCIHP_MAX_HOTPLUG_BUS 256
+
+typedef struct AcpiPciHpState {
+    AcpiPciHpPciStatus acpi_pcihp_pci_status[ACPI_PCIHP_MAX_HOTPLUG_BUS];
+    uint32_t hotplug_select;
+    PCIBus *root;
+    MemoryRegion io;
+} AcpiPciHpState;
+
+void acpi_pcihp_init(AcpiPciHpState *, PCIBus *root,
+                     MemoryRegion *address_space_io);
+
+/* Invoke on device hotplug */
+int acpi_pcihp_device_hotplug(AcpiPciHpState *, PCIDevice *,
+                              PCIHotplugState state);
+
+/* Called on reset */
+void acpi_pcihp_reset(AcpiPciHpState *s);
+
+extern const VMStateDescription vmstate_acpi_pcihp_pci_status;
+
+#define VMSTATE_PCI_HOTPLUG(pcihp, state, test_pcihp) \
+        VMSTATE_UINT32_TEST(pcihp.hotplug_select, state, \
+                            test_pcihp), \
+        VMSTATE_STRUCT_ARRAY_TEST(pcihp.acpi_pcihp_pci_status, state, \
+                                  ACPI_PCIHP_MAX_HOTPLUG_BUS, \
+                                  test_pcihp, 1, \
+                                  vmstate_acpi_pcihp_pci_status, \
+                                  AcpiPciHpPciStatus)
+
+#endif
diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c
new file mode 100644
index 0000000..13938e6
--- /dev/null
+++ b/hw/acpi/pcihp.c
@@ -0,0 +1,312 @@
+/*
+ * QEMU<->ACPI BIOS PCI hotplug interface
+ *
+ * QEMU supports PCI hotplug via ACPI. This module
+ * implements the interface between QEMU and the ACPI BIOS.
+ * Interface specification - see docs/specs/acpi_pci_hotplug.txt
+ *
+ * Copyright (c) 2013, Red Hat Inc, Michael S. Tsirkin (mst@redhat.com)
+ * 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/acpi/pcihp.h"
+
+#include "hw/hw.h"
+#include "hw/i386/pc.h"
+#include "hw/pci/pci.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 "qom/qom-qobject.h"
+#include "qapi/qmp/qint.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 PCI_HOTPLUG_ADDR 0xae00
+#define PCI_HOTPLUG_SIZE 0x0014
+#define PCI_UP_BASE 0xae00
+#define PCI_DOWN_BASE 0xae04
+#define PCI_EJ_BASE 0xae08
+#define PCI_RMV_BASE 0xae0c
+#define PCI_SEL_BASE 0xae10
+
+typedef struct AcpiPciHpFind {
+    int bsel;
+    PCIBus *bus;
+} AcpiPciHpFind;
+
+static int acpi_pcihp_get_bsel(PCIBus *bus)
+{
+    QObject *o = object_property_get_qobject(OBJECT(bus),
+                                             ACPI_PCIHP_PROP_BSEL, NULL);
+    int64_t bsel = -1;
+    if (o) {
+        bsel = qint_get_int(qobject_to_qint(o));
+    }
+    if (bsel < 0) {
+        return -1;
+    }
+    return bsel;
+}
+
+static void acpi_pcihp_test_hotplug_bus(PCIBus *bus, void *opaque)
+{
+    AcpiPciHpFind *find = opaque;
+    if (find->bsel == acpi_pcihp_get_bsel(bus)) {
+        find->bus = bus;
+    }
+}
+
+static PCIBus *acpi_pcihp_find_hotplug_bus(AcpiPciHpState *s, int bsel)
+{
+    AcpiPciHpFind find = { .bsel = bsel, .bus = NULL };
+
+    if (bsel < 0) {
+        return NULL;
+    }
+
+    pci_for_each_bus(s->root, acpi_pcihp_test_hotplug_bus, &find);
+
+    if (!bsel && !find.bus) {
+        find.bus = s->root;
+    }
+    return find.bus;
+}
+
+static bool acpi_pcihp_pc_no_hotplug(AcpiPciHpState *s, PCIDevice *dev)
+{
+    PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev);
+    /*
+     * ACPI doesn't allow hotplug of bridge devices.  Don't allow
+     * hot-unplug of bridge devices unless they were added by hotplug
+     * (and so, not described by acpi).
+     */
+    return (pc->is_bridge && !dev->qdev.hotplugged) || pc->no_hotplug;
+}
+
+static void acpi_pcihp_eject_slot(AcpiPciHpState *s, unsigned bsel, unsigned slots)
+{
+    BusChild *kid, *next;
+    int slot = ffs(slots) - 1;
+    bool slot_free = true;
+    PCIBus *bus = acpi_pcihp_find_hotplug_bus(s, bsel);
+
+    if (!bus) {
+        return;
+    }
+
+    /* Mark request as complete */
+    s->acpi_pcihp_pci_status[bsel].down &= ~(1U << slot);
+
+    QTAILQ_FOREACH_SAFE(kid, &bus->qbus.children, sibling, next) {
+        DeviceState *qdev = kid->child;
+        PCIDevice *dev = PCI_DEVICE(qdev);
+        if (PCI_SLOT(dev->devfn) == slot) {
+            if (acpi_pcihp_pc_no_hotplug(s, dev)) {
+                slot_free = false;
+            } else {
+                qdev_free(qdev);
+            }
+        }
+    }
+    if (slot_free) {
+        s->acpi_pcihp_pci_status[bsel].device_present &= ~(1U << slot);
+    }
+}
+
+static void acpi_pcihp_update_hotplug_bus(AcpiPciHpState *s, int bsel)
+{
+    BusChild *kid, *next;
+    PCIBus *bus = acpi_pcihp_find_hotplug_bus(s, bsel);
+
+    /* Execute any pending removes during reset */
+    while (s->acpi_pcihp_pci_status[bsel].down) {
+        acpi_pcihp_eject_slot(s, bsel, s->acpi_pcihp_pci_status[bsel].down);
+    }
+
+    s->acpi_pcihp_pci_status[bsel].hotplug_enable = ~0;
+    s->acpi_pcihp_pci_status[bsel].device_present = 0;
+
+    if (!bus) {
+        return;
+    }
+    QTAILQ_FOREACH_SAFE(kid, &bus->qbus.children, sibling, next) {
+        DeviceState *qdev = kid->child;
+        PCIDevice *pdev = PCI_DEVICE(qdev);
+        int slot = PCI_SLOT(pdev->devfn);
+
+        if (acpi_pcihp_pc_no_hotplug(s, pdev)) {
+            s->acpi_pcihp_pci_status[bsel].hotplug_enable &= ~(1U << slot);
+        }
+
+        s->acpi_pcihp_pci_status[bsel].device_present |= (1U << slot);
+    }
+}
+
+static void acpi_pcihp_update(AcpiPciHpState *s)
+{
+    int i;
+
+    for (i = 0; i < ACPI_PCIHP_MAX_HOTPLUG_BUS; ++i) {
+        acpi_pcihp_update_hotplug_bus(s, i);
+    }
+}
+
+void acpi_pcihp_reset(AcpiPciHpState *s)
+{
+    acpi_pcihp_update(s);
+}
+
+static void enable_device(AcpiPciHpState *s, unsigned bsel, int slot)
+{
+    s->acpi_pcihp_pci_status[bsel].device_present |= (1U << slot);
+}
+
+static void disable_device(AcpiPciHpState *s, unsigned bsel, int slot)
+{
+    s->acpi_pcihp_pci_status[bsel].down |= (1U << slot);
+}
+
+int acpi_pcihp_device_hotplug(AcpiPciHpState *s, PCIDevice *dev,
+                              PCIHotplugState state)
+{
+    int slot = PCI_SLOT(dev->devfn);
+    int bsel = acpi_pcihp_get_bsel(dev->bus);
+    if (bsel < 0) {
+        return -1;
+    }
+
+    /* 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->acpi_pcihp_pci_status[bsel].device_present |= (1U << slot);
+        return 0;
+    }
+
+    if (state == PCI_HOTPLUG_ENABLED) {
+        enable_device(s, bsel, slot);
+    } else {
+        disable_device(s, bsel, slot);
+    }
+
+    return 0;
+}
+
+static uint64_t pci_read(void *opaque, hwaddr addr, unsigned int size)
+{
+    AcpiPciHpState *s = opaque;
+    uint32_t val = 0;
+    int bsel = s->hotplug_select;
+
+    if (bsel < 0 || bsel > ACPI_PCIHP_MAX_HOTPLUG_BUS) {
+        return 0;
+    }
+
+    switch (addr) {
+    case PCI_UP_BASE - PCI_HOTPLUG_ADDR:
+        /* Manufacture an "up" value to cause a device check on any hotplug
+         * slot with a device.  Extra device checks are harmless. */
+        val = s->acpi_pcihp_pci_status[bsel].device_present &
+            s->acpi_pcihp_pci_status[bsel].hotplug_enable;
+        ACPI_PCIHP_DPRINTF("pci_up_read %" PRIu32 "\n", val);
+        break;
+    case PCI_DOWN_BASE - PCI_HOTPLUG_ADDR:
+        val = s->acpi_pcihp_pci_status[bsel].down;
+        ACPI_PCIHP_DPRINTF("pci_down_read %" PRIu32 "\n", val);
+        break;
+    case PCI_EJ_BASE - PCI_HOTPLUG_ADDR:
+        /* No feature defined yet */
+        ACPI_PCIHP_DPRINTF("pci_features_read %" PRIu32 "\n", val);
+        break;
+    case PCI_RMV_BASE - PCI_HOTPLUG_ADDR:
+        val = s->acpi_pcihp_pci_status[bsel].hotplug_enable;
+        ACPI_PCIHP_DPRINTF("pci_rmv_read %" PRIu32 "\n", val);
+        break;
+    case PCI_SEL_BASE - PCI_HOTPLUG_ADDR:
+        val = s->hotplug_select;
+        ACPI_PCIHP_DPRINTF("pci_sel_read %" PRIu32 "\n", val);
+    default:
+        break;
+    }
+
+    return val;
+}
+
+static void pci_write(void *opaque, hwaddr addr, uint64_t data,
+                      unsigned int size)
+{
+    AcpiPciHpState *s = opaque;
+    switch (addr) {
+    case PCI_EJ_BASE - PCI_HOTPLUG_ADDR:
+        if (s->hotplug_select >= ACPI_PCIHP_MAX_HOTPLUG_BUS) {
+            break;
+        }
+        acpi_pcihp_eject_slot(s, s->hotplug_select, data);
+        ACPI_PCIHP_DPRINTF("pciej write %" HWADDR_PRIx " <== %" PRIu64 "\n",
+                      addr, data);
+        break;
+    case PCI_SEL_BASE - PCI_HOTPLUG_ADDR:
+        s->hotplug_select = data;
+        ACPI_PCIHP_DPRINTF("pcisel write %" HWADDR_PRIx " <== %" PRIu64 "\n",
+                      addr, data);
+    default:
+        break;
+    }
+}
+
+static const MemoryRegionOps acpi_pcihp_io_ops = {
+    .read = pci_read,
+    .write = pci_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+};
+
+void acpi_pcihp_init(AcpiPciHpState *s, PCIBus *root_bus,
+                     MemoryRegion *address_space_io)
+{
+    s->root= root_bus;
+    memory_region_init_io(&s->io, NULL, &acpi_pcihp_io_ops, s,
+                          "acpi-pci-hotplug",
+                          PCI_HOTPLUG_SIZE);
+    memory_region_add_subregion(address_space_io, PCI_HOTPLUG_ADDR, &s->io);
+}
+
+const VMStateDescription vmstate_acpi_pcihp_pci_status = {
+    .name = "acpi_pcihp_pci_status",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT32(up, AcpiPciHpPciStatus),
+        VMSTATE_UINT32(down, AcpiPciHpPciStatus),
+        VMSTATE_END_OF_LIST()
+    }
+};
diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
index a0b63b5..0a47a53 100644
--- a/hw/acpi/Makefile.objs
+++ b/hw/acpi/Makefile.objs
@@ -1,2 +1,2 @@
-common-obj-$(CONFIG_ACPI) += core.o piix4.o ich9.o
+common-obj-$(CONFIG_ACPI) += core.o piix4.o ich9.o pcihp.o
 
-- 
MST

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [Qemu-devel] [PATCH v3 3/4] piix4: add acpi pci hotplug support
  2013-10-07  9:21 [Qemu-devel] [PATCH v3 0/4] acpi hotplug of devices behind a pci bridge Michael S. Tsirkin
  2013-10-07  9:21 ` [Qemu-devel] [PATCH v3 1/4] pci: add pci_for_each_bus_depth_first Michael S. Tsirkin
  2013-10-07  9:21 ` [Qemu-devel] [PATCH v3 2/4] pcihp: generalization of piix4 acpi Michael S. Tsirkin
@ 2013-10-07  9:21 ` Michael S. Tsirkin
  2013-10-07  9:21 ` [Qemu-devel] [PATCH v3 4/4] acpi-build: enable hotplug for PCI bridges Michael S. Tsirkin
  2013-10-13 20:51 ` [Qemu-devel] [PATCH v3 0/4] acpi hotplug of devices behind a pci bridge Michael S. Tsirkin
  4 siblings, 0 replies; 6+ messages in thread
From: Michael S. Tsirkin @ 2013-10-07  9:21 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Gerd Hoffmann, Anthony Liguori, Igor Mammedov,
	Laszlo Ersek, afaerber

Add support for acpi pci hotplug using the
new infrastructure.
PIIX4 legacy interface is maintained as is for
machine types 1.6 and older.

Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
---
 include/hw/i386/pc.h |  5 ++++
 hw/acpi/piix4.c      | 75 +++++++++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 70 insertions(+), 10 deletions(-)

diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index 39db8cb..6865972 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -249,6 +249,11 @@ int e820_add_entry(uint64_t, uint64_t, uint32_t);
 
 #define PC_COMPAT_1_6 \
         {\
+            .driver   = "PIIX4_PM",\
+            .property = "acpi-pci-hotplug-with-bridge-support",\
+            .value    = "off",\
+        }, \
+        {\
             .driver   = "e1000",\
             .property = "mitigation",\
             .value    = "off",\
diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c
index 3bcd890..d516033 100644
--- a/hw/acpi/piix4.c
+++ b/hw/acpi/piix4.c
@@ -30,6 +30,7 @@
 #include "hw/nvram/fw_cfg.h"
 #include "exec/address-spaces.h"
 #include "hw/acpi/piix4.h"
+#include "hw/acpi/pcihp.h"
 
 //#define DEBUG
 
@@ -73,7 +74,6 @@ typedef struct PIIX4PMState {
     uint32_t io_base;
 
     MemoryRegion io_gpe;
-    MemoryRegion io_pci;
     MemoryRegion io_cpu;
     ACPIREGS ar;
 
@@ -88,11 +88,16 @@ typedef struct PIIX4PMState {
     Notifier machine_ready;
     Notifier powerdown_notifier;
 
-    /* for pci hotplug */
+    /* for legacy pci hotplug (compatible with qemu 1.6 and older) */
+    MemoryRegion io_pci;
     struct pci_status pci0_status;
     uint32_t pci0_hotplug_enable;
     uint32_t pci0_slot_device_present;
 
+    /* for new pci hotplug (with PCI2PCI bridge support) */
+    AcpiPciHpState acpi_pci_hotplug;
+    bool use_acpi_pci_hotplug;
+
     uint8_t disable_s3;
     uint8_t disable_s4;
     uint8_t s4_val;
@@ -282,6 +287,18 @@ static int acpi_load_old(QEMUFile *f, void *opaque, int version_id)
     return ret;
 }
 
+static bool vmstate_test_use_acpi_pci_hotplug(void *opaque, int version_id)
+{
+    PIIX4PMState *s = opaque;
+    return s->use_acpi_pci_hotplug;
+}
+
+static bool vmstate_test_no_use_acpi_pci_hotplug(void *opaque, int version_id)
+{
+    PIIX4PMState *s = opaque;
+    return !s->use_acpi_pci_hotplug;
+}
+
 /* qemu-kvm 1.2 uses version 3 but advertised as 2
  * To support incoming qemu-kvm 1.2 migration, change version_id
  * and minimum_version_id to 2 below (which breaks migration from
@@ -304,8 +321,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,
-                       struct pci_status),
+        VMSTATE_STRUCT_TEST(pci0_status, PIIX4PMState,
+                            vmstate_test_no_use_acpi_pci_hotplug,
+                            2, vmstate_pci_status,
+                            struct pci_status),
+        VMSTATE_PCI_HOTPLUG(acpi_pci_hotplug, PIIX4PMState,
+                            vmstate_test_use_acpi_pci_hotplug),
         VMSTATE_END_OF_LIST()
     }
 };
@@ -383,7 +404,11 @@ static void piix4_reset(void *opaque)
         pci_conf[0x5B] = 0x02;
     }
     pm_io_space_update(s);
-    piix4_update_hotplug(s);
+    if (s->use_acpi_pci_hotplug) {
+        acpi_pcihp_reset(&s->acpi_pci_hotplug);
+    } else {
+        piix4_update_hotplug(s);
+    }
 }
 
 static void piix4_pm_powerdown_req(Notifier *n, void *opaque)
@@ -394,6 +419,26 @@ static void piix4_pm_powerdown_req(Notifier *n, void *opaque)
     acpi_pm1_evt_power_down(&s->ar);
 }
 
+static int piix4_acpi_pci_hotplug(DeviceState *qdev, PCIDevice *dev,
+                                  PCIHotplugState state)
+{
+    PIIX4PMState *s = PIIX4_PM(qdev);
+    int ret = acpi_pcihp_device_hotplug(&s->acpi_pci_hotplug, dev, state);
+    if (ret < 0) {
+        return ret;
+    }
+    s->ar.gpe.sts[0] |= PIIX4_PCI_HOTPLUG_STATUS;
+
+    pm_update_sci(s);
+    return 0;
+}
+
+static void piix4_update_bus_hotplug(PCIBus *bus, void *opaque)
+{
+    PIIX4PMState *s = opaque;
+    pci_bus_hotplug(bus, piix4_acpi_pci_hotplug, DEVICE(s));
+}
+
 static void piix4_pm_machine_ready(Notifier *n, void *opaque)
 {
     PIIX4PMState *s = container_of(n, PIIX4PMState, machine_ready);
@@ -407,6 +452,10 @@ static void piix4_pm_machine_ready(Notifier *n, void *opaque)
     pci_conf[0x63] = 0x60;
     pci_conf[0x67] = (memory_region_present(io_as, 0x3f8) ? 0x08 : 0) |
         (memory_region_present(io_as, 0x2f8) ? 0x90 : 0);
+
+    if (s->use_acpi_pci_hotplug) {
+        pci_for_each_bus(d->bus, piix4_update_bus_hotplug, s);
+    }
 }
 
 static void piix4_pm_add_propeties(PIIX4PMState *s)
@@ -528,6 +577,8 @@ static Property piix4_pm_properties[] = {
     DEFINE_PROP_UINT8(ACPI_PM_PROP_S3_DISABLED, PIIX4PMState, disable_s3, 0),
     DEFINE_PROP_UINT8(ACPI_PM_PROP_S4_DISABLED, PIIX4PMState, disable_s4, 0),
     DEFINE_PROP_UINT8(ACPI_PM_PROP_S4_VAL, PIIX4PMState, s4_val, 2),
+    DEFINE_PROP_BOOL("acpi-pci-hotplug-with-bridge-support", PIIX4PMState,
+                     use_acpi_pci_hotplug, true),
     DEFINE_PROP_END_OF_LIST(),
 };
 
@@ -716,11 +767,15 @@ static void piix4_acpi_system_hot_add_init(MemoryRegion *parent,
                           "acpi-gpe0", GPE_LEN);
     memory_region_add_subregion(parent, GPE_BASE, &s->io_gpe);
 
-    memory_region_init_io(&s->io_pci, OBJECT(s), &piix4_pci_ops, s,
-                          "acpi-pci-hotplug", PCI_HOTPLUG_SIZE);
-    memory_region_add_subregion(parent, PCI_HOTPLUG_ADDR,
-                                &s->io_pci);
-    pci_bus_hotplug(bus, piix4_device_hotplug, DEVICE(s));
+    if (s->use_acpi_pci_hotplug) {
+        acpi_pcihp_init(&s->acpi_pci_hotplug, bus, parent);
+    } else {
+        memory_region_init_io(&s->io_pci, OBJECT(s), &piix4_pci_ops, s,
+                              "acpi-pci-hotplug", PCI_HOTPLUG_SIZE);
+        memory_region_add_subregion(parent, PCI_HOTPLUG_ADDR,
+                                    &s->io_pci);
+        pci_bus_hotplug(bus, piix4_device_hotplug, DEVICE(s));
+    }
 
     CPU_FOREACH(cpu) {
         CPUClass *cc = CPU_GET_CLASS(cpu);
-- 
MST

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [Qemu-devel] [PATCH v3 4/4] acpi-build: enable hotplug for PCI bridges
  2013-10-07  9:21 [Qemu-devel] [PATCH v3 0/4] acpi hotplug of devices behind a pci bridge Michael S. Tsirkin
                   ` (2 preceding siblings ...)
  2013-10-07  9:21 ` [Qemu-devel] [PATCH v3 3/4] piix4: add acpi pci hotplug support Michael S. Tsirkin
@ 2013-10-07  9:21 ` Michael S. Tsirkin
  2013-10-13 20:51 ` [Qemu-devel] [PATCH v3 0/4] acpi hotplug of devices behind a pci bridge Michael S. Tsirkin
  4 siblings, 0 replies; 6+ messages in thread
From: Michael S. Tsirkin @ 2013-10-07  9:21 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Gerd Hoffmann, Anthony Liguori, Igor Mammedov,
	Laszlo Ersek, afaerber

This enables support for device hotplug behind
pci bridges. Bridge devices themselves need
to be pre-configured on qemu command line.

Design:
    - at machine init time, assign "bsel" property to bridges with
      hotplug support
    - dynamically (At ACPI table read) generate ACPI code to handle
      hotplug events for each bridge with "bsel" property

Note: ACPI doesn't support adding or removing bridges by hotplug.
We detect and prevent removal of bridges by hotplug,
unless they were added by hotplug previously
(and so, are not described by ACPI).

Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
---
 hw/i386/acpi-build.c   | 346 +++++++++++++++++++++++++++++++++++++++----------
 hw/i386/acpi-dsdt.dsl  |  34 +++--
 hw/i386/ssdt-pcihp.dsl |  11 +-
 3 files changed, 297 insertions(+), 94 deletions(-)

diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index 262d1d6..e8d39d9 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -39,6 +39,7 @@
 
 /* Supported chipsets: */
 #include "hw/acpi/piix4.h"
+#include "hw/acpi/pcihp.h"
 #include "hw/i386/ich9.h"
 #include "hw/pci/pci_bus.h"
 #include "hw/pci-host/q35.h"
@@ -78,6 +79,12 @@ typedef struct AcpiMiscInfo {
     uint16_t pvpanic_port;
 } AcpiMiscInfo;
 
+typedef struct AcpiBuildPciBusHotplugState {
+    GArray *device_table;
+    GArray *notify_table;
+    struct AcpiBuildPciBusHotplugState *parent;
+} AcpiBuildPciBusHotplugState;
+
 static void acpi_get_dsdt(AcpiMiscInfo *info)
 {
     Object *piix = piix4_pm_find();
@@ -171,38 +178,6 @@ static void acpi_get_pm_info(AcpiPmInfo *pm)
                                                NULL);
 }
 
-static void acpi_get_hotplug_info(AcpiMiscInfo *misc)
-{
-    int i;
-    PCIBus *bus = find_i440fx();
-
-    if (!bus) {
-        /* Only PIIX supports ACPI hotplug */
-        memset(misc->slot_hotplug_enable, 0, sizeof misc->slot_hotplug_enable);
-        return;
-    }
-
-    memset(misc->slot_hotplug_enable, 0xff,
-           DIV_ROUND_UP(PCI_SLOT_MAX, BITS_PER_BYTE));
-
-    for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) {
-        PCIDeviceClass *pc;
-        PCIDevice *pdev = bus->devices[i];
-
-        if (!pdev) {
-            continue;
-        }
-
-        pc = PCI_DEVICE_GET_CLASS(pdev);
-
-        if (pc->no_hotplug) {
-            int slot = PCI_SLOT(i);
-
-            clear_bit(slot, misc->slot_hotplug_enable);
-        }
-    }
-}
-
 static void acpi_get_misc_info(AcpiMiscInfo *info)
 {
     info->has_hpet = hpet_find();
@@ -366,6 +341,12 @@ static void build_package(GArray *package, uint8_t op, unsigned min_bytes)
     build_prepend_byte(package, op);
 }
 
+static void build_extop_package(GArray *package, uint8_t op)
+{
+    build_package(package, op, 1);
+    build_prepend_byte(package, 0x5B); /* ExtOpPrefix */
+}
+
 static void build_append_value(GArray *table, uint32_t value, int size)
 {
     uint8_t prefix;
@@ -392,8 +373,44 @@ static void build_append_value(GArray *table, uint32_t value, int size)
     }
 }
 
-static void build_append_notify_target(GArray *method, GArray *target_name,
-                                       uint32_t value, int size)
+static void build_append_int(GArray *table, uint32_t value)
+{
+    if (value == 0x00) {
+        build_append_byte(table, 0x00); /* ZeroOp */
+    } else if (value == 0x01) {
+        build_append_byte(table, 0x01); /* OneOp */
+    } else if (value <= 0xFF) {
+        build_append_value(table, value, 1);
+    } else if (value <= 0xFFFFF) {
+        build_append_value(table, value, 2);
+    } else {
+        build_append_value(table, value, 4);
+    }
+}
+
+static GArray *build_alloc_method(const char *name, uint8_t arg_count)
+{
+    GArray *method = build_alloc_array();
+
+    build_append_nameseg(method, name);
+    build_append_byte(method, arg_count); /* MethodFlags: ArgCount */
+
+    return method;
+}
+
+static void build_append_and_cleanup_method(GArray *device, GArray *method)
+{
+    uint8_t op = 0x14; /* MethodOp */
+
+    build_package(method, op, 0);
+
+    build_append_array(device, method);
+    build_free_array(method);
+}
+
+static void build_append_notify_target_ifequal(GArray *method,
+                                               GArray *target_name,
+                                               uint32_t value, int size)
 {
     GArray *notify = build_alloc_array();
     uint8_t op = 0xA0; /* IfOp */
@@ -413,6 +430,7 @@ static void build_append_notify_target(GArray *method, GArray *target_name,
     build_free_array(notify);
 }
 
+/* End here */
 #define ACPI_PORT_SMI_CMD           0x00b2 /* TODO: this is APM_CNT_IOPORT */
 
 static inline void *acpi_data_push(GArray *table_data, unsigned size)
@@ -621,44 +639,234 @@ static inline char acpi_get_hex(uint32_t val)
 #include "hw/i386/ssdt-pcihp.hex"
 
 static void
-build_append_notify(GArray *device, const char *name,
-                    const char *format, int skip, int count)
+build_append_notify_method(GArray *device, const char *name,
+                           const char *format, int count)
 {
     int i;
-    GArray *method = build_alloc_array();
-    uint8_t op = 0x14; /* MethodOp */
+    GArray *method = build_alloc_method(name, 2);
 
-    build_append_nameseg(method, name);
-    build_append_byte(method, 0x02); /* MethodFlags: ArgCount */
-    for (i = skip; i < count; i++) {
+    for (i = 0; i < count; i++) {
         GArray *target = build_alloc_array();
         build_append_nameseg(target, format, i);
         assert(i < 256); /* Fits in 1 byte */
-        build_append_notify_target(method, target, i, 1);
+        build_append_notify_target_ifequal(method, target, i, 1);
         build_free_array(target);
     }
-    build_package(method, op, 2);
 
-    build_append_array(device, method);
-    build_free_array(method);
+    build_append_and_cleanup_method(device, method);
 }
 
-static void patch_pcihp(int slot, uint8_t *ssdt_ptr, uint32_t eject)
+static void patch_pcihp(int slot, uint8_t *ssdt_ptr)
 {
-    ssdt_ptr[ACPI_PCIHP_OFFSET_HEX] = acpi_get_hex(slot >> 4);
-    ssdt_ptr[ACPI_PCIHP_OFFSET_HEX + 1] = acpi_get_hex(slot);
+    unsigned devfn = PCI_DEVFN(slot, 0);
+
+    ssdt_ptr[ACPI_PCIHP_OFFSET_HEX] = acpi_get_hex(devfn >> 4);
+    ssdt_ptr[ACPI_PCIHP_OFFSET_HEX + 1] = acpi_get_hex(devfn);
     ssdt_ptr[ACPI_PCIHP_OFFSET_ID] = slot;
     ssdt_ptr[ACPI_PCIHP_OFFSET_ADR + 2] = slot;
+}
+
+/* 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 = g_malloc(sizeof *bus_bsel);
+
+    *bus_bsel = (*bsel_alloc)++;
+    object_property_add_uint32_ptr(OBJECT(bus), ACPI_PCIHP_PROP_BSEL,
+                                   bus_bsel, NULL);
 
-    /* Runtime patching of ACPI_EJ0: to disable hotplug for a slot,
-     * replace the method name: _EJ0 by ACPI_EJ0_.
+    return bsel_alloc;
+}
+
+static void acpi_set_pci_info(void)
+{
+    PCIBus *bus = find_i440fx(); /* TODO: Q35 support */
+    unsigned bsel_alloc = 0;
+
+    if (bus) {
+        /* Scan all PCI buses. Generate tables to support
+         * PCI hotplug. Set property to enable acpi based hotplug.
+         */
+        pci_for_each_bus_depth_first(bus, acpi_set_bsel, NULL, &bsel_alloc);
+    }
+}
+
+static void build_pci_bus_state_init(AcpiBuildPciBusHotplugState *state,
+                                     AcpiBuildPciBusHotplugState *parent)
+{
+    state->parent = parent;
+    state->device_table = build_alloc_array();
+    state->notify_table = build_alloc_array();
+}
+
+static void build_pci_bus_state_cleanup(AcpiBuildPciBusHotplugState *state)
+{
+    build_free_array(state->device_table);
+    build_free_array(state->notify_table);
+}
+
+static void *build_pci_bus_begin(PCIBus *bus, void *parent_state)
+{
+    AcpiBuildPciBusHotplugState *parent = parent_state;
+    AcpiBuildPciBusHotplugState *child = g_malloc(sizeof *child);
+
+    build_pci_bus_state_init(child, parent);
+
+    return child;
+}
+
+static void build_pci_bus_end(PCIBus *bus, void *bus_state)
+{
+    AcpiBuildPciBusHotplugState *child = bus_state;
+    AcpiBuildPciBusHotplugState *parent = child->parent;
+    GArray *bus_table = build_alloc_array();
+    DECLARE_BITMAP(slot_hotplug_enable, PCI_SLOT_MAX);
+    uint8_t op;
+    int i;
+    QObject *bsel;
+    GArray *method;
+    bool bus_hotplug_support = false;
+
+    if (bus->parent_dev) {
+        op = 0x82; /* DeviceOp */
+        build_append_nameseg(bus_table, "S%0.02X_",
+                             bus->parent_dev->devfn);
+        build_append_byte(bus_table, 0x08); /* NameOp */
+        build_append_nameseg(bus_table, "_SUN");
+        build_append_value(bus_table, PCI_SLOT(bus->parent_dev->devfn), 1);
+        build_append_byte(bus_table, 0x08); /* NameOp */
+        build_append_nameseg(bus_table, "_ADR");
+        build_append_value(bus_table, (PCI_SLOT(bus->parent_dev->devfn) << 16) |
+                           PCI_FUNC(bus->parent_dev->devfn), 4);
+    } else {
+        op = 0x10; /* ScopeOp */;
+        build_append_nameseg(bus_table, "PCI0");
+    }
+
+    bsel = object_property_get_qobject(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, NULL);
+    if (bsel) {
+        build_append_byte(bus_table, 0x08); /* NameOp */
+        build_append_nameseg(bus_table, "BSEL");
+        build_append_int(bus_table, qint_get_int(qobject_to_qint(bsel)));
+
+        memset(slot_hotplug_enable, 0xff, sizeof slot_hotplug_enable);
+
+        for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) {
+            PCIDeviceClass *pc;
+            PCIDevice *pdev = bus->devices[i];
+
+            if (!pdev) {
+                continue;
+            }
+
+            pc = PCI_DEVICE_GET_CLASS(pdev);
+
+            if (pc->no_hotplug || pc->is_bridge) {
+                int slot = PCI_SLOT(i);
+
+                clear_bit(slot, slot_hotplug_enable);
+            }
+        }
+
+        /* Append Device object for each slot which supports eject */
+        for (i = 0; i < PCI_SLOT_MAX; i++) {
+            bool can_eject = test_bit(i, slot_hotplug_enable);
+            if (can_eject) {
+                void *pcihp = acpi_data_push(bus_table,
+                                             ACPI_PCIHP_SIZEOF);
+                memcpy(pcihp, ACPI_PCIHP_AML, ACPI_PCIHP_SIZEOF);
+                patch_pcihp(i, pcihp);
+                bus_hotplug_support = true;
+            }
+        }
+
+        method = build_alloc_method("DVNT", 2);
+
+        for (i = 0; i < PCI_SLOT_MAX; i++) {
+            GArray *notify;
+            uint8_t op;
+
+            if (!test_bit(i, slot_hotplug_enable)) {
+                continue;
+            }
+
+            notify = build_alloc_array();
+            op = 0xA0; /* IfOp */
+
+            build_append_byte(notify, 0x7B); /* AndOp */
+            build_append_byte(notify, 0x68); /* Arg0Op */
+            build_append_int(notify, 0x1 << i);
+            build_append_byte(notify, 0x00); /* NullName */
+            build_append_byte(notify, 0x86); /* NotifyOp */
+            build_append_nameseg(notify, "S%0.02X_", PCI_DEVFN(i, 0));
+            build_append_byte(notify, 0x69); /* Arg1Op */
+
+            /* Pack it up */
+            build_package(notify, op, 0);
+
+            build_append_array(method, notify);
+
+            build_free_array(notify);
+        }
+
+        build_append_and_cleanup_method(bus_table, method);
+    }
+
+    /* Append PCNT method to notify about events on local and child buses.
+     * Add unconditionally for root since DSDT expects it.
      */
-    /* Sanity check */
-    assert(!memcmp(ssdt_ptr + ACPI_PCIHP_OFFSET_EJ0, "_EJ0", 4));
+    if (bus_hotplug_support || child->notify_table->len || !bus->parent_dev) {
+        method = build_alloc_method("PCNT", 0);
+
+        /* If bus supports hotplug select it and notify about local events */
+        if (bsel) {
+            build_append_byte(method, 0x70); /* StoreOp */
+            build_append_int(method, qint_get_int(qobject_to_qint(bsel)));
+            build_append_nameseg(method, "BNUM");
+            build_append_nameseg(method, "DVNT");
+            build_append_nameseg(method, "PCIU");
+            build_append_int(method, 1); /* Device Check */
+            build_append_nameseg(method, "DVNT");
+            build_append_nameseg(method, "PCID");
+            build_append_int(method, 3); /* Eject Request */
+        }
+
+        /* Notify about child bus events in any case */
+        build_append_array(method, child->notify_table);
+
+        build_append_and_cleanup_method(bus_table, method);
+
+        /* Append description of child buses */
+        build_append_array(bus_table, child->device_table);
+
+        /* Pack it up */
+        if (bus->parent_dev) {
+            build_extop_package(bus_table, op);
+        } else {
+            build_package(bus_table, op, 0);
+        }
 
-    if (!eject) {
-        memcpy(ssdt_ptr + ACPI_PCIHP_OFFSET_EJ0, "EJ0_", 4);
+        /* Append our bus description to parent table */
+        build_append_array(parent->device_table, bus_table);
+
+        /* Also tell parent how to notify us, invoking PCNT method.
+         * At the moment this is not needed for root as we have a single root.
+         */
+        if (bus->parent_dev) {
+            build_append_byte(parent->notify_table, '^'); /* ParentPrefixChar */
+            build_append_byte(parent->notify_table, 0x2E); /* DualNamePrefix */
+            build_append_nameseg(parent->notify_table, "S%0.02X_",
+                                 bus->parent_dev->devfn);
+            build_append_nameseg(parent->notify_table, "PCNT");
+        }
     }
+
+    build_free_array(bus_table);
+    build_pci_bus_state_cleanup(child);
+    g_free(child);
 }
 
 static void patch_pci_windows(PcPciInfo *pci, uint8_t *start, unsigned size)
@@ -730,7 +938,7 @@ build_ssdt(GArray *table_data, GArray *linker,
          *   Method(NTFY, 2) {If (LEqual(Arg0, 0x00)) {Notify(CP00, Arg1)} ...}
          */
         /* Arg0 = Processor ID = APIC ID */
-        build_append_notify(sb_scope, "NTFY", "CP%0.02X", 0, acpi_cpus);
+        build_append_notify_method(sb_scope, "NTFY", "CP%0.02X", acpi_cpus);
 
         /* build "Name(CPON, Package() { One, One, ..., Zero, Zero, ... })" */
         build_append_byte(sb_scope, 0x08); /* NameOp */
@@ -752,24 +960,21 @@ build_ssdt(GArray *table_data, GArray *linker,
         }
 
         {
-            GArray *pci0 = build_alloc_array();
-            uint8_t op = 0x10; /* ScopeOp */;
+            AcpiBuildPciBusHotplugState hotplug_state;
+            PCIBus *bus = find_i440fx(); /* TODO: Q35 support */
 
-            build_append_nameseg(pci0, "PCI0");
+            build_pci_bus_state_init(&hotplug_state, NULL);
 
-            /* build Device object for each slot */
-            for (i = 1; i < PCI_SLOT_MAX; i++) {
-                bool eject = test_bit(i, misc->slot_hotplug_enable);
-                void *pcihp = acpi_data_push(pci0, ACPI_PCIHP_SIZEOF);
-
-                memcpy(pcihp, ACPI_PCIHP_AML, ACPI_PCIHP_SIZEOF);
-                patch_pcihp(i, pcihp, eject);
+            if (bus) {
+                /* Scan all PCI buses. Generate tables to support
+                 * PCI hotplug. Set property to enable acpi based hotplug.
+                */
+                pci_for_each_bus_depth_first(bus, build_pci_bus_begin,
+                                             build_pci_bus_end, &hotplug_state);
             }
 
-            build_append_notify(pci0, "PCNT", "S%0.02X_", 1, PCI_SLOT_MAX);
-            build_package(pci0, op, 3);
-            build_append_array(sb_scope, pci0);
-            build_free_array(pci0);
+            build_append_array(sb_scope, hotplug_state.device_table);
+            build_pci_bus_state_cleanup(&hotplug_state);
         }
 
         build_package(sb_scope, op, 3);
@@ -1045,7 +1250,6 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables)
     acpi_get_cpu_info(&cpu);
     acpi_get_pm_info(&pm);
     acpi_get_dsdt(&misc);
-    acpi_get_hotplug_info(&misc);
     acpi_get_misc_info(&misc);
     acpi_get_pci_info(&pci);
 
@@ -1177,6 +1381,8 @@ void acpi_setup(PcGuestInfo *guest_info)
 
     build_state->guest_info = guest_info;
 
+    acpi_set_pci_info();
+
     acpi_build_tables_init(&tables);
     acpi_build(build_state->guest_info, &tables);
 
diff --git a/hw/i386/acpi-dsdt.dsl b/hw/i386/acpi-dsdt.dsl
index 90efce0..70b8329 100644
--- a/hw/i386/acpi-dsdt.dsl
+++ b/hw/i386/acpi-dsdt.dsl
@@ -133,32 +133,28 @@ DefinitionBlock (
             B0EJ, 32,
         }
 
+        OperationRegion(BNMR, SystemIO, 0xae10, 0x04)
+        Field(BNMR, DWordAcc, NoLock, WriteAsZeros) {
+            BNUM, 32,
+        }
+
+        /* Lock to protect access to fields above. */
+        Mutex(BLCK, 0)
+
         /* Methods called by bulk generated PCI devices below */
 
         /* Methods called by hotplug devices */
-        Method(PCEJ, 1, NotSerialized) {
+        Method(PCEJ, 2, NotSerialized) {
             // _EJ0 method - eject callback
-            Store(ShiftLeft(1, Arg0), B0EJ)
+            Acquire(BLCK, 0xFFFF)
+            Store(Arg0, BNUM)
+            Store(ShiftLeft(1, Arg1), B0EJ)
+            Release(BLCK)
             Return (0x0)
         }
 
         /* Hotplug notification method supplied by SSDT */
         External(\_SB.PCI0.PCNT, MethodObj)
-
-        /* PCI hotplug notify method */
-        Method(PCNF, 0) {
-            // Local0 = iterator
-            Store(Zero, Local0)
-            While (LLess(Local0, 31)) {
-                Increment(Local0)
-                If (And(PCIU, ShiftLeft(1, Local0))) {
-                    PCNT(Local0, 1)
-                }
-                If (And(PCID, ShiftLeft(1, Local0))) {
-                    PCNT(Local0, 3)
-                }
-            }
-        }
     }
 
 
@@ -307,7 +303,9 @@ DefinitionBlock (
         }
         Method(_E01) {
             // PCI hotplug event
-            \_SB.PCI0.PCNF()
+            Acquire(\_SB.PCI0.BLCK, 0xFFFF)
+            \_SB.PCI0.PCNT()
+            Release(\_SB.PCI0.BLCK)
         }
         Method(_E02) {
             // CPU hotplug event
diff --git a/hw/i386/ssdt-pcihp.dsl b/hw/i386/ssdt-pcihp.dsl
index d29a5b9..cc245c3 100644
--- a/hw/i386/ssdt-pcihp.dsl
+++ b/hw/i386/ssdt-pcihp.dsl
@@ -25,6 +25,7 @@ DefinitionBlock ("ssdt-pcihp.aml", "SSDT", 0x01, "BXPC", "BXSSDTPCIHP", 0x1)
     /* Objects supplied by DSDT */
     External(\_SB.PCI0, DeviceObj)
     External(\_SB.PCI0.PCEJ, MethodObj)
+    External(BSEL, IntObj)
 
     Scope(\_SB.PCI0) {
 
@@ -33,19 +34,17 @@ DefinitionBlock ("ssdt-pcihp.aml", "SSDT", 0x01, "BXPC", "BXSSDTPCIHP", 0x1)
         ACPI_EXTRACT_DEVICE_END ssdt_pcihp_end
         ACPI_EXTRACT_DEVICE_STRING ssdt_pcihp_name
 
-        // Method _EJ0 can be patched by BIOS to EJ0_
-        // at runtime, if the slot is detected to not support hotplug.
-        // Extract the offset of the address dword and the
-        // _EJ0 name to allow this patching.
+        // Extract the offsets of the device name, address dword and the slot
+        // name byte - we fill them in for each device.
         Device(SAA) {
             ACPI_EXTRACT_NAME_BYTE_CONST ssdt_pcihp_id
             Name(_SUN, 0xAA)
             ACPI_EXTRACT_NAME_DWORD_CONST ssdt_pcihp_adr
             Name(_ADR, 0xAA0000)
-            ACPI_EXTRACT_METHOD_STRING ssdt_pcihp_ej0
             Method(_EJ0, 1) {
-                Return (PCEJ(_SUN))
+                PCEJ(BSEL, _SUN)
             }
         }
+
     }
 }
-- 
MST

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [Qemu-devel] [PATCH v3 0/4] acpi hotplug of devices behind a pci bridge
  2013-10-07  9:21 [Qemu-devel] [PATCH v3 0/4] acpi hotplug of devices behind a pci bridge Michael S. Tsirkin
                   ` (3 preceding siblings ...)
  2013-10-07  9:21 ` [Qemu-devel] [PATCH v3 4/4] acpi-build: enable hotplug for PCI bridges Michael S. Tsirkin
@ 2013-10-13 20:51 ` Michael S. Tsirkin
  4 siblings, 0 replies; 6+ messages in thread
From: Michael S. Tsirkin @ 2013-10-13 20:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Gerd Hoffmann, Anthony Liguori, Igor Mammedov,
	Laszlo Ersek, afaerber

On Mon, Oct 07, 2013 at 12:21:26PM +0300, Michael S. Tsirkin wrote:
> This is on top of patchset generating ACPI tables in qemu.
> Please review and consider for 1.7.

No comments, I assume it's ok to merge this.

> Changes from v2:
>     - split new code out to a separate file: acpi/pcihp.c
>       this way we can reuse it for q35 later
>     - rebased on top of the latest acpi-build patchset
> 
> Changes from v1:
>     - fixed some bugs
>     - fixed migration and cross version compatibility
>     - rebased to latest bits
> 
> This adds support for device hotplug behind pci bridges, by generating ACPI
> tables describing the current hierarchy.
> Bridge devices themselves need to be pre-configured on qemu command line.
> 
> How to use:
> Describe buses on command line. E.g.:
>     -device pci-bridge,id=bar,chassis_nr=1 -device
>     virtio-net,netdev=bur,addr=0x1,id=far,bus=bar
> use monitor command for hotplug:
>     device_del far
> 
> We don't currently use QOM properties to generate this info since:
>     - info about hotpluggability of devices is lacking
>     - we need to list all devices and buses in a predictable order
>         For now, add a pci core function to do the above predictable order
>         walk.
> 
> This is on top of acpi patchset posted previously.
> with a small patch adding a core function to walk all
> pci buses, on top.
> Can also be found in my git tree
>     git://git.kernel.org/pub/scm/virt/kvm/mst/qemu.git acpi-bridges
> 
> Portability:
>     - Non x86 (or any Linux) platforms don't need any of this code.
>       They can keep happily using SHPC the way
>       they always did.
> 
> Things to note:
> 
>     - Extensive use of glib completely removes
>       pointer math: we use
>       g_array_append_vals exclusively.
> 
>     - As was the case previously,
>       systems that lack working iasl are detected at configure time,
>       pre-generated hex files in source tree are used in this case.
>       This addresses the concern with iasl/big-endian
>       systems.
> 
>     - Cross version migration: when running with -M 1.6
>       and older, new hotplug functionality is unavailable.
>       All ACPI table generation is disabled.
>       We present FW_CFG interface compatible with 1.6.
> 
> Michael S. Tsirkin (4):
>   pci: add pci_for_each_bus_depth_first
>   pcihp: generalization of piix4 acpi
>   piix4: add acpi pci hotplug support
>   acpi-build: enable hotplug for PCI bridges
> 
>  include/hw/acpi/pcihp.h |  72 ++++++++++
>  include/hw/i386/pc.h    |   5 +
>  include/hw/pci/pci.h    |  14 ++
>  hw/acpi/pcihp.c         | 312 +++++++++++++++++++++++++++++++++++++++++++
>  hw/acpi/piix4.c         |  75 +++++++++--
>  hw/i386/acpi-build.c    | 346 ++++++++++++++++++++++++++++++++++++++----------
>  hw/pci/pci.c            |  28 ++++
>  hw/acpi/Makefile.objs   |   2 +-
>  hw/i386/acpi-dsdt.dsl   |  34 +++--
>  hw/i386/ssdt-pcihp.dsl  |  11 +-
>  10 files changed, 794 insertions(+), 105 deletions(-)
>  create mode 100644 include/hw/acpi/pcihp.h
>  create mode 100644 hw/acpi/pcihp.c
> 
> -- 
> MST
> 

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2013-10-13 20:49 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-10-07  9:21 [Qemu-devel] [PATCH v3 0/4] acpi hotplug of devices behind a pci bridge Michael S. Tsirkin
2013-10-07  9:21 ` [Qemu-devel] [PATCH v3 1/4] pci: add pci_for_each_bus_depth_first Michael S. Tsirkin
2013-10-07  9:21 ` [Qemu-devel] [PATCH v3 2/4] pcihp: generalization of piix4 acpi Michael S. Tsirkin
2013-10-07  9:21 ` [Qemu-devel] [PATCH v3 3/4] piix4: add acpi pci hotplug support Michael S. Tsirkin
2013-10-07  9:21 ` [Qemu-devel] [PATCH v3 4/4] acpi-build: enable hotplug for PCI bridges Michael S. Tsirkin
2013-10-13 20:51 ` [Qemu-devel] [PATCH v3 0/4] acpi hotplug of devices behind a pci bridge Michael S. Tsirkin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).