From 155a36650ba5e207269d312c9a3d97b351fa5026 Mon Sep 17 00:00:00 2001 From: Ian Moffett Date: Mon, 13 Oct 2025 12:03:54 -0400 Subject: kern: pci: Parse PCI capability list per device This allows us to see what kind of capabilities the device has (e.g., MSI/MSI-X) Signed-off-by: Ian Moffett --- src/sys/include/io/pci/pci.h | 6 ++++ src/sys/io/pci/pci.c | 66 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 71 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/sys/include/io/pci/pci.h b/src/sys/include/io/pci/pci.h index e6d0a24..27d3787 100644 --- a/src/sys/include/io/pci/pci.h +++ b/src/sys/include/io/pci/pci.h @@ -82,6 +82,9 @@ typedef uint32_t pcival_t; * @vendor: Vendor ID * @device: Device ID * @bar: Base address registers + * @caplist: Set if a capability list is implemented + * @msix: Device implements MSI-X + * @msi: Device implements MSI */ struct pci_device { uint16_t bus; @@ -93,6 +96,9 @@ struct pci_device { uint16_t vendor; uint16_t device; uint32_t bar[6]; + uint8_t caplist : 1; + uint8_t msix : 1; + uint8_t msi : 1; TAILQ_ENTRY(pci_device) link; }; diff --git a/src/sys/io/pci/pci.c b/src/sys/io/pci/pci.c index 2258d52..cc14f78 100644 --- a/src/sys/io/pci/pci.c +++ b/src/sys/io/pci/pci.c @@ -147,6 +147,54 @@ pci_csi_match(struct pci_device *csa, struct pci_device *csb) return pci_cs_match(csa, csb); } +/* + * Use by pci_read_caplist() to compare a value against + * known capabilities + */ +static void +pci_check_cap(struct pci_device *dev, uint32_t cap) +{ + uint8_t id; + + if (dev == NULL) { + return; + } + + id = cap & 0xFF; + switch (id) { + case 0x11: /* MSI-X */ + dev->msix = 1; + break; + case 0x05: + dev->msi = 1; + break; + } +} + +/* + * Parse a capability list of a PCI device + * + * @dev: Device of capability list to parse + */ +static int +pci_read_caplist(struct pci_device *dev) +{ + uint32_t cap; + uint8_t cap_ptr; + + if (dev == NULL) { + return -EINVAL; + } + + cap_ptr = pci_readl(dev, PCIREG_CAPPTR) & 0xFF; + do { + cap = pci_readl(dev, cap_ptr); + pci_check_cap(dev, cap); + cap_ptr = (cap >> 8) & 0xFF; + } while (cap_ptr != 0); + return 0; +} + /* * Attempt to register a PCI device and bail * if it doesn't exist on the bus. @@ -156,11 +204,12 @@ pci_register_dev(struct pci_device *dev) { struct pci_device *devp; pcireg_t vend_dev; - uint32_t classrev; + uint32_t classrev, cmdstatus; uint8_t class, subclass; uint8_t prog_if; uint16_t device_id; uint16_t vendor_id; + uint16_t status; if (dev == NULL) { return; @@ -177,6 +226,10 @@ pci_register_dev(struct pci_device *dev) subclass = PCIREG_SUBCLASS(classrev); prog_if = PCIREG_PROGIF(classrev); + /* Get the status */ + cmdstatus = pci_readl(dev, PCIREG_CMDSTATUS); + status = PCIREG_STATUS(cmdstatus); + /* Does this device exist? */ if (vendor_id == 0xFFFF) { return; @@ -187,6 +240,17 @@ pci_register_dev(struct pci_device *dev) dev->class = class; dev->subclass = subclass; dev->prog_if = prog_if; + dev->caplist = (status >> 4) & 1; + dev->msix = 0; + dev->msi = 0; + + /* + * Determine the device capabilities by parsing the + * capability list if supported. + */ + if (dev->caplist) { + pci_read_caplist(dev); + } /* Set up base address registers */ dev->bar[0] = pci_readl(dev, PCIREG_BAR0); -- cgit v1.2.3