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>,
}