5.2 KiB
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:
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:
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_startto 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_endto the current TSC register value, computetsc_delta = tsc_end - tsc_startand set the state of the procedure to 2 - Else, log and continue
- If the state of the estimation procedure is 0, set
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_statewasSH_FALSE, log the interrupt for debug purposes, it doesn't block anything - If
timer_statewasSH_TRUE, set it toSH_FALSEand 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.