|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [UNIKRAFT PATCH] pci_bus: implement recursive PCI bus enumeration
The PCI bus was previously enumerated in a brute-force manner, scanning
all device on all buses (256 buses and 32 devices per bus, i.e., 8192
devices in total), resulting in tens of thousands of I/O read and write
operations at startup (~55ms overhead).
This patch implements recursive PCI bus enumeration: we first enumerate
the first bus (or the first bus*es* in the case of multiple PCI host
controllers), and recursively probe further buses as we discover further
PCI-to-PCI bridges. This reduces the overhead to ~1-2ms.
Signed-off-by: Hugo Lefeuvre <hugo.lefeuvre@xxxxxxxxx>
---
plat/common/pci_bus.c | 187 ++++++++++++++++++++++++++++++++++++--------------
1 file changed, 135 insertions(+), 52 deletions(-)
diff --git a/plat/common/pci_bus.c b/plat/common/pci_bus.c
index 4077e3c..cfc404e 100644
--- a/plat/common/pci_bus.c
+++ b/plat/common/pci_bus.c
@@ -1,6 +1,7 @@
/* TODO: SPDX Header */
/*
* Authors: Simon Kuenzer <simon.kuenzer@xxxxxxxxx>
+ * Hugo Lefeuvre <hugo.lefeuvre@xxxxxxxxx>
*
* Copyright (c) 2018, NEC Europe Ltd., NEC Corporation. All rights reserved.
*
@@ -71,17 +72,19 @@ static struct pci_bus_handler ph;
#define PCI_CONFIG_ADDR (0xCF8)
#define PCI_CONFIG_DATA (0xCFC)
-/* 8 bits for bus number, 5 bits for devices */
+/* 8 bits for bus number, 5 bits for devices, 3 for functions */
#define PCI_MAX_BUSES (1 << 8)
#define PCI_MAX_DEVICES (1 << 5)
+#define PCI_MAX_FUNCTIONS (1 << 3)
#define PCI_BUS_SHIFT (16)
#define PCI_DEVICE_SHIFT (11)
+#define PCI_FUNCTION_SHIFT (8)
#define PCI_ENABLE_BIT (1 << 31)
#define PCI_CONF_CLASS_ID (0x08)
-#define PCI_CONF_CLASS_ID_SHFT (8)
-#define PCI_CONF_CLASS_ID_MASK (0x00FFFFFF)
+#define PCI_CONF_CLASS_ID_SHFT (16)
+#define PCI_CONF_CLASS_ID_MASK (0xFF00)
#define PCI_CONF_VENDOR_ID (0x00)
#define PCI_CONF_VENDOR_ID_SHFT (0)
@@ -95,6 +98,19 @@ static struct pci_bus_handler ph;
#define PCI_CONF_SUBSYSVEN_ID_SHFT (0)
#define PCI_CONF_SUBSYSVEN_ID_MASK (0xFFFF)
+#define PCI_CONF_SUBCLASS_ID (0x08)
+#define PCI_CONF_SUBCLASS_ID_SHFT (16)
+#define PCI_CONF_SUBCLASS_ID_MASK (0x00FF)
+
+#define PCI_CONF_SECONDARY_BUS (0x18)
+#define PCI_CONF_SECONDARY_BUS_SHFT (0)
+#define PCI_CONF_SECONDARY_BUS_MASK (0xFF00)
+
+#define PCI_HEADER_TYPE_MSB_MASK (0x80)
+#define PCI_CONF_HEADER_TYPE (0x00)
+#define PCI_CONF_HEADER_TYPE_SHFT (16)
+#define PCI_CONF_HEADER_TYPE_MASK (0xFF)
+
#define PCI_CONF_SUBSYS_ID (0x2c)
#define PCI_CONF_SUBSYS_ID_SHFT (16)
#define PCI_CONF_SUBSYS_ID_MASK (0xFFFF)
@@ -218,66 +234,133 @@ static inline int pci_driver_add_device(struct
pci_driver *drv,
return 0;
}
-static int pci_probe(void)
+static void probe_bus(uint32_t);
+
+/* Probe a function. Return 1 if the function does not exist in the device, 0
+ * otherwise.
+ */
+static int probe_function(uint32_t bus, uint32_t device, uint32_t function)
{
+ uint32_t config_addr, config_data, subclass, secondary_bus;
struct pci_address addr;
struct pci_device_id devid;
struct pci_driver *drv;
- uint32_t config_addr, config_data;
- uint32_t bus;
- uint8_t dev;
+
+ config_addr = (PCI_ENABLE_BIT)
+ | (bus << PCI_BUS_SHIFT)
+ | (device << PCI_DEVICE_SHIFT)
+ | (function << PCI_FUNCTION_SHIFT);
+
+ outl(PCI_CONFIG_ADDR, config_addr);
+ config_data = inl(PCI_CONFIG_DATA);
+
+ devid.vendor_id = config_data & PCI_DEVICE_ID_MASK;
+ if (devid.vendor_id == PCI_INVALID_ID) {
+ /* Device doesn't exist */
+ return 1;
+ }
+
+ addr.domain = 0x0;
+ addr.bus = bus;
+ addr.devid = device;
+ addr.function = function;
+
+ /* These I/O reads could be batched, but it practice this does not
+ * appear to make the code more performant.
+ */
+ PCI_CONF_READ(uint32_t, &devid.class_id,
+ config_addr, CLASS_ID);
+ PCI_CONF_READ(uint32_t, &subclass,
+ config_addr, SUBCLASS_ID);
+ PCI_CONF_READ(uint16_t, &devid.vendor_id,
+ config_addr, VENDOR_ID);
+ PCI_CONF_READ(uint16_t, &devid.device_id,
+ config_addr, DEVICE_ID);
+ PCI_CONF_READ(uint16_t, &devid.subsystem_device_id,
+ config_addr, SUBSYS_ID);
+ PCI_CONF_READ(uint16_t, &devid.subsystem_vendor_id,
+ config_addr, SUBSYSVEN_ID);
+
+ uk_pr_info("PCI %02x:%02x.%02x (%04x %04x:%04x): ",
+ (int) addr.bus,
+ (int) addr.devid,
+ (int) addr.function,
+ (int) devid.class_id,
+ (int) devid.vendor_id,
+ (int) devid.device_id);
+
+ drv = pci_find_driver(&devid);
+ if (!drv) {
+ uk_pr_info("<no driver>\n");
+ } else {
+ uk_pr_info("driver %p\n", drv);
+ pci_driver_add_device(drv, &addr, &devid);
+ }
+
+ /* 0x06 = Bridge Device, 0x04 = PCI-to-PCI bridge */
+ if ((devid.class_id == 0x06) && (subclass == 0x04) ) {
+ PCI_CONF_READ(uint32_t, &secondary_bus,
+ config_addr, SECONDARY_BUS);
+ probe_bus(secondary_bus);
+ }
+
+ return 0;
+}
+
+/* Recursive PCI enumeration: this function is called recursively by
+ * probe_function upon discovering PCI-to-PCI bridges.
+ */
+static void probe_bus(uint32_t bus)
+{
+ uint32_t config_addr, device, header_type, function = 0;
+
+ for (device = 0; device < PCI_MAX_DEVICES; ++device) {
+ if (!probe_function(bus, device, function))
+ continue;
+
+ config_addr = (PCI_ENABLE_BIT);
+ PCI_CONF_READ(uint32_t, &header_type,
+ config_addr, HEADER_TYPE);
+
+ /* Is this a multi-function device? */
+ if ((header_type & PCI_HEADER_TYPE_MSB_MASK) == 0)
+ continue;
+
+ /* Check remaining functions */
+ for (function = 1; function < PCI_MAX_FUNCTIONS; function++)
+ probe_function(bus, device, function);
+ }
+}
+
+static int pci_probe(void)
+{
+ uint32_t config_addr, function, header_type, vendor_id;
uk_pr_debug("Probe PCI\n");
- for (bus = 0; bus < PCI_MAX_BUSES; ++bus) {
- for (dev = 0; dev < PCI_MAX_DEVICES; ++dev) {
- config_addr = (PCI_ENABLE_BIT)
- | (bus << PCI_BUS_SHIFT)
- | (dev << PCI_DEVICE_SHIFT);
-
- outl(PCI_CONFIG_ADDR, config_addr);
- config_data = inl(PCI_CONFIG_DATA);
-
- /* TODO: Retrieve the device identfier */
- addr.domain = 0x0;
- addr.bus = bus;
- addr.devid = dev;
- /* TODO: Retrieve the function */
- addr.function = 0x0;
-
- devid.vendor_id = config_data & PCI_DEVICE_ID_MASK;
- if (devid.vendor_id == PCI_INVALID_ID) {
- /* Device doesn't exist */
- continue;
- }
+ config_addr = (PCI_ENABLE_BIT);
+ PCI_CONF_READ(uint32_t, &header_type,
+ config_addr, HEADER_TYPE);
+
+ if ((header_type & PCI_HEADER_TYPE_MSB_MASK) == 0) {
+ /* Single PCI host controller */
+ probe_bus(0);
+ } else {
+ /* Multiple PCI host controllers */
+ for (function = 0; function < PCI_MAX_FUNCTIONS; function++) {
+ config_addr = (PCI_ENABLE_BIT) |
+ (function << PCI_FUNCTION_SHIFT);
- PCI_CONF_READ(uint32_t, &devid.class_id,
- config_addr, CLASS_ID);
- PCI_CONF_READ(uint16_t, &devid.vendor_id,
+ PCI_CONF_READ(uint32_t, &vendor_id,
config_addr, VENDOR_ID);
- PCI_CONF_READ(uint16_t, &devid.device_id,
- config_addr, DEVICE_ID);
- PCI_CONF_READ(uint16_t, &devid.subsystem_device_id,
- config_addr, SUBSYS_ID);
- PCI_CONF_READ(uint16_t, &devid.subsystem_vendor_id,
- config_addr, SUBSYSVEN_ID);
-
- uk_pr_info("PCI %02x:%02x.%02x (%04x %04x:%04x): ",
- (int) addr.bus,
- (int) addr.devid,
- (int) addr.function,
- (int) devid.class_id,
- (int) devid.vendor_id,
- (int) devid.device_id);
- drv = pci_find_driver(&devid);
- if (!drv) {
- uk_pr_info("<no driver>\n");
- continue;
- }
- uk_pr_info("driver %p\n", drv);
- pci_driver_add_device(drv, &addr, &devid);
+
+ if (vendor_id != PCI_INVALID_ID)
+ break;
+
+ probe_bus(function);
}
}
+
return 0;
}
--
2.7.4
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |