|
|
8be556 |
From 95cdd8dd0fa39e7865b9a3549ddb70a06e53d47b Mon Sep 17 00:00:00 2001
|
|
|
8be556 |
From: Marcel Apfelbaum <marcel@redhat.com>
|
|
|
8be556 |
Date: Wed, 24 Jun 2015 13:53:30 +0200
|
|
|
8be556 |
Subject: [PATCH 207/217] hw/pci: introduce PCI Expander Bridge (PXB)
|
|
|
8be556 |
|
|
|
8be556 |
Message-id: <1435154016-26233-38-git-send-email-marcel@redhat.com>
|
|
|
8be556 |
Patchwork-id: 66464
|
|
|
8be556 |
O-Subject: [RHEL-7.2 qemu-kvm-rhev PATCH 37/43] hw/pci: introduce PCI Expander Bridge (PXB)
|
|
|
8be556 |
Bugzilla: 1103313
|
|
|
8be556 |
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
|
|
|
8be556 |
RH-Acked-by: Michael S. Tsirkin <mst@redhat.com>
|
|
|
8be556 |
RH-Acked-by: Igor Mammedov <imammedo@redhat.com>
|
|
|
8be556 |
|
|
|
8be556 |
From: Marcel Apfelbaum <marcel.a@redhat.com>
|
|
|
8be556 |
|
|
|
8be556 |
PXB is a "light-weight" host bridge whose purpose is to enable
|
|
|
8be556 |
the main host bridge to support multiple PCI root buses
|
|
|
8be556 |
for pc machines.
|
|
|
8be556 |
|
|
|
8be556 |
As oposed to PCI-2-PCI bridge's secondary bus, PXB's bus
|
|
|
8be556 |
is a primary bus and can be associated with a NUMA node
|
|
|
8be556 |
(different from the main host bridge) allowing the guest OS
|
|
|
8be556 |
to recognize the proximity of a pass-through device to
|
|
|
8be556 |
other resources as RAM and CPUs.
|
|
|
8be556 |
|
|
|
8be556 |
The PXB is composed from:
|
|
|
8be556 |
- A primary PCI bus (can be associated with a NUMA node)
|
|
|
8be556 |
Acts like a normal pci bus and from the functionality point
|
|
|
8be556 |
of view is an "expansion" of the bus behind the
|
|
|
8be556 |
main host bridge.
|
|
|
8be556 |
- A pci-2-pci bridge behind the primary PCI bus where the actual
|
|
|
8be556 |
devices will be attached.
|
|
|
8be556 |
- A host-bridge PCI device
|
|
|
8be556 |
Situated on the bus behind the main host bridge, allows
|
|
|
8be556 |
the BIOS to configure the bus number and IO/mem resources.
|
|
|
8be556 |
It does not have its own config/data register for configuration
|
|
|
8be556 |
cycles, this being handled by the main host bridge.
|
|
|
8be556 |
- A host-bridge sysbus to comply with QEMU current design.
|
|
|
8be556 |
|
|
|
8be556 |
Signed-off-by: Marcel Apfelbaum <marcel@redhat.com>
|
|
|
8be556 |
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
|
|
|
8be556 |
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
|
|
|
8be556 |
Acked-by: Laszlo Ersek <lersek@redhat.com>
|
|
|
8be556 |
(cherry picked from commit 40d14bef8012087ade60f254487d31db822a1a44)
|
|
|
8be556 |
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
|
|
|
8be556 |
---
|
|
|
8be556 |
hw/pci-bridge/Makefile.objs | 1 +
|
|
|
8be556 |
hw/pci-bridge/pci_expander_bridge.c | 196 ++++++++++++++++++++++++++++++++++++
|
|
|
8be556 |
include/hw/pci/pci.h | 1 +
|
|
|
8be556 |
3 files changed, 198 insertions(+)
|
|
|
8be556 |
create mode 100644 hw/pci-bridge/pci_expander_bridge.c
|
|
|
8be556 |
|
|
|
8be556 |
diff --git a/hw/pci-bridge/Makefile.objs b/hw/pci-bridge/Makefile.objs
|
|
|
8be556 |
index 96c596e..f2adfe3 100644
|
|
|
8be556 |
--- a/hw/pci-bridge/Makefile.objs
|
|
|
8be556 |
+++ b/hw/pci-bridge/Makefile.objs
|
|
|
8be556 |
@@ -1,4 +1,5 @@
|
|
|
8be556 |
common-obj-y += pci_bridge_dev.o
|
|
|
8be556 |
+common-obj-y += pci_expander_bridge.o
|
|
|
8be556 |
common-obj-$(CONFIG_XIO3130) += xio3130_upstream.o xio3130_downstream.o
|
|
|
8be556 |
common-obj-$(CONFIG_IOH3420) += ioh3420.o
|
|
|
8be556 |
common-obj-$(CONFIG_I82801B11) += i82801b11.o
|
|
|
8be556 |
diff --git a/hw/pci-bridge/pci_expander_bridge.c b/hw/pci-bridge/pci_expander_bridge.c
|
|
|
8be556 |
new file mode 100644
|
|
|
8be556 |
index 0000000..88e85c1
|
|
|
8be556 |
--- /dev/null
|
|
|
8be556 |
+++ b/hw/pci-bridge/pci_expander_bridge.c
|
|
|
8be556 |
@@ -0,0 +1,196 @@
|
|
|
8be556 |
+/*
|
|
|
8be556 |
+ * PCI Expander Bridge Device Emulation
|
|
|
8be556 |
+ *
|
|
|
8be556 |
+ * Copyright (C) 2015 Red Hat Inc
|
|
|
8be556 |
+ *
|
|
|
8be556 |
+ * Authors:
|
|
|
8be556 |
+ * Marcel Apfelbaum <marcel@redhat.com>
|
|
|
8be556 |
+ *
|
|
|
8be556 |
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
|
|
|
8be556 |
+ * See the COPYING file in the top-level directory.
|
|
|
8be556 |
+ */
|
|
|
8be556 |
+
|
|
|
8be556 |
+#include "hw/pci/pci.h"
|
|
|
8be556 |
+#include "hw/pci/pci_bus.h"
|
|
|
8be556 |
+#include "hw/pci/pci_host.h"
|
|
|
8be556 |
+#include "hw/pci/pci_bus.h"
|
|
|
8be556 |
+#include "hw/i386/pc.h"
|
|
|
8be556 |
+#include "qemu/range.h"
|
|
|
8be556 |
+#include "qemu/error-report.h"
|
|
|
8be556 |
+
|
|
|
8be556 |
+#define TYPE_PXB_BUS "pxb-bus"
|
|
|
8be556 |
+#define PXB_BUS(obj) OBJECT_CHECK(PXBBus, (obj), TYPE_PXB_BUS)
|
|
|
8be556 |
+
|
|
|
8be556 |
+typedef struct PXBBus {
|
|
|
8be556 |
+ /*< private >*/
|
|
|
8be556 |
+ PCIBus parent_obj;
|
|
|
8be556 |
+ /*< public >*/
|
|
|
8be556 |
+
|
|
|
8be556 |
+ char bus_path[8];
|
|
|
8be556 |
+} PXBBus;
|
|
|
8be556 |
+
|
|
|
8be556 |
+#define TYPE_PXB_DEVICE "pxb"
|
|
|
8be556 |
+#define PXB_DEV(obj) OBJECT_CHECK(PXBDev, (obj), TYPE_PXB_DEVICE)
|
|
|
8be556 |
+
|
|
|
8be556 |
+typedef struct PXBDev {
|
|
|
8be556 |
+ /*< private >*/
|
|
|
8be556 |
+ PCIDevice parent_obj;
|
|
|
8be556 |
+ /*< public >*/
|
|
|
8be556 |
+
|
|
|
8be556 |
+ uint8_t bus_nr;
|
|
|
8be556 |
+} PXBDev;
|
|
|
8be556 |
+
|
|
|
8be556 |
+#define TYPE_PXB_HOST "pxb-host"
|
|
|
8be556 |
+
|
|
|
8be556 |
+static int pxb_bus_num(PCIBus *bus)
|
|
|
8be556 |
+{
|
|
|
8be556 |
+ PXBDev *pxb = PXB_DEV(bus->parent_dev);
|
|
|
8be556 |
+
|
|
|
8be556 |
+ return pxb->bus_nr;
|
|
|
8be556 |
+}
|
|
|
8be556 |
+
|
|
|
8be556 |
+static bool pxb_is_root(PCIBus *bus)
|
|
|
8be556 |
+{
|
|
|
8be556 |
+ return true; /* by definition */
|
|
|
8be556 |
+}
|
|
|
8be556 |
+
|
|
|
8be556 |
+static void pxb_bus_class_init(ObjectClass *class, void *data)
|
|
|
8be556 |
+{
|
|
|
8be556 |
+ PCIBusClass *pbc = PCI_BUS_CLASS(class);
|
|
|
8be556 |
+
|
|
|
8be556 |
+ pbc->bus_num = pxb_bus_num;
|
|
|
8be556 |
+ pbc->is_root = pxb_is_root;
|
|
|
8be556 |
+}
|
|
|
8be556 |
+
|
|
|
8be556 |
+static const TypeInfo pxb_bus_info = {
|
|
|
8be556 |
+ .name = TYPE_PXB_BUS,
|
|
|
8be556 |
+ .parent = TYPE_PCI_BUS,
|
|
|
8be556 |
+ .instance_size = sizeof(PXBBus),
|
|
|
8be556 |
+ .class_init = pxb_bus_class_init,
|
|
|
8be556 |
+};
|
|
|
8be556 |
+
|
|
|
8be556 |
+static const char *pxb_host_root_bus_path(PCIHostState *host_bridge,
|
|
|
8be556 |
+ PCIBus *rootbus)
|
|
|
8be556 |
+{
|
|
|
8be556 |
+ PXBBus *bus = PXB_BUS(rootbus);
|
|
|
8be556 |
+
|
|
|
8be556 |
+ snprintf(bus->bus_path, 8, "0000:%02x", pxb_bus_num(rootbus));
|
|
|
8be556 |
+ return bus->bus_path;
|
|
|
8be556 |
+}
|
|
|
8be556 |
+
|
|
|
8be556 |
+static void pxb_host_class_init(ObjectClass *class, void *data)
|
|
|
8be556 |
+{
|
|
|
8be556 |
+ DeviceClass *dc = DEVICE_CLASS(class);
|
|
|
8be556 |
+ PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(class);
|
|
|
8be556 |
+
|
|
|
8be556 |
+ dc->fw_name = "pci";
|
|
|
8be556 |
+ hc->root_bus_path = pxb_host_root_bus_path;
|
|
|
8be556 |
+}
|
|
|
8be556 |
+
|
|
|
8be556 |
+static const TypeInfo pxb_host_info = {
|
|
|
8be556 |
+ .name = TYPE_PXB_HOST,
|
|
|
8be556 |
+ .parent = TYPE_PCI_HOST_BRIDGE,
|
|
|
8be556 |
+ .class_init = pxb_host_class_init,
|
|
|
8be556 |
+};
|
|
|
8be556 |
+
|
|
|
8be556 |
+/*
|
|
|
8be556 |
+ * Registers the PXB bus as a child of the i440fx root bus.
|
|
|
8be556 |
+ *
|
|
|
8be556 |
+ * Returns 0 on successs, -1 if i440fx host was not
|
|
|
8be556 |
+ * found or the bus number is already in use.
|
|
|
8be556 |
+ */
|
|
|
8be556 |
+static int pxb_register_bus(PCIDevice *dev, PCIBus *pxb_bus)
|
|
|
8be556 |
+{
|
|
|
8be556 |
+ PCIBus *bus = dev->bus;
|
|
|
8be556 |
+ int pxb_bus_num = pci_bus_num(pxb_bus);
|
|
|
8be556 |
+
|
|
|
8be556 |
+ if (bus->parent_dev) {
|
|
|
8be556 |
+ error_report("PXB devices can be attached only to root bus.");
|
|
|
8be556 |
+ return -1;
|
|
|
8be556 |
+ }
|
|
|
8be556 |
+
|
|
|
8be556 |
+ QLIST_FOREACH(bus, &bus->child, sibling) {
|
|
|
8be556 |
+ if (pci_bus_num(bus) == pxb_bus_num) {
|
|
|
8be556 |
+ error_report("Bus %d is already in use.", pxb_bus_num);
|
|
|
8be556 |
+ return -1;
|
|
|
8be556 |
+ }
|
|
|
8be556 |
+ }
|
|
|
8be556 |
+ QLIST_INSERT_HEAD(&dev->bus->child, pxb_bus, sibling);
|
|
|
8be556 |
+
|
|
|
8be556 |
+ return 0;
|
|
|
8be556 |
+}
|
|
|
8be556 |
+
|
|
|
8be556 |
+static int pxb_dev_initfn(PCIDevice *dev)
|
|
|
8be556 |
+{
|
|
|
8be556 |
+ PXBDev *pxb = PXB_DEV(dev);
|
|
|
8be556 |
+ DeviceState *ds, *bds;
|
|
|
8be556 |
+ PCIBus *bus;
|
|
|
8be556 |
+ const char *dev_name = NULL;
|
|
|
8be556 |
+
|
|
|
8be556 |
+ if (dev->qdev.id && *dev->qdev.id) {
|
|
|
8be556 |
+ dev_name = dev->qdev.id;
|
|
|
8be556 |
+ }
|
|
|
8be556 |
+
|
|
|
8be556 |
+ ds = qdev_create(NULL, TYPE_PXB_HOST);
|
|
|
8be556 |
+ bus = pci_bus_new(ds, "pxb-internal", NULL, NULL, 0, TYPE_PXB_BUS);
|
|
|
8be556 |
+
|
|
|
8be556 |
+ bus->parent_dev = dev;
|
|
|
8be556 |
+ bus->address_space_mem = dev->bus->address_space_mem;
|
|
|
8be556 |
+ bus->address_space_io = dev->bus->address_space_io;
|
|
|
8be556 |
+ bus->map_irq = pci_swizzle_map_irq_fn;
|
|
|
8be556 |
+
|
|
|
8be556 |
+ bds = qdev_create(BUS(bus), "pci-bridge");
|
|
|
8be556 |
+ bds->id = dev_name;
|
|
|
8be556 |
+ qdev_prop_set_uint8(bds, "chassis_nr", pxb->bus_nr);
|
|
|
8be556 |
+
|
|
|
8be556 |
+ PCI_HOST_BRIDGE(ds)->bus = bus;
|
|
|
8be556 |
+
|
|
|
8be556 |
+ if (pxb_register_bus(dev, bus)) {
|
|
|
8be556 |
+ return -EINVAL;
|
|
|
8be556 |
+ }
|
|
|
8be556 |
+
|
|
|
8be556 |
+ qdev_init_nofail(ds);
|
|
|
8be556 |
+ qdev_init_nofail(bds);
|
|
|
8be556 |
+
|
|
|
8be556 |
+ pci_word_test_and_set_mask(dev->config + PCI_STATUS,
|
|
|
8be556 |
+ PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK);
|
|
|
8be556 |
+ pci_config_set_class(dev->config, PCI_CLASS_BRIDGE_HOST);
|
|
|
8be556 |
+
|
|
|
8be556 |
+ return 0;
|
|
|
8be556 |
+}
|
|
|
8be556 |
+
|
|
|
8be556 |
+static Property pxb_dev_properties[] = {
|
|
|
8be556 |
+ /* Note: 0 is not a legal a PXB bus number. */
|
|
|
8be556 |
+ DEFINE_PROP_UINT8("bus_nr", PXBDev, bus_nr, 0),
|
|
|
8be556 |
+ DEFINE_PROP_END_OF_LIST(),
|
|
|
8be556 |
+};
|
|
|
8be556 |
+
|
|
|
8be556 |
+static void pxb_dev_class_init(ObjectClass *klass, void *data)
|
|
|
8be556 |
+{
|
|
|
8be556 |
+ DeviceClass *dc = DEVICE_CLASS(klass);
|
|
|
8be556 |
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
|
|
8be556 |
+
|
|
|
8be556 |
+ k->init = pxb_dev_initfn;
|
|
|
8be556 |
+ k->vendor_id = PCI_VENDOR_ID_REDHAT;
|
|
|
8be556 |
+ k->device_id = PCI_DEVICE_ID_REDHAT_PXB;
|
|
|
8be556 |
+ k->class_id = PCI_CLASS_BRIDGE_HOST;
|
|
|
8be556 |
+
|
|
|
8be556 |
+ dc->desc = "PCI Expander Bridge";
|
|
|
8be556 |
+ dc->props = pxb_dev_properties;
|
|
|
8be556 |
+}
|
|
|
8be556 |
+
|
|
|
8be556 |
+static const TypeInfo pxb_dev_info = {
|
|
|
8be556 |
+ .name = TYPE_PXB_DEVICE,
|
|
|
8be556 |
+ .parent = TYPE_PCI_DEVICE,
|
|
|
8be556 |
+ .instance_size = sizeof(PXBDev),
|
|
|
8be556 |
+ .class_init = pxb_dev_class_init,
|
|
|
8be556 |
+};
|
|
|
8be556 |
+
|
|
|
8be556 |
+static void pxb_register_types(void)
|
|
|
8be556 |
+{
|
|
|
8be556 |
+ type_register_static(&pxb_bus_info);
|
|
|
8be556 |
+ type_register_static(&pxb_host_info);
|
|
|
8be556 |
+ type_register_static(&pxb_dev_info);
|
|
|
8be556 |
+}
|
|
|
8be556 |
+
|
|
|
8be556 |
+type_init(pxb_register_types)
|
|
|
8be556 |
diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
|
|
|
8be556 |
index 9586b73..3d1d3ea 100644
|
|
|
8be556 |
--- a/include/hw/pci/pci.h
|
|
|
8be556 |
+++ b/include/hw/pci/pci.h
|
|
|
8be556 |
@@ -90,6 +90,7 @@
|
|
|
8be556 |
#define PCI_DEVICE_ID_REDHAT_TEST 0x0005
|
|
|
8be556 |
#define PCI_DEVICE_ID_REDHAT_SDHCI 0x0007
|
|
|
8be556 |
#define PCI_DEVICE_ID_REDHAT_PCIE_HOST 0x0008
|
|
|
8be556 |
+#define PCI_DEVICE_ID_REDHAT_PXB 0x0009
|
|
|
8be556 |
#define PCI_DEVICE_ID_REDHAT_QXL 0x0100
|
|
|
8be556 |
|
|
|
8be556 |
#define FMT_PCIBUS PRIx64
|
|
|
8be556 |
--
|
|
|
8be556 |
1.8.3.1
|
|
|
8be556 |
|