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}