Skip to main content

kernel/arch/x86_64/cpu/tables/
tss.rs

1// SPDX-License-Identifier: GPL-3.0-only
2//! Task State Segment (TSS): owns the Interrupt Stack Table and its dedicated
3//! stacks for exceptions that need a known-good stack.
4//!
5//! Authors: MarioS271
6
7use crate::kinfo;
8use crate::lib::addr::VirtAddr;
9use crate::lib::types::aligned_stack::AlignedStack;
10use core::cell::UnsafeCell;
11use x86_64::structures::tss::TaskStateSegment;
12
13pub const DOUBLE_FAULT_IST_STACK_INDEX: usize = 0;
14pub const DEBUG_IST_STACK_INDEX: usize = 1;
15pub const NMI_IST_STACK_INDEX: usize = 2;
16pub const MACHINE_CHECK_IST_STACK_INDEX: usize = 3;
17
18/// Owns the `TaskStateSegment` and its four IST stacks; must not move after
19/// [`Tss::init`] (the GDT descriptor points to it).
20pub struct Tss {
21    tss: UnsafeCell<TaskStateSegment>,
22    ist1: AlignedStack<8192>,
23    ist2: AlignedStack<8192>,
24    ist3: AlignedStack<8192>,
25    ist4: AlignedStack<8192>,
26}
27
28/// Safety: `tss` will only be written to by the owner CPU and will only be written to when
29/// the CPU transitions from userspace to kernelspace, where no kernel code could've run before
30unsafe impl Sync for Tss {}
31
32impl Tss {
33    /// Create a new, uninitialized `Tss`; call [`Tss::init`] to populate it.
34    pub const fn new() -> Self {
35        Self {
36            tss: UnsafeCell::new(TaskStateSegment::new()),
37            ist1: AlignedStack::new(),
38            ist2: AlignedStack::new(),
39            ist3: AlignedStack::new(),
40            ist4: AlignedStack::new(),
41        }
42    }
43
44    /// Build the TSS with RSP0 pointing to 0 and the predefined IST stacks
45    ///
46    /// # Safety
47    /// This method must be called exactly once before [`Tss::tss`] is called to avoid undefined
48    /// behavior and multiple mutable references.
49    pub unsafe fn init(&'static self) {
50        let ist1_top = self.ist1.get_stack_top();
51        let ist2_top = self.ist2.get_stack_top();
52        let ist3_top = self.ist3.get_stack_top();
53        let ist4_top = self.ist4.get_stack_top();
54
55        let tss = unsafe { &mut *self.tss.get() };
56
57        tss.privilege_stack_table[0] = VirtAddr::null().as_x86_64();
58        tss.interrupt_stack_table[DOUBLE_FAULT_IST_STACK_INDEX] = ist1_top.as_x86_64();
59        tss.interrupt_stack_table[DEBUG_IST_STACK_INDEX] = ist2_top.as_x86_64();
60        tss.interrupt_stack_table[NMI_IST_STACK_INDEX] = ist3_top.as_x86_64();
61        tss.interrupt_stack_table[MACHINE_CHECK_IST_STACK_INDEX] = ist4_top.as_x86_64();
62
63        kinfo!("Initialized TSS");
64    }
65
66    /// Getter for `Tss::tss`, returns a reference
67    ///
68    /// # Safety
69    /// The caller must guarantee that there are no mutable references or pointers to `self.tss`
70    pub unsafe fn tss(&'static self) -> &'static TaskStateSegment {
71        unsafe { &*self.tss.get() }
72    }
73
74    /// Setter for TSS.RSP0
75    ///
76    /// # Safety
77    /// The caller must guarantee the following:
78    /// - That [`Tss::init`] has already been called
79    /// - This method is only called on the CPU which owns this TSS
80    /// - That no refs/ptrs from [`Tss::tss`] are held when this is called
81    /// - `new_rsp0` is a valid pointer to a valid, mapped kernel stack
82    pub unsafe fn set_rsp0(&self, new_rsp0: VirtAddr) {
83        unsafe { (*self.tss.get()).privilege_stack_table[0] = new_rsp0.as_x86_64() };
84    }
85}