Skip to main content

ax_cpu/arch/x86_64/entry/
boot.rs

1// SPDX-License-Identifier: Apache-2.0 AND MPL-2.0
2// Early IDT gate construction migrated from someboot (周睿).
3//! Early exception gates sharing the native register-save assembly.
4
5use core::{
6    hint::spin_loop,
7    sync::atomic::{AtomicU8, Ordering},
8};
9
10use x86::{
11    bits64::segmentation::Descriptor64,
12    dtables::{self, DescriptorTablePointer},
13    segmentation::{BuildDescriptor, DescriptorBuilder, GateDescriptorBuilder, cs},
14};
15
16use super::super::context::TrapFrame;
17
18#[repr(C, align(16))]
19/// Owned early exception descriptors for the current code mapping and selector.
20/// Keep the value at a stable address while any CPU has it installed.
21pub struct BootVectorTable([Descriptor64; 256]);
22static mut IDT: BootVectorTable = BootVectorTable([Descriptor64::NULL; 256]);
23static STATE: AtomicU8 = AtomicU8::new(0);
24
25core::arch::global_asm!(include_str!("gpr.S"), include_str!("boot.S"),
26    ".purgem PUSH_GENERAL_REGS", ".purgem POP_GENERAL_REGS", dispatch = sym dispatch);
27
28unsafe extern "C" {
29    fn __ax_cpu_x86_boot_0();
30    fn __ax_cpu_x86_boot_1();
31    fn __ax_cpu_x86_boot_2();
32    fn __ax_cpu_x86_boot_3();
33    fn __ax_cpu_x86_boot_4();
34    fn __ax_cpu_x86_boot_5();
35    fn __ax_cpu_x86_boot_6();
36    fn __ax_cpu_x86_boot_7();
37    fn __ax_cpu_x86_boot_8();
38    fn __ax_cpu_x86_boot_9();
39    fn __ax_cpu_x86_boot_10();
40    fn __ax_cpu_x86_boot_11();
41    fn __ax_cpu_x86_boot_12();
42    fn __ax_cpu_x86_boot_13();
43    fn __ax_cpu_x86_boot_14();
44    fn __ax_cpu_x86_boot_15();
45    fn __ax_cpu_x86_boot_16();
46    fn __ax_cpu_x86_boot_17();
47    fn __ax_cpu_x86_boot_18();
48    fn __ax_cpu_x86_boot_19();
49    fn __ax_cpu_x86_boot_20();
50    fn __ax_cpu_x86_boot_21();
51    fn __ax_cpu_x86_boot_22();
52    fn __ax_cpu_x86_boot_23();
53    fn __ax_cpu_x86_boot_24();
54    fn __ax_cpu_x86_boot_25();
55    fn __ax_cpu_x86_boot_26();
56    fn __ax_cpu_x86_boot_27();
57    fn __ax_cpu_x86_boot_28();
58    fn __ax_cpu_x86_boot_29();
59    fn __ax_cpu_x86_boot_30();
60    fn __ax_cpu_x86_boot_31();
61}
62
63unsafe extern "C" fn dispatch(frame: *const TrapFrame) {
64    // SAFETY: the assembly owns a fully initialized native frame until return.
65    let frame = unsafe { &*frame };
66    // SAFETY: this callback executes in CPL0; CR2 is copied before boot policy runs.
67    let fault = unsafe { x86::controlregs::cr2() };
68    crate::trap::boot::boot_trap_handler::handle(&crate::trap::boot::BootException {
69        registers: frame.regs,
70        pc: frame.rip as usize,
71        sp: frame.rsp as usize,
72        status: frame.rflags,
73        syndrome: frame.error_code,
74        vector: frame.vector as u8,
75        fault_address: crate::VirtAddr::from_usize(fault as usize),
76    });
77}
78
79/// Installs the immutable early IDT on the current CPU.
80///
81/// # Safety
82/// Run in CPL0 with interrupts disabled and a valid, sufficiently sized stack.
83/// Every participating CPU must use the same long-mode code selector. Boot
84/// callbacks must not use runtime TLS or SIMD state, and must not unwind.
85/// Keep the IDT and its entry code mapped until replacing it with a runtime IDT.
86pub unsafe fn install() {
87    if STATE
88        .compare_exchange(0, 1, Ordering::Acquire, Ordering::Acquire)
89        .is_ok()
90    {
91        // SAFETY: STATE grants exclusive initialization before any IDT load.
92        unsafe {
93            core::ptr::addr_of_mut!(IDT).write(BootVectorTable::new());
94        }
95        STATE.store(2, Ordering::Release);
96    } else {
97        while STATE.load(Ordering::Acquire) != 2 {
98            spin_loop();
99        }
100    }
101    // SAFETY: acquire observes complete immutable descriptors; the caller keeps
102    // this static table mapped while it is installed on this CPU.
103    unsafe {
104        dtables::lidt(&DescriptorTablePointer {
105            base: core::ptr::addr_of!(IDT.0).cast::<Descriptor64>(),
106            limit: (core::mem::size_of::<BootVectorTable>() - 1) as u16,
107        });
108    }
109}
110
111/// Returns the currently installed IDT base.
112pub fn current_vector_table() -> usize {
113    let mut pointer: DescriptorTablePointer<Descriptor64> = Default::default();
114    // SAFETY: SIDT writes exactly the initialized descriptor pointer object.
115    unsafe {
116        dtables::sidt(&mut pointer);
117    }
118    pointer.base as usize
119}
120
121impl Default for BootVectorTable {
122    fn default() -> Self {
123        Self::new()
124    }
125}
126
127impl BootVectorTable {
128    /// Builds gates using this invocation's mapped entry addresses and CS.
129    pub fn new() -> Self {
130        let entries = [
131            __ax_cpu_x86_boot_0 as *const () as u64,
132            __ax_cpu_x86_boot_1 as *const () as u64,
133            __ax_cpu_x86_boot_2 as *const () as u64,
134            __ax_cpu_x86_boot_3 as *const () as u64,
135            __ax_cpu_x86_boot_4 as *const () as u64,
136            __ax_cpu_x86_boot_5 as *const () as u64,
137            __ax_cpu_x86_boot_6 as *const () as u64,
138            __ax_cpu_x86_boot_7 as *const () as u64,
139            __ax_cpu_x86_boot_8 as *const () as u64,
140            __ax_cpu_x86_boot_9 as *const () as u64,
141            __ax_cpu_x86_boot_10 as *const () as u64,
142            __ax_cpu_x86_boot_11 as *const () as u64,
143            __ax_cpu_x86_boot_12 as *const () as u64,
144            __ax_cpu_x86_boot_13 as *const () as u64,
145            __ax_cpu_x86_boot_14 as *const () as u64,
146            __ax_cpu_x86_boot_15 as *const () as u64,
147            __ax_cpu_x86_boot_16 as *const () as u64,
148            __ax_cpu_x86_boot_17 as *const () as u64,
149            __ax_cpu_x86_boot_18 as *const () as u64,
150            __ax_cpu_x86_boot_19 as *const () as u64,
151            __ax_cpu_x86_boot_20 as *const () as u64,
152            __ax_cpu_x86_boot_21 as *const () as u64,
153            __ax_cpu_x86_boot_22 as *const () as u64,
154            __ax_cpu_x86_boot_23 as *const () as u64,
155            __ax_cpu_x86_boot_24 as *const () as u64,
156            __ax_cpu_x86_boot_25 as *const () as u64,
157            __ax_cpu_x86_boot_26 as *const () as u64,
158            __ax_cpu_x86_boot_27 as *const () as u64,
159            __ax_cpu_x86_boot_28 as *const () as u64,
160            __ax_cpu_x86_boot_29 as *const () as u64,
161            __ax_cpu_x86_boot_30 as *const () as u64,
162            __ax_cpu_x86_boot_31 as *const () as u64,
163        ];
164        let mut table = Self([Descriptor64::NULL; 256]);
165        let selector = cs();
166        for (vector, address) in entries.into_iter().enumerate() {
167            table.0[vector] = DescriptorBuilder::interrupt_descriptor(selector, address)
168                .present()
169                .finish();
170        }
171        table
172    }
173
174    /// Installs this table on the current CPU.
175    ///
176    /// # Safety
177    /// The table must remain unmoved and mapped until a different IDT is loaded.
178    /// Execute at CPL0 with masked IRQs and the same CS used to construct it.
179    /// Callbacks require a valid kernel stack, no SIMD/TLS use and no unwinding.
180    pub unsafe fn install(&self) {
181        // SAFETY: the caller retains this table and the corresponding code.
182        unsafe {
183            dtables::lidt(&DescriptorTablePointer {
184                base: self.0.as_ptr(),
185                limit: (core::mem::size_of::<Self>() - 1) as u16,
186            });
187        }
188    }
189}