Skip to content

Commit e70b8a3

Browse files
authored
Merge pull request #380 from rust-osdev/gdt_entry
Add structures::gdt::Entry type
2 parents 272ff04 + 49b5295 commit e70b8a3

File tree

3 files changed

+90
-37
lines changed

3 files changed

+90
-37
lines changed

src/lib.rs

+1-1
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

+87-34
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,54 @@ use crate::structures::tss::TaskStateSegment;
55
use crate::PrivilegeLevel;
66
use bit_field::BitField;
77
use bitflags::bitflags;
8+
use core::fmt;
89
// imports for intra-doc links
910
#[cfg(doc)]
1011
use crate::registers::segmentation::{Segment, CS, SS};
1112

13+
/// 8-byte entry in a descriptor table.
14+
///
15+
/// A [`GlobalDescriptorTable`] (or LDT) is an array of these entries, and
16+
/// [`SegmentSelector`]s index into this array. Each [`Descriptor`] in the table
17+
/// uses either 1 Entry (if it is a [`UserSegment`](Descriptor::UserSegment)) or
18+
/// 2 Entries (if it is a [`SystemSegment`](Descriptor::SystemSegment)). This
19+
/// type exists to give users access to the raw entry bits in a GDT.
20+
#[derive(Clone, PartialEq, Eq)]
21+
#[repr(transparent)]
22+
pub struct Entry(u64);
23+
24+
impl Entry {
25+
// Create a new Entry from a raw value.
26+
const fn new(raw: u64) -> Self {
27+
Self(raw)
28+
}
29+
30+
/// The raw bits for this entry. Depending on the [`Descriptor`] type, these
31+
/// bits may correspond to those in [`DescriptorFlags`].
32+
pub fn raw(&self) -> u64 {
33+
self.0
34+
}
35+
}
36+
37+
impl fmt::Debug for Entry {
38+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39+
// Display inner value as hex
40+
write!(f, "Entry({:#018x})", self.raw())
41+
}
42+
}
43+
1244
/// A 64-bit mode global descriptor table (GDT).
1345
///
1446
/// In 64-bit mode, segmentation is not supported. The GDT is used nonetheless, for example for
1547
/// switching between user and kernel mode or for loading a TSS.
1648
///
1749
/// 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.
50+
/// Overflowing this limit by adding too many [`Descriptor`]s via
51+
/// [`GlobalDescriptorTable::append`] will panic.
2052
///
2153
/// 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.
54+
/// internally. This means you can add up to `MAX - 1` additional [`Entry`]s to
55+
/// this table. Note that some [`Descriptor`]s may take up 2 [`Entry`]s.
2456
///
2557
/// Data segment registers in ring 0 can be loaded with the null segment selector. When running in
2658
/// ring 3, the `ss` register must point to a valid data segment which can be obtained through the
@@ -40,16 +72,16 @@ use crate::registers::segmentation::{Segment, CS, SS};
4072
/// use x86_64::structures::gdt::{GlobalDescriptorTable, Descriptor};
4173
///
4274
/// 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());
75+
/// gdt.append(Descriptor::kernel_code_segment());
76+
/// gdt.append(Descriptor::user_code_segment());
77+
/// gdt.append(Descriptor::user_data_segment());
4678
///
4779
/// // Add entry for TSS, call gdt.load() then update segment registers
4880
/// ```
4981
5082
#[derive(Debug, Clone)]
5183
pub struct GlobalDescriptorTable<const MAX: usize = 8> {
52-
table: [u64; MAX],
84+
table: [Entry; MAX],
5385
len: usize,
5486
}
5587

@@ -61,57 +93,70 @@ impl GlobalDescriptorTable {
6193
}
6294

