Skip to content

Commit 129c8a6

Browse files
committed
uefi: allocate_pages() now returns NonNull<[u8]>
This aligns the signature with the Rust allocator API.
1 parent a0a76eb commit 129c8a6

File tree

3 files changed

+83
-17
lines changed

3 files changed

+83
-17
lines changed

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

+17-8
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use alloc::vec::Vec;
44
use uefi::boot::{self, AllocateType};
55
use uefi::mem::memory_map::{MemoryMap, MemoryMapMut, MemoryType};
6+
use uefi_raw::table::boot::PAGE_SIZE;
67

78
pub fn test() {
89
info!("Testing memory functions");
@@ -17,20 +18,28 @@ pub fn test() {
1718
}
1819

1920
fn test_allocate_pages() {
20-
let num_pages = 1;
21-
let ptr =
21+
let num_pages = 3;
22+
let mut ptr =
2223
boot::allocate_pages(AllocateType::AnyPages, MemoryType::LOADER_DATA, num_pages).unwrap();
23-
let addr = ptr.as_ptr() as usize;
24-
assert_eq!(addr % 4096, 0, "Page pointer is not page-aligned");
24+
25+
let buffer = unsafe { ptr.as_mut() };
26+
assert_eq!(
27+
buffer.as_ptr().align_offset(PAGE_SIZE),
28+
0,
29+
"Page pointer is not page-aligned"
30+
);
2531

2632
// Verify the page can be written to.
2733
{
28-
let ptr = ptr.as_ptr();
29-
unsafe { ptr.write_volatile(0xff) };
30-
unsafe { ptr.add(4095).write_volatile(0xff) };
34+
buffer[0] = 0xff;
35+
buffer[4095] = 0xff;
36+
buffer[5095] = 0xff;
37+
assert_eq!(buffer[0], 0xff);
38+
assert_eq!(buffer[4095], 0xff);
39+
assert_eq!(buffer[5095], 0xff);
3140
}
3241

33-
unsafe { boot::free_pages(ptr, num_pages) }.unwrap();
42+
unsafe { boot::free_pages(ptr.cast(), num_pages) }.unwrap();
3443
}
3544

3645
fn test_allocate_pool() {

uefi/CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
`&self`.
1717
- **Breaking:** The `pxe::Mode` struct is now opaque. Use method calls to access
1818
mode data instead of direct field access.
19+
- **Breaking**: `allocate_pages` now returns `NonNull<[u8]>` to align it with
20+
the Rust allocator API. There is an example in the documentation of that
21+
function.
1922
- `boot::memory_map()` will never return `Status::BUFFER_TOO_SMALL` from now on,
2023
as this is considered a hard internal error where users can't do anything
2124
about it anyway. It will panic instead.

uefi/src/boot.rs

+63-9
Original file line numberDiff line numberDiff line change
@@ -120,42 +120,70 @@ pub unsafe fn raise_tpl(tpl: Tpl) -> TplGuard {
120120
}
121121
}
122122

123-
/// Allocates memory pages from the system.
123+
/// Allocates a consecutive set of memory pages using the UEFI allocator.
124+
///
125+
/// The caller is responsible to free the memory using [`free_pages`].
124126
///
125127
/// UEFI OS loaders should allocate memory of the type `LoaderData`.
126128
///
129+
/// # Example
130+
///```rust,no_run
131+
/// use uefi::boot::{self, AllocateType};
132+
/// use uefi_raw::table::boot::MemoryType;
133+
///
134+
/// let num_pages = 3;
135+
/// let mut ptr = boot::allocate_pages(
136+
/// AllocateType::AnyPages,
137+
/// MemoryType::LOADER_DATA,
138+
/// num_pages
139+
/// ).unwrap();
140+
///
141+
/// let buffer: &mut [u8] = unsafe { ptr.as_mut() };
142+
/// // now do something with your buffer
143+
///
144+
/// // free the allocation
145+
/// unsafe { boot::free_pages(ptr.cast(), num_pages) }.unwrap();
146+
/// ```
147+
///
127148
/// # Errors
128149
///
129150
/// * [`Status::OUT_OF_RESOURCES`]: allocation failed.
130151
/// * [`Status::INVALID_PARAMETER`]: `mem_ty` is [`MemoryType::PERSISTENT_MEMORY`],
131152
/// [`MemoryType::UNACCEPTED`], or in the range [`MemoryType::MAX`]`..=0x6fff_ffff`.
132153
/// * [`Status::NOT_FOUND`]: the requested pages could not be found.
133-
pub fn allocate_pages(ty: AllocateType, mem_ty: MemoryType, count: usize) -> Result<NonNull<u8>> {
154+
pub fn allocate_pages(
155+
allocation_type: AllocateType,
156+
memory_type: MemoryType,
157+
count: usize,
158+
) -> Result<NonNull<[u8]>> {
134159
let bt = boot_services_raw_panicking();
135160
let bt = unsafe { bt.as_ref() };
136161

137-
let (ty, initial_addr) = match ty {
162+
let (allocation_type_efi, start_address) = match allocation_type {
138163
AllocateType::AnyPages => (0, 0),
139164
AllocateType::MaxAddress(addr) => (1, addr),
140165
AllocateType::Address(addr) => (2, addr),
141166
};
142167

143-
let mut addr1 = initial_addr;
144-
unsafe { (bt.allocate_pages)(ty, mem_ty, count, &mut addr1) }.to_result()?;
168+
let mut addr1 = start_address;
169+
unsafe { (bt.allocate_pages)(allocation_type_efi, memory_type, count, &mut addr1) }
170+
.to_result()?;
145171

146172
// The UEFI spec allows `allocate_pages` to return a valid allocation at
147173
// address zero. Rust does not allow writes through a null pointer (which
148174
// Rust defines as address zero), so this is not very useful. Only return
149175
// the allocation if the address is non-null.
150176
if let Some(ptr) = NonNull::new(addr1 as *mut u8) {
151-
return Ok(ptr);
177+
let slice = NonNull::slice_from_raw_parts(ptr, count * PAGE_SIZE);
178+
return Ok(slice);
152179
}
153180

154181
// Attempt a second allocation. The first allocation (at address zero) has
155182
// not yet been freed, so if this allocation succeeds it should be at a
156183
// non-zero address.
157-
let mut addr2 = initial_addr;
158-
let r = unsafe { (bt.allocate_pages)(ty, mem_ty, count, &mut addr2) }.to_result();
184+
let mut addr2 = start_address;
185+
let r = unsafe { (bt.allocate_pages)(allocation_type_efi, memory_type, count, &mut addr2) }
186+
.to_result();
159187

160188
// Free the original allocation (ignoring errors).
161189
let _unused = unsafe { (bt.free_pages)(addr1, count) };
@@ -164,7 +192,8 @@ pub fn allocate_pages(ty: AllocateType, mem_ty: MemoryType, count: usize) -> Res
164192
// address zero. Otherwise, return a pointer to the second allocation.
165193
r?;
166194
if let Some(ptr) = NonNull::new(addr2 as *mut u8) {
167-
Ok(ptr)
195+
let slice = NonNull::slice_from_raw_parts(ptr, count * PAGE_SIZE);
196+
Ok(slice)
168197
} else {
169198
Err(Status::OUT_OF_RESOURCES.into())
170199
}
@@ -191,6 +220,31 @@ pub unsafe fn free_pages(ptr: NonNull<u8>, count: usize) -> Result {
191220

192221
/// Allocates from a memory pool. The pointer will be 8-byte aligned.
193222
///
223+
/// The caller is responsible to free the memory using [`free_pool`].
224+
///
225+
/// # Arguments
226+
/// - `memory_type`: The [`MemoryType`] used to persist the allocation in the
227+
/// UEFI memory map. Typically, UEFI OS loaders should allocate memory of
228+
/// type [`MemoryType::LOADER_DATA`].
229+
///- `size`: Amount of bytes to allocate.
230+
///
231+
/// # Example
232+
///```rust,no_run
233+
/// use uefi::boot::{self, AllocateType};
234+
/// use uefi_raw::table::boot::MemoryType;
235+
///
236+
/// let mut ptr = boot::allocate_pool(
237+
/// MemoryType::LOADER_DATA,
238+
/// 42
239+
/// ).unwrap();
240+
///
241+
/// let buffer: &mut [u8] = unsafe { ptr.as_mut() };
242+
/// // now do something with your buffer
243+
///
244+
/// // free the allocation
245+
/// unsafe { boot::free_pool(ptr.cast()) }.unwrap();
246+
/// ```
247+
///
194248
/// # Errors
195249
///
196250
/// * [`Status::OUT_OF_RESOURCES`]: allocation failed.

0 commit comments

Comments
 (0)