From b40d495e4e7a8eee0ad2e2ed76a08b3b80dfde6f Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 19 Mar 2020 15:31:58 +0200 Subject: [PATCH] Add non-MSI IRQ functionality to PCI --- arch/amd64/hw/ioapic.c | 325 +++++++++++++++++++++++++++++++++ arch/amd64/hw/irq.c | 26 +-- drivers/pci/pci.c | 54 +++++- include/arch/amd64/hw/ioapic.h | 4 +- include/arch/amd64/hw/irq.h | 3 +- include/drivers/pci/pci.h | 5 + 6 files changed, 401 insertions(+), 16 deletions(-) diff --git a/arch/amd64/hw/ioapic.c b/arch/amd64/hw/ioapic.c index 330680b..39363ac 100644 --- a/arch/amd64/hw/ioapic.c +++ b/arch/amd64/hw/ioapic.c @@ -1,5 +1,6 @@ #include "arch/amd64/hw/ioapic.h" #include "arch/amd64/hw/irq.h" +#include "drivers/pci/pci.h" #include "sys/string.h" #include "sys/mm.h" #include "sys/debug.h" @@ -107,3 +108,327 @@ uint8_t amd64_ioapic_leg_gsi(uint8_t leg_irq) { return leg_irq; } + + + + + + +#define PCI_LINK_MAX_POSSIBLE_IRQS 16 +#define PCI_MAX_IRQ_ROUTES 128 +#define PCI_MAX_LINKS 32 + +static struct acpi_pci_link { + // Fully-qualified path name + char acpi_name[32]; + uint32_t possible_count; + uint32_t possible_interrupts[PCI_LINK_MAX_POSSIBLE_IRQS]; + uint32_t current_interrupt; +} pci_links[PCI_MAX_LINKS]; + +static struct acpi_irq_route { + uint8_t bus; + uint8_t dev; + uint8_t pin; + + int is_named; + union { + char dst_name[32]; + uint32_t dst_interrupt; + }; +} pci_irq_routing[PCI_MAX_IRQ_ROUTES]; +static size_t acpi_pci_irq_routes = 0; + +static struct acpi_pci_link *acpi_pci_link_allocate(void) { + for (size_t i = 0; i < PCI_MAX_LINKS; ++i) { + if (!pci_links[i].acpi_name[0]) { + return &pci_links[i]; + } + } + return NULL; +} + +static int acpi_pci_link_add_possible_interrupt(struct acpi_pci_link *link, uint32_t irq) { + if (link->possible_count == PCI_LINK_MAX_POSSIBLE_IRQS) { + return -1; + } + + link->possible_interrupts[link->possible_count++] = irq; + return 0; +} + +static int acpi_pci_add_named_route(uint8_t bus, uint8_t dev, uint8_t pin, const char *link_name) { + if (acpi_pci_irq_routes == PCI_MAX_IRQ_ROUTES) { + return -1; + } + _assert(strlen(link_name) < 32); + struct acpi_irq_route *route = &pci_irq_routing[acpi_pci_irq_routes++]; + + strcpy(route->dst_name, link_name); + route->is_named = 1; + route->bus = bus; + route->dev = dev; + route->pin = pin; + + return 0; +} + +static int acpi_pci_add_hard_route(uint8_t bus, uint8_t dev, uint8_t pin, uint32_t irq) { + if (acpi_pci_irq_routes == PCI_MAX_IRQ_ROUTES) { + return -1; + } + struct acpi_irq_route *route = &pci_irq_routing[acpi_pci_irq_routes++]; + + route->dst_interrupt = irq; + route->is_named = 0; + route->bus = bus; + route->dev = dev; + route->pin = pin; + + return 0; +} + +static ACPI_STATUS acpi_dev_walk_pci_link_possible_resources(ACPI_RESOURCE *Resource, void *Ctx) { + struct acpi_pci_link *link = Ctx; + + switch (Resource->Type) { + case ACPI_RESOURCE_TYPE_IRQ: { + ACPI_RESOURCE_IRQ *Irq = &Resource->Data.Irq; + + for (UINT32 i = 0; i < Irq->InterruptCount; ++i) { + _assert(acpi_pci_link_add_possible_interrupt(link, Irq->Interrupts[i]) == 0); + } + } + return AE_OK; + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: { + ACPI_RESOURCE_EXTENDED_IRQ *ExtendedIrq = &Resource->Data.ExtendedIrq; + + for (UINT32 i = 0; i < ExtendedIrq->InterruptCount; ++i) { + _assert(acpi_pci_link_add_possible_interrupt(link, ExtendedIrq->Interrupts[i]) == 0); + } + } + return AE_OK; + case ACPI_RESOURCE_TYPE_END_TAG: + return AE_OK; + default: + panic("Unknown resource type for link: %02x\n", Resource->Type); + } +} + +static ACPI_STATUS acpi_dev_walk_pci_link_current_resources(ACPI_RESOURCE *Resource, void *Ctx) { + struct acpi_pci_link *link = Ctx; + + switch (Resource->Type) { + case ACPI_RESOURCE_TYPE_IRQ: { + ACPI_RESOURCE_IRQ *Irq = &Resource->Data.Irq; + if (Irq->InterruptCount == 0) { + // Leave in "Unconfigured" state + return AE_OK; + } + assert(Irq->InterruptCount == 1, "Only one current IRQ config supported now\n"); + + link->current_interrupt = Irq->Interrupts[0]; + } + return AE_OK; + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: { + ACPI_RESOURCE_EXTENDED_IRQ *ExtendedIrq = &Resource->Data.ExtendedIrq; + if (ExtendedIrq->InterruptCount == 0) { + // Leave in "Unconfigured" state + return AE_OK; + } + assert(ExtendedIrq->InterruptCount == 1, "Only one current IRQ config supported now\n"); + + link->current_interrupt = ExtendedIrq->Interrupts[0]; + } + return AE_OK; + case ACPI_RESOURCE_TYPE_END_TAG: + return AE_OK; + default: + panic("Unknown resource type for link: %02x\n", Resource->Type); + } +} + +static ACPI_STATUS acpi_dev_walk_pci_link(ACPI_HANDLE LinkDevice, UINT32 NestingLevel, void *Ctx, void **Res) { + ACPI_STATUS ret; + char ResourceBufferData[256]; + char LinkName[256]; + ACPI_BUFFER ResourceBuffer = { sizeof(ResourceBufferData), ResourceBufferData }; + ACPI_BUFFER LinkNameBuffer = { sizeof(LinkName), LinkName }; + + if (ACPI_FAILURE(ret = AcpiGetName(LinkDevice, ACPI_FULL_PATHNAME, &LinkNameBuffer))) { + return ret; + } + + struct acpi_pci_link *link = acpi_pci_link_allocate(); + _assert(link); + _assert(strlen(LinkName) < 32); + strcpy(link->acpi_name, LinkName); + link->possible_count = 0; + link->current_interrupt = PCI_IRQ_NO_ROUTE; + + if (ACPI_FAILURE(ret = AcpiGetPossibleResources(LinkDevice, &ResourceBuffer))) { + return ret; + } + + if (ACPI_FAILURE(ret = AcpiWalkResourceBuffer(&ResourceBuffer, acpi_dev_walk_pci_link_possible_resources, link))) { + return ret; + } + + if (ACPI_FAILURE(ret = AcpiGetCurrentResources(LinkDevice, &ResourceBuffer))) { + return ret; + } + + if (ACPI_FAILURE(ret = AcpiWalkResourceBuffer(&ResourceBuffer, acpi_dev_walk_pci_link_current_resources, link))) { + return ret; + } + + return AE_OK; +} + +static ACPI_STATUS acpi_dev_walk_pci_buses(ACPI_HANDLE BusDevice, UINT32 NestingLevel, void *Ctx, void **Res) { + ACPI_STATUS ret; + + char BusName[256]; + ACPI_OBJECT BusNumberObject; + ACPI_PCI_ROUTING_TABLE IrqRoutingTable[256]; + ACPI_DEVICE_INFO *DeviceInfo = NULL; + + ACPI_BUFFER BusNameBuffer = { sizeof(BusName), BusName }; + ACPI_BUFFER BusNumberObjectBuffer = { sizeof(BusNumberObject), &BusNumberObject }; + ACPI_BUFFER IrqRoutingTableBuffer = { sizeof(IrqRoutingTable), IrqRoutingTable }; + + if (ACPI_FAILURE(ret = AcpiGetName(BusDevice, ACPI_FULL_PATHNAME, &BusNameBuffer))) { + return ret; + } + + if (ACPI_FAILURE(ret = AcpiGetObjectInfo(BusDevice, &DeviceInfo))) { + return ret; + } + + UINT8 Bus = 0; + int assigned_bus_number = 0; + + // Try to get bus number from _BBN (Base Bus Number) + if (ACPI_FAILURE(ret = AcpiEvaluateObjectTyped(BusDevice, "_BBN", NULL, &BusNumberObjectBuffer, ACPI_TYPE_INTEGER))) { + if (ret != AE_NOT_FOUND) { + return ret; + } + } else { + assigned_bus_number = 1; + kdebug("%s._BBN = %p\n", BusName, BusNumberObject.Integer.Value); + if (BusNumberObject.Integer.Value > 0xFF) { + panic("Bus number is too high: %p\n", BusNumberObject.Integer.Value); + } + Bus = BusNumberObject.Integer.Value; + } + + kdebug("PCI Root Bus: 0x%02x (%s)\n", Bus, BusName); + kdebug(" _HID = %s, _CLS = %s\n", + ((DeviceInfo->Valid & ACPI_VALID_HID) ? DeviceInfo->HardwareId.String : "NONE"), + ((DeviceInfo->Valid & ACPI_VALID_CLS) ? DeviceInfo->ClassCode.String : "NONE")); + if (DeviceInfo->Valid & ACPI_VALID_CID) { + for (size_t i = 0; i < DeviceInfo->CompatibleIdList.Count; ++i) { + kdebug(" _CID = %s\n", DeviceInfo->CompatibleIdList.Ids[i].String); + } + } + + //pci_add_root_bus(Bus); + + // Get _PRT + if (ACPI_FAILURE(ret = AcpiGetIrqRoutingTable(BusDevice, &IrqRoutingTableBuffer))) { + return ret; + } + + UINT32 Offset = 0; + + while (Offset < IrqRoutingTableBuffer.Length) { + ACPI_PCI_ROUTING_TABLE *Route = (ACPI_PCI_ROUTING_TABLE *) ((uintptr_t) IrqRoutingTable + Offset); + + if (!Route->Length) { + break; + } + + UINT8 Device = (Route->Address >> 16) & 0xFF; + _assert((Route->Address & 0xFFFF) == 0xFFFF); + + // Check if it's a "hard" route, meaning it's not a reference to a PCI Link device but + // GSI number is specified directly instead + if (Route->SourceIndex == 0) { + // It's a LNKx reference + _assert(acpi_pci_add_named_route(Bus, Device, Route->Pin, Route->Source) == 0); + } else { + // IRQ number specified directly + _assert(acpi_pci_add_hard_route(Bus, Device, Route->Pin, Route->SourceIndex) == 0); + } + + Offset += Route->Length; + } + + return AE_OK; +} + +int amd64_pci_init_irqs(void) { + ACPI_STATUS ret; + + // 1. Walk links + if (ACPI_FAILURE(ret = AcpiGetDevices("PNP0C0F", acpi_dev_walk_pci_link, NULL, NULL))) { + kerror("ACPI INIT failure %s\n", AcpiFormatException(ret)); + return -1; + } + + // 2. Walk buses + // Will also match PCI Express buses + if (ACPI_FAILURE(ret = AcpiGetDevices("PNP0A03" /* PCI Bus */, acpi_dev_walk_pci_buses, NULL, NULL))) { + kerror("ACPI INIT failure %s\n", AcpiFormatException(ret)); + return -1; + } + + // Dump PCI Link table + kdebug("PCI Interrupt Link Devices:\n"); + for (size_t i = 0; i < 8; ++i) { + if (pci_links[i].acpi_name[0]) { + if (pci_links[i].current_interrupt == PCI_IRQ_NO_ROUTE) { + kdebug("%s: Unmapped\n", pci_links[i].acpi_name); + } else { + kdebug("%s: Current IRQ %u\n", pci_links[i].acpi_name, pci_links[i].current_interrupt); + } + } + } + + return 0; +} + +uint32_t amd64_pci_link_route(const char *link_name) { + for (size_t i = 0; i < PCI_MAX_LINKS; ++i) { + if (!strcmp(link_name, pci_links[i].acpi_name)) { + return pci_links[i].current_interrupt; + } + } + + // No such link exists + kdebug("No link named %s\n", link_name); + return PCI_IRQ_INVALID; +} + +uint32_t amd64_pci_pin_irq_route(struct pci_device *dev, uint8_t pin) { + uint8_t b, d, f; + pci_get_device_address(dev, &b, &d, &f); + + for (size_t i = 0; i < acpi_pci_irq_routes; ++i) { + struct acpi_irq_route *route = &pci_irq_routing[i]; + + if (route->bus == b && route->dev == d && route->pin == pin) { + // Found matching route + if (route->is_named) { + // Resolve LNKx + return amd64_pci_link_route(route->dst_name); + } else { + return route->dst_interrupt; + } + } + } + + // No such route exists + kerror("No PCI IRQ route %02x:%02x:%02x:INT%c#\n", b, d, f, pin + 'A'); + return PCI_IRQ_INVALID; +} diff --git a/arch/amd64/hw/irq.c b/arch/amd64/hw/irq.c index 8cd0824..c7277c0 100644 --- a/arch/amd64/hw/irq.c +++ b/arch/amd64/hw/irq.c @@ -97,19 +97,19 @@ int irq_add_leg_handler(uint8_t leg_irq, irq_handler_func_t handler, void *ctx) } } -//int irq_add_pci_handler(pci_addr_t addr, uint8_t pin, irq_handler_func_t handler, void *ctx) { -// _assert(pin < 4); -// uint32_t irq_route = amd64_pci_pin_irq_route(PCI_BUS(addr), PCI_DEV(addr), PCI_FUNC(addr), pin); -// _assert(irq_route != PCI_IRQ_INVALID); -// -// if (irq_route == PCI_IRQ_NO_ROUTE) { -// panic("TODO: allocate PCI IRQ routes\n"); -// } -// -// kdebug("Assigning handler to " PCI_FMTADDR " INT%c# -> GSI%d", PCI_VAADDR(addr), pin + 'A', irq_route); -// -// return irq_add_handler(irq_route, handler, ctx); -//} +int irq_add_pci_handler(struct pci_device *dev, uint8_t pin, irq_handler_func_t handler, void *ctx) { + _assert(pin < 4); + uint32_t irq_route = amd64_pci_pin_irq_route(dev, pin); + _assert(irq_route != PCI_IRQ_INVALID); + + if (irq_route == PCI_IRQ_NO_ROUTE) { + panic("TODO: allocate PCI IRQ routes\n"); + } + + //kdebug("Assigning handler to " PCI_FMTADDR " INT%c# -> GSI%d", PCI_VAADDR(addr), pin + 'A', irq_route); + + return irq_add_handler(irq_route, handler, ctx); +} void irq_enable_ioapic_mode(void) { ioapic_available = 1; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 25b229f..bc13c53 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1,7 +1,8 @@ #include "arch/amd64/hw/ioapic.h" -#include "drivers/pci/pci.h" +#include "arch/amd64/hw/ioapic.h" #include "arch/amd64/hw/acpi.h" #include "arch/amd64/hw/io.h" +#include "drivers/pci/pci.h" #include "sys/snprintf.h" #include "sys/assert.h" #include "sys/string.h" @@ -20,6 +21,9 @@ struct pci_driver; #define pcie_config_read_dword(dev, off) \ (*(uint32_t *) ((dev)->pcie_config + (off))) +#define pcie_config_write_dword(dev, off, val) \ + (*(uint32_t *) ((dev)->pcie_config + (off))) = (val); + #define PCI_CAP_MSI_64 (1 << 7) #define PCI_CAP_MSI_EN (1 << 0) struct pci_cap_msi { @@ -72,6 +76,26 @@ static size_t g_pci_driver_count; #define PCI_DRIVER_CLASS 1 #define PCI_DRIVER_DEV 2 +void pci_config_write_dword(struct pci_device *dev, uint16_t off, uint32_t val) { + if (PCI_IS_EXPRESS(dev)) { + pcie_config_write_dword(dev, off, val); + } else { + pci_config_write_dword_legacy(dev->bus, dev->dev, dev->func, off, val); + } +} + +void pci_config_write_dword_legacy(uint8_t bus, uint8_t dev, uint8_t func, uint32_t off, uint32_t val) { + uint32_t w0; + w0 = (((uint32_t) bus) << 16) | + (((uint32_t) dev) << 11) | + (((uint32_t) func) << 8) | + (off & ~0x3) | + (1 << 31); + + outl(PCI_PORT_CONFIG_ADDR, w0); + outl(PCI_PORT_CONFIG_DATA, val); +} + uint32_t pci_config_read_dword(struct pci_device *dev, uint16_t off) { if (PCI_IS_EXPRESS(dev)) { return pcie_config_read_dword(dev, off); @@ -110,6 +134,13 @@ void pci_add_irq(struct pci_device *dev, irq_handler_func_t handler, void *ctx) dev->msi->msi32.message_data = vector; } dev->msi->message_control |= PCI_CAP_MSI_EN; + } else { + uint32_t irq_config = pci_config_read_dword(dev, PCI_CONFIG_IRQ); + uint8_t irq_pin = (irq_config >> 8) & 0xFF; + if (irq_pin) { + kdebug("Uses INT%c# IRQ pin\n\n", 'A' + irq_pin - 1); + irq_add_pci_handler(dev, irq_pin - 1, handler, ctx); + } } } @@ -126,10 +157,29 @@ void pci_add_class_driver(uint32_t full_class, pci_driver_func_t func, const cha driver->match = full_class & ~0xFF000000; } +void pci_add_device_driver(uint32_t id, pci_driver_func_t func, const char *name) { + 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++]; + + strcpy(driver->name, name); + driver->init_func = func; + driver->type = PCI_DRIVER_DEV; + driver->match = id; +} + void pci_add_root_bus(uint8_t n) { kwarn("%s: %02x\n", __func__, n); } +void pci_get_device_address(const struct pci_device *dev, uint8_t *b, uint8_t *d, uint8_t *f) { + *b = dev->bus; + *d = dev->dev; + *f = dev->func; +} + static void pci_pick_driver(uint32_t device_id, uint32_t class_id, struct pci_driver **class_driver, struct pci_driver **dev_driver) { struct pci_driver *rclass = NULL, *rdev = NULL; for (size_t i = 0; i < g_pci_driver_count; ++i) { @@ -368,6 +418,8 @@ static void pcie_enumerate_segment(uintptr_t base_address, uint16_t seg, uint8_t } void pci_init(void) { + amd64_pci_init_irqs(); + if (acpi_mcfg) { uint32_t mcfg_entry_count = (acpi_mcfg->hdr.length - sizeof(struct acpi_header) - 8) / sizeof(struct acpi_mcfg_entry); kdebug("MCFG has %u entries:\n", mcfg_entry_count); diff --git a/include/arch/amd64/hw/ioapic.h b/include/arch/amd64/hw/ioapic.h index a8b07c5..d6dc1bf 100644 --- a/include/arch/amd64/hw/ioapic.h +++ b/include/arch/amd64/hw/ioapic.h @@ -3,6 +3,8 @@ // irq.h header to provide controller-agnostic interface #include "sys/types.h" +struct pci_device; + #define IOAPIC_REG_ID 0x00 #define IOAPIC_REG_VER 0x01 #define IOAPIC_REG_REDIR 0x10 @@ -50,4 +52,4 @@ uint8_t amd64_ioapic_leg_gsi(uint8_t leg_irq); #define PCI_IRQ_INVALID 0xFFFFFFFE int amd64_pci_init_irqs(void); -uint32_t amd64_pci_pin_irq_route(uint8_t bus, uint8_t dev, uint8_t func, uint8_t pin); +uint32_t amd64_pci_pin_irq_route(struct pci_device *dev, uint8_t pin); diff --git a/include/arch/amd64/hw/irq.h b/include/arch/amd64/hw/irq.h index 9c6b014..94cf3ca 100644 --- a/include/arch/amd64/hw/irq.h +++ b/include/arch/amd64/hw/irq.h @@ -9,6 +9,7 @@ #define IRQ_UNHANDLED ((uint32_t) -1) typedef uint32_t (*irq_handler_func_t) (void *); +struct pci_device; struct irq_handler { irq_handler_func_t func; @@ -18,7 +19,7 @@ struct irq_handler { int irq_add_handler(uint8_t gsi, irq_handler_func_t handler, void *ctx); int irq_add_leg_handler(uint8_t leg_irq, irq_handler_func_t handler, void *ctx); int irq_add_msi_handler(irq_handler_func_t handler, void *ctx, uint8_t *vector); -//int irq_add_pci_handler(pci_addr_t addr, uint8_t pin, irq_handler_func_t handler, void *ctx); +int irq_add_pci_handler(struct pci_device *dev, uint8_t pin, irq_handler_func_t handler, void *ctx); int irq_has_handler(uint8_t gsi); diff --git a/include/drivers/pci/pci.h b/include/drivers/pci/pci.h index 1e8e421..bfe8450 100644 --- a/include/drivers/pci/pci.h +++ b/include/drivers/pci/pci.h @@ -21,14 +21,19 @@ struct pci_device; typedef void (*pci_driver_func_t)(struct pci_device *dev); +void pci_get_device_address(const struct pci_device *dev, uint8_t *b, uint8_t *d, uint8_t *f); + void pci_init(void); void pci_add_root_bus(uint8_t n); uint32_t pci_config_read_dword_legacy(uint8_t bus, uint8_t dev, uint8_t func, uint32_t off); uint32_t pci_config_read_dword(struct pci_device *dev, uint16_t off); +void pci_config_write_dword_legacy(uint8_t bus, uint8_t dev, uint8_t func, uint32_t off, uint32_t val); +void pci_config_write_dword(struct pci_device *dev, uint16_t off, uint32_t val); void pci_add_irq(struct pci_device *dev, irq_handler_func_t handler, void *ctx); void pci_add_class_driver(uint32_t full_class, pci_driver_func_t func, const char *name); +void pci_add_device_driver(uint32_t id, pci_driver_func_t func, const char *name); // pcidb.c const char *pci_class_string(uint16_t full_class);