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