use core::ptr::NonNull;
use x86_64::{
PrivilegeLevel, VirtAddr,
instructions::tables::load_tss,
registers::segmentation::{CS, DS, ES, SS, Segment, SegmentSelector},
structures::{
gdt::{Descriptor, GlobalDescriptorTable},
tss::TaskStateSegment,
},
};
pub(super) const DOUBLE_FAULT_IST_INDEX: u16 = 0;
pub struct TrapStorage {
pub tss: NonNull<TaskStateSegment>,
pub gdt: NonNull<GlobalDescriptorTable>,
pub double_fault_stack_top: ax_memory_addr::VirtAddr,
}
#[trait_ffi::def_extern_trait(mod_path = "boot")]
pub unsafe trait TrapStorageProvider {
fn take() -> TrapStorage;
}
pub const KCODE64: SegmentSelector = SegmentSelector::new(1, PrivilegeLevel::Ring0);
pub const KDATA: SegmentSelector = SegmentSelector::new(2, PrivilegeLevel::Ring0);
pub const UDATA: SegmentSelector = SegmentSelector::new(3, PrivilegeLevel::Ring3);
pub const UCODE64: SegmentSelector = SegmentSelector::new(4, PrivilegeLevel::Ring3);
fn install_exception_stacks(tss: &mut TaskStateSegment, double_fault_stack_top: VirtAddr) {
tss.interrupt_stack_table[usize::from(DOUBLE_FAULT_IST_INDEX)] = double_fault_stack_top;
}
pub(super) fn init() {
let mut storage = trap_storage_provider::take();
let (gdt, tss): (
&'static mut GlobalDescriptorTable,
&'static mut TaskStateSegment,
) = unsafe { (storage.gdt.as_mut(), storage.tss.as_mut()) };
install_exception_stacks(
tss,
VirtAddr::new(storage.double_fault_stack_top.as_usize() as u64),
);
assert_eq!(gdt.append(Descriptor::kernel_code_segment()), KCODE64);
assert_eq!(gdt.append(Descriptor::kernel_data_segment()), KDATA);
assert_eq!(gdt.append(Descriptor::user_data_segment()), UDATA);
assert_eq!(gdt.append(Descriptor::user_code_segment()), UCODE64);
let tss = gdt.append(Descriptor::tss_segment(&*tss));
gdt.load();
unsafe {
CS::set_reg(KCODE64);
DS::set_reg(KDATA);
ES::set_reg(KDATA);
SS::set_reg(KDATA);
load_tss(tss);
}
}