axhal/arch/x86_64/
gdt.rs

1use core::fmt;
2
3use lazyinit::LazyInit;
4use x86_64::instructions::tables::{lgdt, load_tss};
5use x86_64::registers::segmentation::{CS, Segment, SegmentSelector};
6use x86_64::structures::gdt::{Descriptor, DescriptorFlags};
7use x86_64::structures::{DescriptorTablePointer, tss::TaskStateSegment};
8use x86_64::{PrivilegeLevel, addr::VirtAddr};
9
10#[unsafe(no_mangle)]
11#[percpu::def_percpu]
12static TSS: TaskStateSegment = TaskStateSegment::new();
13
14#[percpu::def_percpu]
15static GDT: LazyInit<GdtStruct> = LazyInit::new();
16
17/// A wrapper of the Global Descriptor Table (GDT) with maximum 16 entries.
18#[repr(align(16))]
19pub struct GdtStruct {
20    table: [u64; 16],
21}
22
23impl GdtStruct {
24    /// Kernel code segment for 32-bit mode.
25    pub const KCODE32_SELECTOR: SegmentSelector = SegmentSelector::new(1, PrivilegeLevel::Ring0);
26    /// Kernel code segment for 64-bit mode.
27    pub const KCODE64_SELECTOR: SegmentSelector = SegmentSelector::new(2, PrivilegeLevel::Ring0);
28    /// Kernel data segment.
29    pub const KDATA_SELECTOR: SegmentSelector = SegmentSelector::new(3, PrivilegeLevel::Ring0);
30    /// User code segment for 32-bit mode.
31    pub const UCODE32_SELECTOR: SegmentSelector = SegmentSelector::new(4, PrivilegeLevel::Ring3);
32    /// User data segment.
33    pub const UDATA_SELECTOR: SegmentSelector = SegmentSelector::new(5, PrivilegeLevel::Ring3);
34    /// User code segment for 64-bit mode.
35    pub const UCODE64_SELECTOR: SegmentSelector = SegmentSelector::new(6, PrivilegeLevel::Ring3);
36    /// TSS segment.
37    pub const TSS_SELECTOR: SegmentSelector = SegmentSelector::new(7, PrivilegeLevel::Ring0);
38
39    /// Constructs a new GDT struct that filled with the default segment
40    /// descriptors, including the given TSS segment.
41    pub fn new(tss: &'static TaskStateSegment) -> Self {
42        let mut table = [0; 16];
43        // first 3 entries are the same as in multiboot.S
44        table[1] = DescriptorFlags::KERNEL_CODE32.bits(); // 0x00cf9b000000ffff
45        table[2] = DescriptorFlags::KERNEL_CODE64.bits(); // 0x00af9b000000ffff
46        table[3] = DescriptorFlags::KERNEL_DATA.bits(); // 0x00cf93000000ffff
47        table[4] = DescriptorFlags::USER_CODE32.bits(); // 0x00cffb000000ffff
48        table[5] = DescriptorFlags::USER_DATA.bits(); // 0x00cff3000000ffff
49        table[6] = DescriptorFlags::USER_CODE64.bits(); // 0x00affb000000ffff
50        if let Descriptor::SystemSegment(low, high) = Descriptor::tss_segment(tss) {
51            table[7] = low;
52            table[8] = high;
53        }
54        Self { table }
55    }
56
57    /// Returns the GDT pointer (base and limit) that can be used in `lgdt`
58    /// instruction.
59    pub fn pointer(&self) -> DescriptorTablePointer {
60        DescriptorTablePointer {
61            base: VirtAddr::new(self.table.as_ptr() as u64),
62            limit: (core::mem::size_of_val(&self.table) - 1) as u16,
63        }
64    }
65
66    /// Loads the GDT into the CPU (executes the `lgdt` instruction), and
67    /// updates the code segment register (`CS`).
68    ///
69    /// # Safety
70    ///
71    /// This function is unsafe because it manipulates the CPU's privileged
72    /// states.
73    pub unsafe fn load(&'static self) {
74        unsafe {
75            lgdt(&self.pointer());
76            CS::set_reg(Self::KCODE64_SELECTOR);
77        }
78    }
79
80    /// Loads the TSS into the CPU (executes the `ltr` instruction).
81    ///
82    /// # Safety
83    ///
84    /// This function is unsafe because it manipulates the CPU's privileged
85    /// states.
86    pub unsafe fn load_tss(&'static self) {
87        unsafe {
88            load_tss(Self::TSS_SELECTOR);
89        }
90    }
91}
92
93impl fmt::Debug for GdtStruct {
94    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
95        f.debug_struct("GdtStruct")
96            .field("pointer", &self.pointer())
97            .field("table", &self.table)
98            .finish()
99    }
100}
101
102/// Initializes the per-CPU TSS and GDT structures and loads them into the
103/// current CPU.
104pub fn init_gdt() {
105    unsafe {
106        let gdt = GDT.current_ref_raw();
107        gdt.init_once(GdtStruct::new(TSS.current_ref_raw()));
108        gdt.load();
109        gdt.load_tss();
110    }
111}
112
113/// Returns the stack pointer for privilege level 0 (RSP0) of the current TSS.
114pub fn tss_get_rsp0() -> memory_addr::VirtAddr {
115    let tss = unsafe { TSS.current_ref_raw() };
116    memory_addr::VirtAddr::from(tss.privilege_stack_table[0].as_u64() as usize)
117}
118
119/// Sets the stack pointer for privilege level 0 (RSP0) of the current TSS.
120///
121/// # Safety
122///
123/// Must be called after initialization and preemption is disabled.
124pub unsafe fn tss_set_rsp0(rsp0: memory_addr::VirtAddr) {
125    let tss = unsafe { TSS.current_ref_mut_raw() };
126    tss.privilege_stack_table[0] = VirtAddr::new_truncate(rsp0.as_usize() as u64);
127}