All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Michael S. Tsirkin" <mst@redhat.com>
To: qemu-devel@nongnu.org
Subject: [Qemu-devel] [PULL 18/29] pcihp: generalization of piix4 acpi
Date: Mon, 20 Jan 2014 16:10:22 +0200	[thread overview]
Message-ID: <1390226872-781-19-git-send-email-mst@redhat.com> (raw)
In-Reply-To: <1390226872-781-1-git-send-email-mst@redhat.com>

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         | 316 ++++++++++++++++++++++++++++++++++++++++++++++++
 hw/acpi/Makefile.objs   |   3 +-
 3 files changed, 389 insertions(+), 2 deletions(-)
 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..3fa3d7c
--- /dev/null
+++ b/hw/acpi/pcihp.c
@@ -0,0 +1,316 @@
+/*
+ * 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);
+
+    /* Make bsel 0 eject root bus if bsel property is not set,
+     * for compatibility with non acpi setups.
+     * TODO: really needed?
+     */
+    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 {
+                object_unparent(OBJECT(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..841eca2 100644
--- a/hw/acpi/Makefile.objs
+++ b/hw/acpi/Makefile.objs
@@ -1,2 +1 @@
-common-obj-$(CONFIG_ACPI) += core.o piix4.o ich9.o
-
+common-obj-$(CONFIG_ACPI) += core.o piix4.o ich9.o pcihp.o
-- 
MST

  parent reply	other threads:[~2014-01-20 14:05 UTC|newest]

Thread overview: 33+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-01-20 14:09 [Qemu-devel] [PULL 00/29] acpi, pci, pc, virtio fixes and enhancements Michael S. Tsirkin
2014-01-20 14:09 ` [Qemu-devel] [PULL 01/29] Python-lang gdb script to extract x86_64 guest vmcore from qemu coredump Michael S. Tsirkin
2014-01-20 14:09 ` [Qemu-devel] [PULL 02/29] Add DSDT node for AppleSMC Michael S. Tsirkin
2014-01-20 14:09 ` [Qemu-devel] [PULL 03/29] ACPI: Fix AppleSMC _STA size Michael S. Tsirkin
2014-01-20 14:09 ` [Qemu-devel] [PULL 04/29] virtio: Fix return value for dummy function vhost_net_virtqueue_pending Michael S. Tsirkin
2014-01-20 14:09 ` [Qemu-devel] [PULL 05/29] acpi unit-test: add test files Michael S. Tsirkin
2014-01-20 14:09 ` [Qemu-devel] [PULL 06/29] configure: added acpi unit-test files Michael S. Tsirkin
2014-01-20 14:09 ` [Qemu-devel] [PULL 07/29] acpi unit-test: compare DSDT and SSDT tables against expected values Michael S. Tsirkin
2014-01-20 14:09 ` [Qemu-devel] [PULL 08/29] configure: add CONFIG_IASL to config-host.h Michael S. Tsirkin
2014-01-20 14:09 ` [Qemu-devel] [PULL 09/29] acpi unit-test: extract iasl executable from configuration Michael S. Tsirkin
2014-01-20 14:09 ` [Qemu-devel] [PULL 10/29] acpi unit-test: added script to rebuild the expected aml files Michael S. Tsirkin
2014-01-20 14:10 ` [Qemu-devel] [PULL 11/29] acpi unit-test: hook to rebuild " Michael S. Tsirkin
2014-01-20 14:10 ` [Qemu-devel] [PULL 12/29] tests: fix acpi to work on bigendian host Michael S. Tsirkin
2014-01-20 14:10 ` [Qemu-devel] [PULL 13/29] acpi unit-test: renamed ssdt_tables to tables Michael S. Tsirkin
2014-01-20 14:10 ` [Qemu-devel] [PULL 14/29] acpi unit-test: resolved iasl crash Michael S. Tsirkin
2014-01-20 14:10 ` [Qemu-devel] [PULL 15/29] acpi unit-test: do not fail on asl mismatch Michael S. Tsirkin
2014-01-20 14:10 ` [Qemu-devel] [PULL 16/29] pc: make: fix dependencies: rebuild when included file is changed Michael S. Tsirkin
2014-01-20 14:10 ` [Qemu-devel] [PULL 17/29] pci: add pci_for_each_bus_depth_first Michael S. Tsirkin
2014-01-20 14:10 ` Michael S. Tsirkin [this message]
2014-01-20 14:10 ` [Qemu-devel] [PULL 19/29] piix4: add acpi pci hotplug support Michael S. Tsirkin
2014-01-20 16:16   ` Michael S. Tsirkin
2014-01-20 16:31     ` Michael S. Tsirkin
2014-01-20 14:10 ` [Qemu-devel] [PULL 20/29] acpi-build: enable hotplug for PCI bridges Michael S. Tsirkin
2014-01-20 14:10 ` [Qemu-devel] [PULL 21/29] acpi: factor out common cpu hotplug code for PIIX4/Q35 Michael S. Tsirkin
2014-01-20 14:10 ` [Qemu-devel] [PULL 22/29] acpi: ich9: add CPU hotplug handling to Q35 machine Michael S. Tsirkin
2014-01-20 14:10 ` [Qemu-devel] [PULL 23/29] pc: set PRST base in DSDT depending on chipset Michael S. Tsirkin
2014-01-20 14:10 ` [Qemu-devel] [PULL 24/29] pc: PIIX DSDT: exclude CPU/PCI hotplug & GPE0 IO range from PCI bus resources Michael S. Tsirkin
2014-01-20 14:10 ` [Qemu-devel] [PULL 25/29] pc: Q35 DSDT: exclude CPU hotplug " Michael S. Tsirkin
2014-01-20 14:10 ` [Qemu-devel] [PULL 26/29] pc: ACPI: expose PRST IO range via _CRS Michael S. Tsirkin
2014-01-20 14:10 ` [Qemu-devel] [PULL 27/29] pc: ACPI: unify source of CPU hotplug IO base/len Michael S. Tsirkin
2014-01-20 14:11 ` [Qemu-devel] [PULL 28/29] pc: ACPI: update acpi-dsdt.hex.generated q35-acpi-dsdt.hex.generated Michael S. Tsirkin
2014-01-20 14:11 ` [Qemu-devel] [PULL 29/29] acpi-test: update expected AML since recent changes Michael S. Tsirkin
2014-01-26  9:47 ` [Qemu-devel] [PULL 00/29] acpi, pci, pc, virtio fixes and enhancements Michael S. Tsirkin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1390226872-781-19-git-send-email-mst@redhat.com \
    --to=mst@redhat.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.