Skip to content

Commit

Permalink
Merge pull request #323 from Freax13/mutable-gdt
Browse files Browse the repository at this point in the history
fix `load_tss` and `GlobalDescriptorTable` and add `SingleUseCell`
  • Loading branch information
Freax13 authored Nov 15, 2021
2 parents 9f2114c + 23cf4c5 commit 230c303
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 8 deletions.
5 changes: 4 additions & 1 deletion src/instructions/tables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ pub fn sidt() -> DescriptorTablePointer {

/// Load the task state register using the `ltr` instruction.
///
/// Loading the task state register changes the type of the entry
/// in the GDT from `Available 64-bit TSS` to `Busy 64-bit TSS`.
///
/// ## Safety
///
/// This function is unsafe because the caller must ensure that the given
Expand All @@ -89,7 +92,7 @@ pub fn sidt() -> DescriptorTablePointer {
#[inline]
pub unsafe fn load_tss(sel: SegmentSelector) {
#[cfg(feature = "inline_asm")]
asm!("ltr {0:x}", in(reg) sel.0, options(nomem, nostack, preserves_flags));
asm!("ltr {0:x}", in(reg) sel.0, options(nostack, preserves_flags));

#[cfg(not(feature = "inline_asm"))]
crate::asm::x86_64_asm_ltr(sel.0);
Expand Down
60 changes: 60 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
#![warn(missing_docs)]
#![deny(missing_debug_implementations)]

use core::cell::UnsafeCell;
use core::sync::atomic::{AtomicBool, Ordering};

pub use crate::addr::{align_down, align_up, PhysAddr, VirtAddr};

/// Makes a function const only when `feature = "const_fn"` is enabled.
Expand Down Expand Up @@ -112,3 +115,60 @@ 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<T>` is a safe alternative to `static mut` or a static `UnsafeCell<T>`.
#[derive(Debug)]
pub struct SingleUseCell<T> {
used: AtomicBool,
value: UnsafeCell<T>,
}

impl<T> SingleUseCell<T> {
/// 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<i32> = 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<T>` 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<T: Send> Sync for SingleUseCell<T> {}

// SAFETY: It's safe to send a `SingleUseCell<T>` to another thread if it's safe to send `T`.
unsafe impl<T: Send> Send for SingleUseCell<T> {}
4 changes: 2 additions & 2 deletions src/structures/gdt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ impl GlobalDescriptorTable {
/// [set_cs](crate::instructions::segmentation::set_cs).
#[cfg(feature = "instructions")]
#[inline]
pub fn load(&'static self) {
pub fn load(&'static mut self) {
// SAFETY: static lifetime ensures no modification after loading.
unsafe { self.load_unsafe() };
}
Expand All @@ -196,7 +196,7 @@ impl GlobalDescriptorTable {
///
#[cfg(feature = "instructions")]
#[inline]
pub unsafe fn load_unsafe(&self) {
pub unsafe fn load_unsafe(&mut self) {
use crate::instructions::tables::lgdt;
lgdt(&self.pointer());
}
Expand Down
10 changes: 5 additions & 5 deletions testing/src/gdt.rs
Original file line number Diff line number Diff line change
@@ -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::VirtAddr;
use x86_64::{SingleUseCell, VirtAddr};

pub const DOUBLE_FAULT_IST_INDEX: u16 = 0;

Expand All @@ -18,12 +18,12 @@ lazy_static! {
};
tss
};
static ref GDT: (GlobalDescriptorTable, Selectors) = {
static ref GDT: (SingleUseCell<GlobalDescriptorTable>, 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));
(
gdt,
SingleUseCell::new(gdt),
Selectors {
code_selector,
tss_selector,
Expand All @@ -38,10 +38,10 @@ struct Selectors {
}

pub fn init() {
use x86_64::instructions::segmentation::{CS, Segment};
use x86_64::instructions::segmentation::{Segment, CS};
use x86_64::instructions::tables::load_tss;

GDT.0.load();
GDT.0.try_get_mut().unwrap().load();
unsafe {
CS::set_reg(GDT.1.code_selector);
load_tss(GDT.1.tss_selector);
Expand Down

0 comments on commit 230c303

Please sign in to comment.