Files
vystem/docs/shelter/irq/irq.md
2026-05-27 19:34:54 +02:00

127 lines
5.2 KiB
Markdown

# 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.