Skip to content

Commit 2d41827

Browse files
authored
Merge pull request #410 from rust-osdev/dpl
Add Descriptor::dpl const method and use it in GDT construction
2 parents a967e24 + 7673551 commit 2d41827

File tree

2 files changed

+34
-18
lines changed

2 files changed

+34
-18
lines changed

src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,13 @@ impl PrivilegeLevel {
5252
///
5353
/// This function panics if the passed value is >3.
5454
#[inline]
55-
pub fn from_u16(value: u16) -> PrivilegeLevel {
55+
pub const fn from_u16(value: u16) -> PrivilegeLevel {
5656
match value {
5757
0 => PrivilegeLevel::Ring0,
5858
1 => PrivilegeLevel::Ring1,
5959
2 => PrivilegeLevel::Ring2,
6060
3 => PrivilegeLevel::Ring3,
61-
i => panic!("{} is not a valid privilege level", i),
61+
_ => panic!("invalid privilege level"),
6262
}
6363
}
6464
}

src/structures/gdt.rs

+32-16
Original file line numberDiff line numberDiff line change
@@ -115,20 +115,7 @@ impl GlobalDescriptorTable {
115115
index
116116
}
117117
};
118-
119-
let rpl = match entry {
120-
Descriptor::UserSegment(value) => {
121-
if DescriptorFlags::from_bits_truncate(value).contains(DescriptorFlags::DPL_RING_3)
122-
{
123-
PrivilegeLevel::Ring3
124-
} else {
125-
PrivilegeLevel::Ring0
126-
}
127-
}
128-
Descriptor::SystemSegment(_, _) => PrivilegeLevel::Ring0,
129-
};
130-
131-
SegmentSelector::new(index as u16, rpl)
118+
SegmentSelector::new(index as u16, entry.dpl())
132119
}
133120

134121
/// Loads the GDT in the CPU using the `lgdt` instruction. This does **not** alter any of the
@@ -187,7 +174,7 @@ impl GlobalDescriptorTable {
187174
///
188175
/// Segmentation is no longer supported in 64-bit mode, so most of the descriptor
189176
/// contents are ignored.
190-
#[derive(Debug, Clone)]
177+
#[derive(Debug, Clone, Copy)]
191178
pub enum Descriptor {
192179
/// Descriptor for a code or data segment.
193180
///
@@ -215,7 +202,8 @@ bitflags! {
215202
const EXECUTABLE = 1 << 43;
216203
/// This flag must be set for user segments (in contrast to system segments).
217204
const USER_SEGMENT = 1 << 44;
218-
/// The DPL for this descriptor is Ring 3. In 64-bit mode, ignored for data segments.
205+
/// These two bits encode the Descriptor Privilege Level (DPL) for this descriptor.
206+
/// If both bits are set, the DPL is Ring 3, if both are unset, the DPL is Ring 0.
219207
const DPL_RING_3 = 3 << 45;
220208
/// Must be set for any segment, causes a segment not present exception if not set.
221209
const PRESENT = 1 << 47;
@@ -283,6 +271,20 @@ impl DescriptorFlags {
283271
}
284272

285273
impl Descriptor {
274+
/// Returns the Descriptor Privilege Level (DPL). When using this descriptor
275+
/// via a [`SegmentSelector`], the RPL and Current Privilege Level (CPL)
276+
/// must less than or equal to the DPL, except for stack segments where the
277+
/// RPL, CPL, and DPL must all be equal.
278+
#[inline]
279+
pub const fn dpl(self) -> PrivilegeLevel {
280+
let value_low = match self {
281+
Descriptor::UserSegment(v) => v,
282+
Descriptor::SystemSegment(v, _) => v,
283+
};
284+
let dpl = (value_low & DescriptorFlags::DPL_RING_3.bits()) >> 45;
285+
PrivilegeLevel::from_u16(dpl as u16)
286+
}
287+
286288
/// Creates a segment descriptor for a 64-bit kernel code segment. Suitable
287289
/// for use with `syscall` or 64-bit `sysenter`.
288290
#[inline]
@@ -408,4 +410,18 @@ mod tests {
408410
// We have one free slot, but the GDT requires two
409411
gdt.add_entry(Descriptor::tss_segment(&TSS));
410412
}
413+
414+
#[test]
415+
pub fn descriptor_dpl() {
416+
assert_eq!(
417+
Descriptor::kernel_code_segment().dpl(),
418+
PrivilegeLevel::Ring0
419+
);
420+
assert_eq!(
421+
Descriptor::kernel_data_segment().dpl(),
422+
PrivilegeLevel::Ring0
423+
);
424+
assert_eq!(Descriptor::user_code_segment().dpl(), PrivilegeLevel::Ring3);
425+
assert_eq!(Descriptor::user_code_segment().dpl(), PrivilegeLevel::Ring3);
426+
}
411427
}

0 commit comments

Comments
 (0)