Skip to content

Commit add9cbc

Browse files
committed
Add structures::gdt::Entry type
The current documentation for the GDT often confuses entries and `Descriptor`s. For example, adding a new `Descriptor` uses a method confusingly named `add_entry`. An entry is a raw 64-bit value that is indexed by a segment selector. The `MAX` length of the GDT is a number of _entries_, not `Descriptor`s. To fix this confusion, this PR makes the following changes: - Adds a transparent `u64` newtype called `Entry`. - Updates the `GlobalDescriptorTable` documentation to correctly use `Entry` or `Descriptor` where appropriate. - Renames the `add_entry` to `append`. - This better expresses that this method might add multiple entries. - Renames `from_raw_slice` to `from_raw_entries` - Renames `as_raw_slice` to `entries` - This method now returns a slice of `Entry`s instead of `u64`s This also fixes an issue where our `assert!`s in `empty()` wouldn't trigger if the GDT was constructed from raw values. Signed-off-by: Joe Richey <[email protected]>
1 parent 272ff04 commit add9cbc

File tree

3 files changed

+70
-34
lines changed

3 files changed

+70
-34
lines changed

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//! and access to various system registers.
33
44
#![cfg_attr(not(test), no_std)]
5-
#![cfg_attr(feature = "const_fn", feature(const_mut_refs))] // GDT add_entry()
5+
#![cfg_attr(feature = "const_fn", feature(const_mut_refs))] // GDT::append()
66
#![cfg_attr(feature = "asm_const", feature(asm_const))]
77
#![cfg_attr(feature = "abi_x86_interrupt", feature(abi_x86_interrupt))]
88
#![cfg_attr(feature = "step_trait", feature(step_trait))]

src/structures/gdt.rs

Lines changed: 67 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,42 @@ use bitflags::bitflags;
99
#[cfg(doc)]
1010
use crate::registers::segmentation::{Segment, CS, SS};
1111

12+
/// 8-byte entry in a descriptor table.
13+
///
14+
/// A [`GlobalDescriptorTable`] (or LDT) is an array of these entries, and
15+
/// [`SegmentSelector`]s index into this array. Each [`Descriptor`] in the table
16+
/// uses either 1 Entry (if it is a [`UserSegment`](Descriptor::UserSegment)) or
17+
/// 2 Entries (if it is a [`SystemSegment`](Descriptor::SystemSegment)). This
18+
/// type exists to give users access to the raw entry bits in a GDT.
19+
#[derive(Clone, Debug)]
20+
#[repr(transparent)]
21+
pub struct Entry(u64);
22+
23+
impl Entry {
24+
// Create a new Entry from a raw value.
25+
const fn new(raw: u64) -> Self {
26+
Self(raw)
27+
}
28+
29+
/// The raw bits for this entry. Depending on the [`Descriptor`] type. This
30+
/// bits may correspond to those in [`DescriptorFlags`].
31+
pub fn raw(&self) -> u64 {
32+
self.0
33+
}
34+
}
35+
1236
/// A 64-bit mode global descriptor table (GDT).
1337
///
1438
/// In 64-bit mode, segmentation is not supported. The GDT is used nonetheless, for example for
1539
/// switching between user and kernel mode or for loading a TSS.
1640
///
1741
/// The GDT has a fixed maximum size given by the `MAX` const generic parameter.
18-
/// Trying to add more entries than this maximum via [`GlobalDescriptorTable::add_entry`]
19-
/// will panic.
42+
/// Overflowing this limit by adding too many [`Descriptor`]s via
43+
/// [`GlobalDescriptorTable::append`] will panic.
2044
///
2145
/// You do **not** need to add a null segment descriptor yourself - this is already done
22-
/// internally. This means you can add up to `MAX - 1` additional [`Descriptor`]s to
23-
/// this table.
46+
/// internally. This means you can add up to `MAX - 1` additional [`Entry`]s to
47+
/// this table. Note that some [`Descriptor`]s may take up 2 [`Entry`]s.
2448
///
2549
/// Data segment registers in ring 0 can be loaded with the null segment selector. When running in
2650
/// ring 3, the `ss` register must point to a valid data segment which can be obtained through the
@@ -40,16 +64,16 @@ use crate::registers::segmentation::{Segment, CS, SS};
4064
/// use x86_64::structures::gdt::{GlobalDescriptorTable, Descriptor};
4165
///
4266
/// let mut gdt = GlobalDescriptorTable::new();
43-
/// gdt.add_entry(Descriptor::kernel_code_segment());
44-
/// gdt.add_entry(Descriptor::user_code_segment());
45-
/// gdt.add_entry(Descriptor::user_data_segment());
67+
/// gdt.append(Descriptor::kernel_code_segment());
68+
/// gdt.append(Descriptor::user_code_segment());
69+
/// gdt.append(Descriptor::user_data_segment());
4670
///
4771
/// // Add entry for TSS, call gdt.load() then update segment registers
4872
/// ```
4973
5074
#[derive(Debug, Clone)]
5175
pub struct GlobalDescriptorTable<const MAX: usize = 8> {
52-
table: [u64; MAX],
76+
table: [Entry; MAX],
5377
len: usize,
5478
}
5579

