Skip to content

Commit ab8109e

Browse files
uefi: Use atomics instead of static mut in allocator
Change the allocator's `BOOT_SERVICES` static to a `SYSTEM_TABLE` containing an `AtomicPtr`. This completes the transition away from any uses of `static mut`.
1 parent 5b2bd72 commit ab8109e

File tree

3 files changed

+31
-26
lines changed

3 files changed

+31
-26
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
- `Logger` no longer requires exterior mutability. `Logger::new` is now `const`,
1515
takes no arguments, and creates the logger in a disabled state. Call
1616
`Logger::set_output` to enable it.
17+
- `uefi::allocator::init` now takes a `&mut SystemTable<Boot>` instead of
18+
`&BootServices`.
1719

1820
## uefi-macros - [Unreleased]
1921

uefi-services/src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,10 @@ pub fn init(st: &mut SystemTable<Boot>) -> Result<Option<Event>> {
101101
#[cfg(feature = "logger")]
102102
init_logger(st);
103103

104-
let boot_services = st.boot_services();
105-
uefi::allocator::init(boot_services);
104+
uefi::allocator::init(st);
106105

107106
// Schedule these tools to be disabled on exit from UEFI boot services
107+
let boot_services = st.boot_services();
108108
boot_services
109109
.create_event(
110110
EventType::SIGNAL_EXIT_BOOT_SERVICES,

uefi/src/allocator.rs

+27-24
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,22 @@
1212
//! Failure to do so will turn subsequent allocation into undefined behaviour.
1313
1414
use core::alloc::{GlobalAlloc, Layout};
15-
use core::ptr::{self, NonNull};
16-
use core::sync::atomic::{AtomicU32, Ordering};
15+
use core::ffi::c_void;
16+
use core::ptr;
17+
use core::sync::atomic::{AtomicPtr, AtomicU32, Ordering};
1718

1819
use crate::proto::loaded_image::LoadedImage;
1920
use crate::table::boot::{BootServices, MemoryType};
21+
use crate::table::{Boot, SystemTable};
2022

21-
/// Reference to the boot services table, used to call the pool memory allocation functions.
23+
/// Reference to the system table, used to call the boot services pool memory
24+
/// allocation functions.
2225
///
23-
/// The inner pointer is only safe to dereference if UEFI boot services have not been
26+
/// The pointer is only safe to dereference if UEFI boot services have not been
2427
/// exited by the host application yet.
25-
static mut BOOT_SERVICES: Option<NonNull<BootServices>> = None;
28+
static SYSTEM_TABLE: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut());
2629

2730
/// The memory type used for pool memory allocations.
28-
/// TODO: Use OnceCell when stablilized.
2931
static MEMORY_TYPE: AtomicU32 = AtomicU32::new(MemoryType::LOADER_DATA.0);
3032

3133
/// Initializes the allocator.
@@ -34,9 +36,10 @@ static MEMORY_TYPE: AtomicU32 = AtomicU32::new(MemoryType::LOADER_DATA.0);
3436
///
3537
/// This function is unsafe because you _must_ make sure that exit_boot_services
3638
/// will be called when UEFI boot services will be exited.
37-
pub unsafe fn init(boot_services: &BootServices) {
38-
BOOT_SERVICES = NonNull::new(boot_services as *const _ as *mut _);
39+
pub unsafe fn init(system_table: &mut SystemTable<Boot>) {
40+
SYSTEM_TABLE.store(system_table.as_ptr().cast_mut(), Ordering::Release);
3941

42+
let boot_services = system_table.boot_services();
4043
if let Ok(loaded_image) =
4144
boot_services.open_protocol_exclusive::<LoadedImage>(boot_services.image_handle())
4245
{
@@ -45,17 +48,18 @@ pub unsafe fn init(boot_services: &BootServices) {
4548
}
4649

4750
/// Access the boot services
48-
fn boot_services() -> NonNull<BootServices> {
49-
unsafe { BOOT_SERVICES.expect("Boot services are unavailable or have been exited") }
51+
fn boot_services() -> *const BootServices {
52+
let ptr = SYSTEM_TABLE.load(Ordering::Acquire);
53+
let system_table =
54+
unsafe { SystemTable::from_ptr(ptr) }.expect("The system table handle is not available");
55+
system_table.boot_services()
5056
}
5157

5258
/// Notify the allocator library that boot services are not safe to call anymore
5359
///
5460
/// You must arrange for this function to be called on exit from UEFI boot services
5561
pub fn exit_boot_services() {
56-
unsafe {
57-
BOOT_SERVICES = None;
58-
}
62+
SYSTEM_TABLE.store(ptr::null_mut(), Ordering::Release);
5963
}
6064

6165
/// Allocator which uses the UEFI pool allocation functions.
@@ -73,19 +77,19 @@ unsafe impl GlobalAlloc for Allocator {
7377
let align = layout.align();
7478
let memory_type = MemoryType(MEMORY_TYPE.load(Ordering::Acquire));
7579

80+
let boot_services = &*boot_services();
81+
7682
if align > 8 {
7783
// The requested alignment is greater than 8, but `allocate_pool` is
7884
// only guaranteed to provide eight-byte alignment. Allocate extra
7985
// space so that we can return an appropriately-aligned pointer
8086
// within the allocation.
81-
let full_alloc_ptr = if let Ok(ptr) = boot_services()
82-
.as_ref()
83-
.allocate_pool(memory_type, size + align)
84-
{
85-
ptr
86-
} else {
87-
return ptr::null_mut();
88-
};
87+
let full_alloc_ptr =
88+
if let Ok(ptr) = boot_services.allocate_pool(memory_type, size + align) {
89+
ptr
90+
} else {
91+
return ptr::null_mut();
92+
};
8993

9094
// Calculate the offset needed to get an aligned pointer within the
9195
// full allocation. If that offset is zero, increase it to `align`
@@ -110,8 +114,7 @@ unsafe impl GlobalAlloc for Allocator {
110114
// The requested alignment is less than or equal to eight, and
111115
// `allocate_pool` always provides eight-byte alignment, so we can
112116
// use `allocate_pool` directly.
113-
boot_services()
114-
.as_ref()
117+
boot_services
115118
.allocate_pool(memory_type, size)
116119
.unwrap_or(ptr::null_mut())
117120
}
@@ -124,7 +127,7 @@ unsafe impl GlobalAlloc for Allocator {
124127
// before the aligned allocation in `alloc`.
125128
ptr = (ptr as *const *mut u8).sub(1).read();
126129
}
127-
boot_services().as_ref().free_pool(ptr).unwrap();
130+
(*boot_services()).free_pool(ptr).unwrap();
128131
}
129132
}
130133

0 commit comments

Comments
 (0)