Skip to main content

kernel/arch/x86_64/cpu/
state.rs

1// SPDX-License-Identifier: GPL-3.0-only
2//! x86_64 specific CPU data such as TSS, GDT, IDT and selectors
3//!
4//! Authors: MarioS271
5
6use super::tables;
7use super::tables::gdt::{gdt_init, gdt_load, GdtSetupInfo};
8use super::tables::idt::idt_init;
9use crate::arch::x86_64::cpu::gs_info::GsInfo;
10use crate::lib::addr::VirtAddr;
11use crate::lib::sync::unchecked_cell::UncheckedCell;
12use core::sync::atomic::{AtomicU16, Ordering};
13use x86_64::structures::gdt::{GlobalDescriptorTable, SegmentSelector};
14use x86_64::structures::idt::InterruptDescriptorTable;
15use crate::lib::panic::kernel_panic;
16use crate::lib::panic_codes::PanicCode;
17
18/// Global CPU state such as IDT and user code/data selectors
19#[repr(align(64))]
20pub struct GlobalCpuState {
21    idt: UncheckedCell<InterruptDescriptorTable>,
22    user_code_selector: AtomicU16,
23    user_data_selector: AtomicU16,
24}
25
26impl GlobalCpuState {
27    /// Constructor; returns a [`GlobalCpuState`] with an uninitialized IDT and zeroed selectors
28    pub const fn new() -> Self {
29        Self {
30            idt: UncheckedCell::new(),
31            user_code_selector: AtomicU16::new(0),
32            user_data_selector: AtomicU16::new(0),
33        }
34    }
35
36    /// Initialize the IDT
37    ///
38    /// # Safety
39    /// The caller must guarantee the following:
40    /// - That this method has never been called before and will never be called again
41    /// - That at the time of calling this method, no references or pointers to this data exist
42    /// - While this method is being called, no other CPU is working with the given data
43    pub unsafe fn init_idt(&'static self) {
44        // Safety (for idt().load()): IDT was placed in its static location one line above
45        unsafe {
46            self.idt.init(idt_init());
47            self.idt().load();
48        }
49    }
50
51    /// Set the user data and code selectors
52    pub fn set_user_selectors(&self, code: u16, data: u16) {
53        self.user_code_selector.store(code, Ordering::Release);
54        self.user_data_selector.store(data, Ordering::Release);
55    }
56
57    /// Getter for `GlobalCpuState::Idt`
58    ///
59    /// # Safety
60    /// The caller must guarantee the following:
61    /// - That this value's `init` method has already been called before
62    /// - That at this method's entire execution time, no mutable references or pointers to this data
63    ///   exist or will exist
64    pub unsafe fn idt(&self) -> &InterruptDescriptorTable {
65        unsafe { self.idt.get() }
66    }
67
68    /// Getter for `GlobalCpuState::user_code_selector`
69    pub fn user_code_selector(&self) -> u16 {
70        self.user_code_selector.load(Ordering::Acquire)
71    }
72
73    /// Getter for `GlobalCpuState::user_data_selector`
74    pub fn user_data_selector(&self) -> u16 {
75        self.user_data_selector.load(Ordering::Acquire)
76    }
77}
78
79
80/// Per-CPU state such as TSS and GDT
81#[repr(align(64))]
82pub struct CpuState {
83    tss: tables::tss::Tss,
84    gdt: UncheckedCell<GlobalDescriptorTable>,
85    gs_info: GsInfo,
86}
87
88impl CpuState {
89    /// Constructor; returns a [`CpuState`] with an uninitialized TSS and GDT
90    pub const fn new() -> Self {
91        Self {
92            tss: tables::tss::Tss::new(),
93            gdt: UncheckedCell::new(),
94            gs_info: GsInfo::new()
95        }
96    }
97
98    /// Initialize the TSS
99    ///
100    /// # Safety
101    /// The caller must guarantee that this method is called exactly once before any calls to
102    /// [`CpuState::tss`] are made
103    pub unsafe fn init_tss(&'static self) {
104        unsafe { self.tss.init() }
105    }
106
107    /// Initialize the GDT
108    ///
109    /// # Safety
110    /// The caller must guarantee the following:
111    /// - That this method has never been called before and will never be called again
112    /// - That at the time of calling this method, no references or pointers to this data exist
113    /// - While this method is being called, no other CPU is working with the given data
114    pub unsafe fn init_gdt(&'static self) -> GdtSetupInfo {
115        let (gdt, gdt_setup_info) = gdt_init(self.tss());
116
117        // Safety (for gdt_load): self.gdt was correctly initialized one line
118        // before the call
119        unsafe {
120            self.gdt.init(gdt);
121            gdt_load(self.gdt(), &gdt_setup_info);
122        }
123
124        gdt_setup_info
125    }
126
127    /// Initialize the GS Info Struct
128    ///
129    /// # Safety
130    /// The caller must guarantee the following:
131    /// - This method is called exactly once on the CPU which owns the given [`CpuState`]
132    /// - This method is called before the first jump to userspace on the CPU which owns the
133    ///   given [`CpuState`]
134    pub unsafe fn init_gs_info(&'static self) {
135        unsafe { self.gs_info.init() };
136    }
137
138    /// Set the kernel stack top of the CPU which owns this [`CpuState`]
139    ///
140    /// # Safety
141    /// The caller must guarantee the following:
142    /// - That [`CpuState::init_tss`] has already been called
143    /// - This method is only called on the CPU which owns this TSS
144    /// - That no refs/ptrs from [`CpuState::tss`] are held when this is called
145    /// - `kernel_stack_top` is a valid pointer to a valid, mapped kernel stack
146    pub unsafe fn set_kernel_stack_top(&self, kernel_stack_top: VirtAddr) {
147        unsafe {
148            self.tss.set_rsp0(kernel_stack_top);
149            self.gs_info.set_kernel_stack_top(kernel_stack_top);
150        };
151    }
152
153    /// Getter for `CpuState::tss`
154    pub fn tss(&self) -> &tables::tss::Tss {
155        &self.tss
156    }
157
158    /// Getter for `CpuState::gdt`
159    ///
160    /// # Safety
161    /// The caller must guarantee the following:
162    /// - That this value's `init` method has already been called before
163    /// - That at this method's entire execution time, no mutable references or pointers to this data
164    ///   exist or will exist
165    pub unsafe fn gdt(&self) -> &GlobalDescriptorTable {
166        unsafe { self.gdt.get() }
167    }
168
169    /// Getter for `CpuState::gs_info`
170    pub fn gs_info(&self) -> &GsInfo {
171        #[cfg(feature = "debug-checks")]
172        if self.gs_info.self_ptr() == 0 {
173            kernel_panic(
174                PanicCode::UninitializedAccess,
175                "Attempted to access an uninitialized GsInfo"
176            );
177        }
178
179        &self.gs_info
180    }
181}