From 1879bfcc20877d9fcc4d252bc5e084e8bd56646f Mon Sep 17 00:00:00 2001 From: Matheus Date: Wed, 19 Jul 2023 15:08:51 -0300 Subject: [PATCH] pci: msi-x support --- kernel-src/include/kernel/pci.h | 29 ++++++++++---- kernel-src/include/x86-64/arch/pci.h | 6 +++ kernel-src/io/pci.c | 58 ++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 8 deletions(-) diff --git a/kernel-src/include/kernel/pci.h b/kernel-src/include/kernel/pci.h index cc15a95b..6c780ba1 100644 --- a/kernel-src/include/kernel/pci.h +++ b/kernel-src/include/kernel/pci.h @@ -11,7 +11,7 @@ #define PCI_CONFIG_COMMAND 0x4 #define PCI_COMMAND_IO 0x1 #define PCI_COMMAND_MMIO 0x2 - #define PCI_COMMAND_IRQ 0x400 + #define PCI_COMMAND_IRQDISABLE 0x400 #define PCI_CONFIG_STATUS 0x6 #define PCI_STATUS_HASCAP 0x10 @@ -35,6 +35,13 @@ typedef struct { int offset; } pcicap_t; +typedef struct { + uintptr_t address; + size_t length; + bool mmio; + bool prefetchable; +} pcibar_t; + typedef struct pcienum_t { struct pcienum_t *next; int bus; @@ -49,15 +56,18 @@ typedef struct pcienum_t { int revision; pcicap_t msi; pcicap_t msix; + pcibar_t bar[6]; + union { + struct { + int bir; + uintmax_t tableoffset; + int pbir; + uintmax_t pboffset; + size_t entrycount; + } msix; + } irq; } pcienum_t; -typedef struct { - uintptr_t address; - size_t length; - bool mmio; - bool prefetchable; -} pcibar_t; - uint8_t pci_read8(int bus, int device, int function, uint32_t offset); uint16_t pci_read16(int bus, int device, int function, uint32_t offset); uint32_t pci_read32(int bus, int device, int function, uint32_t offset); @@ -69,6 +79,9 @@ int pci_getcapoffset(pcienum_t *e, int cap, int n); pcibar_t pci_getbar(pcienum_t *e, int bar); void *pci_mapbar(pcibar_t bar); void pci_setcommand(pcienum_t *e, int mask, int v); +size_t pci_initmsix(pcienum_t *e); +void pci_msixsetmask(pcienum_t *e, int v); +void pci_msixadd(pcienum_t *e, int msixvec, int vec, int edgetrigger, int deassert); void pci_init(); #endif diff --git a/kernel-src/include/x86-64/arch/pci.h b/kernel-src/include/x86-64/arch/pci.h index ec196775..0a9b2c7b 100644 --- a/kernel-src/include/x86-64/arch/pci.h +++ b/kernel-src/include/x86-64/arch/pci.h @@ -8,6 +8,7 @@ #include #include #include +#include static size_t mcfgentrycount; static mcfgentry_t *mcfgentries; @@ -65,6 +66,11 @@ static void mcfg_write32(int bus, int device, int function, uint32_t offset, uin *address = value; } +static uint64_t msiformatmessage(uint32_t *data, int vector, int edgetrigger, int deassert) { + *data = (edgetrigger << 15) | (deassert << 14) | vector; + return 0xfee00000 | (_cpu()->id << 12); +} + static void pci_archinit() { mcfg_t *mcfg = arch_acpi_findtable("MCFG", 0); if (mcfg) { diff --git a/kernel-src/io/pci.c b/kernel-src/io/pci.c index d9c1f70d..3f6acad8 100644 --- a/kernel-src/io/pci.c +++ b/kernel-src/io/pci.c @@ -64,6 +64,58 @@ void pci_setcommand(pcienum_t *e, int mask, int v) { pci_write16(e->bus, e->device, e->function, PCI_CONFIG_COMMAND, current); } +typedef struct { + uint32_t addresslow; + uint32_t addresshigh; + uint32_t data; + uint32_t control; +} __attribute__((packed)) msixmessage_t; + +void pci_msixadd(pcienum_t *e, int msixvec, int vec, int edgetrigger, int deassert) { + __assert(e->msix.exists); + __assert(msixvec < e->irq.msix.entrycount); + + pcibar_t bir = pci_getbar(e, e->irq.msix.bir); + + volatile msixmessage_t *table = (msixmessage_t *)(bir.address + e->irq.msix.tableoffset); + uint32_t data; + uint64_t address = msiformatmessage(&data, vec, edgetrigger, deassert); + + table[msixvec].addresslow = address & 0xffffffff; + table[msixvec].addresshigh = (address >> 32) & 0xffffffff; + table[msixvec].data = data; + table[msixvec].control = 0; +} + +void pci_msixsetmask(pcienum_t *e, int v) { + uint16_t msgctl = pci_read16(e->bus, e->device, e->function, e->msix.offset + 2); + msgctl = v ? msgctl | 0x4000 : msgctl & ~0x4000; + pci_write16(e->bus, e->device, e->function, e->msix.offset + 2, msgctl); +} + +size_t pci_initmsix(pcienum_t *e) { + if (e->msix.exists == false) + return 0; + + uint16_t msgctl = pci_read16(e->bus, e->device, e->function, e->msix.offset + 2); + msgctl |= 0xc000; + pci_write16(e->bus, e->device, e->function, e->msix.offset + 2, msgctl); + + e->irq.msix.entrycount = (msgctl & 0x3ff) + 1; + + uint32_t bir = pci_read32(e->bus, e->device, e->function, e->msix.offset + 4); + e->irq.msix.bir = bir & 0x7; + e->irq.msix.tableoffset = bir & ~0x7; + + bir = pci_read32(e->bus, e->device, e->function, e->msix.offset + 8); + e->irq.msix.pbir = bir & 0x7; + e->irq.msix.pboffset = bir & ~0x7; + + pci_setcommand(e, PCI_COMMAND_IRQDISABLE, 1); + + return e->irq.msix.entrycount; +} + #define BAR_MAP_FLAGS (ARCH_MMU_FLAGS_WRITE | ARCH_MMU_FLAGS_READ | ARCH_MMU_FLAGS_NOEXEC) void *pci_mapbar(pcibar_t bar) { @@ -77,6 +129,9 @@ void *pci_mapbar(pcibar_t bar) { #define BAR_PREFETCHABLE 0x8 pcibar_t pci_getbar(pcienum_t *e, int n) { + if (e->bar[n].length) + return e->bar[n]; + pcibar_t ret; int offset = 0x10 + n * 4; @@ -99,8 +154,11 @@ pcibar_t pci_getbar(pcienum_t *e, int n) { pci_write32(e->bus, e->device, e->function, offset, -1); ret.length = ~(pci_read32(e->bus, e->device, e->function, offset) & 0xfffffff0) + 1; pci_write32(e->bus, e->device, e->function, offset, bar); + ret.address = (uint64_t)pci_mapbar(ret); + __assert(ret.address); } + e->bar[n] = ret; return ret; }