From 14d6b4249d6f1e9e4e5ea942e8b304029ed28959 Mon Sep 17 00:00:00 2001 From: Matheus Date: Thu, 13 Jul 2023 22:52:44 -0300 Subject: [PATCH] pci: device enumeration with MCFG and legacy access --- kernel-src/arch/x86-64/main.c | 2 + kernel-src/include/kernel/pci.h | 41 ++++++++++++++ kernel-src/include/x86-64/arch/pci.h | 84 ++++++++++++++++++++++++++++ kernel-src/io/pci.c | 80 ++++++++++++++++++++++++++ 4 files changed, 207 insertions(+) create mode 100644 kernel-src/include/kernel/pci.h create mode 100644 kernel-src/include/x86-64/arch/pci.h create mode 100644 kernel-src/io/pci.c diff --git a/kernel-src/arch/x86-64/main.c b/kernel-src/arch/x86-64/main.c index 1c0e7a5a..ea161883 100644 --- a/kernel-src/arch/x86-64/main.c +++ b/kernel-src/arch/x86-64/main.c @@ -25,6 +25,7 @@ #include #include #include +#include static cpu_t bsp_cpu; @@ -65,6 +66,7 @@ void kernel_entry() { __assert(vfs_mount(NULL, vfsroot, "/dev", "devfs", NULL) == 0); pseudodevices_init(); + pci_init(); keyboard_init(); arch_ps2_init(); fb_init(); diff --git a/kernel-src/include/kernel/pci.h b/kernel-src/include/kernel/pci.h new file mode 100644 index 00000000..cea6dfb6 --- /dev/null +++ b/kernel-src/include/kernel/pci.h @@ -0,0 +1,41 @@ +#ifndef _PCI_H +#define _PCI_H + +// 2 byte fields +#define PCI_CONFIG_VENDOR 0x0 +#define PCI_CONFIG_DEVICEID 0x2 + +// 1 byte fields +#define PCI_CONFIG_REVISION 0x8 +#define PCI_CONFIG_PROGIF 0x9 +#define PCI_CONFIG_SUBCLASS 0xa +#define PCI_CONFIG_CLASS 0xb +#define PCI_CONFIG_HEADERTYPE 0xe + +#define PCI_HEADERTYPE_MULTIFUNCTION_MASK 0x80 +#define PCI_HEADERTYPE_MASK 0x7f +#define PCI_HEADERTYPE_STANDARD 0 + +typedef struct pcienum_t { + struct pcienum_t *next; + int bus; + int device; + int function; + int type; + int class; + int subclass; + int progif; + int vendor; + int deviceid; + int revision; +} pcienum_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); +void pci_init(); + +#endif diff --git a/kernel-src/include/x86-64/arch/pci.h b/kernel-src/include/x86-64/arch/pci.h new file mode 100644 index 00000000..972f9192 --- /dev/null +++ b/kernel-src/include/x86-64/arch/pci.h @@ -0,0 +1,84 @@ +#ifdef _PCIARCH_H +#error this file should only be included ONCE and only in the io/pci.c file +#else +#define _PCIARCH_H + +#include +#include +#include +#include +#include + +static size_t mcfgentrycount; +static mcfgentry_t *mcfgentries; +static uint32_t (*pci_archread32)(int bus, int device, int function, uint32_t offset); +static void (*pci_archwrite32)(int bus, int device, int function, uint32_t offset, uint32_t value); + +#define CONFADD 0xcf8 +#define CONFDATA 0xcfc + +static uint32_t legacy_read32(int bus, int device, int function, uint32_t offset) { + uint32_t confadd = 0x80000000 | (offset & ~0x3) | (function << 8) | (device << 11) | (bus << 16); + outd(CONFADD, confadd); + return ind(CONFDATA); +} + +static void legacy_write32(int bus, int device, int function, uint32_t offset, uint32_t value) { + uint32_t confadd = 0x80000000 | (offset & ~0x3) | (function << 8) | (device << 11) | (bus << 16); + outd(CONFADD, confadd); + outd(CONFDATA, value); +} + +static inline mcfgentry_t *getmcfgentry(int bus) { + mcfgentry_t *entry; + int i; + + for (i = 0; i < mcfgentrycount; ++i) { + entry = &mcfgentries[i]; + if (bus >= entry->startbus && bus <= entry->endbus) + break; + } + + if (i == mcfgentrycount) + entry = NULL; + + return entry; +} + +static uint32_t mcfg_read32(int bus, int device, int function, uint32_t offset) { + mcfgentry_t *entry = getmcfgentry(bus); + if (entry == NULL) + 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)); + + return *address; +} + +static void mcfg_write32(int bus, int device, int function, uint32_t offset, uint32_t value) { + 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)); + + *address = value; +} + +static void pci_archinit() { + mcfg_t *mcfg = arch_acpi_findtable("MCFG", 0); + if (mcfg) { + printf("pci: MCFG table found. Using extended configuration space\n"); + mcfgentries = mcfg->entries; + pci_archread32 = mcfg_read32; + pci_archwrite32 = mcfg_write32; + mcfgentrycount = (mcfg->header.length - sizeof(sdtheader_t)) / sizeof(mcfgentry_t); + // TODO map mcfg entries as UC + } else { + printf("pci: MCFG table not found. Falling back to legacy access mechanism\n"); + pci_archread32 = legacy_read32; + pci_archwrite32 = legacy_write32; + } +} + +#endif diff --git a/kernel-src/io/pci.c b/kernel-src/io/pci.c new file mode 100644 index 00000000..24d90543 --- /dev/null +++ b/kernel-src/io/pci.c @@ -0,0 +1,80 @@ +#include +#include +#include +#include + +uint8_t pci_read8(int bus, int device, int function, uint32_t offset) { + return (pci_archread32(bus, device, function, offset) >> ((offset & 0b11) * 8)) & 0xff; +} + +uint16_t pci_read16(int bus, int device, int function, uint32_t offset) { + return (pci_archread32(bus, device, function, offset) >> ((offset & 0b11) * 8)) & 0xffff; +} + +void pci_write8(int bus, int device, int function, uint32_t offset, uint8_t v) { + uint32_t old = pci_archread32(bus, device, function, offset); + int bitoffset = 8 * (offset & 0b11); + old &= ~(0xff << bitoffset); + old |= v << bitoffset; + pci_archwrite32(bus, device, function, offset, old); +} + +void pci_write16(int bus, int device, int function, uint32_t offset, uint16_t v) { + uint32_t old = pci_archread32(bus, device, function, offset); + int bitoffset = 8 * (offset & 0b11); + old &= ~(0xffff << bitoffset); + old |= v << bitoffset; + pci_archwrite32(bus, device, function, offset, old); +} + +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); +} + +static pcienum_t *enums; + +// reads all 1s if a function doesn't exist +#define DOESNTEXIST(bus, device, function) (pci_read32(bus, device, function, 0) == 0xffffffff) + +static void enumeratefunction(int bus, int device, int function) { + pcienum_t *e = alloc(sizeof(pcienum_t)); + __assert(e); + e->bus = bus; + e->device = device; + e->function = function; + e->next = enums; + e->class = pci_read8(bus, device, function, PCI_CONFIG_CLASS); + e->subclass = pci_read8(bus, device, function, PCI_CONFIG_SUBCLASS); + e->progif = pci_read8(bus, device, function, PCI_CONFIG_PROGIF); + e->vendor = pci_read16(bus, device, function, PCI_CONFIG_VENDOR); + e->deviceid = pci_read16(bus, device, function, PCI_CONFIG_DEVICEID); + 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); +} + +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; + + for (int i = 0; i < testcount; ++i) { + if (DOESNTEXIST(bus, device, i)) + continue; + enumeratefunction(bus, device, i); + } +} + +void pci_init() { + pci_archinit(); + for (size_t bus = 0; bus < 1; ++bus) { + for (size_t device = 0; device < 32; ++device) { + if (DOESNTEXIST(bus, device, 0)) + continue; + enumeratedevice(bus, device); + } + } +}