Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix load_tss and GlobalDescriptorTable #323

Merged
merged 7 commits into from
Nov 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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> {}

Freax13 marked this conversation as resolved.
Show resolved Hide resolved
// 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