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