Skip to content

Commit 79e88d3

Browse files
committed
uefi: allocator: use shortcut for PAGE_SIZE
Allocating page-aligned memory via the global allocator is not uncommon for UEFI OS loaders. Therefore, it is feasible to use a shortcut in the allocator, and directly use boot::allocate_pages() rather than boot::allocate_pool().
1 parent 34508cf commit 79e88d3

File tree

2 files changed

+43
-12
lines changed

2 files changed

+43
-12
lines changed

uefi/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
- The `Display` impl for `CStr8` now excludes the trailing null character.
2929
- `VariableKeys` initializes with a larger name buffer to work around firmware
3030
bugs on some devices.
31+
- The UEFI `allocator::Allocator` has been optimized for page-aligned
32+
allocations.
3133

3234

3335
# uefi - 0.34.1 (2025-02-07)

uefi/src/allocator.rs

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@
1212
//! will panic.
1313
1414
use crate::boot;
15+
use crate::boot::AllocateType;
1516
use crate::mem::memory_map::MemoryType;
1617
use crate::proto::loaded_image::LoadedImage;
1718
use core::alloc::{GlobalAlloc, Layout};
1819
use core::ptr::{self, NonNull};
1920
use core::sync::atomic::{AtomicU32, Ordering};
21+
use uefi_raw::table::boot::PAGE_SIZE;
2022

2123
/// Get the memory type to use for allocation.
2224
///
@@ -79,7 +81,7 @@ fn alloc_pool_aligned(memory_type: MemoryType, size: usize, align: usize) -> *mu
7981
}
8082
}
8183

82-
/// Allocator which uses the UEFI pool allocation functions.
84+
/// Allocator which uses the UEFI allocation functions.
8385
///
8486
/// The allocator can only be used as long as the UEFI boot services are
8587
/// available and have not been exited.
@@ -110,24 +112,51 @@ unsafe impl GlobalAlloc for Allocator {
110112
.map(|ptr| ptr.as_ptr())
111113
.unwrap_or(ptr::null_mut())
112114
}
115+
// Allocating pages is actually very expected in UEFI OS loaders, so
116+
// it makes sense to provide this optimization.
117+
// In Rust, each allocation's size must be at least the alignment,
118+
// as specified in the Rust type layout [0]. Therefore, we don't
119+
// have a risk of wasting memory. Further, using page-alignment for
120+
// small allocations is quiet unusual.
121+
// [0]: https://doc.rust-lang.org/reference/type-layout.html
122+
PAGE_SIZE => {
123+
log::trace!("Taking PAGE_SIZE shortcut");
124+
let count = size.div_ceil(PAGE_SIZE);
125+
boot::allocate_pages(AllocateType::AnyPages, memory_type, count)
126+
.map(|ptr| ptr.as_ptr())
127+
.unwrap_or(ptr::null_mut())
128+
}
113129
9.. => {
114130
alloc_pool_aligned(memory_type, size, align)
115131
}
116132
}
117133
}
118134

119135
/// Deallocate memory using [`boot::free_pool`].
120-
unsafe fn dealloc(&self, mut ptr: *mut u8, layout: Layout) {
121-
if layout.align() > 8 {
122-
// Retrieve the pointer to the full allocation that was packed right
123-
// before the aligned allocation in `alloc`.
124-
ptr = unsafe { (ptr as *const *mut u8).sub(1).read() };
125-
}
126-
127-
// OK to unwrap: `ptr` is required to be a valid allocation by the trait API.
136+
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
128137
let ptr = NonNull::new(ptr).unwrap();
129-
130-
// Warning: this will panic after exiting boot services.
131-
unsafe { boot::free_pool(ptr) }.unwrap();
138+
match layout.align() {
139+
0..=8 /* UEFI default alignment */ => {
140+
// Warning: this will panic after exiting boot services.
141+
unsafe { boot::free_pool(ptr) }.unwrap();
142+
}
143+
/* Corresponds the the alloc() match arm. */
144+
PAGE_SIZE => {
145+
log::trace!("Taking PAGE_SIZE shortcut");
146+
let count = layout.size().div_ceil(PAGE_SIZE);
147+
unsafe {
148+
boot::free_pages(ptr, count).unwrap()
149+
}
150+
}
151+
9.. => {
152+
let ptr = ptr.as_ptr().cast::<*mut u8>();
153+
// Retrieve the pointer to the full allocation that was packed right
154+
// before the aligned allocation in `alloc`.
155+
let actual_alloc_ptr = unsafe { ptr.sub(1).read() };
156+
let ptr = NonNull::new(actual_alloc_ptr).unwrap();
157+
// Warning: this will panic after exiting boot services.
158+
unsafe { boot::free_pool(ptr) }.unwrap();
159+
}
160+
}
132161
}
133162
}

0 commit comments

Comments
 (0)