x86/bits64/
task.rs

1//! Helpers to program the task state segment.
2//! See Intel 3a, Chapter 7, Section 7
3
4use crate::Ring;
5
6/// Although hardware task-switching is not supported in 64-bit mode,
7/// a 64-bit task state segment (TSS) must exist.
8///
9/// The TSS holds information important to 64-bit mode and that is not
10/// directly related to the task-switch mechanism. This information includes:
11///
12/// # RSPn
13/// The full 64-bit canonical forms of the stack pointers (RSP) for privilege levels 0-2.
14/// RSPx is loaded in whenever an interrupt causes the CPU to change RPL to x.
15/// Note on a syscall entry this field is not used to load a stack, setting the stack there
16/// is the handler's responsibility (however when using the int instruction in user-space,
17/// we load the stack from RSPn).
18///
19/// # ISTn
20/// The full 64-bit canonical forms of the interrupt stack table (IST) pointers.
21/// You can set an interrupt vector to use an IST entry in the Interrupt Descriptor
22/// Table by giving it a number from 0 - 7. If 0 is selected, then the IST mechanism
23/// is not used. If any other number is selected then when that interrupt vector is
24/// called the CPU will load RSP from the corresponding IST entry. This is useful for
25/// handling things like double faults, since you don't have to worry about switching
26/// stacks; the CPU will do it for you.
27///
28/// # I/O map base address
29/// The 16-bit offset to the I/O permission bit map from the 64-bit TSS base.
30///
31/// The operating system must create at least one 64-bit TSS after activating IA-32e mode.
32/// It must execute the LTR instruction (in 64-bit mode) to load the TR register with a
33/// pointer to the 64-bit TSS responsible for both 64-bitmode programs and
34/// compatibility-mode programs ([load_tr](crate::task::load_tr)).
35#[derive(Clone, Copy, Debug, Default)]
36#[repr(C, packed)]
37pub struct TaskStateSegment {
38    pub reserved: u32,
39    /// The full 64-bit canonical forms of the stack pointers (RSP) for privilege levels 0-2.
40    pub rsp: [u64; 3],
41    pub reserved2: u64,
42    /// The full 64-bit canonical forms of the interrupt stack table (IST) pointers.
43    pub ist: [u64; 7],
44    pub reserved3: u64,
45    pub reserved4: u16,
46    /// The 16-bit offset to the I/O permission bit map from the 64-bit TSS base.
47    pub iomap_base: u16,
48}
49
50impl TaskStateSegment {
51    /// Creates a new empty TSS.
52    pub const fn new() -> TaskStateSegment {
53        TaskStateSegment {
54            reserved: 0,
55            rsp: [0; 3],
56            reserved2: 0,
57            ist: [0; 7],
58            reserved3: 0,
59            reserved4: 0,
60            iomap_base: 0,
61        }
62    }
63
64    /// Sets the stack pointer (`stack_ptr`) to be used for when
65    /// an interrupt causes the CPU to change RPL to `pl`.
66    pub fn set_rsp(&mut self, pl: Ring, stack_ptr: u64) {
67        match pl {
68            Ring::Ring0 => self.rsp[0] = stack_ptr,
69            Ring::Ring1 => self.rsp[1] = stack_ptr,
70            Ring::Ring2 => self.rsp[2] = stack_ptr,
71            Ring::Ring3 => unreachable!("Can't set stack for PL3"),
72        }
73    }
74
75    /// Sets the stack pointer (`stack_ptr`) to be used when
76    /// an interrupt with a corresponding IST entry in the Interrupt
77    /// Descriptor table pointing to the given `index` is raised.
78    pub fn set_ist(&mut self, index: usize, stack_ptr: u64) {
79        match index {
80            0 => self.ist[0] = stack_ptr,
81            1 => self.ist[1] = stack_ptr,
82            2 => self.ist[2] = stack_ptr,
83            3 => self.ist[3] = stack_ptr,
84            4 => self.ist[4] = stack_ptr,
85            5 => self.ist[5] = stack_ptr,
86            6 => self.ist[6] = stack_ptr,
87            _ => unreachable!("Can't set IST for this index (out of bounds)."),
88        }
89    }
90}