From 6f74de54f35042fa03e5e8626065de9998a88ea6 Mon Sep 17 00:00:00 2001 From: Quinn Stephens Date: Fri, 14 Feb 2025 23:35:04 -0500 Subject: 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 Signed-off-by: Ian Moffett --- sys/dev/pci/pci.c | 81 ++++++++++++++++++++++++++++++++++--------- sys/include/dev/pci/pci.h | 7 ++++ sys/include/dev/pci/pciregs.h | 11 ++++++ 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_ */ -- cgit v1.2.3