6395
impl<const MAX: usize> GlobalDescriptorTable<MAX> {
64-
/// Creates an empty GDT which can hold `MAX` number of [`Descriptor`]s.
96+
/// Creates an empty GDT which can hold `MAX` number of [`Entry`]s.
6597
#[inline]
6698
pub const fn empty() -> Self {
6799
// TODO: Replace with compiler error when feature(generic_const_exprs) is stable.
68100
assert!(MAX > 0, "A GDT cannot have 0 entries");
69101
assert!(MAX <= (1 << 13), "A GDT can only have at most 2^13 entries");
102+
const NULL: Entry = Entry::new(0);
70103
Self {
71-
table: [0; MAX],
104+
table: [NULL; MAX],
72105
len: 1,
73106
}
74107
}
75108

76109
/// Forms a GDT from a slice of `u64`.
77110
///
78-
/// # Safety
111+
/// This method allows for creation of a GDT with malformed or invalid
112+
/// entries. However, it is safe because loading a GDT with invalid
113+
/// entires doesn't do anything until those entries are used. For example,
114+
/// [`CS::set_reg`] and [`load_tss`](crate::instructions::tables::load_tss)
115+
/// are both unsafe for this reason.
79116
///
80-
/// * The user must make sure that the entries are well formed
81-
/// * Panics if the provided slice has more than `MAX` entries
117+
/// Panics if:
118+
/// * the provided slice has more than `MAX` entries
119+
/// * the provided slice is empty
120+
/// * the first entry is not zero
121+
#[cfg_attr(not(feature = "instructions"), allow(rustdoc::broken_intra_doc_links))]
82122
#[inline]
83-
pub const unsafe fn from_raw_slice(slice: &[u64]) -> Self {
123+
pub const fn from_raw_entries(slice: &[u64]) -> Self {
84124
let len = slice.len();
85-
let mut table = [0; MAX];
125+
let mut table = Self::empty().table;
86126
let mut idx = 0;
87127

128+
assert!(len > 0, "cannot initialize GDT with empty slice");
129+
assert!(slice[0] == 0, "first GDT entry must be zero");
88130
assert!(
89131
len <= MAX,
90132
"cannot initialize GDT with slice exceeding the maximum length"
91133
);
92134

93135
while idx < len {
94-
table[idx] = slice[idx];
136+
table[idx] = Entry::new(slice[idx]);
95137
idx += 1;
96138
}
97139

98140
Self { table, len }
99141
}
100142

101-
/// Get a reference to the internal table.
143+
/// Get a reference to the internal [`Entry`] table.
102144
///
103-
/// The resulting slice may contain system descriptors, which span two `u64`s.
145+
/// The resulting slice may contain system descriptors, which span two [`Entry`]s.
104146
#[inline]
105-
pub fn as_raw_slice(&self) -> &[u64] {
147+
pub fn entries(&self) -> &[Entry] {
106148
&self.table[..self.len]
107149
}
108150

109-
/// Adds the given segment descriptor to the GDT, returning the segment selector.
151+
/// Appends the given segment descriptor to the GDT, returning the segment selector.
152+
///
153+
/// Note that depending on the type of the [`Descriptor`] this may append
154+
/// either one or two new [`Entry`]s to the table.
110155
///
111-
/// Panics if the GDT doesn't have enough free entries to hold the Descriptor.
156+
/// Panics if the GDT doesn't have enough free entries.
112157
#[inline]
113158
#[cfg_attr(feature = "const_fn", rustversion::attr(all(), const))]
114-
pub fn add_entry(&mut self, entry: Descriptor) -> SegmentSelector {
159+
pub fn append(&mut self, entry: Descriptor) -> SegmentSelector {
115160
let index = match entry {
116161
Descriptor::UserSegment(value) => {
117162
if self.len > self.table.len().saturating_sub(1) {
@@ -179,7 +224,7 @@ impl<const MAX: usize> GlobalDescriptorTable<MAX> {
179224
#[cfg_attr(feature = "const_fn", rustversion::attr(all(), const))]
180225
fn push(&mut self, value: u64) -> usize {
181226
let index = self.len;
182-
self.table[index] = value;
227+
self.table[index] = Entry::new(value);
183228
self.len += 1;
184229
index
185230
}
@@ -378,11 +423,11 @@ mod tests {
378423
// Makes a GDT that has two free slots
379424
fn make_six_entry_gdt() -> GlobalDescriptorTable {
380425
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());
426+
gdt.append(Descriptor::kernel_code_segment());
427+
gdt.append(Descriptor::kernel_data_segment());
428+
gdt.append(Descriptor::UserSegment(DescriptorFlags::USER_CODE32.bits()));
429+
gdt.append(Descriptor::user_data_segment());
430+
gdt.append(Descriptor::user_code_segment());
386431
assert_eq!(gdt.len, 6);
387432
gdt
388433
}
@@ -391,7 +436,7 @@ mod tests {
391436

392437
fn make_full_gdt() -> GlobalDescriptorTable {
393438
let mut gdt = make_six_entry_gdt();
394-
gdt.add_entry(Descriptor::tss_segment(&TSS));
439+
gdt.append(Descriptor::tss_segment(&TSS));
395440
assert_eq!(gdt.len, 8);
396441
gdt
397442
}
@@ -400,9 +445,9 @@ mod tests {
400445
pub fn push_max_segments() {
401446
// Make sure we don't panic with user segments
402447
let mut gdt = make_six_entry_gdt();
403-
gdt.add_entry(Descriptor::user_data_segment());
448+
gdt.append(Descriptor::user_data_segment());
404449
assert_eq!(gdt.len, 7);
405-
gdt.add_entry(Descriptor::user_data_segment());
450+
gdt.append(Descriptor::user_data_segment());
406451
assert_eq!(gdt.len, 8);
407452
// Make sure we don't panic with system segments
408453
let _ = make_full_gdt();
@@ -412,15 +457,23 @@ mod tests {
412457
#[should_panic]
413458
pub fn panic_user_segment() {
414459
let mut gdt = make_full_gdt();
415-
gdt.add_entry(Descriptor::user_data_segment());
460+
gdt.append(Descriptor::user_data_segment());
416461
}
417462

418463
#[test]
419464
#[should_panic]
420465
pub fn panic_system_segment() {
421466
let mut gdt = make_six_entry_gdt();
422-
gdt.add_entry(Descriptor::user_data_segment());
467+
gdt.append(Descriptor::user_data_segment());
423468
// We have one free slot, but the GDT requires two
424-
gdt.add_entry(Descriptor::tss_segment(&TSS));
469+
gdt.append(Descriptor::tss_segment(&TSS));
470+
}
471+
472+
#[test]
473+
pub fn from_entries() {
474+
let raw = [0, Flags::KERNEL_CODE64.bits(), Flags::KERNEL_DATA.bits()];
475+
let gdt = GlobalDescriptorTable::<3>::from_raw_entries(&raw);
476+
assert_eq!(gdt.table.len(), 3);
477+
assert_eq!(gdt.entries().len(), 3);
425478
}
426479
}

testing/src/gdt.rs

+2-2
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)