diff --git a/paw/stos/arch/amd64/cpu/intr.c b/paw/stos/arch/amd64/cpu/intr.c new file mode 100644 index 0000000..7028e77 --- /dev/null +++ b/paw/stos/arch/amd64/cpu/intr.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2026, Chloe M. + * Provided under the BSD-3 clause. + * + * Description: Interrupt management + * Author: Chloe M. + */ + +#include +#include +#include + +/* Interrupt priority level shift */ +#define IPL_SHIFT 4 + +/* Globals */ +static INTR_HANDLER Handlers[256]; + +ST_STATUS +HalRegisterIntr(const INTR_HANDLER *Handler, BOOLEAN IsUser) +{ + UCHAR Vector, Idx; + INTR_HANDLER *HandlerSlot; + + if (Handler == NULL) { + return STATUS_INVALID_PARAM; + } + + /* Must have a valid handler */ + if (Handler->Handler == NULL) { + return STATUS_INVALID_PARAM; + } + + /* Ensure that the priority is valid */ + switch (Handler->Irql) { + case IRQL_PASSIVE: + case IRQL_APC: + case IRQL_DEVICE: + case IRQL_HIGH: + break; + default: + return STATUS_INVALID_PARAM; + } + + /* + * We have 16 priorities at every band as 4-bits makes up + * the interrupt priority level. Our job is to find a slot + * that is free for our interrupt handler at its given IRQL. + */ + Vector = MAX(0x20, Handler->Irql << IPL_SHIFT); + for (Idx = Vector; Idx < Vector + 16; ++Idx) { + HandlerSlot = &Handlers[Idx]; + + /* This entry is present, don't overwrite */ + if (HandlerSlot->Present) { + continue; + } + + /* Fill the slot */ + *HandlerSlot = *(INTR_HANDLER *)Handler; + HandlerSlot->Present = 1; + + /* Register the interrupt */ + MdIdtSetEntry( + Idx, + (UPTR)Handler->Handler, + (IsUser) ? IDT_USER_GATE : IDT_INT_GATE, + 0 + ); + + return STATUS_SUCCESS; + } + + /* No entry found */ + return STATUS_NOT_FOUND; +} diff --git a/paw/stos/head/hal/intr.h b/paw/stos/head/hal/intr.h index 4699834..1818a31 100644 --- a/paw/stos/head/hal/intr.h +++ b/paw/stos/head/hal/intr.h @@ -9,6 +9,9 @@ #ifndef _HAL_INTR_H_ #define _HAL_INTR_H_ 1 +#include +#include + /* * Valid interrupt request levels * @@ -23,4 +26,47 @@ #define IRQL_DEVICE 3 #define IRQL_HIGH 4 +/* IRQLs are of this type */ +typedef UCHAR IRQL; + +/* + * Interrupt data that is given to a service routine + * + * @Vector: Interrupt vector that fired + * @Data: Driver specific data + */ +typedef struct { + UCHAR Vector; + VOID *Data; +} INTR_DATA; + +/* + * Interrupt handler descriptor + * + * @Irql: Prioirty of handler + * @Present: Must be 1 to be valid + * @Data: Driver specific data + * @Handler: Reference to interrupt handler + * + * XXX: There is no need to set the present bit when + * creating an instance as it is set automatically. + */ +typedef struct { + IRQL Irql; + UCHAR Present : 1; + VOID *Data; + LONG(*Handler)(INTR_DATA *Data); +} INTR_HANDLER; + +/* + * Register an interrupt handler + * + * @Handler: Interrupt handler to register + * @IsUser: Should be set if user interrupt + * + * XXX: Most of the times, @IsUser should remain unset, + * it is mostly there for the sake of scalability. + */ +ST_STATUS HalRegisterIntr(const INTR_HANDLER *Handler, BOOLEAN IsUser); + #endif /* !_HAL_INTR_H_ */