From 6491d1bd5257892574576df9f3fc90e8d908e191 Mon Sep 17 00:00:00 2001 From: Matheus Date: Sat, 15 Jul 2023 18:02:17 -0300 Subject: [PATCH] pci: pci_getenum, pci_getcapoffset and finding msi and msix on function enumeration --- kernel-src/include/kernel/pci.h | 23 ++++++++ kernel-src/include/x86-64/arch/pci.h | 4 +- kernel-src/io/pci.c | 88 ++++++++++++++++++++++++++-- 3 files changed, 109 insertions(+), 6 deletions(-) diff --git a/kernel-src/include/kernel/pci.h b/kernel-src/include/kernel/pci.h index cea6dfb6..1a458d22 100644 --- a/kernel-src/include/kernel/pci.h +++ b/kernel-src/include/kernel/pci.h @@ -4,6 +4,8 @@ // 2 byte fields #define PCI_CONFIG_VENDOR 0x0 #define PCI_CONFIG_DEVICEID 0x2 +#define PCI_CONFIG_STATUS 0x6 +#define PCI_STATUS_HASCAP 0x10 // 1 byte fields #define PCI_CONFIG_REVISION 0x8 @@ -16,6 +18,16 @@ #define PCI_HEADERTYPE_MASK 0x7f #define PCI_HEADERTYPE_STANDARD 0 +#define PCI_CONFIG_CAP 0x34 + +#define PCI_CAP_MSI 0x5 +#define PCI_CAP_MSIX 0x11 + +typedef struct { + bool exists; + int offset; +} pcicap_t; + typedef struct pcienum_t { struct pcienum_t *next; int bus; @@ -28,14 +40,25 @@ typedef struct pcienum_t { int vendor; int deviceid; int revision; + pcicap_t msi; + pcicap_t msix; } pcienum_t; +typedef struct { + uintptr_t address; + size_t length; + bool mmio; +} 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); void pci_write8(int bus, int device, int function, uint32_t offset, uint8_t v); void pci_write16(int bus, int device, int function, uint32_t offset, uint16_t v); void pci_write32(int bus, int device, int function, uint32_t offset, uint32_t value); +pcienum_t *pci_getenum(int class, int subclass, int progif, int vendor, int deviceid, int revision, int n); +int pci_getcapoffset(pcienum_t *e, int cap, int n); +pcibar_t pci_getbar(pcienum_t *e, int bar); 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 972f9192..ec196775 100644 --- a/kernel-src/include/x86-64/arch/pci.h +++ b/kernel-src/include/x86-64/arch/pci.h @@ -51,7 +51,7 @@ static uint32_t mcfg_read32(int bus, int device, int function, uint32_t offset) return 0xffffffff; uint64_t base = (uint64_t)MAKE_HHDM(entry->address); - uint32_t *address = (uint32_t *)(base + ((bus - entry->startbus) << 20) + (device << 15) + (function << 12)); + uint32_t *address = (uint32_t *)(base | ((bus - entry->startbus) << 20) | (device << 15) | (function << 12) | (offset & ~0x3)); return *address; } @@ -60,7 +60,7 @@ static void mcfg_write32(int bus, int device, int function, uint32_t offset, uin mcfgentry_t *entry = getmcfgentry(bus); uint64_t base = (uint64_t)MAKE_HHDM(entry->address); - uint32_t *address = (uint32_t *)(base + ((bus - entry->startbus) << 20) + (device << 15) + (function << 12)); + uint32_t *address = (uint32_t *)(base | ((bus - entry->startbus) << 20) | (device << 15) | (function << 12) | (offset & ~0x3)); *address = value; } diff --git a/kernel-src/io/pci.c b/kernel-src/io/pci.c index 24d90543..0710a261 100644 --- a/kernel-src/io/pci.c +++ b/kernel-src/io/pci.c @@ -30,12 +30,79 @@ void pci_write16(int bus, int device, int function, uint32_t offset, uint16_t v) uint32_t pci_read32(int bus, int device, int function, uint32_t offset) { return pci_archread32(bus, device, function, offset); } + void pci_write32(int bus, int device, int function, uint32_t offset, uint32_t value) { - return pci_archwrite32(bus, device, function, offset, value); + pci_archwrite32(bus, device, function, offset, value); +} + +int pci_getcapoffset(pcienum_t *e, int cap, int n) { + if ((pci_read16(e->bus, e->device, e->function, PCI_CONFIG_STATUS) & PCI_STATUS_HASCAP) == 0) + return 0; + + int offset = pci_read8(e->bus, e->device, e->function, PCI_CONFIG_CAP); + while (offset) { + if (pci_read8(e->bus, e->device, e->function, offset) == cap) { + if (n-- == 0) + break; + } + + offset = pci_read8(e->bus, e->device, e->function, offset + 1); + } + + return offset; +} + +#define BAR_IO 1 +#define BAR_TYPE_MASK 0x6 +#define BAR_TYPE_64BIT 0x4 + +pcibar_t pci_getbar(pcienum_t *e, int n) { + // TODO map bars (also taking in count the prefetchable flag) + pcibar_t ret; + int offset = 0x10 + n * 4; + + uint32_t bar = pci_read32(e->bus, e->device, e->function, offset); + if (bar & BAR_IO) { + ret.mmio = false; + ret.address = bar & 0xfffffffc; + pci_write32(e->bus, e->device, e->function, offset, -1); + ret.length = ~(pci_read32(e->bus, e->device, e->function, offset) & 0xfffffffc) + 1; + pci_write32(e->bus, e->device, e->function, offset, bar); + } else { + ret.mmio = true; + ret.address = bar & 0xfffffff0; + ret.length = -1; + if ((bar & BAR_TYPE_MASK) == BAR_TYPE_64BIT) + ret.address = (uint64_t)pci_read32(e->bus, e->device, e->function, offset + 4) << 32; + + 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); + } + + return ret; } static pcienum_t *enums; +pcienum_t *pci_getenum(int class, int subclass, int progif, int vendor, int deviceid, int revision, int n) { + pcienum_t *e; + for (e = enums; e; e = e->next) { + if ( + (class == -1 || e->class == class) && + (subclass == -1 || e->subclass == subclass) && + (progif == -1 || e->progif == progif) && + (vendor == -1 || e->vendor == vendor) && + (deviceid == -1 || e->deviceid == deviceid) && + (revision == -1 || e->revision == revision)) { + if (n-- == 0) + break; + } + } + + return e; +} + // reads all 1s if a function doesn't exist #define DOESNTEXIST(bus, device, function) (pci_read32(bus, device, function, 0) == 0xffffffff) @@ -54,12 +121,25 @@ static void enumeratefunction(int bus, int device, int function) { e->revision = pci_read8(bus, device, function, PCI_CONFIG_REVISION); enums = e; - printf("pci: found %02x:%02x.%x %02x%02x: %04x:%04x (rev %2x) \n", bus, device, function, e->class, e->subclass, e->vendor, e->device, e->revision); + int offset = pci_getcapoffset(e, PCI_CAP_MSIX, 0); + if (offset) { + e->msix.exists = true; + e->msix.offset = offset; + } + + offset = pci_getcapoffset(e, PCI_CAP_MSI, 0); + if (offset) { + e->msi.exists = true; + e->msi.offset = offset; + } + + char *irqtype = e->msix.exists ? "msi-x" : (e->msi.exists ? "msi" : "no interrupts"); + printf("pci: found %02x:%02x.%x %02x%02x: %04x:%04x (rev %2x) (%s)\n", bus, device, function, e->class, e->subclass, e->vendor, e->device, e->revision, irqtype); } static void enumeratedevice(int bus, int device) { - bool multifunction = pci_read32(bus, device, 0, PCI_CONFIG_HEADERTYPE) & PCI_HEADERTYPE_MULTIFUNCTION_MASK; - size_t testcount = multifunction ? 1 : 8; + bool multifunction = pci_read8(bus, device, 0, PCI_CONFIG_HEADERTYPE) & PCI_HEADERTYPE_MULTIFUNCTION_MASK; + size_t testcount = multifunction ? 8 : 1; for (int i = 0; i < testcount; ++i) { if (DOESNTEXIST(bus, device, i))