PCIe downstream ports (Root Ports and switches Downstream Ports) appear to firmware as PCI-PCI bridges and a 4K IO space is allocated for them. Because of the available IO space, maximum 16 PCI-PCI bridges can exist per system.
However PCIe devices can work without IO, so add 'disable-io-forwarding' property to bridges that makes the optional IOBASE/IO_LIMIT read-only allowing the firmware to skip allocating IO space. Signed-off-by: Marcel Apfelbaum <mar...@redhat.com> --- hw/pci/pci.c | 21 +++++++++++++++------ hw/pci/pci_bridge.c | 25 +++++++++++++++++++++---- include/hw/pci/pci_bus.h | 4 ++++ 3 files changed, 40 insertions(+), 10 deletions(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 168b9cc..bda3391 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -707,13 +707,18 @@ static void pci_init_w1cmask(PCIDevice *dev) static void pci_init_mask_bridge(PCIDevice *d) { + bool io_forwarding_enabled = !(PCI_BRIDGE(d)->flags & + PCI_BRIDGE_FLAG_DISABLE_IO_FWD); + /* PCI_PRIMARY_BUS, PCI_SECONDARY_BUS, PCI_SUBORDINATE_BUS and PCI_SEC_LETENCY_TIMER */ memset(d->wmask + PCI_PRIMARY_BUS, 0xff, 4); /* base and limit */ - d->wmask[PCI_IO_BASE] = PCI_IO_RANGE_MASK & 0xff; - d->wmask[PCI_IO_LIMIT] = PCI_IO_RANGE_MASK & 0xff; + if (io_forwarding_enabled) { + d->wmask[PCI_IO_BASE] = PCI_IO_RANGE_MASK & 0xff; + d->wmask[PCI_IO_LIMIT] = PCI_IO_RANGE_MASK & 0xff; + } pci_set_word(d->wmask + PCI_MEMORY_BASE, PCI_MEMORY_RANGE_MASK & 0xffff); pci_set_word(d->wmask + PCI_MEMORY_LIMIT, @@ -727,8 +732,10 @@ static void pci_init_mask_bridge(PCIDevice *d) memset(d->wmask + PCI_PREF_BASE_UPPER32, 0xff, 8); /* Supported memory and i/o types */ - d->config[PCI_IO_BASE] |= PCI_IO_RANGE_TYPE_16; - d->config[PCI_IO_LIMIT] |= PCI_IO_RANGE_TYPE_16; + if (io_forwarding_enabled) { + d->config[PCI_IO_BASE] |= PCI_IO_RANGE_TYPE_16; + d->config[PCI_IO_LIMIT] |= PCI_IO_RANGE_TYPE_16; + } pci_word_test_and_set_mask(d->config + PCI_PREF_MEMORY_BASE, PCI_PREF_RANGE_TYPE_64); pci_word_test_and_set_mask(d->config + PCI_PREF_MEMORY_LIMIT, @@ -754,8 +761,10 @@ static void pci_init_mask_bridge(PCIDevice *d) * completeness. */ pci_set_word(d->w1cmask + PCI_BRIDGE_CONTROL, PCI_BRIDGE_CTL_DISCARD_STATUS); - d->cmask[PCI_IO_BASE] |= PCI_IO_RANGE_TYPE_MASK; - d->cmask[PCI_IO_LIMIT] |= PCI_IO_RANGE_TYPE_MASK; + if (io_forwarding_enabled) { + d->cmask[PCI_IO_BASE] |= PCI_IO_RANGE_TYPE_MASK; + d->cmask[PCI_IO_LIMIT] |= PCI_IO_RANGE_TYPE_MASK; + } pci_word_test_and_set_mask(d->cmask + PCI_PREF_MEMORY_BASE, PCI_PREF_RANGE_TYPE_MASK); pci_word_test_and_set_mask(d->cmask + PCI_PREF_MEMORY_LIMIT, diff --git a/hw/pci/pci_bridge.c b/hw/pci/pci_bridge.c index 40c97b1..8b418dd 100644 --- a/hw/pci/pci_bridge.c +++ b/hw/pci/pci_bridge.c @@ -276,10 +276,13 @@ void pci_bridge_disable_base_limit(PCIDevice *dev) { uint8_t *conf = dev->config; - pci_byte_test_and_set_mask(conf + PCI_IO_BASE, - PCI_IO_RANGE_MASK & 0xff); - pci_byte_test_and_clear_mask(conf + PCI_IO_LIMIT, - PCI_IO_RANGE_MASK & 0xff); + if (!(PCI_BRIDGE(dev)->flags & PCI_BRIDGE_FLAG_DISABLE_IO_FWD)) { + pci_byte_test_and_set_mask(conf + PCI_IO_BASE, + PCI_IO_RANGE_MASK & 0xff); + pci_byte_test_and_clear_mask(conf + PCI_IO_LIMIT, + PCI_IO_RANGE_MASK & 0xff); + } + pci_word_test_and_set_mask(conf + PCI_MEMORY_BASE, PCI_MEMORY_RANGE_MASK & 0xffff); pci_word_test_and_clear_mask(conf + PCI_MEMORY_LIMIT, @@ -404,10 +407,24 @@ void pci_bridge_map_irq(PCIBridge *br, const char* bus_name, br->bus_name = bus_name; } +static Property pci_bridge_properties[] = { + DEFINE_PROP_BIT("disable-io-forwarding", PCIBridge, flags, + PCI_BRIDGE_FLAG_DISABLE_IO_FWD_BIT, false), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pci_bridge_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->props = pci_bridge_properties; +} + static const TypeInfo pci_bridge_type_info = { .name = TYPE_PCI_BRIDGE, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIBridge), + .class_init = pci_bridge_class_init, .abstract = true, }; diff --git a/include/hw/pci/pci_bus.h b/include/hw/pci/pci_bus.h index 403fec6..1e02198 100644 --- a/include/hw/pci/pci_bus.h +++ b/include/hw/pci/pci_bus.h @@ -64,6 +64,9 @@ struct PCIBridgeWindows { #define TYPE_PCI_BRIDGE "base-pci-bridge" #define PCI_BRIDGE(obj) OBJECT_CHECK(PCIBridge, (obj), TYPE_PCI_BRIDGE) +#define PCI_BRIDGE_FLAG_DISABLE_IO_FWD_BIT 0 +#define PCI_BRIDGE_FLAG_DISABLE_IO_FWD (1 << PCI_BRIDGE_FLAG_DISABLE_IO_FWD_BIT) + struct PCIBridge { /*< private >*/ PCIDevice parent_obj; @@ -86,6 +89,7 @@ struct PCIBridge { pci_map_irq_fn map_irq; const char *bus_name; + uint32_t flags; }; #endif /* QEMU_PCI_BUS_H */ -- 2.1.0