Add non-MSI IRQ functionality to PCI

This commit is contained in:
Mark
2020-03-19 15:31:58 +02:00
parent 1ddd1b9cc8
commit b40d495e4e
6 changed files with 401 additions and 16 deletions
+325
View File
@@ -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;
}
+13 -13
View File
@@ -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;
+53 -1
View File
@@ -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);
+3 -1
View File
@@ -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);
+2 -1
View File
@@ -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);
+5
View File
@@ -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);