tartan_arch/x86/
protection.rs

1//! Support for protected mode operation.
2//!
3//! This includes the minimal support for segmented memory and hardware task management
4//! that is required to operate in protected mode with a flat memory model.
5
6use crate::x86_common::protection::{IOPermissionBitmap, Selector};
7use static_assertions::const_assert_eq;
8
9#[cfg(doc)]
10use crate::x86_common::paging::ControlRegister3;
11#[cfg(doc)]
12use crate::x86_common::protection::{GateDescriptorFlags, LocalDescriptorTableRegister};
13#[cfg(doc)]
14use crate::x86_common::FlagRegister;
15
16
17/// A task state segment (TSS) with fixed redirect and I/O permission maps.
18///
19/// From the processor's perspective, the layout of the TSS is flexible after the end of
20/// the [`TaskStateSegmentHeader`]. The permission map may be at any offset or missing
21/// from the segment completely. For that reason, this struct is only suitable for use in
22/// setting up a TSS that the caller owns. For reading a TSS created by another system
23/// (e.g., the bootloader), use the [`TaskStateSegmentHeader`] struct on its own.
24#[repr(C)]
25#[derive(Debug, PartialEq, Eq)]
26pub struct BasicTaskStateSegment {
27    /// Saved task data and pointer to bitmaps
28    pub header: TaskStateSegmentHeader,
29    /// I/O permission map and interrupt redirect map
30    pub bitmaps: TaskStateSegmentBitmaps<[u8; IOPermissionBitmap::MAX_SIZE]>,
31}
32
33
34/// Saved task state that makes up the most significant part of a task state segment
35/// (TSS).
36///
37/// Some of the fields of this header are automatically saved by the processor when
38/// switching tasks. Refer to each field's documentation.
39///
40/// This header can be followed by OS-specific data and/or the
41/// [`TaskStateSegmentBitmaps`]. The location of the latter is determined by
42/// [`io_permission_map_offset`](Self::io_permission_map_offset).
43#[repr(C)]
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub struct TaskStateSegmentHeader {
46    /// Segment selector saved by the processor referencing the task that was running
47    /// before this task was activated.
48    ///
49    /// If [`FlagRegister::nested_task`] is set, then this task will be resumed by the
50    /// `IRET` instruction.
51    pub previous_task: u16,
52    _reserved_a: u16,
53    /// Stack pointer and segment for privilege levels 0–2.
54    ///
55    /// Not modified by the processor on a task switch.
56    pub privileged_stack: [PrivilegedStack; 3],
57    /// Page table pointer from [`ControlRegister3`].
58    ///
59    /// Not modified by the processor on a task switch.
60    pub control_register_3: u32,
61    /// `EIP` value saved by the processor.
62    pub instruction_pointer: u32,
63    /// `EFLAGS` value saved by the processor.
64    pub flags: u32,
65    /// `EAX` value saved by the processor.
66    pub general_a: u32,
67    /// `ECX` value saved by the processor.
68    pub general_c: u32,
69    /// `EDX` value saved by the processor.
70    pub general_d: u32,
71    /// `EBX` value saved by the processor.
72    pub general_b: u32,
73    /// `SP` value saved by the processor for privilege level 3.
74    pub stack_pointer: u32,
75    /// `EBP` value saved by the processor.
76    pub base_pointer: u32,
77    /// `ESI` value saved by the processor.
78    pub source_index: u32,
79    /// `EDI` value saved by the processor.
80    pub destination_index: u32,
81    /// `ES` value saved by the processor.
82    pub general_segment_e: Selector,
83    _reserved_b: u16,
84    /// `CS` value saved by the processor.
85    pub code_segment: Selector,
86    _reserved_c: u16,
87    /// `SS` value saved by the processor for privilege level 3.
88    pub stack_segment: Selector,
89    _reserved_d: u16,
90    /// `DS` value saved by the processor.
91    pub data_segment: Selector,
92    _reserved_e: u16,
93    /// `FS` value saved by the processor.
94    pub general_segment_f: Selector,
95    _reserved_f: u16,
96    /// `GS` value saved by the processor.
97    pub general_segment_g: Selector,
98    _reserved_g: u16,
99    /// [`LocalDescriptorTableRegister`] value to load when executing this task.
100    ///
101    /// Not modified by the processor on a task switch.
102    pub local_descriptor_table: Selector,
103    _reserved_h: u16,
104    /// Indicates that a debug exception should be raised when this task is activated.
105    ///
106    /// Not modified by the processor on a task switch.
107    pub debug_trap: bool,
108    /// Offset from the start of this structure to start of the [`IOPermissionBitmap`],
109    /// and to the end of the [`interrupt_redirect`
110    /// bitmap](TaskStateSegmentBitmaps::interrupt_redirect).
111    ///
112    /// The I/O permission map ends at the limit of the containing segment, and must be
113    /// at least two bytes. If this offset is equal to or greater than the limit,
114    /// then the permission map is empty and all ports are assumed to be zero.
115    ///
116    /// The interrupt redirect bitmap begins at this offset minus 32.
117    ///
118    /// Not modified by the processor on a task switch.
119    pub io_permission_map_offset: u16,
120    /// Pointer to the shadow stack.
121    ///
122    /// Not modified by the processor on a task switch.
123    pub shadow_stack_pointer: u32,
124}
125
126const_assert_eq!(108, core::mem::size_of::<TaskStateSegmentHeader>());
127
128impl TaskStateSegmentHeader {
129    /// Create a new zero-initialized header
130    pub const fn new() -> Self {
131        Self {
132            previous_task: 0,
133            privileged_stack: [PrivilegedStack::new(); 3],
134            control_register_3: 0,
135            instruction_pointer: 0,
136            flags: 0,
137            general_a: 0,
138            general_c: 0,
139            general_d: 0,
140            general_b: 0,
141            stack_pointer: 0,
142            base_pointer: 0,
143            source_index: 0,
144            destination_index: 0,
145            general_segment_e: Selector::null(),
146            code_segment: Selector::null(),
147            stack_segment: Selector::null(),
148            data_segment: Selector::null(),
149            general_segment_f: Selector::null(),
150            general_segment_g: Selector::null(),
151            local_descriptor_table: Selector::null(),
152            debug_trap: false,
153            io_permission_map_offset: 0,
154            shadow_stack_pointer: 0,
155
156            _reserved_a: 0,
157            _reserved_b: 0,
158            _reserved_c: 0,
159            _reserved_d: 0,
160            _reserved_e: 0,
161            _reserved_f: 0,
162            _reserved_g: 0,
163            _reserved_h: 0,
164        }
165    }
166}
167
168
169/// Stack pointer and segment for a given privilege level
170#[repr(C)]
171#[derive(Debug, Clone, Copy, PartialEq, Eq)]
172pub struct PrivilegedStack {
173    /// Stack pointer (`SP`) value for this privilege level.
174    pub pointer: u32,
175    /// Stack segment (`SS`) value for this privilege level.
176    pub segment: Selector,
177}
178
179const_assert_eq!(8, core::mem::size_of::<PrivilegedStack>());
180
181impl PrivilegedStack {
182    /// Create a new zero-initialized stack pointer
183    pub const fn new() -> Self {
184        Self { pointer: 0, segment: Selector::null() }
185    }
186}
187
188
189
190/// I/O permission map and interrupt redirect map, which are always located together in
191/// a task state segment (TSS).
192#[repr(C)]
193#[derive(Debug, PartialEq, Eq)]
194pub struct TaskStateSegmentBitmaps<T>
195where
196    T: AsRef<[u8]> + AsMut<[u8]> + Eq + ?Sized,
197{
198    /// Indicates which handler to use for software-triggered interrupts in virtual real
199    /// mode.
200    ///
201    /// If bit `N` (bit `N mod 8` of byte `floor(N / 8)`) is set, it indicates that an
202    /// `INT N` in virtual real mode should be handled using the protected-mode mechanism
203    /// rather than the process's own interrupt table.
204    pub interrupt_redirect: [u8; 32],
205
206    /// The [`IOPermissionBitmap`] for this task.
207    pub io_permission: IOPermissionBitmap<T>,
208}