Skip to content

Commit e9c080e

Browse files
committed
gdt: Add remaining GDT flags
Add the remaining GDT flags. This now allows users to create 32-bit and 64-bit GDTs. We also add flag aliases for common cases. We then verify that these cases match what the Linux kernel uses by default. This also allows us to make additional methods `const fn`. I used [AMD64 Architecture Programmer’s Manual, Volume 2: System Programming](https://www.amd.com/system/files/TechDocs/24593.pdf) as the main reference for this PR. Two remaining open questions: - Should we set `WRITABLE` (aka Readable for code-segments) by default? - This bit is ignored in Long Mode (for code and data) - Linux always sets it by default - This is probably what users want for legacy mode segments. - Should we set `ACCESSED` by default? - Linux [sets it by default](https://github.com/torvalds/linux/blob/00e4db51259a5f936fec1424b884f029479d3981/arch/x86/kernel/cpu/common.c#L124-L129) - Except [sometimes it doesn't](https://github.com/torvalds/linux/blob/ab851d49f6bfc781edd8bd44c72ec1e49211670b/arch/x86/platform/pvh/head.S#L160-L166) - AMD claims this is ignored, but Intel doesn't make the same claim Signed-off-by: Joe Richey <[email protected]>
1 parent 3ce339e commit e9c080e

File tree

1 file changed

+71
-28
lines changed

1 file changed

+71
-28
lines changed

src/structures/gdt.rs

+71-28
Original file line numberDiff line numberDiff line change
@@ -184,55 +184,80 @@ pub enum Descriptor {
184184
bitflags! {
185185
/// Flags for a GDT descriptor. Not all flags are valid for all descriptor types.
186186
pub struct DescriptorFlags: u64 {
187-
/// For data segments, this flag sets the segment as writable. Ignored for code segments.
187+
/// Set by the processor if this segment has been accessed. Only cleared by software.
188+
const ACCESSED = 1 << 40;
189+
/// For data segments, sets the segment as writable. For code segments,
190+
/// sets the segment as _readable_. In 64-bit mode, ignored for all segments.
188191
const WRITABLE = 1 << 41;
189-
/// Marks a code segment as “conforming”. This influences the privilege checks that
190-
/// occur on control transfers.
192+
/// For code segments, sets the segment as “conforming”, influencing the
193+
/// privilege checks that occur on control transfers. For 32-bit data segments,
194+
/// sets the segment as "expand down". In 64-bit mode, ignored for data segments.
191195
const CONFORMING = 1 << 42;
192-
/// This flag must be set for code segments.
196+
/// This flag must be set for code segments and unset for data segments.
193197
const EXECUTABLE = 1 << 43;
194198
/// This flag must be set for user segments (in contrast to system segments).
195199
const USER_SEGMENT = 1 << 44;
200+
/// The DPL for this descriptor is Ring 3. In 64-bit mode, ignored for data segments.
201+
const DPL_RING_3 = 3 << 45;
196202
/// Must be set for any segment, causes a segment not present exception if not set.
197203
const PRESENT = 1 << 47;
198-
/// Must be set for long mode code segments.
204+
/// Available for use by the Operating System
205+
const AVAILABLE = 1 << 52;
206+
/// Must be set for 64-bit code segments, unset otherwise.
199207
const LONG_MODE = 1 << 53;
208+
/// Use 32-bit (as opposed to 16-bit) operands. If [`LONG_MODE`] is set,
209+
/// this must be unset. In 64-bit mode, ignored for data segments.
210+
const DEFAULT_SIZE = 1 << 54
211+
/// Limit field is scaled by 4096 bytes. In 64-bit mode, ignored for all segments.
212+
const GRANULARITY = 1 << 55;
200213

201-
/// The DPL for this descriptor is Ring 3
202-
const DPL_RING_3 = 3 << 45;
214+
/// Bits 0..=15 of the limit field (ignored in 64-bit mode)
215+
const LIMIT_0_15 = 0xFFFF;
216+
/// Bits 16..=19 of the limit field (ignored in 64-bit mode)
217+
const LIMIT_16_19 = 0xF << 48;
218+
/// Bits 0..=23 of the base field (ignored in 64-bit mode)
219+
const BASE_0_23 = 0xFF_FFFF << 16;
220+
/// Bits 24..=31 of the base field (ignored in 64-bit mode)
221+
const BASE_24_31 = 0xFF << 56;
222+
223+
/// Flags that should be set for all flat user segments
224+
const FLAT_COMMON = Self::LIMIT_0_15.bits | Self::LIMIT_16_19.bits
225+
| Self::GRANULARITY.bits | Self::ACCESSED.bits | Self::WRITABLE.bits
226+
| Self::USER_SEGMENT.bits | Self::PRESENT.bits;
227+
228+
/// Flags for a flat 32-bit kernel code segment
229+
const KERNEL_CODE32 = Self::FLAT_COMMON.bits | Self::EXECUTABLE.bits | Self::DEFAULT_SIZE.bits;
230+
/// Flags for a 64-bit kernel code segment
231+
const KERNEL_CODE64 = Self::FLAT_COMMON.bits | Self::EXECUTABLE.bits | Self::LONG_MODE.bits;
232+
/// Flags for a kernel data segment (64-bit or flat 32-bit)
233+
const KERNEL_DATA = Self::FLAT_COMMON.bits | Self::DEFAULT_SIZE.bits;
234+
235+
/// Flags for a flat 32-bit user code segment
236+
const USER_CODE32 = Self::KERNEL_CODE32.bits | Self::DPL_RING_3.bits;
237+
/// Flags for a 64-bit user code segment
238+
const USER_CODE64 = Self::KERNEL_CODE64.bits | Self::DPL_RING_3.bits;
239+
/// Flags for a user data segment (64-bit or flat 32-bit)
240+
const USER_DATA = Self::KERNEL_DATA.bits | Self::DPL_RING_3.bits;
203241
}
204242
}
205243

206244
impl Descriptor {
207-
/// Creates a segment descriptor for a long mode kernel code segment.
245+
/// Creates a segment descriptor for a 64-bit kernel code segment.
208246
#[inline]
209-
pub fn kernel_code_segment() -> Descriptor {
210-
use self::DescriptorFlags as Flags;
211-
212-
let flags = Flags::USER_SEGMENT | Flags::PRESENT | Flags::EXECUTABLE | Flags::LONG_MODE;
213-
Descriptor::UserSegment(flags.bits())
247+
pub const fn kernel_code_segment() -> Descriptor {
248+
Descriptor::UserSegment(DescriptorFlags::KERNEL_CODE64.bits())
214249
}
215250

216251
/// Creates a segment descriptor for a long mode ring 3 data segment.
217252
#[inline]
218-
pub fn user_data_segment() -> Descriptor {
219-
use self::DescriptorFlags as Flags;
220-
221-
let flags = Flags::USER_SEGMENT | Flags::PRESENT | Flags::WRITABLE | Flags::DPL_RING_3;
222-
Descriptor::UserSegment(flags.bits())
253+
pub const fn user_data_segment() -> Descriptor {
254+
Descriptor::UserSegment(DescriptorFlags::USER_DATA.bits())
223255
}
224256

225-
/// Creates a segment descriptor for a long mode ring 3 code segment.
257+
/// Creates a segment descriptor for a 64-bit ring 3 code segment.
226258
#[inline]
227-
pub fn user_code_segment() -> Descriptor {
228-
use self::DescriptorFlags as Flags;
229-
230-
let flags = Flags::USER_SEGMENT
231-
| Flags::PRESENT
232-
| Flags::EXECUTABLE
233-
| Flags::LONG_MODE
234-
| Flags::DPL_RING_3;
235-
Descriptor::UserSegment(flags.bits())
259+
pub const fn user_code_segment() -> Descriptor {
260+
Descriptor::UserSegment(DescriptorFlags::USER_CODE64.bits())
236261
}
237262

238263
/// Creates a TSS system descriptor for the given TSS.
@@ -258,3 +283,21 @@ impl Descriptor {
258283
Descriptor::SystemSegment(low, high)
259284
}
260285
}
286+
287+
#[cfg(test)]
288+
mod tests {
289+
use super::DescriptorFlags as Flags;
290+
291+
#[test]
292+
#[rustfmt::skip]
293+
pub fn linux_kernel_defaults() {
294+
// Make sure our defaults match the ones used by the Linux kernel.
295+
// Constants pulled from an old version of arch/x86/kernel/cpu/common.c
296+
assert_eq!(Flags::KERNEL_CODE32.bits(), 0x00cf9b000000ffff);
297+
assert_eq!(Flags::KERNEL_CODE64.bits(), 0x00af9b000000ffff);
298+
assert_eq!(Flags::KERNEL_DATA.bits(), 0x00cf93000000ffff);
299+
assert_eq!(Flags::USER_CODE32.bits(), 0x00cffb000000ffff);
300+
assert_eq!(Flags::USER_DATA.bits(), 0x00cff3000000ffff);
301+
assert_eq!(Flags::USER_CODE64.bits(), 0x00affb000000ffff);
302+
}
303+
}

0 commit comments

Comments
 (0)