@@ -5,22 +5,54 @@ use crate::structures::tss::TaskStateSegment;
5
5
use crate :: PrivilegeLevel ;
6
6
use bit_field:: BitField ;
7
7
use bitflags:: bitflags;
8
+ use core:: fmt;
8
9
// imports for intra-doc links
9
10
#[ cfg( doc) ]
10
11
use crate :: registers:: segmentation:: { Segment , CS , SS } ;
11
12
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
+
12
44
/// A 64-bit mode global descriptor table (GDT).
13
45
///
14
46
/// In 64-bit mode, segmentation is not supported. The GDT is used nonetheless, for example for
15
47
/// switching between user and kernel mode or for loading a TSS.
16
48
///
17
49
/// 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.
20
52
///
21
53
/// 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.
24
56
///
25
57
/// Data segment registers in ring 0 can be loaded with the null segment selector. When running in
26
58
/// 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};
40
72
/// use x86_64::structures::gdt::{GlobalDescriptorTable, Descriptor};
41
73
///
42
74
/// 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());
46
78
///
47
79
/// // Add entry for TSS, call gdt.load() then update segment registers
48
80
/// ```
49
81
50
82
#[ derive( Debug , Clone ) ]
51
83
pub struct GlobalDescriptorTable < const MAX : usize = 8 > {
52
- table : [ u64 ; MAX ] ,
84
+ table : [ Entry ; MAX ] ,
53
85
len : usize ,
54
86
}
55
87
@@ -61,57 +93,70 @@ impl GlobalDescriptorTable {
61
93
}
62
94
63
95
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.
65
97
#[ inline]
66
98
pub const fn empty ( ) -> Self {
67
99
// TODO: Replace with compiler error when feature(generic_const_exprs) is stable.
68
100
assert ! ( MAX > 0 , "A GDT cannot have 0 entries" ) ;
69
101
assert ! ( MAX <= ( 1 << 13 ) , "A GDT can only have at most 2^13 entries" ) ;
102
+ const NULL : Entry = Entry :: new ( 0 ) ;
70
103
Self {
71
- table : [ 0 ; MAX ] ,
104
+ table : [ NULL ; MAX ] ,
72
105
len : 1 ,
73
106
}
74
107
}
75
108
76
109
/// Forms a GDT from a slice of `u64`.
77
110
///
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.
79
116
///
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) ) ]
82
122
#[ inline]
83
- pub const unsafe fn from_raw_slice ( slice : & [ u64 ] ) -> Self {
123
+ pub const fn from_raw_entries ( slice : & [ u64 ] ) -> Self {
84
124
let len = slice. len ( ) ;
85
- let mut table = [ 0 ; MAX ] ;
125
+ let mut table = Self :: empty ( ) . table ;
86
126
let mut idx = 0 ;
87
127
128
+ assert ! ( len > 0 , "cannot initialize GDT with empty slice" ) ;
129
+ assert ! ( slice[ 0 ] == 0 , "first GDT entry must be zero" ) ;
88
130
assert ! (
89
131
len <= MAX ,
90
132
"cannot initialize GDT with slice exceeding the maximum length"
91
133
) ;
92
134
93
135
while idx < len {
94
- table[ idx] = slice[ idx] ;
136
+ table[ idx] = Entry :: new ( slice[ idx] ) ;
95
137
idx += 1 ;
96
138
}
97
139
98
140
Self { table, len }
99
141
}
100
142
101
- /// Get a reference to the internal table.
143
+ /// Get a reference to the internal [`Entry`] table.
102
144
///
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.
104
146
#[ inline]
105
- pub fn as_raw_slice ( & self ) -> & [ u64 ] {
147
+ pub fn entries ( & self ) -> & [ Entry ] {
106
148
& self . table [ ..self . len ]
107
149
}
108
150
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.
110
155
///
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.
112
157
#[ inline]
113
158
#[ 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 {
115
160
let index = match entry {
116
161
Descriptor :: UserSegment ( value) => {
117
162
if self . len > self . table . len ( ) . saturating_sub ( 1 ) {
@@ -179,7 +224,7 @@ impl<const MAX: usize> GlobalDescriptorTable<MAX> {
179
224
#[ cfg_attr( feature = "const_fn" , rustversion:: attr( all( ) , const ) ) ]
180
225
fn push ( & mut self , value : u64 ) -> usize {
181
226
let index = self . len ;
182
- self . table [ index] = value;
227
+ self . table [ index] = Entry :: new ( value) ;
183
228
self . len += 1 ;
184
229
index
185
230
}
@@ -378,11 +423,11 @@ mod tests {
378
423
// Makes a GDT that has two free slots
379
424
fn make_six_entry_gdt ( ) -> GlobalDescriptorTable {
380
425
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 ( ) ) ;
386
431
assert_eq ! ( gdt. len, 6 ) ;
387
432
gdt
388
433
}
@@ -391,7 +436,7 @@ mod tests {
391
436
392
437
fn make_full_gdt ( ) -> GlobalDescriptorTable {
393
438
let mut gdt = make_six_entry_gdt ( ) ;
394
- gdt. add_entry ( Descriptor :: tss_segment ( & TSS ) ) ;
439
+ gdt. append ( Descriptor :: tss_segment ( & TSS ) ) ;
395
440
assert_eq ! ( gdt. len, 8 ) ;
396
441
gdt
397
442
}
@@ -400,9 +445,9 @@ mod tests {
400
445
pub fn push_max_segments ( ) {
401
446
// Make sure we don't panic with user segments
402
447
let mut gdt = make_six_entry_gdt ( ) ;
403
- gdt. add_entry ( Descriptor :: user_data_segment ( ) ) ;
448
+ gdt. append ( Descriptor :: user_data_segment ( ) ) ;
404
449
assert_eq ! ( gdt. len, 7 ) ;
405
- gdt. add_entry ( Descriptor :: user_data_segment ( ) ) ;
450
+ gdt. append ( Descriptor :: user_data_segment ( ) ) ;
406
451
assert_eq ! ( gdt. len, 8 ) ;
407
452
// Make sure we don't panic with system segments
408
453
let _ = make_full_gdt ( ) ;
@@ -412,15 +457,23 @@ mod tests {
412
457
#[ should_panic]
413
458
pub fn panic_user_segment ( ) {
414
459
let mut gdt = make_full_gdt ( ) ;
415
- gdt. add_entry ( Descriptor :: user_data_segment ( ) ) ;
460
+ gdt. append ( Descriptor :: user_data_segment ( ) ) ;
416
461
}
417
462
418
463
#[ test]
419
464
#[ should_panic]
420
465
pub fn panic_system_segment ( ) {
421
466
let mut gdt = make_six_entry_gdt ( ) ;
422
- gdt. add_entry ( Descriptor :: user_data_segment ( ) ) ;
467
+ gdt. append ( Descriptor :: user_data_segment ( ) ) ;
423
468
// 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 ) ;
425
478
}
426
479
}
0 commit comments