Files
nt-lite/code/kernel/ke_irq.c
T

97 lines
2.8 KiB
C

// ke_irq.c - IRQ dispatch, kernel-side
#include <ke_irq.h>
#include <ke_sched.h>
#include <ke_irql.h>
#include <ke_dpc.h>
#include <hal_api.h>
#include <ke_debug.h>
#define MAX_IRQS 24
#define MAX_IRQ_HANDLERS 8
typedef struct IRQ_HANDLER_LIST {
void (*Handlers[MAX_IRQ_HANDLERS])(void);
uint8_t Count;
} IRQ_HANDLER_LIST;
static IRQ_HANDLER_LIST gIrqHandlers[MAX_IRQS];
static const HAL_API *gHal;
static volatile uint32_t gInterruptDepth = 0;
static KIRQL KiIrqToIrql(uint8_t irq) {
KIRQL irql = (KIRQL)(DIRQL_BASE + irq);
if (irql >= HIGH_LEVEL)
irql = (KIRQL)(HIGH_LEVEL - 1);
return irql;
}
void KeIrqInitialize(const HAL_API *hal) {
gHal = hal;
}
__declspec(dllexport) void KeIrqRegister(uint8_t irq, void (*handler)(void)) {
if (irq >= MAX_IRQS || !handler) return;
IRQ_HANDLER_LIST* list = &gIrqHandlers[irq];
for (uint8_t i = 0; i < list->Count; i++) {
if (list->Handlers[i] == handler)
return;
}
if (list->Count >= MAX_IRQ_HANDLERS) {
DbgPrint("KeIrqRegister: IRQ %u handler table full\n", irq);
return;
}
list->Handlers[list->Count++] = handler;
gHal->Intc.RouteIrq(irq);
}
void KeIrqDispatch(uint8_t irq) {
KIRQL targetIrql = KiIrqToIrql(irq);
KIRQL currentIrql = KeGetCurrentIrql();
// Do not ever lower IRQL on interrupt entry. Nested IRQs are not expected
// while IF is clear, but this keeps the software state sane if that changes
// later.
if (targetIrql < currentIrql)
targetIrql = currentIrql;
KIRQL oldIrql = KeRaiseIrql(targetIrql);
if (irq < MAX_IRQS) {
IRQ_HANDLER_LIST* list = &gIrqHandlers[irq];
for (uint8_t i = 0; i < list->Count; i++) {
if (list->Handlers[i])
list->Handlers[i]();
}
}
// Handlers are no longer allowed to context-switch directly. Timer IRQs
// only request rescheduling; KiExitInterrupt performs the switch after this
// function returns, so the EOI is guaranteed to be sent first. Keep the
// software level at DIRQL until after the controller has been acknowledged.
gHal->Intc.SendEoi();
KeLowerIrql(oldIrql);
}
void KiEnterInterrupt(void) {
gInterruptDepth++;
}
void KiExitInterrupt(void) {
if (gInterruptDepth)
gInterruptDepth--;
// A reschedule is only legal after the interrupt unwound back to the
// interrupted thread's original PASSIVE context. If a future path ever
// permits nested interrupting of DISPATCH/DIRQL code, leave the request
// pending instead of switching while a spinlock/high-IRQL region is active.
if (gInterruptDepth == 0 && KeGetCurrentIrql() == PASSIVE_LEVEL) {
KiDrainDpcQueue();
if (KeGetCurrentIrql() == PASSIVE_LEVEL)
KiScheduleFromInterrupt();
}
}