Skip to main content

strat9_kernel/arch/x86_64/
gdt.rs

1//! Global Descriptor Table (GDT) for Strat9-OS
2//!
3//! Sets up the GDT with kernel code/data segments, user segments for SYSRET,
4//! and a TSS descriptor. The TSS must be initialized before this module.
5//!
6//! ## Segment layout (SYSRET-compatible)
7//!
8//! ```text
9//! Index 0: Null
10//! Index 1: Kernel Code 64-bit  (CS=0x08)     ← SYSCALL loads CS from STAR[47:32]
11//! Index 2: Kernel Data          (SS=0x10)     ← SYSCALL loads SS
12//! Index 3: User Code 32-bit    (dummy, 0x18)  ← STAR[63:48] base
13//! Index 4: User Data            (0x20)        ← SYSRETQ: SS = base+8 | RPL3 = 0x23
14//! Index 5: User Code 64-bit    (0x28)        ← SYSRETQ: CS = base+16 | RPL3 = 0x2B
15//! Index 6-7: TSS (16-byte descriptor)
16//! ```
17//!
18//! STAR MSR: `[47:32]=0x08` (kernel CS), `[63:48]=0x18` (index 3).
19//! SYSRETQ: CS = 0x18+16 = 0x28 | RPL3 = 0x2B, SS = 0x18+8 = 0x20 | RPL3 = 0x23.
20
21use core::{
22    mem::MaybeUninit,
23    sync::atomic::{AtomicBool, Ordering},
24};
25use x86_64::{
26    instructions::{
27        segmentation::{Segment, CS, DS, SS},
28        tables::load_tss,
29    },
30    structures::gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector},
31};
32
33/// GDT storage — per CPU
34static mut GDT: [MaybeUninit<GlobalDescriptorTable>; crate::arch::x86_64::percpu::MAX_CPUS] =
35    [const { MaybeUninit::uninit() }; crate::arch::x86_64::percpu::MAX_CPUS];
36
37/// Cached segment selectors after GDT is loaded (per CPU)
38static mut SELECTORS: [MaybeUninit<Selectors>; crate::arch::x86_64::percpu::MAX_CPUS] =
39    [const { MaybeUninit::uninit() }; crate::arch::x86_64::percpu::MAX_CPUS];
40static SELECTORS_INIT: [AtomicBool; crate::arch::x86_64::percpu::MAX_CPUS] =
41    [const { AtomicBool::new(false) }; crate::arch::x86_64::percpu::MAX_CPUS];
42
43#[derive(Copy, Clone)]
44struct Selectors {
45    kernel_code: SegmentSelector,
46    kernel_data: SegmentSelector,
47    #[allow(dead_code)]
48    user_code32: SegmentSelector,
49    user_data: SegmentSelector,
50    user_code64: SegmentSelector,
51    #[allow(dead_code)]
52    tss: SegmentSelector,
53}
54
55/// Initialize the GDT with kernel segments, user segments, and TSS.
56///
57/// **Prerequisite**: `tss::init()` must be called first.
58///
59/// The segment ordering is critical for SYSRET to work correctly.
60/// SYSRET expects: STAR[63:48] points to a base where base+0 = user_code32,
61/// base+8 = user_data, base+16 = user_code64.
62pub fn init() {
63    init_cpu(0);
64}
65
66/// Initialize the GDT for a given CPU index.
67pub fn init_cpu(cpu_index: usize) {
68    // Bounds check: prevent OOB access into static arrays before any unsafe.
69    assert!(
70        cpu_index < crate::arch::x86_64::percpu::MAX_CPUS,
71        "GDT init_cpu: cpu_index {} >= MAX_CPUS {}",
72        cpu_index,
73        crate::arch::x86_64::percpu::MAX_CPUS,
74    );
75    // SAFETY: Called during init (BSP) or AP bring-up before interrupts are enabled on that CPU.
76    unsafe {
77        let gdt = &mut *GDT[cpu_index].as_mut_ptr();
78        *gdt = GlobalDescriptorTable::new();
79
80        // Index 1: Kernel Code 64-bit (0x08)
81        let kernel_code = gdt.append(Descriptor::kernel_code_segment());
82        // Index 2: Kernel Data (0x10)
83        let kernel_data = gdt.append(Descriptor::kernel_data_segment());
84
85        // Index 3: User Code 32-bit dummy (0x18)
86        // SYSRET requires this slot to exist. We create a 32-bit code segment
87        // with DPL=3 that is never actually used for execution.
88        // Descriptor bits: Present | DPL=3 | Code segment | Readable | 32-bit
89        let user_code32_bits: u64 = (1 << 47)       // Present
90            | (3 << 45)                               // DPL = 3
91            | (1 << 44)                               // S = 1 (code/data)
92            | (1 << 43)                               // Executable
93            | (1 << 41)                               // Readable
94            | (1 << 54); // D = 1 (32-bit default)
95        let user_code32 = gdt.append(Descriptor::UserSegment(user_code32_bits));
96
97        // Index 4: User Data (0x20)
98        let user_data = gdt.append(Descriptor::user_data_segment());
99        // Index 5: User Code 64-bit (0x28)
100        let user_code64 = gdt.append(Descriptor::user_code_segment());
101
102        // Index 6-7: TSS (16-byte descriptor)
103        let tss_sel = gdt.append(Descriptor::tss_segment(super::tss::tss_for(cpu_index)));
104
105        gdt.load_unsafe();
106
107        // Reload segment registers with new selectors
108        CS::set_reg(kernel_code);
109        DS::set_reg(kernel_data);
110        SS::set_reg(kernel_data);
111
112        // Load TSS into task register
113        load_tss(tss_sel);
114
115        SELECTORS[cpu_index].write(Selectors {
116            kernel_code,
117            kernel_data,
118            user_code32,
119            user_data,
120            user_code64,
121            tss: tss_sel,
122        });
123        SELECTORS_INIT[cpu_index].store(true, Ordering::Release);
124
125        log::info!(
126            "GDT[CPU{}] loaded: CS={:#x} DS/SS={:#x} user32={:#x} user_data={:#x} user64={:#x} TSS={:#x}",
127            cpu_index,
128            kernel_code.0,
129            kernel_data.0,
130            user_code32.0,
131            user_data.0,
132            user_code64.0,
133            tss_sel.0,
134        );
135    }
136}
137
138/// Get the kernel code segment selector
139pub fn kernel_code_selector() -> SegmentSelector {
140    selectors_for(current_cpu_index()).kernel_code
141}
142
143/// Get the kernel data segment selector
144pub fn kernel_data_selector() -> SegmentSelector {
145    selectors_for(current_cpu_index()).kernel_data
146}
147
148/// Get the user code 64-bit segment selector (with RPL=3)
149pub fn user_code_selector() -> SegmentSelector {
150    let sel = selectors_for(current_cpu_index()).user_code64;
151    SegmentSelector(sel.0 | 3) // Set RPL=3
152}
153
154/// Get the user data segment selector (with RPL=3)
155pub fn user_data_selector() -> SegmentSelector {
156    let sel = selectors_for(current_cpu_index()).user_data;
157    SegmentSelector(sel.0 | 3) // Set RPL=3
158}
159
160/// Get the raw STAR MSR value for SYSCALL/SYSRET.
161///
162/// STAR[47:32] = kernel CS selector (for SYSCALL entry)
163/// STAR[63:48] = user code 32 selector base (for SYSRET calculation)
164pub fn star_msr_value() -> u64 {
165    let sels = selectors_for(current_cpu_index());
166    let kernel_cs = sels.kernel_code.0 as u64;
167    let user_base = sels.user_code32.0 as u64; // 0x18 (without RPL)
168    (kernel_cs << 32) | (user_base << 48)
169}
170
171/// Performs the selectors for operation.
172fn selectors_for(cpu_index: usize) -> Selectors {
173    if !SELECTORS_INIT[cpu_index].load(Ordering::Acquire) {
174        panic!("GDT selectors for CPU{} not initialized", cpu_index);
175    }
176    // SAFETY: Initialized in init_cpu and stored for 'static lifetime.
177    unsafe { *SELECTORS[cpu_index].as_ptr() }
178}
179
180/// Performs the current cpu index operation.
181fn current_cpu_index() -> usize {
182    crate::arch::x86_64::percpu::current_cpu_index()
183}