7
7
//!
8
8
//! [`helpers`]: uefi::helpers
9
9
10
- use crate :: boot;
10
+ use crate :: boot:: { self , AllocateType } ;
11
11
use crate :: mem:: memory_map:: MemoryType ;
12
12
use crate :: proto:: loaded_image:: LoadedImage ;
13
13
use core:: alloc:: { GlobalAlloc , Layout } ;
14
14
use core:: ptr:: { self , NonNull } ;
15
15
use core:: sync:: atomic:: { AtomicU32 , Ordering } ;
16
+ use uefi_raw:: table:: boot:: PAGE_SIZE ;
16
17
17
18
/// Get the memory type to use for allocation.
18
19
///
@@ -75,6 +76,20 @@ fn alloc_pool_aligned(memory_type: MemoryType, size: usize, align: usize) -> *mu
75
76
}
76
77
}
77
78
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
+
78
93
/// Allocator using UEFI boot services.
79
94
///
80
95
/// 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
86
101
pub struct Allocator ;
87
102
88
103
unsafe 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].
91
107
///
92
108
/// [memory type]: MemoryType
93
109
/// [data type]: LoadedImage::data_type
@@ -96,40 +112,58 @@ unsafe impl GlobalAlloc for Allocator {
96
112
return ptr:: null_mut ( ) ;
97
113
}
98
114
99
- let size = layout. size ( ) ;
100
- let align = layout. align ( ) ;
101
115
let memory_type = get_memory_type ( ) ;
116
+ let use_page_shortcut = layout_allows_page_alloc_shortcut ( & layout) ;
102
117
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 */ ) => {
105
130
// The requested alignment is less than or equal to eight, and
106
131
// `allocate_pool` always provides eight-byte alignment, so we can
107
132
// use `allocate_pool` directly.
108
- boot:: allocate_pool ( memory_type, size)
133
+ boot:: allocate_pool ( memory_type, layout . size ( ) )
109
134
. map ( |ptr| ptr. as_ptr ( ) )
110
135
. unwrap_or ( ptr:: null_mut ( ) )
111
136
}
112
- 9 .. => {
113
- alloc_pool_aligned ( memory_type, size, align)
114
- }
137
+ ( false , 9 ..) => alloc_pool_aligned ( memory_type, layout. size ( ) , layout. align ( ) ) ,
115
138
}
116
139
}
117
140
118
- /// Deallocate memory using [` boot::free_pool`] .
141
+ /// Deallocate memory using the UEFI boot services .
119
142
///
120
143
/// This will panic after exiting boot services.
121
144
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.
126
158
unsafe { boot:: free_pool ( ptr) } . unwrap ( ) ;
127
159
}
128
- 9 .. => {
160
+ ( false , 9 ..) => {
161
+ let ptr = ptr. as_ptr ( ) . cast :: < * mut u8 > ( ) ;
129
162
// Retrieve the pointer to the full allocation that was packed right
130
163
// 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.
133
167
unsafe { boot:: free_pool ( ptr) } . unwrap ( ) ;
134
168
}
135
169
}
0 commit comments