77//!
88//! [`helpers`]: uefi::helpers
99
10- use crate::boot;
10+ use crate::boot::{self, AllocateType} ;
1111use crate::mem::memory_map::MemoryType;
1212use crate::proto::loaded_image::LoadedImage;
1313use core::alloc::{GlobalAlloc, Layout};
1414use core::ptr::{self, NonNull};
1515use core::sync::atomic::{AtomicU32, Ordering};
16+ use uefi_raw::table::boot::PAGE_SIZE;
1617
1718/// Get the memory type to use for allocation.
1819///
@@ -75,6 +76,20 @@ fn alloc_pool_aligned(memory_type: MemoryType, size: usize, align: usize) -> *mu
7576 }
7677}
7778
79+ /// Returns whether the allocation is a multiple of a [`PAGE_SIZE`] and is
80+ /// aligned to [`PAGE_SIZE`].
81+ ///
82+ /// This does not only check the alignment but also the size. For types
83+ /// allocated by Rust itself (e.g., `Box<T>`), the size is always at least the
84+ /// alignment, as specified in the [Rust type layout]. However, to be also safe
85+ /// when it comes to manual invocations, we additionally check if the size is
86+ /// a multiple of [`PAGE_SIZE`].
87+ ///
88+ /// [Rust type layout]: https://doc.rust-lang.org/reference/type-layout.html
89+ const fn layout_allows_page_alloc_shortcut(layout: &Layout) -> bool {
90+ layout.size() % PAGE_SIZE == 0 && layout.align() == PAGE_SIZE
91+ }
92+
7893/// Allocator using UEFI boot services.
7994///
8095/// This type implements [`GlobalAlloc`] and can be marked with the
@@ -86,8 +101,9 @@ fn alloc_pool_aligned(memory_type: MemoryType, size: usize, align: usize) -> *mu
86101pub struct Allocator;
87102
88103unsafe impl GlobalAlloc for Allocator {
89- /// Allocate memory using [`boot::allocate_pool`]. The allocation's [memory
90- /// type] matches the current image's [data type].
104+ /// Allocate memory using the UEFI boot services.
105+ ///
106+ /// The allocation's [memory type] matches the current image's [data type].
91107 ///
92108 /// [memory type]: MemoryType
93109 /// [data type]: LoadedImage::data_type
@@ -96,40 +112,58 @@ unsafe impl GlobalAlloc for Allocator {
96112 return ptr::null_mut();
97113 }
98114
99- let size = layout.size();
100- let align = layout.align();
101115 let memory_type = get_memory_type();
116+ let use_page_shortcut = layout_allows_page_alloc_shortcut(&layout);
102117
103- match align {
104- 0..=8 /* UEFI default alignment */ => {
118+ match (use_page_shortcut, layout.align()) {
119+ // Allocating pages is actually very expected in UEFI OS loaders, so
120+ // it makes sense to provide this optimization.
121+ (true, _) => {
122+ // To spammy, but useful for manual testing.
123+ // log::trace!("Taking PAGE_SIZE shortcut for layout={layout:?}");
124+ let count = layout.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+ }
129+ (false, 0..=8 /* UEFI default alignment */) => {
105130 // The requested alignment is less than or equal to eight, and
106131 // `allocate_pool` always provides eight-byte alignment, so we can
107132 // use `allocate_pool` directly.
108- boot::allocate_pool(memory_type, size)
133+ boot::allocate_pool(memory_type, layout. size() )
109134 .map(|ptr| ptr.as_ptr())
110135 .unwrap_or(ptr::null_mut())
111136 }
112- 9.. => {
113- alloc_pool_aligned(memory_type, size, align)
114- }
137+ (false, 9..) => alloc_pool_aligned(memory_type, layout.size(), layout.align()),
115138 }
116139 }
117140
118- /// Deallocate memory using [` boot::free_pool`] .
141+ /// Deallocate memory using the UEFI boot services .
119142 ///
120143 /// This will panic after exiting boot services.
121144 unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
122- match layout.align() {
123- 0..=8 => {
124- // OK to unwrap: `ptr` is required to be a valid allocation by the trait API.
125- let ptr = NonNull::new(ptr).unwrap();
145+ let ptr = NonNull::new(ptr).unwrap();
146+
147+ let use_page_shortcut = layout_allows_page_alloc_shortcut(&layout);
148+
149+ match (use_page_shortcut, layout.align()) {
150+ (true, _) => {
151+ // To spammy, but useful for manual testing.
152+ // log::trace!("Taking PAGE_SIZE shortcut for layout={layout:?}");
153+ let count = layout.size().div_ceil(PAGE_SIZE);
154+ unsafe { boot::free_pages(ptr, count).unwrap() }
155+ }
156+ (false, 0..=8 /* UEFI default alignment */) => {
157+ // Warning: this will panic after exiting boot services.
126158 unsafe { boot::free_pool(ptr) }.unwrap();
127159 }
128- 9.. => {
160+ (false, 9..) => {
161+ let ptr = ptr.as_ptr().cast::<*mut u8>();
129162 // Retrieve the pointer to the full allocation that was packed right
130163 // before the aligned allocation in `alloc`.
131- let ptr = unsafe { (ptr as *const *mut u8).sub(1).read() };
132- let ptr = NonNull::new(ptr).unwrap();
164+ let actual_alloc_ptr = unsafe { ptr.sub(1).read() };
165+ let ptr = NonNull::new(actual_alloc_ptr).unwrap();
166+ // Warning: this will panic after exiting boot services.
133167 unsafe { boot::free_pool(ptr) }.unwrap();
134168 }
135169 }
0 commit comments