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}