|
|
8be556 |
From 5ff5f42d0e12141a66f7038902627c6802c4ec11 Mon Sep 17 00:00:00 2001
|
|
|
8be556 |
From: Laurent Vivier <lvivier@redhat.com>
|
|
|
8be556 |
Date: Thu, 25 Jun 2015 13:44:40 +0200
|
|
|
8be556 |
Subject: [PATCH 101/217] spapr_pci: enable basic hotplug operations
|
|
|
8be556 |
|
|
|
8be556 |
Message-id: <1435239881-28541-14-git-send-email-lvivier@redhat.com>
|
|
|
8be556 |
Patchwork-id: 66491
|
|
|
8be556 |
O-Subject: [RHEL7.2 qemu-kvm-rhev PATCH 13/14] spapr_pci: enable basic hotplug operations
|
|
|
8be556 |
Bugzilla: 1172478
|
|
|
8be556 |
RH-Acked-by: Michael S. Tsirkin <mst@redhat.com>
|
|
|
8be556 |
RH-Acked-by: Thomas Huth <thuth@redhat.com>
|
|
|
8be556 |
RH-Acked-by: David Gibson <dgibson@redhat.com>
|
|
|
8be556 |
|
|
|
8be556 |
From: Michael Roth <mdroth@linux.vnet.ibm.com>
|
|
|
8be556 |
|
|
|
8be556 |
This enables hotplug of PCI devices to a PHB. Upon hotplug we
|
|
|
8be556 |
generate the OF-nodes required by PAPR specification and
|
|
|
8be556 |
IEEE 1275-1994 "PCI Bus Binding to Open Firmware" for the
|
|
|
8be556 |
device.
|
|
|
8be556 |
|
|
|
8be556 |
We associate the corresponding FDT for these nodes with the DRC
|
|
|
8be556 |
corresponding to the slot, which will be fetched via
|
|
|
8be556 |
ibm,configure-connector RTAS calls by the guest as described by PAPR
|
|
|
8be556 |
specification.
|
|
|
8be556 |
|
|
|
8be556 |
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
|
|
|
8be556 |
Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
|
|
|
8be556 |
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
|
|
|
8be556 |
Signed-off-by: Alexander Graf <agraf@suse.de>
|
|
|
8be556 |
(cherry picked from commit 7454c7af91bdd60216e2b6eead827c012bb4d0d0)
|
|
|
8be556 |
Signed-off-by: Laurent Vivier <lvivier@redhat.com>
|
|
|
8be556 |
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
|
|
|
8be556 |
|
|
|
8be556 |
Conflicts:
|
|
|
8be556 |
hw/ppc/spapr_pci.c
|
|
|
8be556 |
|
|
|
8be556 |
Conflicts on context of missing commits:
|
|
|
8be556 |
46c5874 spapr_pci: Make find_phb()/find_dev() public
|
|
|
8be556 |
ccf9ff8 spapr_pci: Rework device-tree rendering
|
|
|
8be556 |
---
|
|
|
8be556 |
hw/ppc/spapr_pci.c | 399 ++++++++++++++++++++++++++++++++++++++++++++++++++---
|
|
|
8be556 |
1 file changed, 380 insertions(+), 19 deletions(-)
|
|
|
8be556 |
|
|
|
8be556 |
diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c
|
|
|
8be556 |
index 4017279..c0a34ae 100644
|
|
|
8be556 |
--- a/hw/ppc/spapr_pci.c
|
|
|
8be556 |
+++ b/hw/ppc/spapr_pci.c
|
|
|
8be556 |
@@ -33,9 +33,11 @@
|
|
|
8be556 |
#include <libfdt.h>
|
|
|
8be556 |
#include "trace.h"
|
|
|
8be556 |
#include "qemu/error-report.h"
|
|
|
8be556 |
+#include "qapi/qmp/qerror.h"
|
|
|
8be556 |
|
|
|
8be556 |
#include "hw/pci/pci_bus.h"
|
|
|
8be556 |
#include "hw/ppc/spapr_drc.h"
|
|
|
8be556 |
+#include "sysemu/device_tree.h"
|
|
|
8be556 |
|
|
|
8be556 |
/* Copied from the kernel arch/powerpc/platforms/pseries/msi.c */
|
|
|
8be556 |
#define RTAS_QUERY_FN 0
|
|
|
8be556 |
@@ -48,6 +50,14 @@
|
|
|
8be556 |
#define RTAS_TYPE_MSI 1
|
|
|
8be556 |
#define RTAS_TYPE_MSIX 2
|
|
|
8be556 |
|
|
|
8be556 |
+#define _FDT(exp) \
|
|
|
8be556 |
+ do { \
|
|
|
8be556 |
+ int ret = (exp); \
|
|
|
8be556 |
+ if (ret < 0) { \
|
|
|
8be556 |
+ return ret; \
|
|
|
8be556 |
+ } \
|
|
|
8be556 |
+ } while (0)
|
|
|
8be556 |
+
|
|
|
8be556 |
static sPAPRPHBState *find_phb(sPAPREnvironment *spapr, uint64_t buid)
|
|
|
8be556 |
{
|
|
|
8be556 |
sPAPRPHBState *sphb;
|
|
|
8be556 |
@@ -732,6 +742,368 @@ static AddressSpace *spapr_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn)
|
|
|
8be556 |
return &phb->iommu_as;
|
|
|
8be556 |
}
|
|
|
8be556 |
|
|
|
8be556 |
+/* Macros to operate with address in OF binding to PCI */
|
|
|
8be556 |
+#define b_x(x, p, l) (((x) & ((1<<(l))-1)) << (p))
|
|
|
8be556 |
+#define b_n(x) b_x((x), 31, 1) /* 0 if relocatable */
|
|
|
8be556 |
+#define b_p(x) b_x((x), 30, 1) /* 1 if prefetchable */
|
|
|
8be556 |
+#define b_t(x) b_x((x), 29, 1) /* 1 if the address is aliased */
|
|
|
8be556 |
+#define b_ss(x) b_x((x), 24, 2) /* the space code */
|
|
|
8be556 |
+#define b_bbbbbbbb(x) b_x((x), 16, 8) /* bus number */
|
|
|
8be556 |
+#define b_ddddd(x) b_x((x), 11, 5) /* device number */
|
|
|
8be556 |
+#define b_fff(x) b_x((x), 8, 3) /* function number */
|
|
|
8be556 |
+#define b_rrrrrrrr(x) b_x((x), 0, 8) /* register number */
|
|
|
8be556 |
+
|
|
|
8be556 |
+/* for 'reg'/'assigned-addresses' OF properties */
|
|
|
8be556 |
+#define RESOURCE_CELLS_SIZE 2
|
|
|
8be556 |
+#define RESOURCE_CELLS_ADDRESS 3
|
|
|
8be556 |
+
|
|
|
8be556 |
+typedef struct ResourceFields {
|
|
|
8be556 |
+ uint32_t phys_hi;
|
|
|
8be556 |
+ uint32_t phys_mid;
|
|
|
8be556 |
+ uint32_t phys_lo;
|
|
|
8be556 |
+ uint32_t size_hi;
|
|
|
8be556 |
+ uint32_t size_lo;
|
|
|
8be556 |
+} QEMU_PACKED ResourceFields;
|
|
|
8be556 |
+
|
|
|
8be556 |
+typedef struct ResourceProps {
|
|
|
8be556 |
+ ResourceFields reg[8];
|
|
|
8be556 |
+ ResourceFields assigned[7];
|
|
|
8be556 |
+ uint32_t reg_len;
|
|
|
8be556 |
+ uint32_t assigned_len;
|
|
|
8be556 |
+} ResourceProps;
|
|
|
8be556 |
+
|
|
|
8be556 |
+/* fill in the 'reg'/'assigned-resources' OF properties for
|
|
|
8be556 |
+ * a PCI device. 'reg' describes resource requirements for a
|
|
|
8be556 |
+ * device's IO/MEM regions, 'assigned-addresses' describes the
|
|
|
8be556 |
+ * actual resource assignments.
|
|
|
8be556 |
+ *
|
|
|
8be556 |
+ * the properties are arrays of ('phys-addr', 'size') pairs describing
|
|
|
8be556 |
+ * the addressable regions of the PCI device, where 'phys-addr' is a
|
|
|
8be556 |
+ * RESOURCE_CELLS_ADDRESS-tuple of 32-bit integers corresponding to
|
|
|
8be556 |
+ * (phys.hi, phys.mid, phys.lo), and 'size' is a
|
|
|
8be556 |
+ * RESOURCE_CELLS_SIZE-tuple corresponding to (size.hi, size.lo).
|
|
|
8be556 |
+ *
|
|
|
8be556 |
+ * phys.hi = 0xYYXXXXZZ, where:
|
|
|
8be556 |
+ * 0xYY = npt000ss
|
|
|
8be556 |
+ * ||| |
|
|
|
8be556 |
+ * ||| +-- space code: 1 if IO region, 2 if MEM region
|
|
|
8be556 |
+ * ||+------ for non-relocatable IO: 1 if aliased
|
|
|
8be556 |
+ * || for relocatable IO: 1 if below 64KB
|
|
|
8be556 |
+ * || for MEM: 1 if below 1MB
|
|
|
8be556 |
+ * |+------- 1 if region is prefetchable
|
|
|
8be556 |
+ * +-------- 1 if region is non-relocatable
|
|
|
8be556 |
+ * 0xXXXX = bbbbbbbb dddddfff, encoding bus, slot, and function
|
|
|
8be556 |
+ * bits respectively
|
|
|
8be556 |
+ * 0xZZ = rrrrrrrr, the register number of the BAR corresponding
|
|
|
8be556 |
+ * to the region
|
|
|
8be556 |
+ *
|
|
|
8be556 |
+ * phys.mid and phys.lo correspond respectively to the hi/lo portions
|
|
|
8be556 |
+ * of the actual address of the region.
|
|
|
8be556 |
+ *
|
|
|
8be556 |
+ * how the phys-addr/size values are used differ slightly between
|
|
|
8be556 |
+ * 'reg' and 'assigned-addresses' properties. namely, 'reg' has
|
|
|
8be556 |
+ * an additional description for the config space region of the
|
|
|
8be556 |
+ * device, and in the case of QEMU has n=0 and phys.mid=phys.lo=0
|
|
|
8be556 |
+ * to describe the region as relocatable, with an address-mapping
|
|
|
8be556 |
+ * that corresponds directly to the PHB's address space for the
|
|
|
8be556 |
+ * resource. 'assigned-addresses' always has n=1 set with an absolute
|
|
|
8be556 |
+ * address assigned for the resource. in general, 'assigned-addresses'
|
|
|
8be556 |
+ * won't be populated, since addresses for PCI devices are generally
|
|
|
8be556 |
+ * unmapped initially and left to the guest to assign.
|
|
|
8be556 |
+ *
|
|
|
8be556 |
+ * note also that addresses defined in these properties are, at least
|
|
|
8be556 |
+ * for PAPR guests, relative to the PHBs IO/MEM windows, and
|
|
|
8be556 |
+ * correspond directly to the addresses in the BARs.
|
|
|
8be556 |
+ *
|
|
|
8be556 |
+ * in accordance with PCI Bus Binding to Open Firmware,
|
|
|
8be556 |
+ * IEEE Std 1275-1994, section 4.1.1, as implemented by PAPR+ v2.7,
|
|
|
8be556 |
+ * Appendix C.
|
|
|
8be556 |
+ */
|
|
|
8be556 |
+static void populate_resource_props(PCIDevice *d, ResourceProps *rp)
|
|
|
8be556 |
+{
|
|
|
8be556 |
+ int bus_num = pci_bus_num(PCI_BUS(qdev_get_parent_bus(DEVICE(d))));
|
|
|
8be556 |
+ uint32_t dev_id = (b_bbbbbbbb(bus_num) |
|
|
|
8be556 |
+ b_ddddd(PCI_SLOT(d->devfn)) |
|
|
|
8be556 |
+ b_fff(PCI_FUNC(d->devfn)));
|
|
|
8be556 |
+ ResourceFields *reg, *assigned;
|
|
|
8be556 |
+ int i, reg_idx = 0, assigned_idx = 0;
|
|
|
8be556 |
+
|
|
|
8be556 |
+ /* config space region */
|
|
|
8be556 |
+ reg = &rp->reg[reg_idx++];
|
|
|
8be556 |
+ reg->phys_hi = cpu_to_be32(dev_id);
|
|
|
8be556 |
+ reg->phys_mid = 0;
|
|
|
8be556 |
+ reg->phys_lo = 0;
|
|
|
8be556 |
+ reg->size_hi = 0;
|
|
|
8be556 |
+ reg->size_lo = 0;
|
|
|
8be556 |
+
|
|
|
8be556 |
+ for (i = 0; i < PCI_NUM_REGIONS; i++) {
|
|
|
8be556 |
+ if (!d->io_regions[i].size) {
|
|
|
8be556 |
+ continue;
|
|
|
8be556 |
+ }
|
|
|
8be556 |
+
|
|
|
8be556 |
+ reg = &rp->reg[reg_idx++];
|
|
|
8be556 |
+
|
|
|
8be556 |
+ reg->phys_hi = cpu_to_be32(dev_id | b_rrrrrrrr(pci_bar(d, i)));
|
|
|
8be556 |
+ if (d->io_regions[i].type & PCI_BASE_ADDRESS_SPACE_IO) {
|
|
|
8be556 |
+ reg->phys_hi |= cpu_to_be32(b_ss(1));
|
|
|
8be556 |
+ } else {
|
|
|
8be556 |
+ reg->phys_hi |= cpu_to_be32(b_ss(2));
|
|
|
8be556 |
+ }
|
|
|
8be556 |
+ reg->phys_mid = 0;
|
|
|
8be556 |
+ reg->phys_lo = 0;
|
|
|
8be556 |
+ reg->size_hi = cpu_to_be32(d->io_regions[i].size >> 32);
|
|
|
8be556 |
+ reg->size_lo = cpu_to_be32(d->io_regions[i].size);
|
|
|
8be556 |
+
|
|
|
8be556 |
+ if (d->io_regions[i].addr == PCI_BAR_UNMAPPED) {
|
|
|
8be556 |
+ continue;
|
|
|
8be556 |
+ }
|
|
|
8be556 |
+
|
|
|
8be556 |
+ assigned = &rp->assigned[assigned_idx++];
|
|
|
8be556 |
+ assigned->phys_hi = cpu_to_be32(reg->phys_hi | b_n(1));
|
|
|
8be556 |
+ assigned->phys_mid = cpu_to_be32(d->io_regions[i].addr >> 32);
|
|
|
8be556 |
+ assigned->phys_lo = cpu_to_be32(d->io_regions[i].addr);
|
|
|
8be556 |
+ assigned->size_hi = reg->size_hi;
|
|
|
8be556 |
+ assigned->size_lo = reg->size_lo;
|
|
|
8be556 |
+ }
|
|
|
8be556 |
+
|
|
|
8be556 |
+ rp->reg_len = reg_idx * sizeof(ResourceFields);
|
|
|
8be556 |
+ rp->assigned_len = assigned_idx * sizeof(ResourceFields);
|
|
|
8be556 |
+}
|
|
|
8be556 |
+
|
|
|
8be556 |
+static int spapr_populate_pci_child_dt(PCIDevice *dev, void *fdt, int offset,
|
|
|
8be556 |
+ int phb_index, int drc_index,
|
|
|
8be556 |
+ const char *drc_name)
|
|
|
8be556 |
+{
|
|
|
8be556 |
+ ResourceProps rp;
|
|
|
8be556 |
+ bool is_bridge = false;
|
|
|
8be556 |
+ int pci_status;
|
|
|
8be556 |
+
|
|
|
8be556 |
+ if (pci_default_read_config(dev, PCI_HEADER_TYPE, 1) ==
|
|
|
8be556 |
+ PCI_HEADER_TYPE_BRIDGE) {
|
|
|
8be556 |
+ is_bridge = true;
|
|
|
8be556 |
+ }
|
|
|
8be556 |
+
|
|
|
8be556 |
+ /* in accordance with PAPR+ v2.7 13.6.3, Table 181 */
|
|
|
8be556 |
+ _FDT(fdt_setprop_cell(fdt, offset, "vendor-id",
|
|
|
8be556 |
+ pci_default_read_config(dev, PCI_VENDOR_ID, 2)));
|
|
|
8be556 |
+ _FDT(fdt_setprop_cell(fdt, offset, "device-id",
|
|
|
8be556 |
+ pci_default_read_config(dev, PCI_DEVICE_ID, 2)));
|
|
|
8be556 |
+ _FDT(fdt_setprop_cell(fdt, offset, "revision-id",
|
|
|
8be556 |
+ pci_default_read_config(dev, PCI_REVISION_ID, 1)));
|
|
|
8be556 |
+ _FDT(fdt_setprop_cell(fdt, offset, "class-code",
|
|
|
8be556 |
+ pci_default_read_config(dev, PCI_CLASS_DEVICE, 2)
|
|
|
8be556 |
+ << 8));
|
|
|
8be556 |
+ if (pci_default_read_config(dev, PCI_INTERRUPT_PIN, 1)) {
|
|
|
8be556 |
+ _FDT(fdt_setprop_cell(fdt, offset, "interrupts",
|
|
|
8be556 |
+ pci_default_read_config(dev, PCI_INTERRUPT_PIN, 1)));
|
|
|
8be556 |
+ }
|
|
|
8be556 |
+
|
|
|
8be556 |
+ if (!is_bridge) {
|
|
|
8be556 |
+ _FDT(fdt_setprop_cell(fdt, offset, "min-grant",
|
|
|
8be556 |
+ pci_default_read_config(dev, PCI_MIN_GNT, 1)));
|
|
|
8be556 |
+ _FDT(fdt_setprop_cell(fdt, offset, "max-latency",
|
|
|
8be556 |
+ pci_default_read_config(dev, PCI_MAX_LAT, 1)));
|
|
|
8be556 |
+ }
|
|
|
8be556 |
+
|
|
|
8be556 |
+ if (pci_default_read_config(dev, PCI_SUBSYSTEM_ID, 2)) {
|
|
|
8be556 |
+ _FDT(fdt_setprop_cell(fdt, offset, "subsystem-id",
|
|
|
8be556 |
+ pci_default_read_config(dev, PCI_SUBSYSTEM_ID, 2)));
|
|
|
8be556 |
+ }
|
|
|
8be556 |
+
|
|
|
8be556 |
+ if (pci_default_read_config(dev, PCI_SUBSYSTEM_VENDOR_ID, 2)) {
|
|
|
8be556 |
+ _FDT(fdt_setprop_cell(fdt, offset, "subsystem-vendor-id",
|
|
|
8be556 |
+ pci_default_read_config(dev, PCI_SUBSYSTEM_VENDOR_ID, 2)));
|
|
|
8be556 |
+ }
|
|
|
8be556 |
+
|
|
|
8be556 |
+ _FDT(fdt_setprop_cell(fdt, offset, "cache-line-size",
|
|
|
8be556 |
+ pci_default_read_config(dev, PCI_CACHE_LINE_SIZE, 1)));
|
|
|
8be556 |
+
|
|
|
8be556 |
+ /* the following fdt cells are masked off the pci status register */
|
|
|
8be556 |
+ pci_status = pci_default_read_config(dev, PCI_STATUS, 2);
|
|
|
8be556 |
+ _FDT(fdt_setprop_cell(fdt, offset, "devsel-speed",
|
|
|
8be556 |
+ PCI_STATUS_DEVSEL_MASK & pci_status));
|
|
|
8be556 |
+
|
|
|
8be556 |
+ if (pci_status & PCI_STATUS_FAST_BACK) {
|
|
|
8be556 |
+ _FDT(fdt_setprop(fdt, offset, "fast-back-to-back", NULL, 0));
|
|
|
8be556 |
+ }
|
|
|
8be556 |
+ if (pci_status & PCI_STATUS_66MHZ) {
|
|
|
8be556 |
+ _FDT(fdt_setprop(fdt, offset, "66mhz-capable", NULL, 0));
|
|
|
8be556 |
+ }
|
|
|
8be556 |
+ if (pci_status & PCI_STATUS_UDF) {
|
|
|
8be556 |
+ _FDT(fdt_setprop(fdt, offset, "udf-supported", NULL, 0));
|
|
|
8be556 |
+ }
|
|
|
8be556 |
+
|
|
|
8be556 |
+ /* NOTE: this is normally generated by firmware via path/unit name,
|
|
|
8be556 |
+ * but in our case we must set it manually since it does not get
|
|
|
8be556 |
+ * processed by OF beforehand
|
|
|
8be556 |
+ */
|
|
|
8be556 |
+ _FDT(fdt_setprop_string(fdt, offset, "name", "pci"));
|
|
|
8be556 |
+ _FDT(fdt_setprop(fdt, offset, "ibm,loc-code", drc_name, strlen(drc_name)));
|
|
|
8be556 |
+ _FDT(fdt_setprop_cell(fdt, offset, "ibm,my-drc-index", drc_index));
|
|
|
8be556 |
+
|
|
|
8be556 |
+ _FDT(fdt_setprop_cell(fdt, offset, "#address-cells",
|
|
|
8be556 |
+ RESOURCE_CELLS_ADDRESS));
|
|
|
8be556 |
+ _FDT(fdt_setprop_cell(fdt, offset, "#size-cells",
|
|
|
8be556 |
+ RESOURCE_CELLS_SIZE));
|
|
|
8be556 |
+ _FDT(fdt_setprop_cell(fdt, offset, "ibm,req#msi-x",
|
|
|
8be556 |
+ RESOURCE_CELLS_SIZE));
|
|
|
8be556 |
+
|
|
|
8be556 |
+ populate_resource_props(dev, &rp);
|
|
|
8be556 |
+ _FDT(fdt_setprop(fdt, offset, "reg", (uint8_t *)rp.reg, rp.reg_len));
|
|
|
8be556 |
+ _FDT(fdt_setprop(fdt, offset, "assigned-addresses",
|
|
|
8be556 |
+ (uint8_t *)rp.assigned, rp.assigned_len));
|
|
|
8be556 |
+
|
|
|
8be556 |
+ return 0;
|
|
|
8be556 |
+}
|
|
|
8be556 |
+
|
|
|
8be556 |
+/* create OF node for pci device and required OF DT properties */
|
|
|
8be556 |
+static void *spapr_create_pci_child_dt(sPAPRPHBState *phb, PCIDevice *dev,
|
|
|
8be556 |
+ int drc_index, const char *drc_name,
|
|
|
8be556 |
+ int *dt_offset)
|
|
|
8be556 |
+{
|
|
|
8be556 |
+ void *fdt;
|
|
|
8be556 |
+ int offset, ret, fdt_size;
|
|
|
8be556 |
+ int slot = PCI_SLOT(dev->devfn);
|
|
|
8be556 |
+ int func = PCI_FUNC(dev->devfn);
|
|
|
8be556 |
+ char nodename[512];
|
|
|
8be556 |
+
|
|
|
8be556 |
+ fdt = create_device_tree(&fdt_size);
|
|
|
8be556 |
+ if (func != 0) {
|
|
|
8be556 |
+ sprintf(nodename, "pci@%d,%d", slot, func);
|
|
|
8be556 |
+ } else {
|
|
|
8be556 |
+ sprintf(nodename, "pci@%d", slot);
|
|
|
8be556 |
+ }
|
|
|
8be556 |
+ offset = fdt_add_subnode(fdt, 0, nodename);
|
|
|
8be556 |
+ ret = spapr_populate_pci_child_dt(dev, fdt, offset, phb->index, drc_index,
|
|
|
8be556 |
+ drc_name);
|
|
|
8be556 |
+ g_assert(!ret);
|
|
|
8be556 |
+
|
|
|
8be556 |
+ *dt_offset = offset;
|
|
|
8be556 |
+ return fdt;
|
|
|
8be556 |
+}
|
|
|
8be556 |
+
|
|
|
8be556 |
+static void spapr_phb_add_pci_device(sPAPRDRConnector *drc,
|
|
|
8be556 |
+ sPAPRPHBState *phb,
|
|
|
8be556 |
+ PCIDevice *pdev,
|
|
|
8be556 |
+ Error **errp)
|
|
|
8be556 |
+{
|
|
|
8be556 |
+ sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
|
|
|
8be556 |
+ DeviceState *dev = DEVICE(pdev);
|
|
|
8be556 |
+ int drc_index = drck->get_index(drc);
|
|
|
8be556 |
+ const char *drc_name = drck->get_name(drc);
|
|
|
8be556 |
+ void *fdt = NULL;
|
|
|
8be556 |
+ int fdt_start_offset = 0;
|
|
|
8be556 |
+
|
|
|
8be556 |
+ /* boot-time devices get their device tree node created by SLOF, but for
|
|
|
8be556 |
+ * hotplugged devices we need QEMU to generate it so the guest can fetch
|
|
|
8be556 |
+ * it via RTAS
|
|
|
8be556 |
+ */
|
|
|
8be556 |
+ if (dev->hotplugged) {
|
|
|
8be556 |
+ fdt = spapr_create_pci_child_dt(phb, pdev, drc_index, drc_name,
|
|
|
8be556 |
+ &fdt_start_offset);
|
|
|
8be556 |
+ }
|
|
|
8be556 |
+
|
|
|
8be556 |
+ drck->attach(drc, DEVICE(pdev),
|
|
|
8be556 |
+ fdt, fdt_start_offset, !dev->hotplugged, errp);
|
|
|
8be556 |
+ if (*errp) {
|
|
|
8be556 |
+ g_free(fdt);
|
|
|
8be556 |
+ }
|
|
|
8be556 |
+}
|
|
|
8be556 |
+
|
|
|
8be556 |
+static void spapr_phb_remove_pci_device_cb(DeviceState *dev, void *opaque)
|
|
|
8be556 |
+{
|
|
|
8be556 |
+ /* some version guests do not wait for completion of a device
|
|
|
8be556 |
+ * cleanup (generally done asynchronously by the kernel) before
|
|
|
8be556 |
+ * signaling to QEMU that the device is safe, but instead sleep
|
|
|
8be556 |
+ * for some 'safe' period of time. unfortunately on a busy host
|
|
|
8be556 |
+ * this sleep isn't guaranteed to be long enough, resulting in
|
|
|
8be556 |
+ * bad things like IRQ lines being left asserted during final
|
|
|
8be556 |
+ * device removal. to deal with this we call reset just prior
|
|
|
8be556 |
+ * to finalizing the device, which will put the device back into
|
|
|
8be556 |
+ * an 'idle' state, as the device cleanup code expects.
|
|
|
8be556 |
+ */
|
|
|
8be556 |
+ pci_device_reset(PCI_DEVICE(dev));
|
|
|
8be556 |
+ object_unparent(OBJECT(dev));
|
|
|
8be556 |
+}
|
|
|
8be556 |
+
|
|
|
8be556 |
+static void spapr_phb_remove_pci_device(sPAPRDRConnector *drc,
|
|
|
8be556 |
+ sPAPRPHBState *phb,
|
|
|
8be556 |
+ PCIDevice *pdev,
|
|
|
8be556 |
+ Error **errp)
|
|
|
8be556 |
+{
|
|
|
8be556 |
+ sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
|
|
|
8be556 |
+
|
|
|
8be556 |
+ drck->detach(drc, DEVICE(pdev), spapr_phb_remove_pci_device_cb, phb, errp);
|
|
|
8be556 |
+}
|
|
|
8be556 |
+
|
|
|
8be556 |
+static sPAPRDRConnector *spapr_phb_get_pci_drc(sPAPRPHBState *phb,
|
|
|
8be556 |
+ PCIDevice *pdev)
|
|
|
8be556 |
+{
|
|
|
8be556 |
+ uint32_t busnr = pci_bus_num(PCI_BUS(qdev_get_parent_bus(DEVICE(pdev))));
|
|
|
8be556 |
+ return spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_PCI,
|
|
|
8be556 |
+ (phb->index << 16) |
|
|
|
8be556 |
+ (busnr << 8) |
|
|
|
8be556 |
+ pdev->devfn);
|
|
|
8be556 |
+}
|
|
|
8be556 |
+
|
|
|
8be556 |
+static void spapr_phb_hot_plug_child(HotplugHandler *plug_handler,
|
|
|
8be556 |
+ DeviceState *plugged_dev, Error **errp)
|
|
|
8be556 |
+{
|
|
|
8be556 |
+ sPAPRPHBState *phb = SPAPR_PCI_HOST_BRIDGE(DEVICE(plug_handler));
|
|
|
8be556 |
+ PCIDevice *pdev = PCI_DEVICE(plugged_dev);
|
|
|
8be556 |
+ sPAPRDRConnector *drc = spapr_phb_get_pci_drc(phb, pdev);
|
|
|
8be556 |
+ Error *local_err = NULL;
|
|
|
8be556 |
+
|
|
|
8be556 |
+ /* if DR is disabled we don't need to do anything in the case of
|
|
|
8be556 |
+ * hotplug or coldplug callbacks
|
|
|
8be556 |
+ */
|
|
|
8be556 |
+ if (!phb->dr_enabled) {
|
|
|
8be556 |
+ /* if this is a hotplug operation initiated by the user
|
|
|
8be556 |
+ * we need to let them know it's not enabled
|
|
|
8be556 |
+ */
|
|
|
8be556 |
+ if (plugged_dev->hotplugged) {
|
|
|
8be556 |
+ error_set(errp, QERR_BUS_NO_HOTPLUG,
|
|
|
8be556 |
+ object_get_typename(OBJECT(phb)));
|
|
|
8be556 |
+ }
|
|
|
8be556 |
+ return;
|
|
|
8be556 |
+ }
|
|
|
8be556 |
+
|
|
|
8be556 |
+ g_assert(drc);
|
|
|
8be556 |
+
|
|
|
8be556 |
+ spapr_phb_add_pci_device(drc, phb, pdev, &local_err);
|
|
|
8be556 |
+ if (local_err) {
|
|
|
8be556 |
+ error_propagate(errp, local_err);
|
|
|
8be556 |
+ return;
|
|
|
8be556 |
+ }
|
|
|
8be556 |
+}
|
|
|
8be556 |
+
|
|
|
8be556 |
+static void spapr_phb_hot_unplug_child(HotplugHandler *plug_handler,
|
|
|
8be556 |
+ DeviceState *plugged_dev, Error **errp)
|
|
|
8be556 |
+{
|
|
|
8be556 |
+ sPAPRPHBState *phb = SPAPR_PCI_HOST_BRIDGE(DEVICE(plug_handler));
|
|
|
8be556 |
+ PCIDevice *pdev = PCI_DEVICE(plugged_dev);
|
|
|
8be556 |
+ sPAPRDRConnectorClass *drck;
|
|
|
8be556 |
+ sPAPRDRConnector *drc = spapr_phb_get_pci_drc(phb, pdev);
|
|
|
8be556 |
+ Error *local_err = NULL;
|
|
|
8be556 |
+
|
|
|
8be556 |
+ if (!phb->dr_enabled) {
|
|
|
8be556 |
+ error_set(errp, QERR_BUS_NO_HOTPLUG,
|
|
|
8be556 |
+ object_get_typename(OBJECT(phb)));
|
|
|
8be556 |
+ return;
|
|
|
8be556 |
+ }
|
|
|
8be556 |
+
|
|
|
8be556 |
+ g_assert(drc);
|
|
|
8be556 |
+
|
|
|
8be556 |
+ drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
|
|
|
8be556 |
+ if (!drck->release_pending(drc)) {
|
|
|
8be556 |
+ spapr_phb_remove_pci_device(drc, phb, pdev, &local_err);
|
|
|
8be556 |
+ if (local_err) {
|
|
|
8be556 |
+ error_propagate(errp, local_err);
|
|
|
8be556 |
+ return;
|
|
|
8be556 |
+ }
|
|
|
8be556 |
+ }
|
|
|
8be556 |
+}
|
|
|
8be556 |
+
|
|
|
8be556 |
static void spapr_phb_realize(DeviceState *dev, Error **errp)
|
|
|
8be556 |
{
|
|
|
8be556 |
SysBusDevice *s = SYS_BUS_DEVICE(dev);
|
|
|
8be556 |
@@ -825,6 +1197,7 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
|
|
|
8be556 |
&sphb->memspace, &sphb->iospace,
|
|
|
8be556 |
PCI_DEVFN(0, 0), PCI_NUM_PINS, TYPE_PCI_BUS);
|
|
|
8be556 |
phb->bus = bus;
|
|
|
8be556 |
+ qbus_set_hotplug_handler(BUS(phb->bus), DEVICE(sphb), NULL);
|
|
|
8be556 |
|
|
|
8be556 |
/*
|
|
|
8be556 |
* Initialize PHB address space.
|
|
|
8be556 |
@@ -1061,6 +1434,7 @@ static void spapr_phb_class_init(ObjectClass *klass, void *data)
|
|
|
8be556 |
PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
|
|
|
8be556 |
DeviceClass *dc = DEVICE_CLASS(klass);
|
|
|
8be556 |
sPAPRPHBClass *spc = SPAPR_PCI_HOST_BRIDGE_CLASS(klass);
|
|
|
8be556 |
+ HotplugHandlerClass *hp = HOTPLUG_HANDLER_CLASS(klass);
|
|
|
8be556 |
|
|
|
8be556 |
hc->root_bus_path = spapr_phb_root_bus_path;
|
|
|
8be556 |
dc->realize = spapr_phb_realize;
|
|
|
8be556 |
@@ -1070,6 +1444,8 @@ static void spapr_phb_class_init(ObjectClass *klass, void *data)
|
|
|
8be556 |
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
|
|
8be556 |
dc->cannot_instantiate_with_device_add_yet = false;
|
|
|
8be556 |
spc->finish_realize = spapr_phb_finish_realize;
|
|
|
8be556 |
+ hp->plug = spapr_phb_hot_plug_child;
|
|
|
8be556 |
+ hp->unplug = spapr_phb_hot_unplug_child;
|
|
|
8be556 |
}
|
|
|
8be556 |
|
|
|
8be556 |
static const TypeInfo spapr_phb_info = {
|
|
|
8be556 |
@@ -1078,6 +1454,10 @@ static const TypeInfo spapr_phb_info = {
|
|
|
8be556 |
.instance_size = sizeof(sPAPRPHBState),
|
|
|
8be556 |
.class_init = spapr_phb_class_init,
|
|
|
8be556 |
.class_size = sizeof(sPAPRPHBClass),
|
|
|
8be556 |
+ .interfaces = (InterfaceInfo[]) {
|
|
|
8be556 |
+ { TYPE_HOTPLUG_HANDLER },
|
|
|
8be556 |
+ { }
|
|
|
8be556 |
+ }
|
|
|
8be556 |
};
|
|
|
8be556 |
|
|
|
8be556 |
PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index)
|
|
|
8be556 |
@@ -1091,17 +1471,6 @@ PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index)
|
|
|
8be556 |
return PCI_HOST_BRIDGE(dev);
|
|
|
8be556 |
}
|
|
|
8be556 |
|
|
|
8be556 |
-/* Macros to operate with address in OF binding to PCI */
|
|
|
8be556 |
-#define b_x(x, p, l) (((x) & ((1<<(l))-1)) << (p))
|
|
|
8be556 |
-#define b_n(x) b_x((x), 31, 1) /* 0 if relocatable */
|
|
|
8be556 |
-#define b_p(x) b_x((x), 30, 1) /* 1 if prefetchable */
|
|
|
8be556 |
-#define b_t(x) b_x((x), 29, 1) /* 1 if the address is aliased */
|
|
|
8be556 |
-#define b_ss(x) b_x((x), 24, 2) /* the space code */
|
|
|
8be556 |
-#define b_bbbbbbbb(x) b_x((x), 16, 8) /* bus number */
|
|
|
8be556 |
-#define b_ddddd(x) b_x((x), 11, 5) /* device number */
|
|
|
8be556 |
-#define b_fff(x) b_x((x), 8, 3) /* function number */
|
|
|
8be556 |
-#define b_rrrrrrrr(x) b_x((x), 0, 8) /* register number */
|
|
|
8be556 |
-
|
|
|
8be556 |
typedef struct sPAPRTCEDT {
|
|
|
8be556 |
void *fdt;
|
|
|
8be556 |
int node_off;
|
|
|
8be556 |
@@ -1171,14 +1540,6 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb,
|
|
|
8be556 |
return bus_off;
|
|
|
8be556 |
}
|
|
|
8be556 |
|
|
|
8be556 |
-#define _FDT(exp) \
|
|
|
8be556 |
- do { \
|
|
|
8be556 |
- int ret = (exp); \
|
|
|
8be556 |
- if (ret < 0) { \
|
|
|
8be556 |
- return ret; \
|
|
|
8be556 |
- } \
|
|
|
8be556 |
- } while (0)
|
|
|
8be556 |
-
|
|
|
8be556 |
/* Write PHB properties */
|
|
|
8be556 |
_FDT(fdt_setprop_string(fdt, bus_off, "device_type", "pci"));
|
|
|
8be556 |
_FDT(fdt_setprop_string(fdt, bus_off, "compatible", "IBM,Logical_PHB"));
|
|
|
8be556 |
--
|
|
|
8be556 |
1.8.3.1
|
|
|
8be556 |
|