use ax_lazyinit::LazyInit;
use x86_64::{
addr::VirtAddr,
structures::idt::{Entry, InterruptDescriptorTable},
};
const NUM_INT: usize = 256;
static IDT: LazyInit<InterruptDescriptorTable> = LazyInit::new();
const fn exception_ist_index(vector: usize) -> Option<u16> {
match vector {
8 => Some(super::gdt::DOUBLE_FAULT_IST_INDEX),
_ => None,
}
}
pub(super) fn init() {
IDT.call_once(|| {
unsafe extern "C" {
#[link_name = "trap_handler_table"]
static ENTRIES: [i32; NUM_INT];
}
let mut table = InterruptDescriptorTable::new();
let entries = unsafe {
core::mem::transmute::<&mut InterruptDescriptorTable, &mut [Entry<()>; NUM_INT]>(
&mut table,
)
};
let base = unsafe { ENTRIES.as_ptr() } as isize;
for (i, entry) in entries.iter_mut().enumerate() {
let offset = unsafe { *ENTRIES.as_ptr().add(i) } as isize;
let handler = VirtAddr::new((base + offset) as u64);
let opt = unsafe { entry.set_handler_addr(handler) };
if let Some(index) = exception_ist_index(i) {
unsafe {
opt.set_stack_index(index);
}
}
if i == 0x3 || i == 0x80 {
opt.set_privilege_level(x86_64::PrivilegeLevel::Ring3);
}
}
table
});
IDT.load();
}
#[cfg(test)]
mod tests {
use super::exception_ist_index;
#[test]
fn double_fault_uses_a_dedicated_interrupt_stack() {
assert_eq!(exception_ist_index(8), Some(0));
}
}