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}