Skip to main content

kernel/arch/x86_64/tables/
gdt.rs

1// SPDX-License-Identifier: GPL-3.0-only
2//! Global Descriptor Table (GDT): owns the kernel segment descriptors and the TSS
3//! selector, and loads them into the CPU.
4//!
5//! Authors: MarioS271
6
7use crate::kinfo;
8use spin::Once;
9use x86_64::registers::segmentation::{Segment, CS, DS, ES, SS};
10use x86_64::instructions::tables::load_tss;
11use x86_64::structures::gdt::{GlobalDescriptorTable, Descriptor};
12
13/// Owns the kernel `GlobalDescriptorTable`; must not move after [`Gdt::init`]
14/// (the CPU holds its address).
15pub struct Gdt {
16    table: Once<GlobalDescriptorTable>,
17}
18
19impl Gdt {
20    /// Create a new, uninitialized `Gdt`; call [`Gdt::init`] to populate and load it.
21    pub const fn new() -> Self {
22        Self { table: Once::new() }
23    }
24
25    /// Build and load the GDT, then reload the segment registers and load the TSS.
26    ///
27    /// # Panics
28    /// Panics if [`super::tss::Tss::init`] was not called first.
29    pub fn init(&'static self, tss: &'static super::tss::Tss) {
30        let mut gdt = GlobalDescriptorTable::new();
31
32        let code = gdt.append(Descriptor::kernel_code_segment());
33        let data = gdt.append(Descriptor::kernel_data_segment());
34        let _ucode = gdt.append(Descriptor::user_code_segment());
35        let _udata = gdt.append(Descriptor::user_data_segment());
36        let tss_sel = gdt.append(Descriptor::tss_segment(tss.get()));
37
38        self.table.call_once(|| gdt);
39        self.table.get().unwrap().load();
40
41        // Safe: the GDT is stored in a static-lifetime Once and will not move;
42        // selectors point to valid descriptors appended above.
43        unsafe {
44            CS::set_reg(code);
45            SS::set_reg(data);
46            DS::set_reg(data);
47            ES::set_reg(data);
48            load_tss(tss_sel);
49        }
50
51        kinfo!("Initialized GDT");
52    }
53}