Skip to content

Commit 03dc5e9

Browse files
Merge pull request #1343 from nicholasbishop/bishop-update-alloc-impl-2
Update the uefi::allocator module to use the global system table
2 parents 32b04a5 + 85667fb commit 03dc5e9

File tree

4 files changed

+71
-73
lines changed

4 files changed

+71
-73
lines changed

uefi/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ how to integrate the `uefi` crate into them.
1212
take a `BootServices` argument. The global system table is used instead.
1313
- **Breaking:** `GraphicsOutput::modes` no longer takes a `BootServices`
1414
argument. The global system table is used instead.
15+
- `allocator::init` and `allocator::exit_boot_services` have been
16+
deprecated. These functions are now no-ops. The allocator now internally uses
17+
the global system table.
1518

1619

1720
# uefi - 0.31.0 (2024-08-21)

uefi/src/allocator.rs

Lines changed: 54 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -3,94 +3,84 @@
33
//! If the `global_allocator` feature is enabled, the [`Allocator`] will be used
44
//! as the global Rust allocator.
55
//!
6-
//! # Usage
7-
//!
8-
//! Call the `init` function with a reference to the boot services table.
9-
//! Failure to do so before calling a memory allocating function will panic.
10-
//!
11-
//! Call the `exit_boot_services` function before exiting UEFI boot services.
12-
//! Failure to do so will turn subsequent allocation into undefined behaviour.
6+
//! This allocator can only be used while boot services are active. If boot
7+
//! services are not active, `alloc` will return a null pointer, and `dealloc`
8+
//! will panic.
139
1410
use core::alloc::{GlobalAlloc, Layout};
15-
use core::ffi::c_void;
16-
use core::ptr;
17-
use core::sync::atomic::{AtomicPtr, AtomicU32, Ordering};
11+
use core::ptr::{self, NonNull};
12+
use core::sync::atomic::{AtomicU32, Ordering};
1813

14+
use crate::boot;
1915
use crate::mem::memory_map::MemoryType;
2016
use crate::proto::loaded_image::LoadedImage;
21-
use crate::table::boot::BootServices;
2217
use crate::table::{Boot, SystemTable};
2318

24-
/// Reference to the system table, used to call the boot services pool memory
25-
/// allocation functions.
26-
///
27-
/// The pointer is only safe to dereference if UEFI boot services have not been
28-
/// exited by the host application yet.
29-
static SYSTEM_TABLE: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut());
19+
/// Deprecated; this function is now a no-op.
20+
#[deprecated = "this function is now a no-op"]
21+
#[allow(unused_unsafe, clippy::missing_safety_doc)]
22+
pub unsafe fn init(_: &mut SystemTable<Boot>) {}
3023

31-
/// The memory type used for pool memory allocations.
32-
static MEMORY_TYPE: AtomicU32 = AtomicU32::new(MemoryType::LOADER_DATA.0);
24+
/// Deprecated; this function is now a no-op.
25+
#[deprecated = "this function is now a no-op"]
26+
#[allow(clippy::missing_const_for_fn)]
27+
pub fn exit_boot_services() {}
3328

