From 02e039b3f53e3d9ea2f0eeaa66296cc8a12af2cc Mon Sep 17 00:00:00 2001 From: nakst <> Date: Fri, 17 Sep 2021 10:29:38 +0100 Subject: [PATCH] when booting from EFI, accept any peripheral IRQ for PCI --- drivers/pci.cpp | 2 +- kernel/module.h | 3 +- kernel/x86_64.cpp | 224 ++++++++++++++++++++++++++-------------------- kernel/x86_64.h | 8 +- 4 files changed, 133 insertions(+), 104 deletions(-) diff --git a/drivers/pci.cpp b/drivers/pci.cpp index 25a4b44..f396779 100644 --- a/drivers/pci.cpp +++ b/drivers/pci.cpp @@ -242,7 +242,7 @@ bool KPCIDevice::EnableSingleInterrupt(KIRQHandler irqHandler, void *context, co EnableFeatures(K_PCI_FEATURE_INTERRUPTS); - if (KRegisterIRQ(interruptLine, irqHandler, context, cOwnerName)) { + if (KRegisterIRQ(KBootedFromEFI() ? -1 : interruptLine, irqHandler, context, cOwnerName, this)) { return true; } diff --git a/kernel/module.h b/kernel/module.h index a26113d..3e998f7 100644 --- a/kernel/module.h +++ b/kernel/module.h @@ -87,7 +87,8 @@ void EsPrint(const char *format, ...); typedef bool (*KIRQHandler)(uintptr_t interruptIndex /* tag for MSI */, void *context); // Interrupts are active high and level triggered, unless overridden by the ACPI MADT table. -bool KRegisterIRQ(uintptr_t interruptIndex, KIRQHandler handler, void *context, const char *cOwnerName); +bool KRegisterIRQ(intptr_t interruptIndex, KIRQHandler handler, void *context, const char *cOwnerName, + struct KPCIDevice *pciDevice = nullptr /* do not use; see KPCIDevice::EnableSingleInterrupt */); struct KMSIInformation { // Both fields are zeroed if the MSI could not be registered. diff --git a/kernel/x86_64.cpp b/kernel/x86_64.cpp index 354c6b3..32ca660 100644 --- a/kernel/x86_64.cpp +++ b/kernel/x86_64.cpp @@ -13,7 +13,7 @@ typedef struct ACPIProcessor ArchCPU; #define TIMER_INTERRUPT (0x40) #define YIELD_IPI (0x41) -// Note: IRQ_BASE is currently 0x50. +#define IRQ_BASE (0x50) #define CALL_FUNCTION_ON_ALL_PROCESSORS_IPI (0xF0) #define KERNEL_PANIC_IPI (0) // NMIs ignore the interrupt vector. @@ -78,6 +78,22 @@ uint8_t coreL1Commit[(0xFFFF800200000000 - 0xFFFF800100000000) >> (/* ENTRIES_PE #ifdef IMPLEMENTATION +struct MSIHandler { + KIRQHandler callback; + void *context; +}; + +struct IRQHandler { + KIRQHandler callback; + void *context; + intptr_t line; + KPCIDevice *pciDevice; + const char *cOwnerName; +}; + +MSIHandler msiHandlers[INTERRUPT_VECTOR_MSI_COUNT]; +IRQHandler irqHandlers[0x40]; + extern uintptr_t bootloaderInformationOffset; extern "C" bool simdSSE3Support; extern "C" bool simdSSSE3Support; @@ -601,12 +617,78 @@ void ArchDelay1Ms() { } } -struct MSIHandler { - KIRQHandler callback; - void *context; -}; +bool SetupInterruptRedirectionEntry(uintptr_t _line) { + KSpinlockAssertLocked(&scheduler.lock); -MSIHandler msiHandlers[INTERRUPT_VECTOR_MSI_COUNT]; + static uint32_t alreadySetup = 0; + + if (alreadySetup & (1 << _line)) { + return true; + } + + // Work out which interrupt the IoApic will sent to the processor. + // TODO Use the upper 4 bits for IRQ priority. + + uintptr_t line = _line; + uintptr_t thisProcessorIRQ = line + IRQ_BASE; + + bool activeLow = false; + bool levelTriggered = true; + + // If there was an interrupt override entry in the MADT table, + // then we'll have to use that number instead. + + for (uintptr_t i = 0; i < acpi.interruptOverrideCount; i++) { + ACPIInterruptOverride *interruptOverride = acpi.interruptOverrides + i; + + if (interruptOverride->sourceIRQ == line) { + line = interruptOverride->gsiNumber; + activeLow = interruptOverride->activeLow; + levelTriggered = interruptOverride->levelTriggered; + break; + } + } + + KernelLog(LOG_INFO, "Arch", "IRQ flags", "SetupInterruptRedirectionEntry - IRQ %d is active %z, %z triggered.\n", + line, activeLow ? "low" : "high", levelTriggered ? "level" : "edge"); + + ACPIIoApic *ioApic; + bool foundIoApic = false; + + // Look for the IoApic to which this interrupt is sent. + + for (uintptr_t i = 0; i < acpi.ioapicCount; i++) { + ioApic = acpi.ioApics + i; + if (line >= ioApic->gsiBase && line < (ioApic->gsiBase + (0xFF & (ioApic->ReadRegister(1) >> 16)))) { + foundIoApic = true; + line -= ioApic->gsiBase; + break; + } + } + + // We couldn't find the IoApic that handles this interrupt. + + if (!foundIoApic) { + KernelLog(LOG_ERROR, "Arch", "no IOAPIC", "SetupInterruptRedirectionEntry - Could not find an IOAPIC handling interrupt line %d.\n", line); + return false; + } + + // A normal priority interrupt. + + uintptr_t redirectionTableIndex = line * 2 + 0x10; + uint32_t redirectionEntry = thisProcessorIRQ; + if (activeLow) redirectionEntry |= (1 << 13); + if (levelTriggered) redirectionEntry |= (1 << 15); + + // Send the interrupt to the processor that registered the interrupt. + + ioApic->WriteRegister(redirectionTableIndex, 1 << 16); // Mask the interrupt while we modify the entry. + ioApic->WriteRegister(redirectionTableIndex + 1, GetLocalStorage()->archCPU->apicID << 24); + ioApic->WriteRegister(redirectionTableIndex, redirectionEntry); + + alreadySetup |= 1 << _line; + return true; +} void KUnregisterMSI(uintptr_t tag) { KSpinlockAcquire(&scheduler.lock); @@ -638,88 +720,48 @@ KMSIInformation KRegisterMSI(KIRQHandler handler, void *context, const char *cOw return {}; } -#define IRQ_BASE 0x50 -KIRQHandler irqHandlers[0x20][0x10]; -void *irqHandlerContext[0x20][0x10]; -size_t usedIrqHandlers[0x20]; - -bool KRegisterIRQ(uintptr_t interrupt, KIRQHandler handler, void *context, const char *cOwnerName) { +bool KRegisterIRQ(intptr_t line, KIRQHandler handler, void *context, const char *cOwnerName, KPCIDevice *pciDevice) { KSpinlockAcquire(&scheduler.lock); EsDefer(KSpinlockRelease(&scheduler.lock)); - // Work out which interrupt the IoApic will sent to the processor. - // TODO Use the upper 4 bits for IRQ priority. - uintptr_t thisProcessorIRQ = interrupt + IRQ_BASE; - - // Register the IRQ handler. - if (interrupt > 0x20) KernelPanic("KRegisterIRQ - Unexpected IRQ %d\n", interrupt); - if (usedIrqHandlers[interrupt] == 0x10) { - // There are too many overloaded interrupts. - return false; - } - irqHandlers[interrupt][usedIrqHandlers[interrupt]] = handler; - irqHandlerContext[interrupt][usedIrqHandlers[interrupt]] = context; - - KernelLog(LOG_INFO, "Arch", "register IRQ", "KRegisterIRQ - Registering IRQ %d to '%z'.\n", - interrupt, cOwnerName); - - if (usedIrqHandlers[interrupt]) { - // IRQ already registered. - usedIrqHandlers[interrupt]++; - return true; + if (line == -1 && !pciDevice) { + KernelPanic("KRegisterIRQ - Interrupt line is %d, and pciDevice is %x.\n", line, pciDevice); } - usedIrqHandlers[interrupt]++; + // Save the handler callback and context. - bool activeLow = false; - bool levelTriggered = true; + if (line > 0x20 || line < -1) KernelPanic("KRegisterIRQ - Unexpected IRQ %d\n", line); + bool found = false; - // If there was an interrupt override entry in the MADT table, - // then we'll have to use that number instead. - for (uintptr_t i = 0; i < acpi.interruptOverrideCount; i++) { - ACPIInterruptOverride *interruptOverride = acpi.interruptOverrides + i; - if (interruptOverride->sourceIRQ == interrupt) { - interrupt = interruptOverride->gsiNumber; - activeLow = interruptOverride->activeLow; - levelTriggered = interruptOverride->levelTriggered; + for (uintptr_t i = 0; i < sizeof(irqHandlers) / sizeof(irqHandlers[0]); i++) { + if (!irqHandlers[i].callback) { + found = true; + irqHandlers[i].callback = handler; + irqHandlers[i].context = context; + irqHandlers[i].line = line; + irqHandlers[i].pciDevice = pciDevice; + irqHandlers[i].cOwnerName = cOwnerName; break; } } - KernelLog(LOG_INFO, "Arch", "IRQ flags", "KRegisterIRQ - IRQ %d is active %z, %z triggered.\n", - interrupt, activeLow ? "low" : "high", levelTriggered ? "level" : "edge"); - - ACPIIoApic *ioApic; - bool foundIoApic = false; - - // Look for the IoApic to which this interrupt is sent. - for (uintptr_t i = 0; i < acpi.ioapicCount; i++) { - ioApic = acpi.ioApics + i; - if (interrupt >= ioApic->gsiBase - && interrupt < (ioApic->gsiBase + (0xFF & (ioApic->ReadRegister(1) >> 16)))) { - foundIoApic = true; - interrupt -= ioApic->gsiBase; - break; - } - } - - // We couldn't find the IoApic that handles this interrupt. - if (!foundIoApic) { + if (!found) { + KernelLog(LOG_ERROR, "Arch", "too many IRQ handlers", "The limit of IRQ handlers was reached (%d), and the handler for '%z' was not registered.\n", + sizeof(irqHandlers) / sizeof(irqHandlers[0]), cOwnerName); return false; } - // A normal priority interrupt. - uintptr_t redirectionTableIndex = interrupt * 2 + 0x10; - uint32_t redirectionEntry = thisProcessorIRQ; - if (activeLow) redirectionEntry |= (1 << 13); - if (levelTriggered) redirectionEntry |= (1 << 15); + KernelLog(LOG_INFO, "Arch", "register IRQ", "KRegisterIRQ - Registered IRQ %d to '%z'.\n", line, cOwnerName); - // Mask the interrupt while we modify the entry. - ioApic->WriteRegister(redirectionTableIndex, 1 << 16); - - // Send the interrupt to the processor that registered the interrupt. - ioApic->WriteRegister(redirectionTableIndex + 1, GetLocalStorage()->archCPU->apicID << 24); - ioApic->WriteRegister(redirectionTableIndex, redirectionEntry); + if (line != -1) { + if (!SetupInterruptRedirectionEntry(line)) { + return false; + } + } else { + SetupInterruptRedirectionEntry(9); + SetupInterruptRedirectionEntry(10); + SetupInterruptRedirectionEntry(11); + } return true; } @@ -1036,16 +1078,15 @@ extern "C" void InterruptHandler(InterruptContext *context) { acpi.lapic.EndOfInterrupt(); } else if (interrupt >= INTERRUPT_VECTOR_MSI_START && interrupt < INTERRUPT_VECTOR_MSI_START + INTERRUPT_VECTOR_MSI_COUNT && local) { - MSIHandler handler = msiHandlers[interrupt - INTERRUPT_VECTOR_MSI_START]; + MSIHandler *handler = &msiHandlers[interrupt - INTERRUPT_VECTOR_MSI_START]; local->irqSwitchThread = false; - if (!handler.callback) { + if (!handler->callback) { KernelLog(LOG_ERROR, "Arch", "unexpected MSI", "Unexpected MSI vector %X (no handler).\n", interrupt); } else { - handler.callback(interrupt - INTERRUPT_VECTOR_MSI_START, handler.context); + handler->callback(interrupt - INTERRUPT_VECTOR_MSI_START, handler->context); } - acpi.lapic.EndOfInterrupt(); if (local->irqSwitchThread && scheduler.started && local->schedulerReady) { @@ -1065,29 +1106,18 @@ extern "C" void InterruptHandler(InterruptContext *context) { } else if (interrupt >= IRQ_BASE && interrupt < IRQ_BASE + 0x20) { GetLocalStorage()->inIRQ = true; - size_t overloads = usedIrqHandlers[interrupt - IRQ_BASE]; - bool handledInterrupt = false; + uintptr_t line = interrupt - IRQ_BASE; + KernelLog(LOG_VERBOSE, "Arch", "IRQ start", "IRQ start %d.\n", line); - KernelLog(LOG_VERBOSE, "Arch", "IRQ start", "IRQ start %d.\n", interrupt - IRQ_BASE); - - for (uintptr_t i = 0; i < overloads; i++) { - KIRQHandler handler = irqHandlers[interrupt - IRQ_BASE][i]; - - if (handler(interrupt - IRQ_BASE, irqHandlerContext[interrupt - IRQ_BASE][i])) { - handledInterrupt = true; - } + 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; + handler->callback(interrupt - IRQ_BASE, handler->context); } - KernelLog(LOG_VERBOSE, "Arch", "IRQ end", "IRQ end %d.\n", interrupt - IRQ_BASE); - - bool rejectedByAll = !handledInterrupt; - - if (rejectedByAll) { - // TODO Now what? - // KernelLog(LOG_ERROR, "Arch", "unhandled IRQ", - // "InterruptHandler - Unhandled IRQ %d, rejected by %d %z\n", - // interrupt, overloads, (overloads != 1) ? "overloads" : "overload"); - } + KernelLog(LOG_VERBOSE, "Arch", "IRQ end", "IRQ end %d.\n", line); GetLocalStorage()->inIRQ = false; } diff --git a/kernel/x86_64.h b/kernel/x86_64.h index d97e9f7..ec46d7e 100644 --- a/kernel/x86_64.h +++ b/kernel/x86_64.h @@ -18,16 +18,14 @@ extern "C" void processorGDTR(); extern "C" void SetupProcessor2(struct NewProcessorStorage *); extern "C" void ProcessorInstallTSS(uint32_t *gdt, uint32_t *tss); -bool HasSSSE3Support(); -uintptr_t GetBootloaderInformationOffset(); - -void ArchDelay1Ms(); // Spin for approximately 1ms. Use only during initialisation. Not thread-safe. - struct NewProcessorStorage { struct CPULocalStorage *local; uint32_t *gdt; }; NewProcessorStorage AllocateNewProcessorStorage(struct ACPIProcessor *archCPU); +bool HasSSSE3Support(); +uintptr_t GetBootloaderInformationOffset(); +void ArchDelay1Ms(); // Spin for approximately 1ms. Use only during initialisation. Not thread-safe. #endif