mirror of https://gitlab.com/nakst/essence
				
				
				
			when booting from EFI, accept any peripheral IRQ for PCI
This commit is contained in:
		
							parent
							
								
									cf0e16bc1c
								
							
						
					
					
						commit
						02e039b3f5
					
				|  | @ -242,7 +242,7 @@ bool KPCIDevice::EnableSingleInterrupt(KIRQHandler irqHandler, void *context, co | ||||||
| 
 | 
 | ||||||
| 	EnableFeatures(K_PCI_FEATURE_INTERRUPTS); | 	EnableFeatures(K_PCI_FEATURE_INTERRUPTS); | ||||||
| 
 | 
 | ||||||
| 	if (KRegisterIRQ(interruptLine, irqHandler, context, cOwnerName)) { | 	if (KRegisterIRQ(KBootedFromEFI() ? -1 : interruptLine, irqHandler, context, cOwnerName, this)) { | ||||||
| 		return true; | 		return true; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -87,7 +87,8 @@ void EsPrint(const char *format, ...); | ||||||
| typedef bool (*KIRQHandler)(uintptr_t interruptIndex /* tag for MSI */, void *context); | 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.
 | // 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 { | struct KMSIInformation { | ||||||
| 	// Both fields are zeroed if the MSI could not be registered.
 | 	// Both fields are zeroed if the MSI could not be registered.
 | ||||||
|  |  | ||||||
|  | @ -13,7 +13,7 @@ typedef struct ACPIProcessor ArchCPU; | ||||||
| 
 | 
 | ||||||
| #define TIMER_INTERRUPT (0x40) | #define TIMER_INTERRUPT (0x40) | ||||||
| #define YIELD_IPI (0x41) | #define YIELD_IPI (0x41) | ||||||
| // Note: IRQ_BASE is currently 0x50.
 | #define IRQ_BASE (0x50) | ||||||
| #define CALL_FUNCTION_ON_ALL_PROCESSORS_IPI (0xF0) | #define CALL_FUNCTION_ON_ALL_PROCESSORS_IPI (0xF0) | ||||||
| #define KERNEL_PANIC_IPI (0) // NMIs ignore the interrupt vector.
 | #define KERNEL_PANIC_IPI (0) // NMIs ignore the interrupt vector.
 | ||||||
| 
 | 
 | ||||||
|  | @ -78,6 +78,22 @@ uint8_t coreL1Commit[(0xFFFF800200000000 - 0xFFFF800100000000) >> (/* ENTRIES_PE | ||||||
| 
 | 
 | ||||||
| #ifdef IMPLEMENTATION | #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 uintptr_t bootloaderInformationOffset; | ||||||
| extern "C" bool simdSSE3Support; | extern "C" bool simdSSE3Support; | ||||||
| extern "C" bool simdSSSE3Support; | extern "C" bool simdSSSE3Support; | ||||||
|  | @ -601,12 +617,78 @@ void ArchDelay1Ms() { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct MSIHandler { | bool SetupInterruptRedirectionEntry(uintptr_t _line) { | ||||||
| 	KIRQHandler callback; | 	KSpinlockAssertLocked(&scheduler.lock); | ||||||
| 	void *context; |  | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| 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) { | void KUnregisterMSI(uintptr_t tag) { | ||||||
| 	KSpinlockAcquire(&scheduler.lock); | 	KSpinlockAcquire(&scheduler.lock); | ||||||
|  | @ -638,88 +720,48 @@ KMSIInformation KRegisterMSI(KIRQHandler handler, void *context, const char *cOw | ||||||
| 	return {}; | 	return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #define IRQ_BASE 0x50 | bool KRegisterIRQ(intptr_t line, KIRQHandler handler, void *context, const char *cOwnerName, KPCIDevice *pciDevice) { | ||||||
| KIRQHandler irqHandlers[0x20][0x10]; |  | ||||||
| void *irqHandlerContext[0x20][0x10]; |  | ||||||
| size_t usedIrqHandlers[0x20]; |  | ||||||
| 
 |  | ||||||
| bool KRegisterIRQ(uintptr_t interrupt, KIRQHandler handler, void *context, const char *cOwnerName) { |  | ||||||
| 	KSpinlockAcquire(&scheduler.lock); | 	KSpinlockAcquire(&scheduler.lock); | ||||||
| 	EsDefer(KSpinlockRelease(&scheduler.lock)); | 	EsDefer(KSpinlockRelease(&scheduler.lock)); | ||||||
| 
 | 
 | ||||||
| 	// Work out which interrupt the IoApic will sent to the processor.
 | 	if (line == -1 && !pciDevice) { | ||||||
| 	// TODO Use the upper 4 bits for IRQ priority.
 | 		KernelPanic("KRegisterIRQ - Interrupt line is %d, and pciDevice is %x.\n", line, pciDevice); | ||||||
| 	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; |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	usedIrqHandlers[interrupt]++; | 	// Save the handler callback and context.
 | ||||||
| 
 | 
 | ||||||
| 	bool activeLow = false; | 	if (line > 0x20 || line < -1) KernelPanic("KRegisterIRQ - Unexpected IRQ %d\n", line); | ||||||
| 	bool levelTriggered = true; | 	bool found = false; | ||||||
| 
 | 
 | ||||||
| 	// If there was an interrupt override entry in the MADT table,
 | 	for (uintptr_t i = 0; i < sizeof(irqHandlers) / sizeof(irqHandlers[0]); i++) { | ||||||
| 	// then we'll have to use that number instead.
 | 		if (!irqHandlers[i].callback) { | ||||||
| 	for (uintptr_t i = 0; i < acpi.interruptOverrideCount; i++) { | 			found = true; | ||||||
| 		ACPIInterruptOverride *interruptOverride = acpi.interruptOverrides + i; | 			irqHandlers[i].callback = handler; | ||||||
| 		if (interruptOverride->sourceIRQ == interrupt) { | 			irqHandlers[i].context = context; | ||||||
| 			interrupt = interruptOverride->gsiNumber; | 			irqHandlers[i].line = line; | ||||||
| 			activeLow = interruptOverride->activeLow; | 			irqHandlers[i].pciDevice = pciDevice; | ||||||
| 			levelTriggered = interruptOverride->levelTriggered; | 			irqHandlers[i].cOwnerName = cOwnerName; | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	KernelLog(LOG_INFO, "Arch", "IRQ flags", "KRegisterIRQ - IRQ %d is active %z, %z triggered.\n", | 	if (!found) { | ||||||
| 			interrupt, activeLow ? "low" : "high", levelTriggered ? "level" : "edge"); | 		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); | ||||||
| 	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) { |  | ||||||
| 		return false; | 		return false; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// A normal priority interrupt.
 | 	KernelLog(LOG_INFO, "Arch", "register IRQ", "KRegisterIRQ - Registered IRQ %d to '%z'.\n", line, cOwnerName); | ||||||
| 	uintptr_t redirectionTableIndex = interrupt * 2 + 0x10; |  | ||||||
| 	uint32_t redirectionEntry = thisProcessorIRQ; |  | ||||||
| 	if (activeLow) redirectionEntry |= (1 << 13); |  | ||||||
| 	if (levelTriggered) redirectionEntry |= (1 << 15); |  | ||||||
| 
 | 
 | ||||||
| 	// Mask the interrupt while we modify the entry.
 | 	if (line != -1) { | ||||||
| 	ioApic->WriteRegister(redirectionTableIndex, 1 << 16); | 		if (!SetupInterruptRedirectionEntry(line)) { | ||||||
| 
 | 			return false; | ||||||
| 	// Send the interrupt to the processor that registered the interrupt.
 | 		} | ||||||
| 	ioApic->WriteRegister(redirectionTableIndex + 1, GetLocalStorage()->archCPU->apicID << 24);  | 	} else { | ||||||
| 	ioApic->WriteRegister(redirectionTableIndex, redirectionEntry); | 		SetupInterruptRedirectionEntry(9); | ||||||
|  | 		SetupInterruptRedirectionEntry(10); | ||||||
|  | 		SetupInterruptRedirectionEntry(11); | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
|  | @ -1036,16 +1078,15 @@ extern "C" void InterruptHandler(InterruptContext *context) { | ||||||
| 
 | 
 | ||||||
| 		acpi.lapic.EndOfInterrupt(); | 		acpi.lapic.EndOfInterrupt(); | ||||||
| 	} else if (interrupt >= INTERRUPT_VECTOR_MSI_START && interrupt < INTERRUPT_VECTOR_MSI_START + INTERRUPT_VECTOR_MSI_COUNT && local) { | 	} 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; | 		local->irqSwitchThread = false; | ||||||
| 
 | 
 | ||||||
| 		if (!handler.callback) { | 		if (!handler->callback) { | ||||||
| 			KernelLog(LOG_ERROR, "Arch", "unexpected MSI", "Unexpected MSI vector %X (no handler).\n", interrupt); | 			KernelLog(LOG_ERROR, "Arch", "unexpected MSI", "Unexpected MSI vector %X (no handler).\n", interrupt); | ||||||
| 		} else { | 		} else { | ||||||
| 			handler.callback(interrupt - INTERRUPT_VECTOR_MSI_START, handler.context); | 			handler->callback(interrupt - INTERRUPT_VECTOR_MSI_START, handler->context); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 		acpi.lapic.EndOfInterrupt(); | 		acpi.lapic.EndOfInterrupt(); | ||||||
| 
 | 
 | ||||||
| 		if (local->irqSwitchThread && scheduler.started && local->schedulerReady) { | 		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) { | 		} else if (interrupt >= IRQ_BASE && interrupt < IRQ_BASE + 0x20) { | ||||||
| 			GetLocalStorage()->inIRQ = true; | 			GetLocalStorage()->inIRQ = true; | ||||||
| 
 | 
 | ||||||
| 			size_t overloads = usedIrqHandlers[interrupt - IRQ_BASE]; | 			uintptr_t line = interrupt - IRQ_BASE; | ||||||
| 			bool handledInterrupt = false; | 			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 < sizeof(irqHandlers) / sizeof(irqHandlers[0]); i++) { | ||||||
| 
 | 				IRQHandler *handler = &irqHandlers[i]; | ||||||
| 			for (uintptr_t i = 0; i < overloads; i++) { | 				if (!handler->callback) continue; | ||||||
| 				KIRQHandler handler = irqHandlers[interrupt - IRQ_BASE][i]; | 				if (handler->line != -1 && (uintptr_t) handler->line != line) continue; | ||||||
| 
 | 				if (handler->line == -1 && line != 9 && line != 10 && line != 11) continue; | ||||||
| 				if (handler(interrupt - IRQ_BASE, irqHandlerContext[interrupt - IRQ_BASE][i])) { | 				handler->callback(interrupt - IRQ_BASE, handler->context); | ||||||
| 					handledInterrupt = true; |  | ||||||
| 				} |  | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			KernelLog(LOG_VERBOSE, "Arch", "IRQ end", "IRQ end %d.\n", interrupt - IRQ_BASE); | 			KernelLog(LOG_VERBOSE, "Arch", "IRQ end", "IRQ end %d.\n", line); | ||||||
| 
 |  | ||||||
| 			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");
 |  | ||||||
| 			} |  | ||||||
| 
 | 
 | ||||||
| 			GetLocalStorage()->inIRQ = false; | 			GetLocalStorage()->inIRQ = false; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -18,16 +18,14 @@ extern "C" void processorGDTR(); | ||||||
| extern "C" void SetupProcessor2(struct NewProcessorStorage *); | extern "C" void SetupProcessor2(struct NewProcessorStorage *); | ||||||
| extern "C" void ProcessorInstallTSS(uint32_t *gdt, uint32_t *tss); | 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 NewProcessorStorage { | ||||||
| 	struct CPULocalStorage *local; | 	struct CPULocalStorage *local; | ||||||
| 	uint32_t *gdt; | 	uint32_t *gdt; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| NewProcessorStorage AllocateNewProcessorStorage(struct ACPIProcessor *archCPU); | NewProcessorStorage AllocateNewProcessorStorage(struct ACPIProcessor *archCPU); | ||||||
|  | bool HasSSSE3Support(); | ||||||
|  | uintptr_t GetBootloaderInformationOffset(); | ||||||
|  | void ArchDelay1Ms(); // Spin for approximately 1ms. Use only during initialisation. Not thread-safe.
 | ||||||
| 
 | 
 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 nakst
						nakst