mirror of https://gitlab.com/nakst/essence
use ACPI to get PCI interrupt information
This commit is contained in:
parent
02e039b3f5
commit
75a97ae783
111
drivers/acpi.cpp
111
drivers/acpi.cpp
|
@ -653,50 +653,73 @@ UINT32 ACPIPowerButtonPressed(void *) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void ACPIFoundDevice(const char *name, ACPI_HANDLE object) {
|
||||
if (0 == EsCRTstrcmp(name, "PCI0")) {
|
||||
ACPI_BUFFER buffer = {};
|
||||
ACPI_STATUS status = AcpiGetIrqRoutingTable(object, &buffer);
|
||||
if (status != AE_BUFFER_OVERFLOW) return;
|
||||
buffer.Pointer = EsHeapAllocate(buffer.Length, false, K_FIXED);
|
||||
EsDefer(EsHeapFree(buffer.Pointer, buffer.Length, K_FIXED));
|
||||
if (!buffer.Pointer) return;
|
||||
status = AcpiGetIrqRoutingTable(object, &buffer);
|
||||
if (status != AE_OK) return;
|
||||
ACPI_PCI_ROUTING_TABLE *table = (ACPI_PCI_ROUTING_TABLE *) buffer.Pointer;
|
||||
int32_t ACPIFindIRQ(ACPI_HANDLE object) {
|
||||
ACPI_BUFFER buffer = {};
|
||||
ACPI_STATUS status = AcpiGetCurrentResources(object, &buffer);
|
||||
if (status != AE_BUFFER_OVERFLOW) return -1;
|
||||
buffer.Pointer = EsHeapAllocate(buffer.Length, false, K_FIXED);
|
||||
EsDefer(EsHeapFree(buffer.Pointer, buffer.Length, K_FIXED));
|
||||
if (!buffer.Pointer) return -1;
|
||||
status = AcpiGetCurrentResources(object, &buffer);
|
||||
if (status != AE_OK) return -1;
|
||||
ACPI_RESOURCE *resource = (ACPI_RESOURCE *) buffer.Pointer;
|
||||
|
||||
while (table->Length) {
|
||||
KernelLog(LOG_INFO, "ACPI", "PRT entry", "length: %d; pin: %d; address: %x; source index: %d; source: %z\n",
|
||||
table->Length, table->Pin, table->Address, table->SourceIndex, table->Source);
|
||||
table = (ACPI_PCI_ROUTING_TABLE *) ((uint8_t *) table + table->Length);
|
||||
}
|
||||
} else if (0 == EsCRTmemcmp(name, "LNK", 3)) {
|
||||
ACPI_BUFFER buffer = {};
|
||||
ACPI_STATUS status = AcpiGetCurrentResources(object, &buffer);
|
||||
if (status != AE_BUFFER_OVERFLOW) return;
|
||||
buffer.Pointer = EsHeapAllocate(buffer.Length, false, K_FIXED);
|
||||
EsDefer(EsHeapFree(buffer.Pointer, buffer.Length, K_FIXED));
|
||||
if (!buffer.Pointer) return;
|
||||
status = AcpiGetCurrentResources(object, &buffer);
|
||||
if (status != AE_OK) return;
|
||||
ACPI_RESOURCE *resource = (ACPI_RESOURCE *) buffer.Pointer;
|
||||
|
||||
while (resource->Type != ACPI_RESOURCE_TYPE_END_TAG) {
|
||||
if (resource->Type == ACPI_RESOURCE_TYPE_IRQ) {
|
||||
KernelLog(LOG_INFO, "ACPI", "IRQ resource", "count: %d; first: %d\n",
|
||||
resource->Data.Irq.InterruptCount, resource->Data.Irq.Interrupts[0]);
|
||||
} else if (resource->Type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) {
|
||||
KernelLog(LOG_INFO, "ACPI", "IRQ resource", "count: %d; first: %d; (extended)\n",
|
||||
resource->Data.ExtendedIrq.InterruptCount, resource->Data.ExtendedIrq.Interrupts[0]);
|
||||
while (resource->Type != ACPI_RESOURCE_TYPE_END_TAG) {
|
||||
if (resource->Type == ACPI_RESOURCE_TYPE_IRQ) {
|
||||
if (resource->Data.Irq.InterruptCount) {
|
||||
return resource->Data.Irq.Interrupts[0];
|
||||
}
|
||||
} else if (resource->Type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) {
|
||||
if (resource->Data.ExtendedIrq.InterruptCount) {
|
||||
return resource->Data.ExtendedIrq.Interrupts[0];
|
||||
}
|
||||
|
||||
resource = (ACPI_RESOURCE *) ((uint8_t *) resource + resource->Length);
|
||||
}
|
||||
|
||||
// TODO.
|
||||
resource = (ACPI_RESOURCE *) ((uint8_t *) resource + resource->Length);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void ACPIEnumeratePRTEntries(ACPI_HANDLE pciBus) {
|
||||
// TODO Other PCI buses.
|
||||
// TODO Is this always bus 0?
|
||||
|
||||
ACPI_BUFFER buffer = {};
|
||||
ACPI_STATUS status = AcpiGetIrqRoutingTable(pciBus, &buffer);
|
||||
if (status != AE_BUFFER_OVERFLOW) return;
|
||||
buffer.Pointer = EsHeapAllocate(buffer.Length, false, K_FIXED);
|
||||
EsDefer(EsHeapFree(buffer.Pointer, buffer.Length, K_FIXED));
|
||||
if (!buffer.Pointer) return;
|
||||
status = AcpiGetIrqRoutingTable(pciBus, &buffer);
|
||||
if (status != AE_OK) return;
|
||||
ACPI_PCI_ROUTING_TABLE *table = (ACPI_PCI_ROUTING_TABLE *) buffer.Pointer;
|
||||
|
||||
while (table->Length) {
|
||||
ACPI_HANDLE source;
|
||||
|
||||
if (AE_OK == AcpiGetHandle(pciBus, table->Source, &source)) {
|
||||
int32_t irq = ACPIFindIRQ(source);
|
||||
|
||||
if (irq != -1) {
|
||||
KernelLog(LOG_INFO, "ACPI", "PRT entry", "Pin: %d; PCI slot: %X; IRQ: %d\n",
|
||||
table->Pin, (table->Address >> 16) & 0xFF, irq);
|
||||
|
||||
if (irq != 9 && irq != 10 && irq != 11) {
|
||||
KernelLog(LOG_ERROR, "ACPI", "unexpected IRQ", "IRQ %d was unexpected; expected values are 9, 10 or 11.\n", irq);
|
||||
} else if ((table->Address >> 16) > 0xFF) {
|
||||
KernelLog(LOG_ERROR, "ACPI", "unexpected address", "Address %x was larger than expected.\n", table->Address);
|
||||
} else if (table->Pin > 3) {
|
||||
KernelLog(LOG_ERROR, "ACPI", "unexpected pin", "Pin %d was larger than expected.\n", table->Pin);
|
||||
} else {
|
||||
pciIRQLines[table->Address >> 16][table->Pin] = irq;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
table = (ACPI_PCI_ROUTING_TABLE *) ((uint8_t *) table + table->Length);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void ACPIInitialise2() {
|
||||
|
@ -724,15 +747,21 @@ void ACPIInitialise2() {
|
|||
EsMemoryCopy(name, &information->Name, 4);
|
||||
name[4] = 0;
|
||||
|
||||
KernelLog(LOG_INFO, "ACPI", "device object", "Found device object '%z' with HID '%z' and UID '%z'.\n",
|
||||
KernelLog(LOG_INFO, "ACPI", "device object", "Found device object '%z' with HID '%z', UID '%z' and address %x.\n",
|
||||
name, (information->Valid & ACPI_VALID_HID) ? information->HardwareId.String : "??",
|
||||
(information->Valid & ACPI_VALID_UID) ? information->UniqueId.String : "??");
|
||||
|
||||
ACPIFoundDevice(name, object);
|
||||
(information->Valid & ACPI_VALID_UID) ? information->UniqueId.String : "??",
|
||||
(information->Valid & ACPI_VALID_ADR) ? information->Address : 0);
|
||||
|
||||
ACPI_FREE(information);
|
||||
return AE_OK;
|
||||
}, nullptr, &result);
|
||||
|
||||
ACPI_HANDLE pciBus;
|
||||
char pciBusPath[] = "\\_SB_.PCI0";
|
||||
|
||||
if (AE_OK == AcpiGetHandle(nullptr, pciBusPath, &pciBus)) {
|
||||
ACPIEnumeratePRTEntries(pciBus);
|
||||
}
|
||||
#endif
|
||||
|
||||
acpi.StartupApplicationProcessors();
|
||||
|
|
|
@ -240,8 +240,21 @@ bool KPCIDevice::EnableSingleInterrupt(KIRQHandler irqHandler, void *context, co
|
|||
return true;
|
||||
}
|
||||
|
||||
if (interruptPin == 0) {
|
||||
// The device does not support interrupts.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (interruptPin > 4) {
|
||||
KernelLog(LOG_ERROR, "PCI", "bad interrupt pin", "Interrupt pin should be between 0 and 4; got %d.\n", interruptPin);
|
||||
return false;
|
||||
}
|
||||
|
||||
EnableFeatures(K_PCI_FEATURE_INTERRUPTS);
|
||||
|
||||
// If we booted from EFI, we need to get the interrupt line from ACPI.
|
||||
// See the comment in InterruptHandler for what happens when passing -1.
|
||||
|
||||
if (KRegisterIRQ(KBootedFromEFI() ? -1 : interruptLine, irqHandler, context, cOwnerName, this)) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -74,6 +74,8 @@ struct VirtualAddressSpaceData {
|
|||
|
||||
uint8_t coreL1Commit[(0xFFFF800200000000 - 0xFFFF800100000000) >> (/* ENTRIES_PER_PAGE_TABLE_BITS */ 9 + K_PAGE_BITS + 3)];
|
||||
|
||||
uint8_t pciIRQLines[0x100 /* slots */][4 /* pins */];
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef IMPLEMENTATION
|
||||
|
@ -1112,8 +1114,29 @@ extern "C" void InterruptHandler(InterruptContext *context) {
|
|||
for (uintptr_t i = 0; i < sizeof(irqHandlers) / sizeof(irqHandlers[0]); i++) {
|
||||
IRQHandler *handler = &irqHandlers[i];
|
||||
if (!handler->callback) continue;
|
||||
if (handler->line != -1 && (uintptr_t) handler->line != line) continue;
|
||||
if (handler->line == -1 && line != 9 && line != 10 && line != 11) continue;
|
||||
|
||||
if (handler->line == -1) {
|
||||
// Before we get the actual IRQ line information from ACPI (which might take it a while),
|
||||
// only test that the IRQ is in the correct range for PCI interrupts.
|
||||
// This is a bit slower because we have to dispatch the interrupt to more drivers,
|
||||
// but it shouldn't break anything because they're all supposed to handle overloading anyway.
|
||||
// This is mess. Hopefully all modern computers will use MSIs for anything important.
|
||||
|
||||
if (line != 9 && line != 10 && line != 11) {
|
||||
continue;
|
||||
} else {
|
||||
uint8_t mappedLine = pciIRQLines[handler->pciDevice->slot][handler->pciDevice->interruptPin - 1];
|
||||
|
||||
if (mappedLine && line != mappedLine) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ((uintptr_t) handler->line != line) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
handler->callback(interrupt - IRQ_BASE, handler->context);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue