Skip to content

Commit c0728be

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(). ## Testing We can look at the TRACE messages of `cargo xtask run` to verify that the shortcut is taken.
1 parent 34508cf commit c0728be

File tree

3 files changed

+66
-22
lines changed

3 files changed

+66
-22
lines changed

uefi-test-runner/src/boot/memory.rs

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,12 @@ mod bootservices {
5858
/// Tests that use [`uefi::allocator::Allocator`], which is configured as the
5959
/// global allocator.
6060
mod global {
61+
use alloc::boxed::Box;
62+
use uefi_raw::table::boot::PAGE_SIZE;
63+
6164
/// Simple test to ensure our custom allocator works with the `alloc` crate.
6265
pub fn alloc_vec() {
63-
info!("Allocating a vector through the `alloc` crate");
66+
info!("Allocating a vector using the global allocator");
6467

6568
#[allow(clippy::useless_vec)]
6669
let mut values = vec![-5, 16, 23, 4, 0];
@@ -71,17 +74,27 @@ mod global {
7174
}
7275

7376
/// Simple test to ensure our custom allocator works with correct alignment.
77+
#[allow(dead_code)] // Ignore warning due to field not being read.
7478
pub fn alloc_alignment() {
75-
info!("Allocating a structure with alignment to 0x100");
76-
77-
#[repr(align(0x100))]
78-
struct Block(
79-
// Ignore warning due to field not being read.
80-
#[allow(dead_code)] [u8; 0x100],
81-
);
79+
{
80+
info!("Allocating a structure with alignment of 0x100 using the global allocator");
81+
#[repr(align(0x100))]
82+
struct Block([u8; 0x100]);
8283

83-
let value = vec![Block([1; 0x100])];
84-
assert_eq!(value.as_ptr() as usize % 0x100, 0, "Wrong alignment");
84+
let value = vec![Block([1; 0x100])];
85+
assert_eq!(value.as_ptr() as usize % 0x100, 0, "Wrong alignment");
86+
}
87+
{
88+
info!("Allocating a memory page ({PAGE_SIZE}) using the global allocator");
89+
#[repr(align(4096))]
90+
struct Page([u8; PAGE_SIZE]);
91+
let value = Box::new(Page([0; PAGE_SIZE]));
92+
assert_eq!(
93+
value.0.as_ptr().align_offset(PAGE_SIZE),
94+
0,
95+
"Wrong alignment"
96+
);
97+
}
8598
}
8699
}
87100

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)