@@ -61,28 +85,29 @@ impl GlobalDescriptorTable {
6185
}
6286

6387
impl<const MAX: usize> GlobalDescriptorTable<MAX> {
64-
/// Creates an empty GDT which can hold `MAX` number of [`Descriptor`]s.
88+
/// Creates an empty GDT which can hold `MAX` number of [`Entry`]s.
6589
#[inline]
6690
pub const fn empty() -> Self {
6791
// TODO: Replace with compiler error when feature(generic_const_exprs) is stable.
6892
assert!(MAX > 0, "A GDT cannot have 0 entries");
6993
assert!(MAX <= (1 << 13), "A GDT can only have at most 2^13 entries");
94+
const NULL: Entry = Entry::new(0);
7095
Self {
71-
table: [0; MAX],
96+
table: [NULL; MAX],
7297
len: 1,
7398
}
7499
}
75100

76-
/// Forms a GDT from a slice of `u64`.
101+
/// Forms a GDT from a slice of raw [`Entry`] values.
77102
///
78103
/// # Safety
79104
///
80105
/// * The user must make sure that the entries are well formed
81106
/// * Panics if the provided slice has more than `MAX` entries
82107
#[inline]
83-
pub const unsafe fn from_raw_slice(slice: &[u64]) -> Self {
108+
pub const unsafe fn from_raw_entries(slice: &[u64]) -> Self {
84109
let len = slice.len();
85-
let mut table = [0; MAX];
110+
let mut table = Self::empty().table;
86111
let mut idx = 0;
87112

88113
assert!(
@@ -91,27 +116,30 @@ impl<const MAX: usize> GlobalDescriptorTable<MAX> {
91116
);
92117

93118
while idx < len {
94-
table[idx] = slice[idx];
119+
table[idx] = Entry::new(slice[idx]);
95120
idx += 1;
96121
}
97122

98123
Self { table, len }
99124
}
100125

101-
/// Get a reference to the internal table.
126+
/// Get a reference to the internal [`Entry`] table.
102127
///
103-
/// The resulting slice may contain system descriptors, which span two `u64`s.
128+
/// The resulting slice may contain system descriptors, which span two [`Entry`]s.
104129
#[inline]
105-
pub fn as_raw_slice(&self) -> &[u64] {
130+
pub fn entries(&self) -> &[Entry] {
106131
&self.table[..self.len]
107132
}
108133

109134
/// Adds the given segment descriptor to the GDT, returning the segment selector.
110135
///
111-
/// Panics if the GDT doesn't have enough free entries to hold the Descriptor.
136+
/// Note that depending on the type of the [`Descriptor`] this may add either
137+
/// one or two new entries.
138+
///
139+
/// Panics if the GDT doesn't have enough free entries.
112140
#[inline]
113141
#[cfg_attr(feature = "const_fn", rustversion::attr(all(), const))]
114-
pub fn add_entry(&mut self, entry: Descriptor) -> SegmentSelector {
142+
pub fn append(&mut self, entry: Descriptor) -> SegmentSelector {
115143
let index = match entry {
116144
Descriptor::UserSegment(value) => {
117145
if self.len > self.table.len().saturating_sub(1) {
@@ -179,7 +207,7 @@ impl<const MAX: usize> GlobalDescriptorTable<MAX> {
179207
#[cfg_attr(feature = "const_fn", rustversion::attr(all(), const))]
180208
fn push(&mut self, value: u64) -> usize {
181209
let index = self.len;
182-
self.table[index] = value;
210+
self.table[index] = Entry::new(value);
183211
self.len += 1;
184212
index
185213
}
@@ -378,11 +406,11 @@ mod tests {
378406
// Makes a GDT that has two free slots
379407
fn make_six_entry_gdt() -> GlobalDescriptorTable {
380408
let mut gdt = GlobalDescriptorTable::new();
381-
gdt.add_entry(Descriptor::kernel_code_segment());
382-
gdt.add_entry(Descriptor::kernel_data_segment());
383-
gdt.add_entry(Descriptor::UserSegment(DescriptorFlags::USER_CODE32.bits()));
384-
gdt.add_entry(Descriptor::user_data_segment());
385-
gdt.add_entry(Descriptor::user_code_segment());
409+
gdt.append(Descriptor::kernel_code_segment());
410+
gdt.append(Descriptor::kernel_data_segment());
411+
gdt.append(Descriptor::UserSegment(DescriptorFlags::USER_CODE32.bits()));
412+
gdt.append(Descriptor::user_data_segment());
413+
gdt.append(Descriptor::user_code_segment());
386414
assert_eq!(gdt.len, 6);
387415
gdt
388416
}
@@ -391,7 +419,7 @@ mod tests {
391419

392420
fn make_full_gdt() -> GlobalDescriptorTable {
393421
let mut gdt = make_six_entry_gdt();
394-
gdt.add_entry(Descriptor::tss_segment(&TSS));
422+
gdt.append(Descriptor::tss_segment(&TSS));
395423
assert_eq!(gdt.len, 8);
396424
gdt
397425
}
@@ -400,9 +428,9 @@ mod tests {
400428
pub fn push_max_segments() {
401429
// Make sure we don't panic with user segments
402430
let mut gdt = make_six_entry_gdt();
403-
gdt.add_entry(Descriptor::user_data_segment());
431+
gdt.append(Descriptor::user_data_segment());
404432
assert_eq!(gdt.len, 7);
405-
gdt.add_entry(Descriptor::user_data_segment());
433+
gdt.append(Descriptor::user_data_segment());
406434
assert_eq!(gdt.len, 8);
407435
// Make sure we don't panic with system segments
408436
let _ = make_full_gdt();
@@ -412,15 +440,23 @@ mod tests {
412440
#[should_panic]
413441
pub fn panic_user_segment() {
414442
let mut gdt = make_full_gdt();
415-
gdt.add_entry(Descriptor::user_data_segment());
443+
gdt.append(Descriptor::user_data_segment());
416444
}
417445

418446
#[test]
419447
#[should_panic]
420448
pub fn panic_system_segment() {
421449
let mut gdt = make_six_entry_gdt();
422-
gdt.add_entry(Descriptor::user_data_segment());
450+
gdt.append(Descriptor::user_data_segment());
423451
// We have one free slot, but the GDT requires two
424-
gdt.add_entry(Descriptor::tss_segment(&TSS));
452+
gdt.append(Descriptor::tss_segment(&TSS));
453+
}
454+
455+
#[test]
456+
pub fn from_entries() {
457+
let raw = [0, Flags::KERNEL_CODE64.bits(), Flags::KERNEL_DATA.bits()];
458+
let gdt = unsafe { GlobalDescriptorTable::<3>::from_raw_entries(&raw) };
459+
assert_eq!(gdt.table.len(), 3);
460+
assert_eq!(gdt.entries().len(), 3);
425461
}
426462
}

testing/src/gdt.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ lazy_static! {
2020
};
2121
static ref GDT: (SingleUseCell<GlobalDescriptorTable>, Selectors) = {
2222
let mut gdt = GlobalDescriptorTable::new();
23-
let code_selector = gdt.add_entry(Descriptor::kernel_code_segment());
24-
let tss_selector = gdt.add_entry(Descriptor::tss_segment(&TSS));
23+
let code_selector = gdt.append(Descriptor::kernel_code_segment());
24+
let tss_selector = gdt.append(Descriptor::tss_segment(&TSS));
2525
(
2626
SingleUseCell::new(gdt),
2727
Selectors {

0 commit comments

Comments
 (0)