31 lines
1.3 KiB
C
31 lines
1.3 KiB
C
// ke_irq.c - IRQ dispatch, kernel-side
|
|
#include <ke_irq.h>
|
|
#include <hal_api.h>
|
|
|
|
#define MAX_IRQS 24
|
|
|
|
static void (*gIrqHandlers[MAX_IRQS])(void);
|
|
static const HAL_API *gHal;
|
|
|
|
void KeIrqInitialize(const HAL_API *hal) {
|
|
gHal = hal;
|
|
}
|
|
|
|
__declspec(dllexport) void KeIrqRegister(uint8_t irq, void (*handler)(void)) {
|
|
if (irq >= MAX_IRQS || !handler) return;
|
|
gIrqHandlers[irq] = handler;
|
|
gHal->Intc.RouteIrq(irq);
|
|
}
|
|
|
|
void KeIrqDispatch(uint8_t irq) {
|
|
// KeIrqDispatch sends EOI after the handler returns, but the handler switched context before returning.
|
|
// The EOI call is still sitting on the idle thread's stack, waiting for that thread to be resumed.
|
|
// The LAPIC sees ISR[0x20] still set. Its rule is: don't deliver another interrupt at the same or lower priority until EOI is written.
|
|
// So the timer never fires again. The user process runs completely unpreempted until it calls SYS_EXIT_PROCESS, which calls KeYield, which cooperatively switches back
|
|
// and only then does the idle thread resume, KeIrqDispatch finally sends its EOI, and the timer starts firing again.
|
|
// To fix this we send EOI before calling the handler, not after.
|
|
gHal->Intc.SendEoi();
|
|
if (irq < MAX_IRQS && gIrqHandlers[irq])
|
|
gIrqHandlers[irq]();
|
|
}
|