# IRQ handlers The current IRQ handlers are defined in `shelter/lib/include/irq/irq.h` and implemented in `shelter/lib/src/irq/irq.c`. The current handlers are tailored to fit the needs of the boot process and are therefore not complete at all. ## Overview All IRQ handling is done through the macros defined inside `shelter/lib/src/irq/irq_handler.asm`. The current IRQ handlers are currently capable of managing three categories: - CPU faults - Legacy IRQs - LAPIC interrupts The main dispatching is done inside `sh_irq_dispatch(sh_irq_INTERRUPT_FRAME *frame)`. The `frame` argument is built by pushing registers on the stack in the ASM stub and look like this: ``` C typedef struct { sh_uint64 r11,r10,r9,r8,rdx,rcx,rax; sh_uint64 vector; sh_uint64 error_code; sh_uint64 rip; sh_uint64 cs; sh_uint64 rflags; } sh_irq_INTERRUPT_FRAME; #pragma pack() ``` This frame will probably be modified/expanded in futures updates. When we say "block", we mean a simple infinite loop. ## CPU faults The following table provide the informations related to CPU faults handling: Name | Behaviour -----|---------- Division by zero | Log and block Debug | Log and block NMI | Log and block Breakpoints | Log and continue, usage of breakpoints isn't supported but it won't block the kernel Overflow | Log and block Bound range | Log and block Device not available | Log and block Double fault | Log and block Co-processor segment overrun | Log and block Invalid TSS | Log and block Segment not present | Log and block Stack segment fault | Log and block General protection fault | Log and block Page fault | Log and block Alignement check | Log and block Machine check | Log and block SIMD floating point exception | Log and block Virtualization exception | Log and block Control protection exception | Log and block Hypervisor exception exception | Log and block VMM communication exception | Log and block Security exception | Log and block CPU faults are the only exception where the frame will be presented. ## Legacy IRQs In Shelter, legacy IRQs are remapped on vectors 32 to 47. Depending on vector, two behavious are possibles: - If vector is 32, the IRQ come from the PIT, starting a specific procedure depending on the context - If vector is between 33 and 47, the IRQ is treated by registered IRQ handlers Legacy IRQs have two main state: - Managed by PIC: in this case, they are just logged and then ignored, except for the PIT IRQ0 depending on the context - Managed by IOAPIC: in this case, PIT IRQ0 are logged and then ignored, and others IRQ are delegated to drivers handlers The switch between PIC and IOAPIC is effectuated by `sh_irq_switch_irq_management()`. Only after this switch is correctly done, drivers handles can be registered using `sh_irq_legacy_register_handler(sh_uint8 legacy_irq,sh_irq_HANDLER_PTR handler)`. Handlers can't be registered for IRQ 0. Once a handler is set, it can't be removed or updated. The `sh_irq_legacy_register_handler()` function automatically unmask the GSI corresponding to the legacy IRQ. The type `sh_irq_HANDLER_PTR` is defined like this: ``` C typedef void (*sh_irq_HANDLER_PTR)(); ``` No arguments are provided. ### PIT IRQ The PIT is mainly used to estimate the CPU frequency. For this, each PIT IRQ is handled differently depending on the context. The `sh_irq_start_tsc()` function signal to the IRQ handler that the CPU frequency estimation procedure has been triggered, setting the internal state of the procedure to 0, and reseting the TSC values (`tsc_start`, `tsc_end` and `tsc_delta`). When a PIT IRQ arrives: - If the legacy IRQs are managed by IOAPIC, simply log and continue - If not, do the following: - If the state of the estimation procedure is 0, set `tsc_start` to the current TSC register value and set the state of the procedure to 1 - Else if the state of the estimation procedure is 1, set `tsc_end` to the current TSC register value, compute `tsc_delta = tsc_end - tsc_start` and set the state of the procedure to 2 - Else, log and continue The `sh_irq_get_tsc_delta()` return `tsc_delta`. A value of 0 indicating that the procedure isn't finished yet. ### Others legacy IRQs Others legacy IRQs (vectors 33 to 47) are handled through a common loader that work like this: - If the legacy IRQs are managed by IOAPIC: - If a handler pointer is defined for this legacy IRQ, call it and continue once finished - Else, just continue without logging - Else, log and block ## LAPIC interrupts LAPIC interrupts can be of two sorts: - one shot timer: mapped on vector 254 - spurious vector: mapped on vector 255 ### One shot timer The purpose of this handler is to know when the one shot timer generated an interrupt. Each CPU maintain in its per-CPU struct a boolean named `timer_state.` The `sh_irq_start_timer()` function is executed by the function to arm the `timer_state` flag: it set this flag to `SH_TRUE`. When an one shot timer interrupt arrives: - If `timer_state` was `SH_FALSE`, log the interrupt for debug purposes, it doesn't block anything - If `timer_state` was `SH_TRUE`, set it to `SH_FALSE` and then resume The `sh_irq_get_timer_state()` function is a wrapper that return the local `timer_state` of any CPU. ### Spurious vector In case of interrupts on the spurious vector, we just log it for debug purposes and continue.