diff --git a/kernel-src/include/kernel/interrupt.h b/kernel-src/include/kernel/interrupt.h index b524eea7..8baf9b26 100644 --- a/kernel-src/include/kernel/interrupt.h +++ b/kernel-src/include/kernel/interrupt.h @@ -19,6 +19,8 @@ struct _isr_t; typedef struct _isr_t { + struct _isr_t *next; + struct _isr_t *prev; void (*func)(struct _isr_t *self, context_t *ctx); void (*eoi)(struct _isr_t *self); long id; diff --git a/kernel-src/include/x86-64/arch/cpu.h b/kernel-src/include/x86-64/arch/cpu.h index ca5e4d0b..27b24a70 100644 --- a/kernel-src/include/x86-64/arch/cpu.h +++ b/kernel-src/include/x86-64/arch/cpu.h @@ -25,6 +25,7 @@ typedef struct cpu_t { thread_t *idlethread; timerentry_t schedtimerentry; void *schedulerstack; + isr_t *isrqueue; } cpu_t; #define CPU_HALT() asm volatile("hlt") diff --git a/kernel-src/sys/interrupt.c b/kernel-src/sys/interrupt.c index c5d73221..00f8ec7c 100644 --- a/kernel-src/sys/interrupt.c +++ b/kernel-src/sys/interrupt.c @@ -4,6 +4,92 @@ #include #include +void arch_interrupt_disable(); +void arch_interrupt_enable(); + +#define DOPENDING_SAVE() arch_context_saveandcall(dopending, NULL) + +static void removefromqueue(isr_t *isr) { + if (isr->prev) + isr->prev->next = isr->next; + else + _cpu()->isrqueue = isr->next; + + if (isr->next) + isr->next->prev = isr->prev; +} + +static void insertinqueue(isr_t *isr) { + if (isr->pending) + return; + + isr->next = _cpu()->isrqueue; + if (isr->next) + isr->next->prev = isr; + _cpu()->isrqueue = isr; +} + +static void dopending(context_t *ctx) { + bool entrystatus = _cpu()->intstatus; + if (entrystatus) { + _cpu()->intstatus = false; + arch_interrupt_disable(); + } + + isr_t *list = NULL; + isr_t *iterator = _cpu()->isrqueue; + + // build a list of pending ISRs and remove them from the main queue + + while (iterator) { + isr_t *next = iterator->next; + if (_cpu()->ipl > iterator->priority) { + removefromqueue(iterator); + + if (list) + list->prev = iterator; + iterator->next = list; + iterator->prev = NULL; + list = iterator; + + __assert(iterator->pending); + } + + iterator = next; + } + + if (list == NULL) + goto cleanup; + + // run pending ISRs + + while (list) { + isr_t *isr = list; + list = list->next; + if (list) + list->prev = NULL; + isr->pending = false; + + long oldipl = interrupt_raiseipl(isr->priority); + + isr->func(isr, ctx); + interrupt_set(false); + + interrupt_loweripl(oldipl); + + // if any new interrupts are pending, they will be taken care of here + // XXX this is kinda hacky could be very very bad if there are a lot of interrupts + dopending(ctx); + } + + cleanup: + if (entrystatus) { + _cpu()->intstatus = true; + arch_interrupt_enable(); + } +} + + void interrupt_isr(int vec, context_t *ctx) { isr_t *isr = &_cpu()->isr[vec]; _cpu()->intstatus = false; @@ -18,7 +104,9 @@ void interrupt_isr(int vec, context_t *ctx) { interrupt_set(false); interrupt_loweripl(oldipl); + dopending(ctx); } else { + insertinqueue(isr); isr->pending = true; } @@ -58,7 +146,8 @@ long interrupt_loweripl(long ipl) { _cpu()->ipl = ipl; interrupt_set(oldintstatus); - // TODO do pending interrupts + if (oldintstatus) + DOPENDING_SAVE(); return oldipl; } @@ -66,21 +155,26 @@ long interrupt_loweripl(long ipl) { long interrupt_raiseipl(long ipl) { bool oldintstatus = interrupt_set(false); long oldipl = _cpu()->ipl; + if (oldipl > ipl) _cpu()->ipl = ipl; + interrupt_set(oldintstatus); + return oldipl; } -void arch_interrupt_disable(); -void arch_interrupt_enable(); - bool interrupt_set(bool status) { if (status) arch_interrupt_enable(); else arch_interrupt_disable(); + bool old = _cpu()->intstatus; _cpu()->intstatus = status; + + if (status) + DOPENDING_SAVE(); + return old; }