use core::{arch::global_asm, cell::UnsafeCell};
use patina::SIZE_4GB;
global_asm!(include_str!("interrupt_handler.asm"));
unsafe extern "efiapi" {
fn AsmGetVectorAddress(index: usize) -> u64;
}
#[derive(Clone, Copy)]
#[repr(C)]
struct IdtEntry {
offset_low: u16,
selector: u16,
ist: u8,
type_attr: u8,
offset_mid: u16,
offset_high: u32,
reserved: u32,
}
impl IdtEntry {
const fn empty() -> Self {
Self { offset_low: 0, selector: 0, ist: 0, type_attr: 0, offset_mid: 0, offset_high: 0, reserved: 0 }
}
fn set_handler(&mut self, addr: u64, selector: u16, ist_index: u8) {
self.offset_low = addr as u16;
self.offset_mid = (addr >> 16) as u16;
self.offset_high = (addr >> 32) as u32;
self.selector = selector;
self.ist = ist_index & 0x7;
self.type_attr = 0x8E; self.reserved = 0;
}
}
#[repr(C, align(16))]
struct Idt {
entries: [IdtEntry; 256],
}
struct StaticIdt(UnsafeCell<Idt>);
unsafe impl Sync for StaticIdt {}
#[repr(C, packed)]
struct Idtr {
limit: u16,
base: u64,
}
fn get_vector_address(index: usize) -> u64 {
assert!(index < 256, "Invalid vector index! 0x{index:#X?}");
unsafe { AsmGetVectorAddress(index) }
}
static IDT: StaticIdt = StaticIdt(UnsafeCell::new(Idt { entries: [IdtEntry::empty(); 256] }));
pub fn initialize_idt() {
let cs = crate::gdt::CODE_SELECTOR;
let idt = unsafe { &mut *IDT.0.get() };
for vector in 0..=255usize {
let ist_index = u8::from(vector == 8 || vector == 14);
idt.entries.get_mut(vector).expect("vector out of bounds").set_handler(
get_vector_address(vector),
cs,
ist_index,
);
}
assert!((IDT.0.get() as usize) < SIZE_4GB, "IDT above 4GB, MP services will fail");
#[cfg(target_os = "uefi")]
{
let idtr = Idtr { limit: (core::mem::size_of::<Idt>() - 1) as u16, base: IDT.0.get() as u64 };
unsafe { core::arch::asm!("lidt [{}]", in(reg) core::ptr::addr_of!(idtr), options(nostack)) };
}
log::info!("Loaded IDT");
}