From 6a95990b68ba6de98fc41452f3d3cf002e6287c6 Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 27 Jan 2020 17:53:57 +0200 Subject: [PATCH] Some basic framework for PCI(e) enumeration --- include/sys/amd64/hw/ahci.h | 4 +- include/sys/amd64/hw/pci/pci.h | 8 ++ sys/amd64/hw/irq.c | 28 ++++ sys/amd64/hw/irqs_s.S | 11 ++ sys/amd64/hw/pci/ahci.c | 46 +++--- sys/amd64/hw/pci/pci.c | 246 ++++++++++++++++++++++++++++++++- 6 files changed, 314 insertions(+), 29 deletions(-) diff --git a/include/sys/amd64/hw/ahci.h b/include/sys/amd64/hw/ahci.h index 51bac16..d34daad 100644 --- a/include/sys/amd64/hw/ahci.h +++ b/include/sys/amd64/hw/ahci.h @@ -1,7 +1,7 @@ #pragma once -#include "sys/amd64/hw/pci/pci.h" struct ahci_registers; +struct pci_device; struct ahci_fis_reg_h2d { uint8_t type; @@ -26,7 +26,7 @@ struct ahci_fis_reg_h2d { }; struct ahci_controller { -// pci_addr_t addr; + struct pci_device *pci_dev; uintptr_t abar_phys; struct ahci_registers *regs; }; diff --git a/include/sys/amd64/hw/pci/pci.h b/include/sys/amd64/hw/pci/pci.h index 722a9b1..c80b0f4 100644 --- a/include/sys/amd64/hw/pci/pci.h +++ b/include/sys/amd64/hw/pci/pci.h @@ -10,14 +10,22 @@ #define PCI_CONFIG_INFO 0x0C #define PCI_CONFIG_BAR(n) (0x10 + (n) * 4) #define PCI_CONFIG_SUBSYSTEM 0x2C +#define PCI_CONFIG_CAPABILITIES 0x34 #define PCI_CONFIG_IRQ 0x3C #define PCI_CONFIG_BRIDGE 0x18 #define PCI_ID(vnd, dev) (((uint32_t) (vnd)) | ((uint32_t) (dev) << 16)) +struct pci_device; +typedef void (*pci_driver_func_t)(struct pci_device *dev); + void pci_init(void); void pci_add_root_bus(uint8_t n); +uint32_t pci_config_read_dword(struct pci_device *dev, uint16_t off); + +void pci_add_class_driver(uint32_t full_class, pci_driver_func_t func); + // pcidb.c const char *pci_class_string(uint16_t full_class); diff --git a/sys/amd64/hw/irq.c b/sys/amd64/hw/irq.c index 1f4ee69..f1f9c76 100644 --- a/sys/amd64/hw/irq.c +++ b/sys/amd64/hw/irq.c @@ -136,8 +136,12 @@ int irq_has_handler(uint8_t gsi) { return !!handler_list[0].func; } +extern void amd64_msi_handler(); + void irq_init(int cpu) { + // Special entry amd64_idt_set(cpu, 32, (uintptr_t) amd64_irq0, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32); + amd64_idt_set(cpu, 33, (uintptr_t) amd64_irq1, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32); amd64_idt_set(cpu, 34, (uintptr_t) amd64_irq2, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32); amd64_idt_set(cpu, 35, (uintptr_t) amd64_irq3, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32); @@ -154,6 +158,30 @@ void irq_init(int cpu) { amd64_idt_set(cpu, 46, (uintptr_t) amd64_irq14, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32); amd64_idt_set(cpu, 47, (uintptr_t) amd64_irq15, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32); + // Message signaled interrupt support + // Message address register format: + // 0 .. 1 Unused + // 2 Destination mode + // 3 Redirection hint + // 4 .. 11 Reserved + // 12 .. 19 Destination APIC ID + // 31 .. 20 0xFEE + // + // DM RH Behavior + // 0 0 Destination ID field specifies target CPU + // 1 0 Destination ID must point to a valid cpu + // 0 1 Only matching CPU receives interrupt (without redirection) + // 1 1 Redirection is limited to only the Destination ID's logical group + // + // Message data register format: + // 0 .. 7 Interrupt vector (0x80) + // 8 .. 10 Delivery mode + // 11 .. 13 Reserved + // 14 Trigger level (don't care, all are edge-trigggered) + // 15 Level/Edge trigger + // 16 .. 63 Reserved + amd64_idt_set(cpu, 0x80, (uintptr_t) amd64_msi_handler, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32); + #if defined(AMD64_MAX_SMP) // Common for all CPUs amd64_idt_set(cpu, IPI_VECTOR_GENERIC, (uintptr_t) amd64_irq_ipi, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32); diff --git a/sys/amd64/hw/irqs_s.S b/sys/amd64/hw/irqs_s.S index 4c5d494..894a1a7 100644 --- a/sys/amd64/hw/irqs_s.S +++ b/sys/amd64/hw/irqs_s.S @@ -46,3 +46,14 @@ _msg0: amd64_idt_load: lidt (%rdi) ret + +.global amd64_msi_handler +amd64_msi_handler: + cli + leaq _msg1(%rip), %rsi + movq $1, %rdi + call debugs + hlt + +_msg1: + .string "MSI handler\n" diff --git a/sys/amd64/hw/pci/ahci.c b/sys/amd64/hw/pci/ahci.c index a0afc03..f56ded3 100644 --- a/sys/amd64/hw/pci/ahci.c +++ b/sys/amd64/hw/pci/ahci.c @@ -553,7 +553,7 @@ static void ahci_port_init(struct ahci_controller *ahci, struct ahci_port *port, ahci_port_add(port); } -/* static */ void ahci_controller_init(struct ahci_controller *ahci) { +static void ahci_controller_init(struct ahci_controller *ahci) { // Check controller version kinfo("AHCI controller version is %02x.%02x\n", (ahci->regs->vs >> 16), (ahci->regs->vs & 0xFFFF)); @@ -582,27 +582,23 @@ static void ahci_port_init(struct ahci_controller *ahci, struct ahci_port *port, //// PCI-specific -//static void pci_ahci_init(pci_addr_t addr) { -// //uint32_t info = pci_config_read_dword(addr, PCI_CONFIG_CLASS); -// -// //if (((info >> 8) & 0xFF) == 0x01) { -// // // AHCI 1.0 controller -// // uint32_t abar_phys = pci_config_read_dword(addr, PCI_CONFIG_BAR(5)); -// // if (abar_phys & 1) { -// // kwarn("AHCI controller " PCI_FMTADDR " has ABAR in I/O space\n", PCI_VAADDR(addr)); -// // return; -// // } -// -// // struct ahci_controller *obj = kmalloc(sizeof(struct ahci_controller)); -// -// // obj->addr = addr; -// // obj->abar_phys = abar_phys; -// // obj->regs = (void *) MM_VIRTUALIZE(abar_phys); -// -// // ahci_controller_init(obj); -// //} -//} -// -//static __init void ahci_register_class(void) { -// pci_add_class_driver(0x0106, pci_ahci_init); -//} +static void pci_ahci_init(struct pci_device *pci_dev) { + // TODO: change pcie -> pci + uint32_t abar_phys = pci_config_read_dword(pci_dev, PCI_CONFIG_BAR(5)); + if (abar_phys & 1) { + kwarn("AHCI controller has ABAR in I/O space\n"); + return; + } + + struct ahci_controller *obj = kmalloc(sizeof(struct ahci_controller)); + + obj->pci_dev = pci_dev; + obj->abar_phys = abar_phys; + obj->regs = (void *) MM_VIRTUALIZE(abar_phys); + + ahci_controller_init(obj); +} + +static __init void ahci_register_class(void) { + pci_add_class_driver(0x010601, pci_ahci_init); +} diff --git a/sys/amd64/hw/pci/pci.c b/sys/amd64/hw/pci/pci.c index 28eb80d..74a0722 100644 --- a/sys/amd64/hw/pci/pci.c +++ b/sys/amd64/hw/pci/pci.c @@ -2,13 +2,255 @@ #include "sys/amd64/hw/ioapic.h" #include "sys/amd64/hw/acpi.h" #include "sys/amd64/hw/io.h" -#include "sys/panic.h" #include "sys/assert.h" +#include "sys/panic.h" #include "sys/debug.h" +#include "sys/heap.h" +#include "sys/mm.h" + +#define PCI_MAX_DRIVERS 64 + +#define pcie_config_read_dword(dev, off) \ + (*(uint32_t *) ((dev)->pcie_config + (off))) + +#define PCI_CAP_MSI_64 (1 << 7) +#define PCI_CAP_MSI_EN (1 << 0) +struct pci_cap_msi { + uint8_t cap_id; + uint8_t cap_link; + uint16_t message_control; + union { + struct { + uint32_t message_address; + uint16_t message_data; + } __attribute__((packed)) msi32; + struct { + uint64_t message_address; + uint16_t message_data; + } __attribute__((packed)) msi64; + }; +} __attribute__((packed)); + +struct pci_device { + // PCI address + uint8_t bus; + uint8_t dev; + uint8_t func; + + // PCIe addressing: segment group number and configuration space pointer + uint16_t pcie_segment_group; + void *pcie_config; + + // Interrupt resources + struct pci_cap_msi *msi; + int irq_pin; + + // For list + struct pci_device *next; +}; + +struct pci_driver { + pci_driver_func_t init_func; + uint32_t type; + uint32_t match; +}; + +static struct pci_device *g_pci_devices = NULL; +static struct pci_driver g_pci_drivers[PCI_MAX_DRIVERS] = {0}; +static size_t g_pci_driver_count; + +#define PCI_DRIVER_CLASS 1 +#define PCI_DRIVER_DEV 2 + +uint32_t pci_config_read_dword(struct pci_device *dev, uint16_t off) { + // TODO: check if device is really PCIe + return pcie_config_read_dword(dev, off); +} + +void pci_add_class_driver(uint32_t full_class, pci_driver_func_t func) { + if (g_pci_driver_count == PCI_MAX_DRIVERS) { + panic("Too many PCI drivers loaded\n"); + } + + struct pci_driver *driver = &g_pci_drivers[g_pci_driver_count++]; + + driver->init_func = func; + driver->type = PCI_DRIVER_CLASS; + driver->match = full_class & ~0xFF000000; +} void pci_add_root_bus(uint8_t n) { kwarn("%s: %02x\n", __func__, n); } -void pci_init(void) { +static void pci_pick_driver(uint32_t device_id, uint32_t class_id, pci_driver_func_t *class_driver, pci_driver_func_t *dev_driver) { + pci_driver_func_t rclass = NULL, rdev = NULL; + for (size_t i = 0; i < g_pci_driver_count; ++i) { + if (rclass && rdev) { + break; + } + + if (g_pci_drivers[i].type == PCI_DRIVER_CLASS && !rclass) { + uint32_t match = g_pci_drivers[i].match; + // Check if prog_if has to be matched + // 0xFF means (match all prog. IF) + if ((match & 0xFF) == 0xFF) { + match &= ~0xFF; + class_id &= ~0xFF; + } + + if (match == class_id) { + rclass = g_pci_drivers[i].init_func; + continue; + } + } else if (g_pci_drivers[i].type == PCI_DRIVER_DEV && !rdev) { + if (device_id == g_pci_drivers[i].match) { + rdev = g_pci_drivers[i].init_func; + continue; + } + } + } + + *class_driver = rclass; + *dev_driver = rdev; +} + +static void pci_device_add(struct pci_device *dev) { + dev->next = g_pci_devices; + g_pci_devices = dev; +} + +static int pcie_device_setup(struct pci_device *dev) { + uint32_t class, caps_offset, irq_info; + uint32_t id; + uint8_t irq_pin; + + class = pcie_config_read_dword(dev, PCI_CONFIG_CLASS); + caps_offset = pcie_config_read_dword(dev, PCI_CONFIG_CAPABILITIES) & 0xFF; + irq_info = pcie_config_read_dword(dev, PCI_CONFIG_IRQ); + id = pcie_config_read_dword(dev, PCI_CONFIG_ID); + + kinfo("%02x:%02x:%02x:\n", dev->bus, dev->dev, dev->func); + kinfo(" Class %02x:%02x:%02x\n", (class >> 24), (class >> 16) & 0xFF, (class >> 8) & 0xFF); + kinfo(" Device %04x:%04x\n", id & 0xFFFF, (id >> 16) & 0xFFFF); + + irq_pin = (irq_info >> 8) & 0xFF; + if (irq_pin) { + dev->irq_pin = irq_pin - 1; + kinfo(" IRQ pin INT%c#\n", dev->irq_pin + 'A'); + } + + while (caps_offset) { + uint8_t *link = (uint8_t *) (dev->pcie_config + caps_offset); + + switch (link[0]) { + case 0x05: + kinfo(" * MSI capability\n"); + dev->msi = (struct pci_cap_msi *) link; + break; + case 0x10: + kinfo(" * PCIe capability\n"); + break; + default: + // Unknown capability + kinfo(" * Device capability: %02x\n", link[0]); + break; + } + + caps_offset = link[1]; + } + + pci_driver_func_t driver_class, driver_dev; + pci_pick_driver(id, class >> 8, &driver_class, &driver_dev); + + if (driver_dev) { + driver_dev(dev); + return 0; + } + + if (driver_class) { + driver_class(dev); + return 0; + } + + return -1; +} + +static void pcie_enumerate_device(uintptr_t base_address, uint16_t seg, uint8_t start_bus, uint8_t bus, uint8_t dev_no) { + void *cfg; + uint32_t id; + + for (uint8_t func = 0; func < 8; ++func) { + uint32_t d = ((uint32_t) (bus - start_bus) << 20) | ((uint32_t) dev_no << 15) | ((uint32_t) func << 12); + cfg = (void *) MM_VIRTUALIZE(base_address + d); + id = *(uint32_t *) cfg; + if ((id & 0xFFFF) == 0xFFFF) { + continue; + } + + struct pci_device *dev = kmalloc(sizeof(struct pci_device)); + _assert(dev); + dev->bus = bus; + dev->dev = dev_no; + dev->func = func; + + dev->pcie_segment_group = seg; + dev->pcie_config = cfg; + dev->msi = NULL; + dev->irq_pin = -1; + + pcie_device_setup(dev); + pci_device_add(dev); + } +} + +static void pcie_enumerate_bus(uintptr_t base_address, uint16_t seg, uint8_t start_bus, uint8_t bus) { + for (uint8_t dev = 0; dev < 32; ++dev) { + // Check if function 0 is present + void *cfg = (void *) MM_VIRTUALIZE(base_address + (((bus - start_bus) << 20) | (dev << 15))); + + if (((*(uint32_t *) cfg) & 0xFFFF) != 0xFFFF) { + uint32_t header = *(uint32_t *) (cfg + 0x0C); + header >>= 16; + header &= 0x7F; + + switch (header) { + case 0x00: + pcie_enumerate_device(base_address, seg, start_bus, bus, dev); + break; + default: + kwarn("Skipping unsupported header type: %02x\n", header); + break; + } + } + } +} + +static void pcie_enumerate_segment(uintptr_t base_address, uint16_t seg, uint8_t start_bus, uint8_t end_bus) { + for (uint16_t bus = start_bus; bus < end_bus; ++bus) { + pcie_enumerate_bus(base_address, seg, start_bus, bus); + } +} + +void pci_init(void) { + if (!acpi_mcfg) { + panic("TODO: legacy PCI\n"); + } + + uint32_t mcfg_entry_count = (acpi_mcfg->hdr.length - sizeof(struct acpi_header) - 8) / sizeof(struct acpi_mcfg_entry); + kinfo("MCFG has %u entries:\n", mcfg_entry_count); + for (uint32_t i = 0; i < mcfg_entry_count; ++i) { + kinfo("%u:\n", i); + kinfo(" Base address: %p\n", acpi_mcfg->entry[i].base_address); + kinfo(" Segment group #%u\n", acpi_mcfg->entry[i].pci_segment_group); + kinfo(" PCI buses: %02x-%02x\n", acpi_mcfg->entry[i].start_pci_bus, acpi_mcfg->entry[i].end_pci_bus); + } + + // Start enumerating buses specified in MCFG + for (uint32_t i = 0; i < mcfg_entry_count; ++i) { + pcie_enumerate_segment(acpi_mcfg->entry[i].base_address, + acpi_mcfg->entry[i].pci_segment_group, + acpi_mcfg->entry[i].start_pci_bus, + acpi_mcfg->entry[i].end_pci_bus); + } }