Skip to content

Commit 230c303

Browse files
authored
Merge pull request #323 from Freax13/mutable-gdt
fix `load_tss` and `GlobalDescriptorTable` and add `SingleUseCell`
2 parents 9f2114c + 23cf4c5 commit 230c303

File tree

4 files changed

+71
-8
lines changed

4 files changed

+71
-8
lines changed

src/instructions/tables.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ pub fn sidt() -> DescriptorTablePointer {
8181

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

9497
#[cfg(not(feature = "inline_asm"))]
9598
crate::asm::x86_64_asm_ltr(sel.0);

src/lib.rs

+60
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
#![warn(missing_docs)]
1414
#![deny(missing_debug_implementations)]
1515

16+
use core::cell::UnsafeCell;
17+
use core::sync::atomic::{AtomicBool, Ordering};
18+
1619
pub use crate::addr::{align_down, align_up, PhysAddr, VirtAddr};
1720

1821
/// Makes a function const only when `feature = "const_fn"` is enabled.
@@ -112,3 +115,60 @@ impl PrivilegeLevel {
112115
}
113116
}
114117
}
118+
119+
/// A wrapper that can be used to safely create one mutable reference `&'static mut T` from a static variable.
120+
///
121+
/// `SingleUseCell` is safe because it ensures that it only ever gives out one reference.
122+
///
123+
/// ``SingleUseCell<T>` is a safe alternative to `static mut` or a static `UnsafeCell<T>`.
124+
#[derive(Debug)]
125+
pub struct SingleUseCell<T> {
126+
used: AtomicBool,
127+
value: UnsafeCell<T>,
128+
}
129+
130+
impl<T> SingleUseCell<T> {
131+
/// Construct a new SingleUseCell.
132+
pub const fn new(value: T) -> Self {
133+
Self {
134+
used: AtomicBool::new(false),
135+
value: UnsafeCell::new(value),
136+
}
137+
}
138+
139+
/// Try to acquire a mutable reference to the wrapped value.
140+
/// This will only succeed the first time the function is
141+
/// called and fail on all following calls.
142+
///
143+
/// ```
144+
/// use x86_64::SingleUseCell;
145+
///
146+
/// static FOO: SingleUseCell<i32> = SingleUseCell::new(0);
147+
///
148+
/// // Call `try_get_mut` for the first time and get a reference.
149+
/// let first: &'static mut i32 = FOO.try_get_mut().unwrap();
150+
/// assert_eq!(first, &0);
151+
///
152+
/// // Calling `try_get_mut` again will return `None`.
153+
/// assert_eq!(FOO.try_get_mut(), None);
154+
/// ```
155+
pub fn try_get_mut(&self) -> Option<&mut T> {
156+
let already_used = self.used.swap(true, Ordering::AcqRel);
157+
if already_used {
158+
None
159+
} else {
160+
Some(unsafe {
161+
// SAFETY: no reference has been given out yet and we won't give out another.
162+
&mut *self.value.get()
163+
})
164+
}
165+
}
166+
}
167+
168+
// SAFETY: Sharing a `SingleUseCell<T>` between threads is safe regardless of whether `T` is `Sync`
169+
// because we only expose the inner value once to one thread. The `T: Send` bound makes sure that
170+
// sending a unique reference to another thread is safe.
171+
unsafe impl<T: Send> Sync for SingleUseCell<T> {}
172+
173+
// SAFETY: It's safe to send a `SingleUseCell<T>` to another thread if it's safe to send `T`.
174+
unsafe impl<T: Send> Send for SingleUseCell<T> {}

src/structures/gdt.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ impl GlobalDescriptorTable {
177177
/// [set_cs](crate::instructions::segmentation::set_cs).
178178
#[cfg(feature = "instructions")]
179179
#[inline]
180-
pub fn load(&'static self) {
180+
pub fn load(&'static mut self) {
181181
// SAFETY: static lifetime ensures no modification after loading.
182182
unsafe { self.load_unsafe() };
183183
}
@@ -196,7 +196,7 @@ impl GlobalDescriptorTable {
196196
///
197197
#[cfg(feature = "instructions")]
198198
#[inline]
199-
pub unsafe fn load_unsafe(&self) {
199+
pub unsafe fn load_unsafe(&mut self) {
200200
use crate::instructions::tables::lgdt;
201201
lgdt(&self.pointer());
202202
}

testing/src/gdt.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use lazy_static::lazy_static;
22
use x86_64::structures::gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector};
33
use x86_64::structures::tss::TaskStateSegment;
4-
use x86_64::VirtAddr;
4+
use x86_64::{SingleUseCell, VirtAddr};
55

66
pub const DOUBLE_FAULT_IST_INDEX: u16 = 0;
77

@@ -18,12 +18,12 @@ lazy_static! {
1818
};
1919
tss
2020
};
21-
static ref GDT: (GlobalDescriptorTable, Selectors) = {
21+
static ref GDT: (SingleUseCell<GlobalDescriptorTable>, Selectors) = {
2222
let mut gdt = GlobalDescriptorTable::new();
2323
let code_selector = gdt.add_entry(Descriptor::kernel_code_segment());
2424
let tss_selector = gdt.add_entry(Descriptor::tss_segment(&TSS));
2525
(
26-
gdt,
26+
SingleUseCell::new(gdt),
2727
Selectors {
2828
code_selector,
2929
tss_selector,
@@ -38,10 +38,10 @@ struct Selectors {
3838
}
3939

4040
pub fn init() {
41-
use x86_64::instructions::segmentation::{CS, Segment};
41+
use x86_64::instructions::segmentation::{Segment, CS};
4242
use x86_64::instructions::tables::load_tss;
4343

44-
GDT.0.load();
44+
GDT.0.try_get_mut().unwrap().load();
4545
unsafe {
4646
CS::set_reg(GDT.1.code_selector);
4747
load_tss(GDT.1.tss_selector);

0 commit comments

Comments
 (0)