From bf9ad82ee961b839654e2322f083c13532f2c0c6 Mon Sep 17 00:00:00 2001 From: Ian Moffett Date: Thu, 11 Jul 2024 01:30:14 -0400 Subject: kernel/amd64: pci: Add support for PCI MSI-X Signed-off-by: Ian Moffett --- sys/arch/amd64/pci/pci_machdep.c | 65 ++++++++++++++++++++++++++++++++++++++++ sys/dev/pci/pci.c | 43 ++++++++++++++++++++++++++ sys/include/dev/pci/pci.h | 8 +++++ sys/include/dev/pci/pciregs.h | 5 ++++ 4 files changed, 121 insertions(+) diff --git a/sys/arch/amd64/pci/pci_machdep.c b/sys/arch/amd64/pci/pci_machdep.c index 50d0d57..ea34b25 100644 --- a/sys/arch/amd64/pci/pci_machdep.c +++ b/sys/arch/amd64/pci/pci_machdep.c @@ -30,10 +30,14 @@ #include #include #include +#include #include #include #include #include +#include +#include +#include /* Base address masks for BARs */ #define PCI_BAR_MEMMASK ~7 @@ -121,3 +125,64 @@ pci_map_bar(struct pci_device *dev, uint8_t barno, void **vap) bar = dev->bar[barno] & PCI_BAR_MEMMASK; return bus_map(bar, size, 0, vap); } + +/* + * Enable MSI-X for a device and allocate an + * interrupt vector. + * + * @dev: Device to enable MSI-X for. + * @intr: MSI-X interrupt descriptor. + */ +int +pci_enable_msix(struct pci_device *dev, const struct msi_intr *intr) +{ + volatile uint64_t *tbl; + struct cpu_info *ci; + uint32_t data, msg_ctl; + uint64_t msg_addr, tmp; + uint16_t tbl_off; + uint8_t bir; + uint8_t vector; + + if (dev->msix_capoff == 0) + return -ENOTSUP; + + /* Get the data from cap offset 0x04 */ + data = pci_readl(dev, (dev->msix_capoff + 0x04)); + bir = data & 3; + tbl_off = data & ~3; + + ci = this_cpu(); + msg_addr = (0xFEE00000 | (ci->apicid << 12)); + + /* Calculate the start of the message table */ + tbl = (void *)((dev->bar[bir] & PCI_BAR_MEMMASK) + MMIO_OFFSET); + tbl = (void *)((char *)tbl + tbl_off); + + /* Get the vector and setup handler */ + vector = intr_alloc_vector(intr->name, IPL_BIO); + idt_set_desc(vector, IDT_INT_GATE, ISR(intr->handler), 0); + + /* + * Setup the message data at bits 95:64 of the message + * table by ORing the interrupt vector to it. We also + * unmask the interrupt with bit 1 of the vector control. + */ + tmp = mmio_read64(&tbl[1]); + tmp |= vector; + tmp &= ~BIT(32); + + /* Write the message table */ + mmio_write64(&tbl[0], msg_addr); + mmio_write64(&tbl[1], tmp); + + /* + * Set bit 16 of message control to enable MSI-X. + * Message control lives at cap offset 0x00 in bits + * 31:16. + */ + msg_ctl = pci_readl(dev, dev->msix_capoff); + msg_ctl |= BIT(31); + pci_writel(dev, dev->msix_capoff, msg_ctl); + return 0; +} diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c index 587881a..65f315d 100644 --- a/sys/dev/pci/pci.c +++ b/sys/dev/pci/pci.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +58,47 @@ pci_dev_exists(uint8_t bus, uint8_t slot, uint8_t func) return true; } +/* + * Attempt to search for a capability within the device + * capability list if it supports one. + * + * @dev: Target device. + * @id: Requested capability ID. + * + * The offset is returned if found, otherwise 0. + * A value less than zero is returned on error. + */ +static int +pci_get_cap(struct pci_device *dev, uint8_t id) +{ + uint16_t status; + uint32_t cap; + uint8_t curid; + uint8_t cap_ptr; + + /* Does the device even support this? */ + status = pci_readl(dev, PCIREG_CMDSTATUS) >> 16; + if (!ISSET(status, PCI_STATUS_CAPLIST)) { + return -ENOTSUP; + } + + cap_ptr = pci_readl(dev, PCIREG_CAPPTR) & 0xFF; + + /* Go through the capability list */ + while (cap_ptr != 0) { + cap = pci_readl(dev, cap_ptr); + curid = cap & 0xFF; + + if (curid == id) { + return cap_ptr; + } + + cap_ptr = (cap >> 8) & 0xFF; + } + + return 0; +} + /* * Sets other device information (device id, vendor id, etc) * @@ -85,6 +127,7 @@ pci_set_device_info(struct pci_device *dev) dev->bar[5] = pci_readl(dev, PCIREG_BAR5); dev->irq_line = pci_readl(dev, PCIREG_IRQLINE) & 0xFF; + dev->msix_capoff = pci_get_cap(dev, PCI_CAP_MSIX); } /* diff --git a/sys/include/dev/pci/pci.h b/sys/include/dev/pci/pci.h index 3340e1a..497bfc7 100644 --- a/sys/include/dev/pci/pci.h +++ b/sys/include/dev/pci/pci.h @@ -54,6 +54,7 @@ struct pci_device { uint8_t slot; uint8_t func; + uint16_t msix_capoff; uint16_t device_id; uint16_t vendor_id; uint8_t pci_class; @@ -64,11 +65,18 @@ struct pci_device { TAILQ_ENTRY(pci_device) link; }; +struct msi_intr { + const char *name; + void(*handler)(void *); +}; + pcireg_t pci_readl(struct pci_device *dev, uint32_t offset); struct pci_device *pci_get_device(struct pci_lookup lookup, uint16_t lookup_type); int pci_map_bar(struct pci_device *dev, uint8_t barno, void **vap); void pci_writel(struct pci_device *dev, uint32_t offset, pcireg_t val); + +int pci_enable_msix(struct pci_device *dev, const struct msi_intr *intr); int pci_init(void); #endif /* !_PCI_H_ */ diff --git a/sys/include/dev/pci/pciregs.h b/sys/include/dev/pci/pciregs.h index c1b02e9..763fade 100644 --- a/sys/include/dev/pci/pciregs.h +++ b/sys/include/dev/pci/pciregs.h @@ -42,6 +42,7 @@ #define PCIREG_BAR4 0x20 /* 32 bits */ #define PCIREG_BAR5 0x24 /* 32 bits */ #define PCIREG_IRQLINE 0x3C /* 8 bits */ +#define PCIREG_CAPPTR 0x34 /* 8 bits */ #define PCIREG_CMDSTATUS 0x04 /* command (15:0), status (31:16) */ /* Macros to extract PCIREG_CLASSREV bits */ @@ -64,4 +65,8 @@ #define PCI_STATUS_CAPLIST BIT(4) #define PCI_STATUS_66MHZ BIT(5) +/* Capability IDs */ +#define PCI_CAP_MSI 0x05 +#define PCI_CAP_MSIX 0x11 + #endif /* _PCI_PCIREGS_H_ */ -- cgit v1.2.3