summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorQuinn Stephens <quinn@osmora.org>2025-02-14 23:35:04 -0500
committerIan Moffett <ian@osmora.org>2025-02-14 23:36:33 -0500
commite7cf7a914a4835cc8d29af79a2e3b5fad220bd3d (patch)
tree03bae3bbc09514d292a635fa228f9a722e428d71 /sys
parent97fa59f389778fb7257a71ad1015a3eb2234a084 (diff)
kernel: pci: Optimize PCI bus scanning
From fe1beed19be88e0c43ff2a68994d6abc04a52f54 Mon Sep 17 00:00:00 2001 From: Quinn Stephens <quinn@osmora.org> Date: Fri, 14 Feb 2025 23:30:03 -0500 Subject: [PATCH] kernel: pci: Optimize PCI bus scanning Uses recursive bus/bridge scanning, skips nonexistent devices, and only scans for multiple functions on multifunction devices. This may result in PCI scanning being up to 100x as fast. Signed-off-by: Quinn Stephens <quinn@osmora.org> Signed-off-by: Ian Moffett <ian@osmora.org>
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/pci/pci.c81
-rw-r--r--sys/include/dev/pci/pci.h7
-rw-r--r--sys/include/dev/pci/pciregs.h11
3 files changed, 82 insertions, 17 deletions
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c
index a7b8bac..aefbe86 100644
--- a/sys/dev/pci/pci.c
+++ b/sys/dev/pci/pci.c
@@ -109,7 +109,7 @@ pci_get_cap(struct pci_device *dev, uint8_t id)
static void
pci_set_device_info(struct pci_device *dev)
{
- uint32_t classrev;
+ uint32_t classrev, buses;
int capoff;
dev->vendor_id = pci_readl(dev, PCIREG_VENDOR_ID) & 0xFFFF;
@@ -119,19 +119,36 @@ pci_set_device_info(struct pci_device *dev)
dev->pci_class = PCIREG_CLASS(classrev);
dev->pci_subclass = PCIREG_SUBCLASS(classrev);
dev->prog_if = PCIREG_PROGIF(classrev);
-
- dev->bar[0] = pci_readl(dev, PCIREG_BAR0);
- dev->bar[1] = pci_readl(dev, PCIREG_BAR1);
- dev->bar[2] = pci_readl(dev, PCIREG_BAR2);
- dev->bar[3] = pci_readl(dev, PCIREG_BAR3);
- dev->bar[4] = pci_readl(dev, PCIREG_BAR4);
- dev->bar[5] = pci_readl(dev, PCIREG_BAR5);
-
- dev->irq_line = pci_readl(dev, PCIREG_IRQLINE) & 0xFF;
- capoff = pci_get_cap(dev, PCI_CAP_MSIX);
- dev->msix_capoff = (capoff < 0) ? 0 : capoff;
+ dev->hdr_type = (uint8_t)pci_readl(dev, PCIREG_HDRTYPE);
+
+ /* Set type-specific data */
+ switch (dev->hdr_type & ~BIT(7)) {
+ case PCI_HDRTYPE_NORMAL:
+ dev->bar[0] = pci_readl(dev, PCIREG_BAR0);
+ dev->bar[1] = pci_readl(dev, PCIREG_BAR1);
+ dev->bar[2] = pci_readl(dev, PCIREG_BAR2);
+ dev->bar[3] = pci_readl(dev, PCIREG_BAR3);
+ dev->bar[4] = pci_readl(dev, PCIREG_BAR4);
+ dev->bar[5] = pci_readl(dev, PCIREG_BAR5);
+
+ dev->irq_line = pci_readl(dev, PCIREG_IRQLINE) & 0xFF;
+ capoff = pci_get_cap(dev, PCI_CAP_MSIX);
+ dev->msix_capoff = (capoff < 0) ? 0 : capoff;
+ break;
+ case PCI_HDRTYPE_BRIDGE:
+ buses = pci_readl(dev, PCIREG_BUSES);
+ dev->pri_bus = PCIREG_PRIBUS(buses);
+ dev->sec_bus = PCIREG_SECBUS(buses);
+ dev->sub_bus = PCIREG_SUBBUS(buses);
+ break;
+ default:
+ break;
+ }
}
+static void
+pci_scan_bus(uint8_t bus);
+
/*
* Attempt to register a device.
*
@@ -159,14 +176,45 @@ pci_register_device(uint8_t bus, uint8_t slot, uint8_t func)
dev->func = func;
pci_set_device_info(dev);
+
+ /* Check if this is a valid bridge */
+ if (
+ (dev->hdr_type & ~BIT(7)) == PCI_HDRTYPE_BRIDGE &&
+ dev->sec_bus > dev->bus &&
+ dev->sub_bus >= dev->sec_bus
+ ) {
+ /* Scan all subordinate buses */
+ for (uint8_t bus = dev->sec_bus; bus <= dev->sub_bus; ++bus) {
+ pci_scan_bus(bus);
+ }
+ }
+
TAILQ_INSERT_TAIL(&device_list, dev, link);
}
static void
pci_scan_bus(uint8_t bus)
{
- for (int slot = 0; slot < 32; ++slot) {
- for (int func = 0; func < 8; ++func) {
+ struct pci_device dev;
+
+ dev.bus = bus;
+ dev.func = 0;
+ for (uint8_t slot = 0; slot < 32; ++slot) {
+ dev.slot = slot;
+
+ /* Skip nonexistent device */
+ if ((uint16_t)pci_readl(&dev, PCIREG_VENDOR_ID) == 0xFFFF) {
+ continue;
+ }
+
+ /* Register single-function device */
+ if (!(pci_readl(&dev, PCIREG_HDRTYPE) & BIT(7))) {
+ pci_register_device(bus, slot, 0);
+ continue;
+ }
+
+ /* Register all functions */
+ for (uint8_t func = 0; func < 8; ++func) {
pci_register_device(bus, slot, func);
}
}
@@ -220,9 +268,8 @@ pci_init(void)
TAILQ_INIT(&device_list);
pr_trace("Scanning each bus...\n");
- for (uint16_t i = 0; i < 256; ++i) {
- pci_scan_bus(i);
- }
+ /* Recursively scan bus 0 */
+ pci_scan_bus(0);
return 0;
}
diff --git a/sys/include/dev/pci/pci.h b/sys/include/dev/pci/pci.h
index b5bb32c..8118cee 100644
--- a/sys/include/dev/pci/pci.h
+++ b/sys/include/dev/pci/pci.h
@@ -60,8 +60,15 @@ struct pci_device {
uint8_t pci_class;
uint8_t pci_subclass;
uint8_t prog_if;
+ uint8_t hdr_type;
+
+ uint8_t pri_bus;
+ uint8_t sec_bus;
+ uint8_t sub_bus;
+
uintptr_t bar[6];
uint8_t irq_line;
+
TAILQ_ENTRY(pci_device) link;
};
diff --git a/sys/include/dev/pci/pciregs.h b/sys/include/dev/pci/pciregs.h
index f0ed4d2..888d12f 100644
--- a/sys/include/dev/pci/pciregs.h
+++ b/sys/include/dev/pci/pciregs.h
@@ -35,8 +35,10 @@
#define PCIREG_VENDOR_ID 0x00 /* 16 bits */
#define PCIREG_DEVICE_ID 0x02 /* 16 bits */
#define PCIREG_CLASSREV 0x08 /* 32 bits */
+#define PCIREG_HDRTYPE 0x0e /* 8 bits */
#define PCIREG_BAR0 0x10 /* 32 bits */
#define PCIREG_BAR1 0x14 /* 32 bits */
+#define PCIREG_BUSES 0x18 /* 24 bits */
#define PCIREG_BAR2 0x18 /* 32 bits */
#define PCIREG_BAR3 0x1C /* 32 bits */
#define PCIREG_BAR4 0x20 /* 32 bits */
@@ -51,6 +53,11 @@
#define PCIREG_REVID(CLASSREV) (CLASSREV & 0xFF)
#define PCIREG_PROGIF(CLASSREV) ((CLASSREV >> 8) & 0xFF)
+/* Macros to extract PCI_BUSES bits */
+#define PCIREG_PRIBUS(BUSES) (BUSES & 0xFF)
+#define PCIREG_SECBUS(BUSES) ((BUSES >> 8) & 0xFF)
+#define PCIREG_SUBBUS(BUSES) ((BUSES >> 16) & 0xFF)
+
/* Macros to extract PCIREG_CMDSTATUS bits */
#define PCIREG_COMMAND(CMDSTATUS) (CMDSTATUS & 0xFFFF)
#define PCIREG_STATUS(CMDSTATUS) (CMDSTATUS >> 16)
@@ -74,4 +81,8 @@
#define PCI_BAR_32(BAR) (PCI_BAR_TYPE(BAR) == 0x0)
#define PCI_BAR_64(BAR) (PCI_BAR_TYPE(BAR) == 0x2)
+/* PCI header types */
+#define PCI_HDRTYPE_NORMAL 0x00
+#define PCI_HDRTYPE_BRIDGE 0x01
+
#endif /* _PCI_PCIREGS_H_ */