x86_64/structures/
gdt.rs

1//! Types for the Global Descriptor Table and segment selectors.
2
3pub use crate::registers::segmentation::SegmentSelector;
4use crate::structures::tss::TaskStateSegment;
5use crate::PrivilegeLevel;
6use bit_field::BitField;
7use bitflags::bitflags;
8use core::fmt;
9// imports for intra-doc links
10#[cfg(doc)]
11use crate::registers::segmentation::{Segment, CS, SS};
12
13#[cfg(all(feature = "instructions", target_arch = "x86_64"))]
14use core::sync::atomic::{AtomicU64 as EntryValue, Ordering};
15#[cfg(not(all(feature = "instructions", target_arch = "x86_64")))]
16use u64 as EntryValue;
17
18/// 8-byte entry in a descriptor table.
19///
20/// A [`GlobalDescriptorTable`] (or LDT) is an array of these entries, and
21/// [`SegmentSelector`]s index into this array. Each [`Descriptor`] in the table
22/// uses either 1 Entry (if it is a [`UserSegment`](Descriptor::UserSegment)) or
23/// 2 Entries (if it is a [`SystemSegment`](Descriptor::SystemSegment)). This
24/// type exists to give users access to the raw entry bits in a GDT.
25#[repr(transparent)]
26pub struct Entry(EntryValue);
27
28impl Entry {
29    // Create a new Entry from a raw value.
30    const fn new(raw: u64) -> Self {
31        #[cfg(all(feature = "instructions", target_arch = "x86_64"))]
32        let raw = EntryValue::new(raw);
33        Self(raw)
34    }
35
36    /// The raw bits for this entry. Depending on the [`Descriptor`] type, these
37    /// bits may correspond to those in [`DescriptorFlags`].
38    pub fn raw(&self) -> u64 {
39        // TODO: Make this const fn when AtomicU64::load is const.
40        #[cfg(all(feature = "instructions", target_arch = "x86_64"))]
41        let raw = self.0.load(Ordering::SeqCst);
42        #[cfg(not(all(feature = "instructions", target_arch = "x86_64")))]
43        let raw = self.0;
44        raw
45    }
46}
47
48impl Clone for Entry {
49    fn clone(&self) -> Self {
50        Self::new(self.raw())
51    }
52}
53
54impl PartialEq for Entry {
55    fn eq(&self, other: &Self) -> bool {
56        self.raw() == other.raw()
57    }
58}
59
60impl Eq for Entry {}
61
62impl fmt::Debug for Entry {
63    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64        // Display inner value as hex
65        write!(f, "Entry({:#018x})", self.raw())
66    }
67}
68
69/// A 64-bit mode global descriptor table (GDT).
70///
71/// In 64-bit mode, segmentation is not supported. The GDT is used nonetheless, for example for
72/// switching between user and kernel mode or for loading a TSS.
73///
74/// The GDT has a fixed maximum size given by the `MAX` const generic parameter.
75/// Overflowing this limit by adding too many [`Descriptor`]s via
76/// [`GlobalDescriptorTable::append`] will panic.
77///
78/// You do **not** need to add a null segment descriptor yourself - this is already done
79/// internally. This means you can add up to `MAX - 1` additional [`Entry`]s to
80/// this table. Note that some [`Descriptor`]s may take up 2 [`Entry`]s.
81///
82/// Data segment registers in ring 0 can be loaded with the null segment selector. When running in
83/// ring 3, the `ss` register must point to a valid data segment which can be obtained through the
84/// [`Descriptor::user_data_segment()`](Descriptor::user_data_segment) function. Code segments must
85/// be valid and non-null at all times and can be obtained through the
86/// [`Descriptor::kernel_code_segment()`](Descriptor::kernel_code_segment) and
87/// [`Descriptor::user_code_segment()`](Descriptor::user_code_segment) in rings 0 and 3
88/// respectively.
89///
90/// For more info, see:
91/// [x86 Instruction Reference for `mov`](https://www.felixcloutier.com/x86/mov#64-bit-mode-exceptions),
92/// [Intel Manual](https://software.intel.com/sites/default/files/managed/39/c5/325462-sdm-vol-1-2abcd-3abcd.pdf),
93/// [AMD Manual](https://www.amd.com/system/files/TechDocs/24593.pdf)
94///
95/// # Example
96/// ```
97/// use x86_64::structures::gdt::{GlobalDescriptorTable, Descriptor};
98///
99/// let mut gdt = GlobalDescriptorTable::new();
100/// gdt.append(Descriptor::kernel_code_segment());
101/// gdt.append(Descriptor::user_code_segment());
102/// gdt.append(Descriptor::user_data_segment());
103///
104/// // Add entry for TSS, call gdt.load() then update segment registers
105/// ```
106
107#[derive(Debug, Clone)]
108pub struct GlobalDescriptorTable<const MAX: usize = 8> {
109    table: [Entry; MAX],
110    len: usize,
111}
112
113impl GlobalDescriptorTable {
114    /// Creates an empty GDT with the default length of 8.
115    pub const fn new() -> Self {
116        Self::empty()
117    }
118}
119
120impl Default for GlobalDescriptorTable {
121    #[inline]
122    fn default() -> Self {
123        Self::new()
124    }
125}
126
127impl<const MAX: usize> GlobalDescriptorTable<MAX> {
128    /// Creates an empty GDT which can hold `MAX` number of [`Entry`]s.
129    #[inline]
130    pub const fn empty() -> Self {
131        // TODO: Replace with compiler error when feature(generic_const_exprs) is stable.
132        assert!(MAX > 0, "A GDT cannot have 0 entries");
133        assert!(MAX <= (1 << 13), "A GDT can only have at most 2^13 entries");
134
135        // TODO: Replace with inline_const when it's stable.
136        #[allow(clippy::declare_interior_mutable_const)]
137        const NULL: Entry = Entry::new(0);
138        Self {
139            table: [NULL; MAX],
140            len: 1,
141        }
142    }
143
144    /// Forms a GDT from a slice of `u64`.
145    ///
146    /// This method allows for creation of a GDT with malformed or invalid
147    /// entries. However, it is safe because loading a GDT with invalid
148    /// entires doesn't do anything until those entries are used. For example,
149    /// [`CS::set_reg`] and [`load_tss`](crate::instructions::tables::load_tss)
150    /// are both unsafe for this reason.
151    ///
152    /// Panics if:
153    /// * the provided slice has more than `MAX` entries
154    /// * the provided slice is empty
155    /// * the first entry is not zero
156    #[cfg_attr(
157        not(all(feature = "instructions", target_arch = "x86_64")),
158        allow(rustdoc::broken_intra_doc_links)
159    )]
160    #[inline]
161    pub const fn from_raw_entries(slice: &[u64]) -> Self {
162        let len = slice.len();
163        let mut table = Self::empty().table;
164        let mut idx = 0;
165
166        assert!(len > 0, "cannot initialize GDT with empty slice");
167        assert!(slice[0] == 0, "first GDT entry must be zero");
168        assert!(
169            len <= MAX,
170            "cannot initialize GDT with slice exceeding the maximum length"
171        );
172
173        while idx < len {
174            table[idx] = Entry::new(slice[idx]);
175            idx += 1;
176        }
177
178        Self { table, len }
179    }
180
181    /// Get a reference to the internal [`Entry`] table.
182    ///
183    /// The resulting slice may contain system descriptors, which span two [`Entry`]s.
184    #[inline]
185    pub fn entries(&self) -> &[Entry] {
186        &self.table[..self.len]
187    }
188
189    /// Appends the given segment descriptor to the GDT, returning the segment selector.
190    ///
191    /// Note that depending on the type of the [`Descriptor`] this may append
192    /// either one or two new [`Entry`]s to the table.
193    ///
194    /// Panics if the GDT doesn't have enough free entries.
195    #[inline]
196    #[rustversion::attr(since(1.83), const)]
197    pub fn append(&mut self, entry: Descriptor) -> SegmentSelector {
198        let index = match entry {
199            Descriptor::UserSegment(value) => {
200                if self.len > self.table.len().saturating_sub(1) {
201                    panic!("GDT full")
202                }
203                self.push(value)
204            }
205            Descriptor::SystemSegment(value_low, value_high) => {
206                if self.len > self.table.len().saturating_sub(2) {
207                    panic!("GDT requires two free spaces to hold a SystemSegment")
208                }
209                let index = self.push(value_low);
210                self.push(value_high);
211                index
212            }
213        };
214        SegmentSelector::new(index as u16, entry.dpl())
215    }
216
217    /// Loads the GDT in the CPU using the `lgdt` instruction. This does **not** alter any of the
218    /// segment registers; you **must** (re)load them yourself using [the appropriate
219    /// functions](crate::instructions::segmentation):
220    /// [`SS::set_reg()`] and [`CS::set_reg()`].
221    #[cfg(all(feature = "instructions", target_arch = "x86_64"))]
222    #[inline]
223    pub fn load(&'static self) {
224        // SAFETY: static lifetime ensures no modification after loading.
225        unsafe { self.load_unsafe() };
226    }
227
228    /// Loads the GDT in the CPU using the `lgdt` instruction. This does **not** alter any of the
229    /// segment registers; you **must** (re)load them yourself using [the appropriate
230    /// functions](crate::instructions::segmentation):
231    /// [`SS::set_reg()`] and [`CS::set_reg()`].
232    ///
233    /// # Safety
234    ///
235    /// Unlike `load` this function will not impose a static lifetime constraint
236    /// this means its up to the user to ensure that there will be no modifications
237    /// after loading and that the GDT will live for as long as it's loaded.
238    ///
239    #[cfg(all(feature = "instructions", target_arch = "x86_64"))]
240    #[inline]
241    pub unsafe fn load_unsafe(&self) {
242        use crate::instructions::tables::lgdt;
243        unsafe {
244            lgdt(&self.pointer());
245        }
246    }
247
248    #[inline]
249    #[rustversion::attr(since(1.83), const)]
250    fn push(&mut self, value: u64) -> usize {
251        let index = self.len;
252        self.table[index] = Entry::new(value);
253        self.len += 1;
254        index
255    }
256
257    /// Returns the value of the limit for a gdt pointer. It is one less than the number of bytes of the table.
258    pub const fn limit(&self) -> u16 {
259        use core::mem::size_of;
260        // 0 < self.next_free <= MAX <= 2^13, so the limit calculation
261        // will not underflow or overflow.
262        (self.len * size_of::<u64>() - 1) as u16
263    }
264
265    /// Creates the descriptor pointer for this table. This pointer can only be
266    /// safely used if the table is never modified or destroyed while in use.
267    #[cfg(all(feature = "instructions", target_arch = "x86_64"))]
268    fn pointer(&self) -> super::DescriptorTablePointer {
269        super::DescriptorTablePointer {
270            base: crate::VirtAddr::new(self.table.as_ptr() as u64),
271            limit: self.limit(),
272        }
273    }
274}
275
276/// A 64-bit mode segment descriptor.
277///
278/// Segmentation is no longer supported in 64-bit mode, so most of the descriptor
279/// contents are ignored.
280#[derive(Debug, Clone, Copy)]
281pub enum Descriptor {
282    /// Descriptor for a code or data segment.
283    ///
284    /// Since segmentation is no longer supported in 64-bit mode, almost all of
285    /// code and data descriptors is ignored. Only some flags are still used.
286    UserSegment(u64),
287    /// A system segment descriptor such as a LDT or TSS descriptor.
288    SystemSegment(u64, u64),
289}
290
291bitflags! {
292    /// Flags for a GDT descriptor. Not all flags are valid for all descriptor types.
293    #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
294    pub struct DescriptorFlags: u64 {
295        /// Set by the processor if this segment has been accessed. Only cleared by software.
296        /// _Setting_ this bit in software prevents GDT writes on first use.
297        const ACCESSED          = 1 << 40;
298        /// For 32-bit data segments, sets the segment as writable. For 32-bit code segments,
299        /// sets the segment as _readable_. In 64-bit mode, ignored for all segments.
300        const WRITABLE          = 1 << 41;
301        /// For code segments, sets the segment as “conforming”, influencing the
302        /// privilege checks that occur on control transfers. For 32-bit data segments,
303        /// sets the segment as "expand down". In 64-bit mode, ignored for data segments.
304        const CONFORMING        = 1 << 42;
305        /// This flag must be set for code segments and unset for data segments.
306        const EXECUTABLE        = 1 << 43;
307        /// This flag must be set for user segments (in contrast to system segments).
308        const USER_SEGMENT      = 1 << 44;
309        /// These two bits encode the Descriptor Privilege Level (DPL) for this descriptor.
310        /// If both bits are set, the DPL is Ring 3, if both are unset, the DPL is Ring 0.
311        const DPL_RING_3        = 3 << 45;
312        /// Must be set for any segment, causes a segment not present exception if not set.
313        const PRESENT           = 1 << 47;
314        /// Available for use by the Operating System
315        const AVAILABLE         = 1 << 52;
316        /// Must be set for 64-bit code segments, unset otherwise.
317        const LONG_MODE         = 1 << 53;
318        /// Use 32-bit (as opposed to 16-bit) operands. If [`LONG_MODE`][Self::LONG_MODE] is set,
319        /// this must be unset. In 64-bit mode, ignored for data segments.
320        const DEFAULT_SIZE      = 1 << 54;
321        /// Limit field is scaled by 4096 bytes. In 64-bit mode, ignored for all segments.
322        const GRANULARITY       = 1 << 55;
323
324        /// Bits `0..=15` of the limit field (ignored in 64-bit mode)
325        const LIMIT_0_15        = 0xFFFF;
326        /// Bits `16..=19` of the limit field (ignored in 64-bit mode)
327        const LIMIT_16_19       = 0xF << 48;
328        /// Bits `0..=23` of the base field (ignored in 64-bit mode, except for fs and gs)
329        const BASE_0_23         = 0xFF_FFFF << 16;
330        /// Bits `24..=31` of the base field (ignored in 64-bit mode, except for fs and gs)
331        const BASE_24_31        = 0xFF << 56;
332    }
333}
334
335/// The following constants define default values for common GDT entries. They
336/// are all "flat" segments, meaning they can access the entire address space.
337/// These values all set [`WRITABLE`][DescriptorFlags::WRITABLE] and
338/// [`ACCESSED`][DescriptorFlags::ACCESSED]. They also match the values loaded
339/// by the `syscall`/`sysret` and `sysenter`/`sysexit` instructions.
340///
341/// In short, these values disable segmentation, permission checks, and access
342/// tracking at the GDT level. Kernels using these values should use paging to
343/// implement this functionality.
344impl DescriptorFlags {
345    // Flags that we set for all our default segments
346    const COMMON: Self = Self::from_bits_truncate(
347        Self::USER_SEGMENT.bits()
348            | Self::PRESENT.bits()
349            | Self::WRITABLE.bits()
350            | Self::ACCESSED.bits()
351            | Self::LIMIT_0_15.bits()
352            | Self::LIMIT_16_19.bits()
353            | Self::GRANULARITY.bits(),
354    );
355    /// A kernel data segment (64-bit or flat 32-bit)
356    pub const KERNEL_DATA: Self =
357        Self::from_bits_truncate(Self::COMMON.bits() | Self::DEFAULT_SIZE.bits());
358    /// A flat 32-bit kernel code segment
359    pub const KERNEL_CODE32: Self = Self::from_bits_truncate(
360        Self::COMMON.bits() | Self::EXECUTABLE.bits() | Self::DEFAULT_SIZE.bits(),
361    );
362    /// A 64-bit kernel code segment
363    pub const KERNEL_CODE64: Self = Self::from_bits_truncate(
364        Self::COMMON.bits() | Self::EXECUTABLE.bits() | Self::LONG_MODE.bits(),
365    );
366    /// A user data segment (64-bit or flat 32-bit)
367    pub const USER_DATA: Self =
368        Self::from_bits_truncate(Self::KERNEL_DATA.bits() | Self::DPL_RING_3.bits());
369    /// A flat 32-bit user code segment
370    pub const USER_CODE32: Self =
371        Self::from_bits_truncate(Self::KERNEL_CODE32.bits() | Self::DPL_RING_3.bits());
372    /// A 64-bit user code segment
373    pub const USER_CODE64: Self =
374        Self::from_bits_truncate(Self::KERNEL_CODE64.bits() | Self::DPL_RING_3.bits());
375}
376
377impl Descriptor {
378    /// Returns the Descriptor Privilege Level (DPL). When using this descriptor
379    /// via a [`SegmentSelector`], the RPL and Current Privilege Level (CPL)
380    /// must less than or equal to the DPL, except for stack segments where the
381    /// RPL, CPL, and DPL must all be equal.
382    #[inline]
383    pub const fn dpl(self) -> PrivilegeLevel {
384        let value_low = match self {
385            Descriptor::UserSegment(v) => v,
386            Descriptor::SystemSegment(v, _) => v,
387        };
388        let dpl = (value_low & DescriptorFlags::DPL_RING_3.bits()) >> 45;
389        PrivilegeLevel::from_u16(dpl as u16)
390    }
391
392    /// Creates a segment descriptor for a 64-bit kernel code segment. Suitable
393    /// for use with `syscall` or 64-bit `sysenter`.
394    #[inline]
395    pub const fn kernel_code_segment() -> Descriptor {
396        Descriptor::UserSegment(DescriptorFlags::KERNEL_CODE64.bits())
397    }
398
399    /// Creates a segment descriptor for a kernel data segment (32-bit or
400    /// 64-bit). Suitable for use with `syscall` or `sysenter`.
401    #[inline]
402    pub const fn kernel_data_segment() -> Descriptor {
403        Descriptor::UserSegment(DescriptorFlags::KERNEL_DATA.bits())
404    }
405
406    /// Creates a segment descriptor for a ring 3 data segment (32-bit or
407    /// 64-bit). Suitable for use with `sysret` or `sysexit`.
408    #[inline]
409    pub const fn user_data_segment() -> Descriptor {
410        Descriptor::UserSegment(DescriptorFlags::USER_DATA.bits())
411    }
412
413    /// Creates a segment descriptor for a 64-bit ring 3 code segment. Suitable
414    /// for use with `sysret` or `sysexit`.
415    #[inline]
416    pub const fn user_code_segment() -> Descriptor {
417        Descriptor::UserSegment(DescriptorFlags::USER_CODE64.bits())
418    }
419
420    /// Creates a TSS system descriptor for the given TSS.
421    ///
422    /// While it is possible to create multiple Descriptors that point to the
423    /// same TSS, this generally isn't recommended, as the TSS usually contains
424    /// per-CPU information such as the RSP and IST pointers. Instead, there
425    /// should be exactly one TSS and one corresponding TSS Descriptor per CPU.
426    /// Then, each of these descriptors should be placed in a GDT (which can
427    /// either be global or per-CPU).
428    #[inline]
429    pub fn tss_segment(tss: &'static TaskStateSegment) -> Descriptor {
430        // SAFETY: The pointer is derived from a &'static reference, which ensures its validity.
431        unsafe { Self::tss_segment_unchecked(tss) }
432    }
433
434    /// Similar to [`Descriptor::tss_segment`], but unsafe since it does not enforce a lifetime
435    /// constraint on the provided TSS.
436    ///
437    /// # Safety
438    /// The caller must ensure that the passed pointer is valid for as long as the descriptor is
439    /// being used.
440    #[inline]
441    pub unsafe fn tss_segment_unchecked(tss: *const TaskStateSegment) -> Descriptor {
442        use self::DescriptorFlags as Flags;
443        use core::mem::size_of;
444
445        let ptr = tss as u64;
446
447        let mut low = Flags::PRESENT.bits();
448        // base
449        low.set_bits(16..40, ptr.get_bits(0..24));
450        low.set_bits(56..64, ptr.get_bits(24..32));
451        // limit (the `-1` in needed since the bound is inclusive)
452        low.set_bits(0..16, (size_of::<TaskStateSegment>() - 1) as u64);
453        // type (0b1001 = available 64-bit tss)
454        low.set_bits(40..44, 0b1001);
455
456        let mut high = 0;
457        high.set_bits(0..32, ptr.get_bits(32..64));
458
459        Descriptor::SystemSegment(low, high)
460    }
461}
462
463#[cfg(test)]
464mod tests {
465    use super::DescriptorFlags as Flags;
466    use super::*;
467
468    #[test]
469    #[rustfmt::skip]
470    pub fn linux_kernel_defaults() {
471        // Make sure our defaults match the ones used by the Linux kernel.
472        // Constants pulled from an old version of arch/x86/kernel/cpu/common.c
473        assert_eq!(Flags::KERNEL_CODE64.bits(), 0x00af9b000000ffff);
474        assert_eq!(Flags::KERNEL_CODE32.bits(), 0x00cf9b000000ffff);
475        assert_eq!(Flags::KERNEL_DATA.bits(),   0x00cf93000000ffff);
476        assert_eq!(Flags::USER_CODE64.bits(),   0x00affb000000ffff);
477        assert_eq!(Flags::USER_CODE32.bits(),   0x00cffb000000ffff);
478        assert_eq!(Flags::USER_DATA.bits(),     0x00cff3000000ffff);
479    }
480
481    // Makes a GDT that has two free slots
482    fn make_six_entry_gdt() -> GlobalDescriptorTable {
483        let mut gdt = GlobalDescriptorTable::new();
484        gdt.append(Descriptor::kernel_code_segment());
485        gdt.append(Descriptor::kernel_data_segment());
486        gdt.append(Descriptor::UserSegment(DescriptorFlags::USER_CODE32.bits()));
487        gdt.append(Descriptor::user_data_segment());
488        gdt.append(Descriptor::user_code_segment());
489        assert_eq!(gdt.len, 6);
490        gdt
491    }
492
493    static TSS: TaskStateSegment = TaskStateSegment::new();
494
495    fn make_full_gdt() -> GlobalDescriptorTable {
496        let mut gdt = make_six_entry_gdt();
497        gdt.append(Descriptor::tss_segment(&TSS));
498        assert_eq!(gdt.len, 8);
499        gdt
500    }
501
502    #[test]
503    pub fn push_max_segments() {
504        // Make sure we don't panic with user segments
505        let mut gdt = make_six_entry_gdt();
506        gdt.append(Descriptor::user_data_segment());
507        assert_eq!(gdt.len, 7);
508        gdt.append(Descriptor::user_data_segment());
509        assert_eq!(gdt.len, 8);
510        // Make sure we don't panic with system segments
511        let _ = make_full_gdt();
512    }
513
514    #[test]
515    #[should_panic]
516    pub fn panic_user_segment() {
517        let mut gdt = make_full_gdt();
518        gdt.append(Descriptor::user_data_segment());
519    }
520
521    #[test]
522    #[should_panic]
523    pub fn panic_system_segment() {
524        let mut gdt = make_six_entry_gdt();
525        gdt.append(Descriptor::user_data_segment());
526        // We have one free slot, but the GDT requires two
527        gdt.append(Descriptor::tss_segment(&TSS));
528    }
529
530    #[test]
531    pub fn from_entries() {
532        let raw = [0, Flags::KERNEL_CODE64.bits(), Flags::KERNEL_DATA.bits()];
533        let gdt = GlobalDescriptorTable::<3>::from_raw_entries(&raw);
534        assert_eq!(gdt.table.len(), 3);
535        assert_eq!(gdt.entries().len(), 3);
536    }
537
538    #[test]
539    pub fn descriptor_dpl() {
540        assert_eq!(
541            Descriptor::kernel_code_segment().dpl(),
542            PrivilegeLevel::Ring0
543        );
544        assert_eq!(
545            Descriptor::kernel_data_segment().dpl(),
546            PrivilegeLevel::Ring0
547        );
548        assert_eq!(Descriptor::user_code_segment().dpl(), PrivilegeLevel::Ring3);
549        assert_eq!(Descriptor::user_code_segment().dpl(), PrivilegeLevel::Ring3);
550    }
551}