Skip to main content

ax_cpu/arch/x86_64/
gdt.rs

1use core::ptr::NonNull;
2
3use x86_64::{
4    PrivilegeLevel, VirtAddr,
5    instructions::tables::load_tss,
6    registers::segmentation::{CS, DS, ES, SS, Segment, SegmentSelector},
7    structures::{
8        gdt::{Descriptor, GlobalDescriptorTable},
9        tss::TaskStateSegment,
10    },
11};
12
13pub(super) const DOUBLE_FAULT_IST_INDEX: u16 = 0;
14
15/// Runtime-owned memory retained by this CPU's descriptor registers.
16pub struct TrapStorage {
17    /// A fresh, exclusively owned TSS for this CPU.
18    pub tss: NonNull<TaskStateSegment>,
19    /// A fresh, exclusively owned GDT for this CPU.
20    pub gdt: NonNull<GlobalDescriptorTable>,
21    /// Top of this CPU's mapped, writable double-fault stack.
22    pub double_fault_stack_top: ax_memory_addr::VirtAddr,
23}
24
25/// Supplies descriptor storage once for each initialized CPU.
26///
27/// # Safety
28///
29/// Returned objects must be initialized, uniquely owned by the current CPU,
30/// pinned, and mapped until CPU shutdown. The stack must be exclusive to that
31/// CPU and large enough for its double-fault diagnostic path. Repeated calls
32/// must fail before handing out an already installed object. In userspace
33/// builds the provider must export `__CPU_LOCAL_TSS_OFFSET`, the absolute
34/// offset of this same TSS from the kernel GS base, for pre-stack entry code.
35#[trait_ffi::def_extern_trait(mod_path = "boot")]
36pub unsafe trait TrapStorageProvider {
37    /// Transfers this CPU's fresh storage to its descriptor initialization.
38    fn take() -> TrapStorage;
39}
40
41/// Kernel code segment for 64-bit mode.
42pub const KCODE64: SegmentSelector = SegmentSelector::new(1, PrivilegeLevel::Ring0);
43/// Kernel data segment.
44pub const KDATA: SegmentSelector = SegmentSelector::new(2, PrivilegeLevel::Ring0);
45/// User data segment.
46pub const UDATA: SegmentSelector = SegmentSelector::new(3, PrivilegeLevel::Ring3);
47/// User code segment for 64-bit mode.
48pub const UCODE64: SegmentSelector = SegmentSelector::new(4, PrivilegeLevel::Ring3);
49
50fn install_exception_stacks(tss: &mut TaskStateSegment, double_fault_stack_top: VirtAddr) {
51    tss.interrupt_stack_table[usize::from(DOUBLE_FAULT_IST_INDEX)] = double_fault_stack_top;
52}
53
54/// Initializes the per-CPU TSS and GDT structures and loads them into the
55/// current CPU.
56pub(super) fn init() {
57    let mut storage = trap_storage_provider::take();
58    // SAFETY: the provider contract transfers initialized, unique, permanent
59    // storage. CPU bring-up owns this IRQ-disabled CPU until setup completes.
60    let (gdt, tss): (
61        &'static mut GlobalDescriptorTable,
62        &'static mut TaskStateSegment,
63    ) = unsafe { (storage.gdt.as_mut(), storage.tss.as_mut()) };
64    install_exception_stacks(
65        tss,
66        VirtAddr::new(storage.double_fault_stack_top.as_usize() as u64),
67    );
68    assert_eq!(gdt.append(Descriptor::kernel_code_segment()), KCODE64);
69    assert_eq!(gdt.append(Descriptor::kernel_data_segment()), KDATA);
70    assert_eq!(gdt.append(Descriptor::user_data_segment()), UDATA);
71    assert_eq!(gdt.append(Descriptor::user_code_segment()), UCODE64);
72    let tss = gdt.append(Descriptor::tss_segment(&*tss));
73    gdt.load();
74    // SAFETY: the GDT above contains these selectors and its TSS descriptor
75    // references the current CPU's permanent, initialized storage.
76    unsafe {
77        CS::set_reg(KCODE64);
78        DS::set_reg(KDATA);
79        ES::set_reg(KDATA);
80        SS::set_reg(KDATA);
81        load_tss(tss);
82    }
83}