From 48dbe8f254dd59f603af8382e0e09f971514d106 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Thu, 14 Apr 2022 22:37:01 -0700 Subject: [PATCH 1/9] 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 --- src/lib.rs | 2 +- src/structures/gdt.rs | 98 +++++++++++++++++++++++++++++-------------- testing/src/gdt.rs | 4 +- 3 files changed, 70 insertions(+), 34 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6c32eebd6..2319dacfa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ //! and access to various system registers. #![cfg_attr(not(test), no_std)] -#![cfg_attr(feature = "const_fn", feature(const_mut_refs))] // GDT add_entry() +#![cfg_attr(feature = "const_fn", feature(const_mut_refs))] // GDT::append() #![cfg_attr(feature = "asm_const", feature(asm_const))] #![cfg_attr(feature = "abi_x86_interrupt", feature(abi_x86_interrupt))] #![cfg_attr(feature = "step_trait", feature(step_trait))] diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index bcf65d388..3225e273b 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -9,18 +9,42 @@ use bitflags::bitflags; #[cfg(doc)] use crate::registers::segmentation::{Segment, CS, SS}; +/// 8-byte entry in a descriptor table. +/// +/// A [`GlobalDescriptorTable`] (or LDT) is an array of these entries, and +/// [`SegmentSelector`]s index into this array. Each [`Descriptor`] in the table +/// uses either 1 Entry (if it is a [`UserSegment`](Descriptor::UserSegment)) or +/// 2 Entries (if it is a [`SystemSegment`](Descriptor::SystemSegment)). This +/// type exists to give users access to the raw entry bits in a GDT. +#[derive(Clone, Debug)] +#[repr(transparent)] +pub struct Entry(u64); + +impl Entry { + // Create a new Entry from a raw value. + const fn new(raw: u64) -> Self { + Self(raw) + } + + /// The raw bits for this entry. Depending on the [`Descriptor`] type, these + /// bits may correspond to those in [`DescriptorFlags`]. + pub fn raw(&self) -> u64 { + self.0 + } +} + /// A 64-bit mode global descriptor table (GDT). /// /// In 64-bit mode, segmentation is not supported. The GDT is used nonetheless, for example for /// switching between user and kernel mode or for loading a TSS. /// /// The GDT has a fixed maximum size given by the `MAX` const generic parameter. -/// Trying to add more entries than this maximum via [`GlobalDescriptorTable::add_entry`] -/// will panic. +/// Overflowing this limit by adding too many [`Descriptor`]s via +/// [`GlobalDescriptorTable::append`] will panic. /// /// You do **not** need to add a null segment descriptor yourself - this is already done -/// internally. This means you can add up to `MAX - 1` additional [`Descriptor`]s to -/// this table. +/// internally. This means you can add up to `MAX - 1` additional [`Entry`]s to +/// this table. Note that some [`Descriptor`]s may take up 2 [`Entry`]s. /// /// Data segment registers in ring 0 can be loaded with the null segment selector. When running in /// 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}; /// use x86_64::structures::gdt::{GlobalDescriptorTable, Descriptor}; /// /// let mut gdt = GlobalDescriptorTable::new(); -/// gdt.add_entry(Descriptor::kernel_code_segment()); -/// gdt.add_entry(Descriptor::user_code_segment()); -/// gdt.add_entry(Descriptor::user_data_segment()); +/// gdt.append(Descriptor::kernel_code_segment()); +/// gdt.append(Descriptor::user_code_segment()); +/// gdt.append(Descriptor::user_data_segment()); /// /// // Add entry for TSS, call gdt.load() then update segment registers /// ``` #[derive(Debug, Clone)] pub struct GlobalDescriptorTable { - table: [u64; MAX], + table: [Entry; MAX], len: usize, } @@ -61,28 +85,29 @@ impl GlobalDescriptorTable { } impl GlobalDescriptorTable { - /// Creates an empty GDT which can hold `MAX` number of [`Descriptor`]s. + /// Creates an empty GDT which can hold `MAX` number of [`Entry`]s. #[inline] pub const fn empty() -> Self { // TODO: Replace with compiler error when feature(generic_const_exprs) is stable. assert!(MAX > 0, "A GDT cannot have 0 entries"); assert!(MAX <= (1 << 13), "A GDT can only have at most 2^13 entries"); + const NULL: Entry = Entry::new(0); Self { - table: [0; MAX], + table: [NULL; MAX], len: 1, } } - /// Forms a GDT from a slice of `u64`. + /// Forms a GDT from a slice of raw [`Entry`] values. /// /// # Safety /// /// * The user must make sure that the entries are well formed /// * Panics if the provided slice has more than `MAX` entries #[inline] - pub const unsafe fn from_raw_slice(slice: &[u64]) -> Self { + pub const unsafe fn from_raw_entries(slice: &[u64]) -> Self { let len = slice.len(); - let mut table = [0; MAX]; + let mut table = Self::empty().table; let mut idx = 0; assert!( @@ -91,27 +116,30 @@ impl GlobalDescriptorTable { ); while idx < len { - table[idx] = slice[idx]; + table[idx] = Entry::new(slice[idx]); idx += 1; } Self { table, len } } - /// Get a reference to the internal table. + /// Get a reference to the internal [`Entry`] table. /// - /// The resulting slice may contain system descriptors, which span two `u64`s. + /// The resulting slice may contain system descriptors, which span two [`Entry`]s. #[inline] - pub fn as_raw_slice(&self) -> &[u64] { + pub fn entries(&self) -> &[Entry] { &self.table[..self.len] } /// Adds the given segment descriptor to the GDT, returning the segment selector. /// - /// Panics if the GDT doesn't have enough free entries to hold the Descriptor. + /// Note that depending on the type of the [`Descriptor`] this may add either + /// one or two new entries. + /// + /// Panics if the GDT doesn't have enough free entries. #[inline] #[cfg_attr(feature = "const_fn", rustversion::attr(all(), const))] - pub fn add_entry(&mut self, entry: Descriptor) -> SegmentSelector { + pub fn append(&mut self, entry: Descriptor) -> SegmentSelector { let index = match entry { Descriptor::UserSegment(value) => { if self.len > self.table.len().saturating_sub(1) { @@ -179,7 +207,7 @@ impl GlobalDescriptorTable { #[cfg_attr(feature = "const_fn", rustversion::attr(all(), const))] fn push(&mut self, value: u64) -> usize { let index = self.len; - self.table[index] = value; + self.table[index] = Entry::new(value); self.len += 1; index } @@ -378,11 +406,11 @@ mod tests { // Makes a GDT that has two free slots fn make_six_entry_gdt() -> GlobalDescriptorTable { let mut gdt = GlobalDescriptorTable::new(); - gdt.add_entry(Descriptor::kernel_code_segment()); - gdt.add_entry(Descriptor::kernel_data_segment()); - gdt.add_entry(Descriptor::UserSegment(DescriptorFlags::USER_CODE32.bits())); - gdt.add_entry(Descriptor::user_data_segment()); - gdt.add_entry(Descriptor::user_code_segment()); + gdt.append(Descriptor::kernel_code_segment()); + gdt.append(Descriptor::kernel_data_segment()); + gdt.append(Descriptor::UserSegment(DescriptorFlags::USER_CODE32.bits())); + gdt.append(Descriptor::user_data_segment()); + gdt.append(Descriptor::user_code_segment()); assert_eq!(gdt.len, 6); gdt } @@ -391,7 +419,7 @@ mod tests { fn make_full_gdt() -> GlobalDescriptorTable { let mut gdt = make_six_entry_gdt(); - gdt.add_entry(Descriptor::tss_segment(&TSS)); + gdt.append(Descriptor::tss_segment(&TSS)); assert_eq!(gdt.len, 8); gdt } @@ -400,9 +428,9 @@ mod tests { pub fn push_max_segments() { // Make sure we don't panic with user segments let mut gdt = make_six_entry_gdt(); - gdt.add_entry(Descriptor::user_data_segment()); + gdt.append(Descriptor::user_data_segment()); assert_eq!(gdt.len, 7); - gdt.add_entry(Descriptor::user_data_segment()); + gdt.append(Descriptor::user_data_segment()); assert_eq!(gdt.len, 8); // Make sure we don't panic with system segments let _ = make_full_gdt(); @@ -412,15 +440,23 @@ mod tests { #[should_panic] pub fn panic_user_segment() { let mut gdt = make_full_gdt(); - gdt.add_entry(Descriptor::user_data_segment()); + gdt.append(Descriptor::user_data_segment()); } #[test] #[should_panic] pub fn panic_system_segment() { let mut gdt = make_six_entry_gdt(); - gdt.add_entry(Descriptor::user_data_segment()); + gdt.append(Descriptor::user_data_segment()); // We have one free slot, but the GDT requires two - gdt.add_entry(Descriptor::tss_segment(&TSS)); + gdt.append(Descriptor::tss_segment(&TSS)); + } + + #[test] + pub fn from_entries() { + let raw = [0, Flags::KERNEL_CODE64.bits(), Flags::KERNEL_DATA.bits()]; + let gdt = unsafe { GlobalDescriptorTable::<3>::from_raw_entries(&raw) }; + assert_eq!(gdt.table.len(), 3); + assert_eq!(gdt.entries().len(), 3); } } diff --git a/testing/src/gdt.rs b/testing/src/gdt.rs index 14fc74023..f4d2643c7 100644 --- a/testing/src/gdt.rs +++ b/testing/src/gdt.rs @@ -20,8 +20,8 @@ lazy_static! { }; static ref GDT: (SingleUseCell, Selectors) = { let mut gdt = GlobalDescriptorTable::new(); - let code_selector = gdt.add_entry(Descriptor::kernel_code_segment()); - let tss_selector = gdt.add_entry(Descriptor::tss_segment(&TSS)); + let code_selector = gdt.append(Descriptor::kernel_code_segment()); + let tss_selector = gdt.append(Descriptor::tss_segment(&TSS)); ( SingleUseCell::new(gdt), Selectors { From 255124a08352bde1433b59e97ad6c96df74d918f Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Fri, 15 Apr 2022 02:33:53 -0700 Subject: [PATCH 2/9] Add PartialEq, Eq, and Debug impls Signed-off-by: Joe Richey --- src/structures/gdt.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index 3225e273b..6ffacc84e 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -5,6 +5,7 @@ use crate::structures::tss::TaskStateSegment; use crate::PrivilegeLevel; use bit_field::BitField; use bitflags::bitflags; +use core::fmt; // imports for intra-doc links #[cfg(doc)] use crate::registers::segmentation::{Segment, CS, SS}; @@ -16,7 +17,7 @@ use crate::registers::segmentation::{Segment, CS, SS}; /// uses either 1 Entry (if it is a [`UserSegment`](Descriptor::UserSegment)) or /// 2 Entries (if it is a [`SystemSegment`](Descriptor::SystemSegment)). This /// type exists to give users access to the raw entry bits in a GDT. -#[derive(Clone, Debug)] +#[derive(Clone, PartialEq, Eq)] #[repr(transparent)] pub struct Entry(u64); @@ -33,6 +34,13 @@ impl Entry { } } +impl fmt::Debug for Entry { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Display inner value as hex + write!(f, "Entry({:#018x})", self.raw()) + } +} + /// A 64-bit mode global descriptor table (GDT). /// /// In 64-bit mode, segmentation is not supported. The GDT is used nonetheless, for example for From 07f7ebf44fea0b27b1d9a9b29272fbc188fa5cbf Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Fri, 15 Apr 2022 02:49:38 -0700 Subject: [PATCH 3/9] Make from_raw_entries safe Also update the documentation Signed-off-by: Joe Richey --- src/structures/gdt.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index 6ffacc84e..d3ef086e0 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -106,18 +106,26 @@ impl GlobalDescriptorTable { } } - /// Forms a GDT from a slice of raw [`Entry`] values. + /// Forms a GDT from a slice of `u64`. /// - /// # Safety + /// This method allows for creation of a GDT with malformed or invalid + /// entries. However, it is safe because loading a GDT with invalid + /// entires doesn't do anything until those entries are used. For example, + /// [`CS::set_reg`] and [`load_tss`](crate::instructions::tables::load_tss) + /// are both unsafe for this reason. /// - /// * The user must make sure that the entries are well formed - /// * Panics if the provided slice has more than `MAX` entries + /// Panics if: + /// * the provided slice has more than `MAX` entries + /// * the provided slice is empty + /// * the first entry is not zero #[inline] - pub const unsafe fn from_raw_entries(slice: &[u64]) -> Self { + pub const fn from_raw_entries(slice: &[u64]) -> Self { let len = slice.len(); let mut table = Self::empty().table; let mut idx = 0; + assert!(len > 0, "cannot initialize GDT with empty slice"); + assert!(slice[0] == 0, "first GDT entry must be zero"); assert!( len <= MAX, "cannot initialize GDT with slice exceeding the maximum length" @@ -463,7 +471,7 @@ mod tests { #[test] pub fn from_entries() { let raw = [0, Flags::KERNEL_CODE64.bits(), Flags::KERNEL_DATA.bits()]; - let gdt = unsafe { GlobalDescriptorTable::<3>::from_raw_entries(&raw) }; + let gdt = GlobalDescriptorTable::<3>::from_raw_entries(&raw); assert_eq!(gdt.table.len(), 3); assert_eq!(gdt.entries().len(), 3); } From 49b5295aacca3e9d039107ba0d76816f5cc0ce60 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Fri, 15 Apr 2022 11:27:52 -0700 Subject: [PATCH 4/9] Fix nits/warnings in docs Signed-off-by: Joe Richey --- src/structures/gdt.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index d3ef086e0..c81d58137 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -118,6 +118,7 @@ impl GlobalDescriptorTable { /// * the provided slice has more than `MAX` entries /// * the provided slice is empty /// * the first entry is not zero + #[cfg_attr(not(feature = "instructions"), allow(rustdoc::broken_intra_doc_links))] #[inline] pub const fn from_raw_entries(slice: &[u64]) -> Self { let len = slice.len(); @@ -147,10 +148,10 @@ impl GlobalDescriptorTable { &self.table[..self.len] } - /// Adds the given segment descriptor to the GDT, returning the segment selector. + /// Appends the given segment descriptor to the GDT, returning the segment selector. /// - /// Note that depending on the type of the [`Descriptor`] this may add either - /// one or two new entries. + /// Note that depending on the type of the [`Descriptor`] this may append + /// either one or two new [`Entry`]s to the table. /// /// Panics if the GDT doesn't have enough free entries. #[inline] From b61f341d92e5cee6faa6cc27df45df8cf1a70fdb Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Thu, 14 Apr 2022 23:43:22 -0700 Subject: [PATCH 5/9] Use AtomicU64 for Entry type This gives our GDT the appropriate atomic interior mutablity if we could potentially call `lgdt` and `ltr`. Signed-off-by: Joe Richey --- src/structures/gdt.rs | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index c81d58137..d3cb597da 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -10,6 +10,11 @@ use core::fmt; #[cfg(doc)] use crate::registers::segmentation::{Segment, CS, SS}; +#[cfg(feature = "instructions")] +use core::sync::atomic::{AtomicU64 as EntryValue, Ordering}; +#[cfg(not(feature = "instructions"))] +use u64 as EntryValue; + /// 8-byte entry in a descriptor table. /// /// A [`GlobalDescriptorTable`] (or LDT) is an array of these entries, and @@ -17,23 +22,43 @@ use crate::registers::segmentation::{Segment, CS, SS}; /// uses either 1 Entry (if it is a [`UserSegment`](Descriptor::UserSegment)) or /// 2 Entries (if it is a [`SystemSegment`](Descriptor::SystemSegment)). This /// type exists to give users access to the raw entry bits in a GDT. -#[derive(Clone, PartialEq, Eq)] #[repr(transparent)] -pub struct Entry(u64); +pub struct Entry(EntryValue); impl Entry { // Create a new Entry from a raw value. const fn new(raw: u64) -> Self { + #[cfg(feature = "instructions")] + let raw = EntryValue::new(raw); Self(raw) } /// The raw bits for this entry. Depending on the [`Descriptor`] type, these /// bits may correspond to those in [`DescriptorFlags`]. pub fn raw(&self) -> u64 { - self.0 + // TODO: Make this const fn when AtomicU64::load is const. + #[cfg(feature = "instructions")] + let raw = self.0.load(Ordering::SeqCst); + #[cfg(not(feature = "instructions"))] + let raw = self.0; + raw + } +} + +impl Clone for Entry { + fn clone(&self) -> Self { + Self::new(self.raw()) } } +impl PartialEq for Entry { + fn eq(&self, other: &Self) -> bool { + self.raw() == other.raw() + } +} + +impl Eq for Entry {} + impl fmt::Debug for Entry { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // Display inner value as hex From 8792dc9d464a630f410060854401213d4883c531 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Fri, 15 Apr 2022 00:51:52 -0700 Subject: [PATCH 6/9] Fix Clippy warning about const item used to construct array Signed-off-by: Joe Richey --- src/structures/gdt.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index d3cb597da..a61252f23 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -124,6 +124,9 @@ impl GlobalDescriptorTable { // TODO: Replace with compiler error when feature(generic_const_exprs) is stable. assert!(MAX > 0, "A GDT cannot have 0 entries"); assert!(MAX <= (1 << 13), "A GDT can only have at most 2^13 entries"); + + // TODO: Replace with inline_const when it's stable. + #[allow(clippy::declare_interior_mutable_const)] const NULL: Entry = Entry::new(0); Self { table: [NULL; MAX], From 055d014edf3cd1446bfeb7abffb57fc6d8934c2b Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Thu, 14 Apr 2022 23:48:23 -0700 Subject: [PATCH 7/9] Allow GDT to be loaded with shared reference Now that the `Entry` type has the appropriate interior mutability, we can safely load a GDT using `&'static self`. Signed-off-by: Joe Richey --- src/structures/gdt.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index a61252f23..cc937a78e 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -223,7 +223,7 @@ impl GlobalDescriptorTable { /// [`SS::set_reg()`] and [`CS::set_reg()`]. #[cfg(feature = "instructions")] #[inline] - pub fn load(&'static mut self) { + pub fn load(&'static self) { // SAFETY: static lifetime ensures no modification after loading. unsafe { self.load_unsafe() }; } @@ -241,7 +241,7 @@ impl GlobalDescriptorTable { /// #[cfg(feature = "instructions")] #[inline] - pub unsafe fn load_unsafe(&mut self) { + pub unsafe fn load_unsafe(&self) { use crate::instructions::tables::lgdt; unsafe { lgdt(&self.pointer()); From b2fadb8430f27fabd496d40a25b3c9597ec826dd Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Thu, 14 Apr 2022 23:49:46 -0700 Subject: [PATCH 8/9] Remove SignleUseCell It's no longer used, so we don't need it anymore. Signed-off-by: Joe Richey --- src/lib.rs | 60 ---------------------------------------------- testing/src/gdt.rs | 8 +++---- 2 files changed, 4 insertions(+), 64 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2319dacfa..1c0f74d76 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,9 +11,6 @@ #![deny(missing_debug_implementations)] #![deny(unsafe_op_in_unsafe_fn)] -use core::cell::UnsafeCell; -use core::sync::atomic::{AtomicBool, Ordering}; - pub use crate::addr::{align_down, align_up, PhysAddr, VirtAddr}; pub mod addr; @@ -66,60 +63,3 @@ impl PrivilegeLevel { } } } - -/// A wrapper that can be used to safely create one mutable reference `&'static mut T` from a static variable. -/// -/// `SingleUseCell` is safe because it ensures that it only ever gives out one reference. -/// -/// ``SingleUseCell` is a safe alternative to `static mut` or a static `UnsafeCell`. -#[derive(Debug)] -pub struct SingleUseCell { - used: AtomicBool, - value: UnsafeCell, -} - -impl SingleUseCell { - /// Construct a new SingleUseCell. - pub const fn new(value: T) -> Self { - Self { - used: AtomicBool::new(false), - value: UnsafeCell::new(value), - } - } - - /// Try to acquire a mutable reference to the wrapped value. - /// This will only succeed the first time the function is - /// called and fail on all following calls. - /// - /// ``` - /// use x86_64::SingleUseCell; - /// - /// static FOO: SingleUseCell = SingleUseCell::new(0); - /// - /// // Call `try_get_mut` for the first time and get a reference. - /// let first: &'static mut i32 = FOO.try_get_mut().unwrap(); - /// assert_eq!(first, &0); - /// - /// // Calling `try_get_mut` again will return `None`. - /// assert_eq!(FOO.try_get_mut(), None); - /// ``` - pub fn try_get_mut(&self) -> Option<&mut T> { - let already_used = self.used.swap(true, Ordering::AcqRel); - if already_used { - None - } else { - Some(unsafe { - // SAFETY: no reference has been given out yet and we won't give out another. - &mut *self.value.get() - }) - } - } -} - -// SAFETY: Sharing a `SingleUseCell` between threads is safe regardless of whether `T` is `Sync` -// because we only expose the inner value once to one thread. The `T: Send` bound makes sure that -// sending a unique reference to another thread is safe. -unsafe impl Sync for SingleUseCell {} - -// SAFETY: It's safe to send a `SingleUseCell` to another thread if it's safe to send `T`. -unsafe impl Send for SingleUseCell {} diff --git a/testing/src/gdt.rs b/testing/src/gdt.rs index f4d2643c7..2e8440642 100644 --- a/testing/src/gdt.rs +++ b/testing/src/gdt.rs @@ -1,7 +1,7 @@ use lazy_static::lazy_static; use x86_64::structures::gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector}; use x86_64::structures::tss::TaskStateSegment; -use x86_64::{SingleUseCell, VirtAddr}; +use x86_64::VirtAddr; pub const DOUBLE_FAULT_IST_INDEX: u16 = 0; @@ -18,12 +18,12 @@ lazy_static! { }; tss }; - static ref GDT: (SingleUseCell, Selectors) = { + static ref GDT: (GlobalDescriptorTable, Selectors) = { let mut gdt = GlobalDescriptorTable::new(); let code_selector = gdt.append(Descriptor::kernel_code_segment()); let tss_selector = gdt.append(Descriptor::tss_segment(&TSS)); ( - SingleUseCell::new(gdt), + gdt, Selectors { code_selector, tss_selector, @@ -41,7 +41,7 @@ pub fn init() { use x86_64::instructions::segmentation::{Segment, CS}; use x86_64::instructions::tables::load_tss; - GDT.0.try_get_mut().unwrap().load(); + GDT.0.load(); unsafe { CS::set_reg(GDT.1.code_selector); load_tss(GDT.1.tss_selector); From 8bb01e87887476f59b652f6d03b19f2002fcb341 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Fri, 15 Apr 2022 00:53:09 -0700 Subject: [PATCH 9/9] Add additional tests for TSS entry Signed-off-by: Joe Richey --- testing/src/gdt.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/testing/src/gdt.rs b/testing/src/gdt.rs index 2e8440642..28024da9e 100644 --- a/testing/src/gdt.rs +++ b/testing/src/gdt.rs @@ -20,6 +20,8 @@ lazy_static! { }; static ref GDT: (GlobalDescriptorTable, Selectors) = { let mut gdt = GlobalDescriptorTable::new(); + // Add an unused segment so we get a different value for CS + gdt.append(Descriptor::kernel_data_segment()); let code_selector = gdt.append(Descriptor::kernel_code_segment()); let tss_selector = gdt.append(Descriptor::tss_segment(&TSS)); ( @@ -41,9 +43,16 @@ pub fn init() { use x86_64::instructions::segmentation::{Segment, CS}; use x86_64::instructions::tables::load_tss; + // Make sure loading CS actually changes the value GDT.0.load(); - unsafe { - CS::set_reg(GDT.1.code_selector); - load_tss(GDT.1.tss_selector); - } + assert_ne!(CS::get_reg(), GDT.1.code_selector); + unsafe { CS::set_reg(GDT.1.code_selector) }; + assert_eq!(CS::get_reg(), GDT.1.code_selector); + + // Loading the TSS should mark the GDT entry as busy + let tss_idx: usize = GDT.1.tss_selector.index().into(); + let old_tss_entry = GDT.0.entries()[tss_idx].clone(); + unsafe { load_tss(GDT.1.tss_selector) }; + let new_tss_entry = GDT.0.entries()[tss_idx].clone(); + assert_ne!(old_tss_entry, new_tss_entry); }