Skip to main content

kernel/arch/x86_64/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::types::aligned_stack::AlignedStack;
9use spin::Once;
10use x86_64::structures::tss::TaskStateSegment;
11use x86_64::VirtAddr;
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: Once<TaskStateSegment>,
22    ist1: AlignedStack<8192>,
23    ist2: AlignedStack<8192>,
24    ist3: AlignedStack<8192>,
25    ist4: AlignedStack<8192>,
26}
27
28impl Tss {
29    /// Create a new, uninitialized `Tss`; call [`Tss::init`] to populate it.
30    pub const fn new() -> Self {
31        Self {
32            tss: Once::new(),
33            ist1: AlignedStack { array: [0u8; 8192] },
34            ist2: AlignedStack { array: [0u8; 8192] },
35            ist3: AlignedStack { array: [0u8; 8192] },
36            ist4: AlignedStack { array: [0u8; 8192] },
37        }
38    }
39
40    /// Build the TSS, wiring IST slots 0–3 to this instance's four dedicated stacks.
41    pub fn init(&self) {
42        // Safe: .add(len) computes a one-past-the-end pointer, which is valid per
43        // Rust's pointer rules; the arrays are owned by this instance and will not
44        // move or be dropped for the lifetime of the kernel.
45        unsafe {
46            let ist1_top = VirtAddr::from_ptr(self.ist1.array.as_ptr().add(self.ist1.array.len()));
47            let ist2_top = VirtAddr::from_ptr(self.ist2.array.as_ptr().add(self.ist2.array.len()));
48            let ist3_top = VirtAddr::from_ptr(self.ist3.array.as_ptr().add(self.ist3.array.len()));
49            let ist4_top = VirtAddr::from_ptr(self.ist4.array.as_ptr().add(self.ist4.array.len()));
50
51            self.tss.call_once(|| {
52                let mut tss = TaskStateSegment::new();
53                tss.interrupt_stack_table[DOUBLE_FAULT_IST_STACK_INDEX] = ist1_top;
54                tss.interrupt_stack_table[DEBUG_IST_STACK_INDEX] = ist2_top;
55                tss.interrupt_stack_table[NMI_IST_STACK_INDEX] = ist3_top;
56                tss.interrupt_stack_table[MACHINE_CHECK_IST_STACK_INDEX] = ist4_top;
57                tss
58            });
59        }
60
61        kinfo!("Initialized TSS");
62    }
63
64    /// Return a reference to the initialized `TaskStateSegment`.
65    ///
66    /// # Panics
67    /// Panics if [`Tss::init`] has not been called yet.
68    pub fn get(&self) -> &TaskStateSegment {
69        self.tss.get().unwrap()
70    }
71}