34-
/// Initializes the allocator.
35-
///
36-
/// # Safety
29+
/// Get the memory type to use for allocation.
3730
///
38-
/// This function is unsafe because you _must_ make sure that exit_boot_services
39-
/// will be called when UEFI boot services will be exited.
40-
pub unsafe fn init(system_table: &mut SystemTable<Boot>) {
41-
SYSTEM_TABLE.store(system_table.as_ptr().cast_mut(), Ordering::Release);
31+
/// The first time this is called, the data type of the loaded image will be
32+
/// retrieved. That value is cached in a static and reused on subsequent
33+
/// calls. If the memory type of the loaded image cannot be retrieved for some
34+
/// reason, a default of `LOADER_DATA` is used.
35+
fn get_memory_type() -> MemoryType {
36+
// Initialize to a `RESERVED` to indicate the actual value hasn't been set yet.
37+
static MEMORY_TYPE: AtomicU32 = AtomicU32::new(MemoryType::RESERVED.0);
4238

43-
let boot_services = system_table.boot_services();
44-
if let Ok(loaded_image) =
45-
boot_services.open_protocol_exclusive::<LoadedImage>(boot_services.image_handle())
46-
{
47-
MEMORY_TYPE.store(loaded_image.data_type().0, Ordering::Release);
39+
let memory_type = MEMORY_TYPE.load(Ordering::Acquire);
40+
if memory_type == MemoryType::RESERVED.0 {
41+
let memory_type = if let Ok(loaded_image) =
42+
boot::open_protocol_exclusive::<LoadedImage>(boot::image_handle())
43+
{
44+
loaded_image.data_type()
45+
} else {
46+
MemoryType::LOADER_DATA
47+
};
48+
MEMORY_TYPE.store(memory_type.0, Ordering::Release);
49+
memory_type
50+
} else {
51+
MemoryType(memory_type)
4852
}
4953
}
5054

51-
/// Access the boot services
52-
fn boot_services() -> *const BootServices {
53-
let ptr = SYSTEM_TABLE.load(Ordering::Acquire);
54-
let system_table =
55-
unsafe { SystemTable::from_ptr(ptr) }.expect("The system table handle is not available");
56-
system_table.boot_services()
57-
}
58-
59-
/// Notify the allocator library that boot services are not safe to call anymore
60-
///
61-
/// You must arrange for this function to be called on exit from UEFI boot services
62-
pub fn exit_boot_services() {
63-
SYSTEM_TABLE.store(ptr::null_mut(), Ordering::Release);
64-
}
65-
6655
/// Allocator which uses the UEFI pool allocation functions.
6756
///
6857
/// Only valid for as long as the UEFI boot services are available.
6958
#[derive(Debug)]
7059
pub struct Allocator;
7160

7261
unsafe impl GlobalAlloc for Allocator {
73-
/// Allocate memory using [`BootServices::allocate_pool`]. The allocation is
62+
/// Allocate memory using [`boot::allocate_pool`]. The allocation is
7463
/// of type [`MemoryType::LOADER_DATA`] for UEFI applications, [`MemoryType::BOOT_SERVICES_DATA`]
7564
/// for UEFI boot drivers and [`MemoryType::RUNTIME_SERVICES_DATA`] for UEFI runtime drivers.
7665
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
66+
if !boot::are_boot_services_active() {
67+
return ptr::null_mut();
68+
}
69+
7770
let size = layout.size();
7871
let align = layout.align();
79-
let memory_type = MemoryType(MEMORY_TYPE.load(Ordering::Acquire));
80-
81-
let boot_services = &*boot_services();
72+
let memory_type = get_memory_type();
8273

8374
if align > 8 {
8475
// The requested alignment is greater than 8, but `allocate_pool` is
8576
// only guaranteed to provide eight-byte alignment. Allocate extra
8677
// space so that we can return an appropriately-aligned pointer
8778
// within the allocation.
88-
let full_alloc_ptr =
89-
if let Ok(ptr) = boot_services.allocate_pool(memory_type, size + align) {
90-
ptr.as_ptr()
91-
} else {
92-
return ptr::null_mut();
93-
};
79+
let full_alloc_ptr = if let Ok(ptr) = boot::allocate_pool(memory_type, size + align) {
80+
ptr.as_ptr()
81+
} else {
82+
return ptr::null_mut();
83+
};
9484

9585
// Calculate the offset needed to get an aligned pointer within the
9686
// full allocation. If that offset is zero, increase it to `align`
@@ -115,20 +105,24 @@ unsafe impl GlobalAlloc for Allocator {
115105
// The requested alignment is less than or equal to eight, and
116106
// `allocate_pool` always provides eight-byte alignment, so we can
117107
// use `allocate_pool` directly.
118-
boot_services
119-
.allocate_pool(memory_type, size)
108+
boot::allocate_pool(memory_type, size)
120109
.map(|ptr| ptr.as_ptr())
121110
.unwrap_or(ptr::null_mut())
122111
}
123112
}
124113

125-
/// Deallocate memory using [`BootServices::free_pool`].
114+
/// Deallocate memory using [`boot::free_pool`].
126115
unsafe fn dealloc(&self, mut ptr: *mut u8, layout: Layout) {
127116
if layout.align() > 8 {
128117
// Retrieve the pointer to the full allocation that was packed right
129118
// before the aligned allocation in `alloc`.
130119
ptr = (ptr as *const *mut u8).sub(1).read();
131120
}
132-
(*boot_services()).free_pool(ptr).unwrap();
121+
122+
// OK to unwrap: `ptr` is required to be a valid allocation by the trait API.
123+
let ptr = NonNull::new(ptr).unwrap();
124+
125+
// Warning: this will panic after exiting boot services.
126+
boot::free_pool(ptr).unwrap();
133127
}
134128
}

uefi/src/boot.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,18 @@ pub unsafe fn set_image_handle(image_handle: Handle) {
6060
IMAGE_HANDLE.store(image_handle.as_ptr(), Ordering::Release);
6161
}
6262

63+
/// Return true if boot services are active, false otherwise.
64+
pub(crate) fn are_boot_services_active() -> bool {
65+
let Some(st) = table::system_table_raw() else {
66+
return false;
67+
};
68+
69+
// SAFETY: valid per requirements of `set_system_table`.
70+
let st = unsafe { st.as_ref() };
71+
72+
!st.boot_services.is_null()
73+
}
74+
6375
fn boot_services_raw_panicking() -> NonNull<uefi_raw::table::boot::BootServices> {
6476
let st = table::system_table_raw_panicking();
6577
// SAFETY: valid per requirements of `set_system_table`.

uefi/src/helpers/mod.rs

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -48,36 +48,25 @@ pub fn system_table() -> SystemTable<Boot> {
4848
/// Initialize all helpers defined in [`uefi::helpers`] whose Cargo features
4949
/// are activated.
5050
///
51-
/// This must be called as early as possible, before trying to use logging or
52-
/// memory allocation capabilities.
51+
/// This must be called as early as possible, before trying to use logging.
5352
///
5453
/// **PLEASE NOTE** that these helpers are meant for the pre exit boot service
5554
/// epoch. Limited functionality might work after exiting them, such as logging
5655
/// to the debugcon device.
5756
#[allow(clippy::missing_const_for_fn)]
5857
pub fn init() -> Result<()> {
59-
// Setup logging and memory allocation
60-
58+
// Set up logging.
6159
#[cfg(feature = "logger")]
6260
unsafe {
6361
let mut st = table::system_table_boot().expect("boot services are not active");
6462
logger::init(&mut st);
6563
}
6664

67-
#[cfg(feature = "global_allocator")]
68-
unsafe {
69-
let mut st = table::system_table_boot().expect("boot services are not active");
70-
crate::allocator::init(&mut st);
71-
}
72-
7365
Ok(())
7466
}
7567

7668
#[allow(clippy::missing_const_for_fn)]
7769
pub(crate) fn exit() {
7870
#[cfg(feature = "logger")]
7971
logger::disable();
80-
81-
#[cfg(feature = "global_allocator")]
82-
crate::allocator::exit_boot_services();
8372
}

0 commit comments

Comments
 (0)