Skip to main content

ax_cpu/arch/x86_64/
boot.rs

1// SPDX-License-Identifier: Apache-2.0 AND MPL-2.0
2// Early control-register setup and stack handoff migrated from someboot (周睿).
3//! Helper functions to initialize the CPU states on systems bootstrapping.
4
5pub use x86_64::{
6    PrivilegeLevel, VirtAddr as DescriptorAddress,
7    addr::VirtAddrNotValid,
8    registers::segmentation::SegmentSelector,
9    structures::{
10        gdt::{Descriptor, Entry as GdtEntry, GlobalDescriptorTable},
11        tss::{InvalidIoMap, TaskStateSegment},
12    },
13};
14
15pub use super::gdt::{TrapStorage, TrapStorageProvider, trap_storage_provider};
16
17/// Initializes trap handling on the current CPU.
18///
19/// In detail, it initializes the GDT, IDT on x86_64 platforms. If the `uspace`
20/// feature is enabled, it also initializes relevant model-specific registers to
21/// configure the handler for `syscall` instruction.
22///
23/// # Notes
24/// Before calling this function, the platform entry path must have installed
25/// and verified the current CPU area. Architecture trap initialization is not
26/// a second per-CPU binder.
27pub fn init_trap() {
28    super::gdt::init();
29    super::idt::init();
30    #[cfg(feature = "uspace")]
31    super::uspace::init_syscall();
32}
33
34pub use super::entry::boot::{
35    BootVectorTable, current_vector_table, install as install_boot_vectors,
36};
37
38/// Control-register state installed before a CPU enters the kernel runtime.
39///
40/// This matches Linux's x86 `CR0_STATE`: paging and protected mode are active,
41/// supervisor writes honor read-only PTEs, alignment checking is available,
42/// and reset-time cache-disable state is not inherited by secondary CPUs.
43pub const KERNEL_CR0_STATE: usize = x86::controlregs::Cr0::CR0_ENABLE_PAGING.bits()
44    | x86::controlregs::Cr0::CR0_ALIGNMENT_MASK.bits()
45    | x86::controlregs::Cr0::CR0_WRITE_PROTECT.bits()
46    | x86::controlregs::Cr0::CR0_NUMERIC_ERROR.bits()
47    | x86::controlregs::Cr0::CR0_EXTENSION_TYPE.bits()
48    | x86::controlregs::Cr0::CR0_MONITOR_COPROCESSOR.bits()
49    | x86::controlregs::Cr0::CR0_PROTECTED_MODE.bits();
50
51/// Checks the complete early kernel CR0 contract on this CPU.
52///
53/// # Safety
54/// Execute at CPL0 before running tasks or enabling interrupts.
55pub unsafe fn assert_kernel_cr0_state() {
56    // SAFETY: the boot owner executes this check at CPL0.
57    let current = unsafe { x86::controlregs::cr0() };
58    assert_eq!(
59        current.bits(),
60        KERNEL_CR0_STATE,
61        "invalid x86_64 kernel CR0 state on this CPU"
62    );
63}
64
65/// Enables architectural execute-disable page permissions.
66///
67/// # Safety
68/// Execute at CPL0 on a CPU with NX support, before installing NX descriptors.
69pub unsafe fn enable_execute_disable() {
70    // SAFETY: the boot owner has established long mode with NX-capable hardware.
71    unsafe {
72        let value = x86::msr::rdmsr(x86::msr::IA32_EFER);
73        x86::msr::wrmsr(x86::msr::IA32_EFER, value | (1 << 11));
74    }
75}
76
77/// Installs the kernel CR0 state and enables global page translations.
78///
79/// # Safety
80/// Execute at CPL0 with valid long-mode page tables and no active tasks.
81/// Mappings must already have coherent cache attributes; this is boot setup,
82/// not a live cache-mode transition.
83pub unsafe fn configure_paging() {
84    use x86::controlregs::{self, Cr0, Cr4};
85    // SAFETY: the caller owns boot control-register state and installed mappings.
86    unsafe {
87        controlregs::cr0_write(Cr0::from_bits_truncate(KERNEL_CR0_STATE));
88        controlregs::cr4_write(controlregs::cr4() | Cr4::CR4_ENABLE_GLOBAL_PAGES);
89        assert_kernel_cr0_state();
90    }
91}
92
93/// Enables supported x87, SSE and AVX components in the boot XCR0 policy.
94///
95/// # Safety
96/// Execute at CPL0 before any task owns extended register state. All subsequent
97/// save areas and context switches must support the enabled components.
98pub unsafe fn enable_xsave_features() {
99    use x86::{controlregs, cpuid::CpuId};
100    let Some(info) = CpuId::new().get_feature_info() else {
101        return;
102    };
103    if !info.has_xsave() {
104        return;
105    }
106    // SAFETY: CPUID establishes XSAVE support. OSXSAVE precedes XSETBV,
107    // mandatory x87/SSE components precede the optional AVX component.
108    unsafe {
109        controlregs::cr4_write(controlregs::cr4() | controlregs::Cr4::CR4_ENABLE_OS_XSAVE);
110        let mut bits = controlregs::Xcr0::XCR0_FPU_MMX_STATE | controlregs::Xcr0::XCR0_SSE_STATE;
111        if info.has_avx() {
112            bits |= controlregs::Xcr0::XCR0_AVX_STATE;
113        }
114        controlregs::xcr0_write(bits);
115    }
116}
117
118/// Transfers to a Rust/C entry on a new stack with a terminating return slot.
119///
120/// # Safety
121/// `stack` must be a mapped, writable, 16-byte-aligned stack top with sufficient
122/// capacity. `entry` must be valid executable code in the active address space
123/// and must never return. No references to the abandoned stack may remain live.
124#[unsafe(naked)]
125pub unsafe extern "C" fn jump_to(_entry: usize, _stack: usize) -> ! {
126    core::arch::naked_asm!("mov rsp, rsi", "push 0", "xor ebp, ebp", "jmp rdi",);
127}