From ce6a28ee057da3e4a587dada369e33a8486b0066 Mon Sep 17 00:00:00 2001 From: Marcel Apfelbaum Date: Tue, 2 Jun 2015 14:22:57 +0300 Subject: hw/pci: made pci_bus_is_root a PCIBusClass method Refactoring it as a method of PCIBusClass will allow different implementations for subclasses. Removed the assumption that the root bus does not have a parent device because is specific only to the default class implementation. Signed-off-by: Marcel Apfelbaum Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Acked-by: Laszlo Ersek --- include/hw/pci/pci.h | 2 ++ include/hw/pci/pci_bus.h | 8 ++++++++ 2 files changed, 10 insertions(+) (limited to 'include/hw/pci') diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 5d050c830f..df05c96c2d 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -340,6 +340,8 @@ typedef PCIINTxRoute (*pci_route_irq_fn)(void *opaque, int pin); #define TYPE_PCI_BUS "PCI" #define PCI_BUS(obj) OBJECT_CHECK(PCIBus, (obj), TYPE_PCI_BUS) +#define PCI_BUS_CLASS(klass) OBJECT_CLASS_CHECK(PCIBusClass, (klass), TYPE_PCI_BUS) +#define PCI_BUS_GET_CLASS(obj) OBJECT_GET_CLASS(PCIBusClass, (obj), TYPE_PCI_BUS) #define TYPE_PCIE_BUS "PCIE" bool pci_bus_is_express(PCIBus *bus); diff --git a/include/hw/pci/pci_bus.h b/include/hw/pci/pci_bus.h index fabaeee86b..b5ba9c4a98 100644 --- a/include/hw/pci/pci_bus.h +++ b/include/hw/pci/pci_bus.h @@ -8,6 +8,14 @@ * use accessor functions in pci.h, pci_bridge.h */ +typedef struct PCIBusClass { + /*< private >*/ + BusClass parent_class; + /*< public >*/ + + bool (*is_root)(PCIBus *bus); +} PCIBusClass; + struct PCIBus { BusState qbus; PCIIOMMUFunc iommu_fn; -- cgit 1.4.1 From 602141d9974d726063907851528c89d617730156 Mon Sep 17 00:00:00 2001 From: Marcel Apfelbaum Date: Tue, 2 Jun 2015 14:22:58 +0300 Subject: hw/pci: made pci_bus_num a PCIBusClass method Refactoring it as a method of PCIBusClass will allow different implementations for subclasses. Signed-off-by: Marcel Apfelbaum Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Acked-by: Laszlo Ersek --- hw/pci/pci.c | 13 ++++++++++--- include/hw/pci/pci_bus.h | 1 + 2 files changed, 11 insertions(+), 3 deletions(-) (limited to 'include/hw/pci') diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 132d19ec2a..2f24f74cf8 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -93,6 +93,14 @@ static bool pcibus_is_root(PCIBus *bus) return !bus->parent_dev; } +static int pcibus_num(PCIBus *bus) +{ + if (pcibus_is_root(bus)) { + return 0; /* pci host bridge */ + } + return bus->parent_dev->config[PCI_SECONDARY_BUS]; +} + static void pci_bus_class_init(ObjectClass *klass, void *data) { BusClass *k = BUS_CLASS(klass); @@ -106,6 +114,7 @@ static void pci_bus_class_init(ObjectClass *klass, void *data) k->reset = pcibus_reset; pbc->is_root = pcibus_is_root; + pbc->bus_num = pcibus_num; } static const TypeInfo pci_bus_info = { @@ -390,9 +399,7 @@ PCIBus *pci_register_bus(DeviceState *parent, const char *name, int pci_bus_num(PCIBus *s) { - if (pci_bus_is_root(s)) - return 0; /* pci host bridge */ - return s->parent_dev->config[PCI_SECONDARY_BUS]; + return PCI_BUS_GET_CLASS(s)->bus_num(s); } static int get_pci_config_device(QEMUFile *f, void *pv, size_t size) diff --git a/include/hw/pci/pci_bus.h b/include/hw/pci/pci_bus.h index b5ba9c4a98..7b9939e346 100644 --- a/include/hw/pci/pci_bus.h +++ b/include/hw/pci/pci_bus.h @@ -14,6 +14,7 @@ typedef struct PCIBusClass { /*< public >*/ bool (*is_root)(PCIBus *bus); + int (*bus_num)(PCIBus *bus); } PCIBusClass; struct PCIBus { -- cgit 1.4.1 From 40d14bef8012087ade60f254487d31db822a1a44 Mon Sep 17 00:00:00 2001 From: Marcel Apfelbaum Date: Tue, 2 Jun 2015 14:23:06 +0300 Subject: hw/pci: introduce PCI Expander Bridge (PXB) PXB is a "light-weight" host bridge whose purpose is to enable the main host bridge to support multiple PCI root buses for pc machines. As oposed to PCI-2-PCI bridge's secondary bus, PXB's bus is a primary bus and can be associated with a NUMA node (different from the main host bridge) allowing the guest OS to recognize the proximity of a pass-through device to other resources as RAM and CPUs. The PXB is composed from: - A primary PCI bus (can be associated with a NUMA node) Acts like a normal pci bus and from the functionality point of view is an "expansion" of the bus behind the main host bridge. - A pci-2-pci bridge behind the primary PCI bus where the actual devices will be attached. - A host-bridge PCI device Situated on the bus behind the main host bridge, allows the BIOS to configure the bus number and IO/mem resources. It does not have its own config/data register for configuration cycles, this being handled by the main host bridge. - A host-bridge sysbus to comply with QEMU current design. Signed-off-by: Marcel Apfelbaum Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Acked-by: Laszlo Ersek --- hw/pci-bridge/Makefile.objs | 1 + hw/pci-bridge/pci_expander_bridge.c | 196 ++++++++++++++++++++++++++++++++++++ include/hw/pci/pci.h | 1 + 3 files changed, 198 insertions(+) create mode 100644 hw/pci-bridge/pci_expander_bridge.c (limited to 'include/hw/pci') diff --git a/hw/pci-bridge/Makefile.objs b/hw/pci-bridge/Makefile.objs index 96c596eb31..f2adfe348c 100644 --- a/hw/pci-bridge/Makefile.objs +++ b/hw/pci-bridge/Makefile.objs @@ -1,4 +1,5 @@ common-obj-y += pci_bridge_dev.o +common-obj-y += pci_expander_bridge.o common-obj-$(CONFIG_XIO3130) += xio3130_upstream.o xio3130_downstream.o common-obj-$(CONFIG_IOH3420) += ioh3420.o common-obj-$(CONFIG_I82801B11) += i82801b11.o diff --git a/hw/pci-bridge/pci_expander_bridge.c b/hw/pci-bridge/pci_expander_bridge.c new file mode 100644 index 0000000000..88e85c1339 --- /dev/null +++ b/hw/pci-bridge/pci_expander_bridge.c @@ -0,0 +1,196 @@ +/* + * PCI Expander Bridge Device Emulation + * + * Copyright (C) 2015 Red Hat Inc + * + * Authors: + * Marcel Apfelbaum + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "hw/pci/pci.h" +#include "hw/pci/pci_bus.h" +#include "hw/pci/pci_host.h" +#include "hw/pci/pci_bus.h" +#include "hw/i386/pc.h" +#include "qemu/range.h" +#include "qemu/error-report.h" + +#define TYPE_PXB_BUS "pxb-bus" +#define PXB_BUS(obj) OBJECT_CHECK(PXBBus, (obj), TYPE_PXB_BUS) + +typedef struct PXBBus { + /*< private >*/ + PCIBus parent_obj; + /*< public >*/ + + char bus_path[8]; +} PXBBus; + +#define TYPE_PXB_DEVICE "pxb" +#define PXB_DEV(obj) OBJECT_CHECK(PXBDev, (obj), TYPE_PXB_DEVICE) + +typedef struct PXBDev { + /*< private >*/ + PCIDevice parent_obj; + /*< public >*/ + + uint8_t bus_nr; +} PXBDev; + +#define TYPE_PXB_HOST "pxb-host" + +static int pxb_bus_num(PCIBus *bus) +{ + PXBDev *pxb = PXB_DEV(bus->parent_dev); + + return pxb->bus_nr; +} + +static bool pxb_is_root(PCIBus *bus) +{ + return true; /* by definition */ +} + +static void pxb_bus_class_init(ObjectClass *class, void *data) +{ + PCIBusClass *pbc = PCI_BUS_CLASS(class); + + pbc->bus_num = pxb_bus_num; + pbc->is_root = pxb_is_root; +} + +static const TypeInfo pxb_bus_info = { + .name = TYPE_PXB_BUS, + .parent = TYPE_PCI_BUS, + .instance_size = sizeof(PXBBus), + .class_init = pxb_bus_class_init, +}; + +static const char *pxb_host_root_bus_path(PCIHostState *host_bridge, + PCIBus *rootbus) +{ + PXBBus *bus = PXB_BUS(rootbus); + + snprintf(bus->bus_path, 8, "0000:%02x", pxb_bus_num(rootbus)); + return bus->bus_path; +} + +static void pxb_host_class_init(ObjectClass *class, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(class); + PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(class); + + dc->fw_name = "pci"; + hc->root_bus_path = pxb_host_root_bus_path; +} + +static const TypeInfo pxb_host_info = { + .name = TYPE_PXB_HOST, + .parent = TYPE_PCI_HOST_BRIDGE, + .class_init = pxb_host_class_init, +}; + +/* + * Registers the PXB bus as a child of the i440fx root bus. + * + * Returns 0 on successs, -1 if i440fx host was not + * found or the bus number is already in use. + */ +static int pxb_register_bus(PCIDevice *dev, PCIBus *pxb_bus) +{ + PCIBus *bus = dev->bus; + int pxb_bus_num = pci_bus_num(pxb_bus); + + if (bus->parent_dev) { + error_report("PXB devices can be attached only to root bus."); + return -1; + } + + QLIST_FOREACH(bus, &bus->child, sibling) { + if (pci_bus_num(bus) == pxb_bus_num) { + error_report("Bus %d is already in use.", pxb_bus_num); + return -1; + } + } + QLIST_INSERT_HEAD(&dev->bus->child, pxb_bus, sibling); + + return 0; +} + +static int pxb_dev_initfn(PCIDevice *dev) +{ + PXBDev *pxb = PXB_DEV(dev); + DeviceState *ds, *bds; + PCIBus *bus; + const char *dev_name = NULL; + + if (dev->qdev.id && *dev->qdev.id) { + dev_name = dev->qdev.id; + } + + ds = qdev_create(NULL, TYPE_PXB_HOST); + bus = pci_bus_new(ds, "pxb-internal", NULL, NULL, 0, TYPE_PXB_BUS); + + bus->parent_dev = dev; + bus->address_space_mem = dev->bus->address_space_mem; + bus->address_space_io = dev->bus->address_space_io; + bus->map_irq = pci_swizzle_map_irq_fn; + + bds = qdev_create(BUS(bus), "pci-bridge"); + bds->id = dev_name; + qdev_prop_set_uint8(bds, "chassis_nr", pxb->bus_nr); + + PCI_HOST_BRIDGE(ds)->bus = bus; + + if (pxb_register_bus(dev, bus)) { + return -EINVAL; + } + + qdev_init_nofail(ds); + qdev_init_nofail(bds); + + pci_word_test_and_set_mask(dev->config + PCI_STATUS, + PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK); + pci_config_set_class(dev->config, PCI_CLASS_BRIDGE_HOST); + + return 0; +} + +static Property pxb_dev_properties[] = { + /* Note: 0 is not a legal a PXB bus number. */ + DEFINE_PROP_UINT8("bus_nr", PXBDev, bus_nr, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pxb_dev_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = pxb_dev_initfn; + k->vendor_id = PCI_VENDOR_ID_REDHAT; + k->device_id = PCI_DEVICE_ID_REDHAT_PXB; + k->class_id = PCI_CLASS_BRIDGE_HOST; + + dc->desc = "PCI Expander Bridge"; + dc->props = pxb_dev_properties; +} + +static const TypeInfo pxb_dev_info = { + .name = TYPE_PXB_DEVICE, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PXBDev), + .class_init = pxb_dev_class_init, +}; + +static void pxb_register_types(void) +{ + type_register_static(&pxb_bus_info); + type_register_static(&pxb_host_info); + type_register_static(&pxb_dev_info); +} + +type_init(pxb_register_types) diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index df05c96c2d..7940700055 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -91,6 +91,7 @@ #define PCI_DEVICE_ID_REDHAT_ROCKER 0x0006 #define PCI_DEVICE_ID_REDHAT_SDHCI 0x0007 #define PCI_DEVICE_ID_REDHAT_PCIE_HOST 0x0008 +#define PCI_DEVICE_ID_REDHAT_PXB 0x0009 #define PCI_DEVICE_ID_REDHAT_QXL 0x0100 #define FMT_PCIBUS PRIx64 -- cgit 1.4.1 From 6a3042b23bbb1fa92c00ea9267c830e7f2e99313 Mon Sep 17 00:00:00 2001 From: Marcel Apfelbaum Date: Tue, 2 Jun 2015 14:23:09 +0300 Subject: hw/pci: add support for NUMA nodes PCI root buses can be attached to a specific NUMA node. PCI buses are not attached by default to a NUMA node. Signed-off-by: Marcel Apfelbaum Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Acked-by: Laszlo Ersek --- hw/pci/pci.c | 11 +++++++++++ include/hw/pci/pci.h | 1 + include/hw/pci/pci_bus.h | 1 + include/sysemu/sysemu.h | 1 + 4 files changed, 14 insertions(+) (limited to 'include/hw/pci') diff --git a/hw/pci/pci.c b/hw/pci/pci.c index a95664092e..4989408ece 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -101,6 +101,11 @@ static int pcibus_num(PCIBus *bus) return bus->parent_dev->config[PCI_SECONDARY_BUS]; } +static uint16_t pcibus_numa_node(PCIBus *bus) +{ + return NUMA_NODE_UNASSIGNED; +} + static void pci_bus_class_init(ObjectClass *klass, void *data) { BusClass *k = BUS_CLASS(klass); @@ -115,6 +120,7 @@ static void pci_bus_class_init(ObjectClass *klass, void *data) pbc->is_root = pcibus_is_root; pbc->bus_num = pcibus_num; + pbc->numa_node = pcibus_numa_node; } static const TypeInfo pci_bus_info = { @@ -402,6 +408,11 @@ int pci_bus_num(PCIBus *s) return PCI_BUS_GET_CLASS(s)->bus_num(s); } +int pci_bus_numa_node(PCIBus *bus) +{ + return PCI_BUS_GET_CLASS(bus)->numa_node(bus); +} + static int get_pci_config_device(QEMUFile *f, void *pv, size_t size) { PCIDevice *s = container_of(pv, PCIDevice, config); diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 7940700055..c2a427f4aa 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -382,6 +382,7 @@ PCIDevice *pci_nic_init_nofail(NICInfo *nd, PCIBus *rootbus, PCIDevice *pci_vga_init(PCIBus *bus); int pci_bus_num(PCIBus *s); +int pci_bus_numa_node(PCIBus *bus); void pci_for_each_device(PCIBus *bus, int bus_num, void (*fn)(PCIBus *bus, PCIDevice *d, void *opaque), void *opaque); diff --git a/include/hw/pci/pci_bus.h b/include/hw/pci/pci_bus.h index 7b9939e346..403fec6e58 100644 --- a/include/hw/pci/pci_bus.h +++ b/include/hw/pci/pci_bus.h @@ -15,6 +15,7 @@ typedef struct PCIBusClass { bool (*is_root)(PCIBus *bus); int (*bus_num)(PCIBus *bus); + uint16_t (*numa_node)(PCIBus *bus); } PCIBusClass; struct PCIBus { diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 8a52934728..4fcc20e957 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -137,6 +137,7 @@ extern const char *mem_path; extern int mem_prealloc; #define MAX_NODES 128 +#define NUMA_NODE_UNASSIGNED MAX_NODES /* The following shall be true for all CPUs: * cpu->cpu_index < max_cpus <= MAX_CPUMASK_BITS -- cgit 1.4.1