diff --git a/drivers/acpi.cpp b/drivers/acpi.cpp index a09410c..c8596e7 100644 --- a/drivers/acpi.cpp +++ b/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(); diff --git a/drivers/pci.cpp b/drivers/pci.cpp index f396779..6547907 100644 --- a/drivers/pci.cpp +++ b/drivers/pci.cpp @@ -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; } diff --git a/kernel/x86_64.cpp b/kernel/x86_64.cpp index 32ca660..060523c 100644 --- a/kernel/x86_64.cpp +++ b/kernel/x86_64.cpp @@ -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); }