Skip to main content

vmi_arch_amd64/segment/
mod.rs

1mod descriptor;
2pub use self::descriptor::SegmentDescriptor;
3
4mod selector;
5pub use self::selector::{DescriptorTable, Selector};
6
7/// Determines the type of segment descriptor.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum DescriptorType {
10    /// The descriptor is for a system segment.
11    System,
12
13    /// The descriptor is for a code or data segment.
14    CodeOrData,
15}
16
17/// Determines the default length for effective addresses and operands
18/// referenced by instructions in the segment.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum OperationSize {
21    /// 16-bit addresses and 16-bit or 8-bit operands are assumed.
22    Default,
23
24    /// 32-bit addresses and 32-bit or 8-bit operands are assumed.
25    Big,
26}
27
28/// Determines the scaling of the segment limit field.
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub enum Granularity {
31    /// The segment limit is interpreted in byte units.
32    Byte,
33
34    /// The segment limit is interpreted in 4-KByte units.
35    Page4K,
36}
37
38/// The access rights of a segment descriptor.
39#[derive(Default, Clone, Copy, PartialEq, Eq)]
40pub struct SegmentAccess(pub u32);
41
42impl SegmentAccess {
43    /// Indicates the segment or gate type and specifies the kinds of access
44    /// that can be made to the segment and the direction of growth. The
45    /// interpretation of this field depends on whether the descriptor type
46    /// flag specifies an application (code or data) descriptor or a system
47    /// descriptor. The encoding of the type field is different for code,
48    /// data, and system descriptors.
49    pub fn typ(self) -> u8 {
50        (self.0 & 0b1111) as _
51    }
52
53    /// Specifies whether the segment descriptor is for a system segment (S flag
54    /// is clear) or a code or data segment (S flag is set).
55    pub fn descriptor_type(self) -> DescriptorType {
56        if (self.0 >> 4) & 1 == 0 {
57            DescriptorType::System
58        }
59        else {
60            DescriptorType::CodeOrData
61        }
62    }
63
64    /// Specifies the privilege level of the segment. The privilege level can
65    /// range from 0 to 3, with 0 being the most privileged level. The DPL
66    /// is used to control access to the segment. See Section 5.5, “Privilege
67    /// Levels”, for a description of the relationship of the DPL to the CPL of
68    /// the executing code segment and the RPL of a segment selector.
69    pub fn descriptor_privilege_level(self) -> u8 {
70        ((self.0 >> 5) & 0b11) as _
71    }
72
73    /// Indicates whether the segment is present in memory (set) or not present
74    /// (clear). If this flag is clear, the processor generates a
75    /// segment-not-present exception (#NP) when a segment selector that
76    /// points to the segment descriptor is loaded into a segment register.
77    /// Memory management software can use this flag to control which
78    /// segments are actually loaded into physical memory at a given
79    /// time. It offers a control in addition to paging for managing virtual
80    /// memory.
81    pub fn present(self) -> bool {
82        (self.0 >> 7) & 1 != 0
83    }
84
85    /// This bit is available for use by system software.
86    pub fn available_bit(self) -> bool {
87        (self.0 >> 8) & 1 != 0
88    }
89
90    /// In IA-32e mode, bit 21 of the second doubleword of the segment
91    /// descriptor indicates whether a code segment contains native 64-bit
92    /// code. A value of 1 indicates instructions in this code segment
93    /// are executed in 64-bit mode. A value of 0 indicates the instructions in
94    /// this code segment are executed in compatibility mode. If L-bit is
95    /// set, then D-bit must be cleared. When not in IA-32e mode
96    /// or for non-code segments, bit 21 is reserved and should always be set to
97    /// 0.
98    pub fn long_mode(self) -> bool {
99        (self.0 >> 9) & 1 != 0
100    }
101
102    /// Performs different functions depending on whether the segment descriptor
103    /// is an executable code segment, an expand-down data segment, or a
104    /// stack segment. (This flag should always be set to 1 for 32-bit code
105    /// and data segments and to 0 for 16-bit code and data segments.)
106    ///
107    /// - Executable code segment. The flag is called the D flag and it
108    ///   indicates the default length for effective addresses and operands
109    ///   referenced by instructions in the segment. If the flag is set, 32-bit
110    ///   addresses and 32-bit or 8-bit operands are assumed; if it is clear,
111    ///   16-bit addresses and 16-bit or 8-bit operands are assumed. The
112    ///   instruction prefix 66H can be used to select an operand size other
113    ///   than the default, and the prefix 67H can be used select an address
114    ///   size other than the default.
115    ///
116    /// - Stack segment (data segment pointed to by the SS register). The flag
117    ///   is called the B (big) flag and it specifies the size of the stack
118    ///   pointer used for implicit stack operations (such as pushes, pops, and
119    ///   calls). If the flag is set, a 32-bit stack pointer is used, which is
120    ///   stored in the 32-bit ESP register; if the flag is clear, a 16-bit
121    ///   stack pointer is used, which is stored in the 16- bit SP register. If
122    ///   the stack segment is set up to be an expand-down data segment
123    ///   (described in the next paragraph), the B flag also specifies the upper
124    ///   bound of the stack segment.
125    ///
126    /// - Expand-down data segment. The flag is called the B flag and it
127    ///   specifies the upper bound of the segment. If the flag is set, the
128    ///   upper bound is FFFFFFFFH (4 GBytes); if the flag is clear, the upper
129    ///   bound is FFFFH (64 KBytes).
130    pub fn operation_size(self) -> OperationSize {
131        if (self.0 >> 10) & 1 == 0 {
132            OperationSize::Default
133        }
134        else {
135            OperationSize::Big
136        }
137    }
138
139    /// Determines the scaling of the segment limit field. When the granularity
140    /// flag is clear, the segment limit is interpreted in byte units; when
141    /// flag is set, the segment limit is interpreted in 4-KByte units.
142    /// (This flag does not affect the granularity of the base address; it is
143    /// always byte granular.) When the granularity flag is set, the twelve
144    /// least significant bits of an offset are not tested when checking the
145    /// offset against the segment limit. For example, when the granularity flag
146    /// is set, a limit of 0 results in valid offsets from 0 to 4095.
147    pub fn granularity(self) -> Granularity {
148        if (self.0 >> 11) & 1 == 0 {
149            Granularity::Byte
150        }
151        else {
152            Granularity::Page4K
153        }
154    }
155}
156
157impl From<u32> for SegmentAccess {
158    fn from(value: u32) -> Self {
159        Self(value)
160    }
161}
162
163impl From<SegmentAccess> for u32 {
164    fn from(value: SegmentAccess) -> Self {
165        value.0
166    }
167}
168
169impl std::fmt::Debug for SegmentAccess {
170    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
171        f.debug_struct("Segment")
172            .field("type", &self.typ())
173            .field("descriptor_type", &self.descriptor_type())
174            .field(
175                "descriptor_privilege_level",
176                &self.descriptor_privilege_level(),
177            )
178            .field("present", &self.present())
179            .field("available_bit", &self.available_bit())
180            .field("long_mode", &self.long_mode())
181            .field("operation_size", &self.operation_size())
182            .field("granularity", &self.granularity())
183            .finish()
184    }
185}