Skip to content

Commit c5d594e

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

File tree

3 files changed

+71
-17
lines changed

3 files changed

+71
-17
lines changed

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

Lines changed: 17 additions & 8 deletions
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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
`proto::device_path::text` to `proto::device_path`.
2626
- **Breaking:** `exit_boot_services` now consumes a `Option<MemoryType>` which
2727
defaults to the recommended value of `MemoryType::LOADER_DATA`.
28+
- **Breaking**: `allocate_pages` now returns `NonNull<[u8]>` to align it with
29+
the Rust allocator API. There is an example in the documentation of that
30+
function.
2831
- `boot::memory_map()` will never return `Status::BUFFER_TOO_SMALL` from now on,
2932
as this is considered a hard internal error where users can't do anything
3033
about it anyway. It will panic instead.

uefi/src/boot.rs

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -120,42 +120,83 @@ 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+
/// // ⚠️ Creating the reference is safe, but reading the uninitialized memory
142+
/// // causes Undefined Behavior (UB)! Please make sure to initialize the memory
143+
/// // first by:
144+
/// // - using `core::ptr::write`,
145+
/// // - directly writing to slice indices,
146+
/// // - zeroing the memory,
147+
/// // - using `.copy_from_slice()`,
148+
/// // - or a similar operation.
149+
/// let buffer: &mut [u8] = unsafe { ptr.as_mut() };
150+
/// // Now initialize the content of the buffer, cast it, etc.
151+
/// // Please follow Rust guidelines on safety and UB! ⚠️
152+
///
153+
/// // free the allocation
154+
/// unsafe { boot::free_pages(ptr.cast(), num_pages) }.unwrap();
155+
/// ```
156+
///
157+
/// # Safety
158+
/// Using this function is safe but reading on initialized memory is not.
159+
/// Please look into the example code.
160+
///
127161
/// # Errors
128162
///
129163
/// * [`Status::OUT_OF_RESOURCES`]: allocation failed.
130164
/// * [`Status::INVALID_PARAMETER`]: `mem_ty` is [`MemoryType::PERSISTENT_MEMORY`],
131165
/// [`MemoryType::UNACCEPTED`], or in the range [`MemoryType::MAX`]`..=0x6fff_ffff`.
132166
/// * [`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>> {
167+
pub fn allocate_pages(
168+
allocation_type: AllocateType,
169+
memory_type: MemoryType,
170+
count: usize,
171+
) -> Result<NonNull<[u8]>> {
134172
let bt = boot_services_raw_panicking();
135173
let bt = unsafe { bt.as_ref() };
136174

137-
let (ty, initial_addr) = match ty {
175+
let (allocation_type_efi, start_address) = match allocation_type {
138176
AllocateType::AnyPages => (0, 0),
139177
AllocateType::MaxAddress(addr) => (1, addr),
140178
AllocateType::Address(addr) => (2, addr),
141179
};
142180

143-
let mut addr1 = initial_addr;
144-
unsafe { (bt.allocate_pages)(ty, mem_ty, count, &mut addr1) }.to_result()?;
181+
let mut addr1 = start_address;
182+
unsafe { (bt.allocate_pages)(allocation_type_efi, memory_type, count, &mut addr1) }
183+
.to_result()?;
145184

146185
// The UEFI spec allows `allocate_pages` to return a valid allocation at
147186
// address zero. Rust does not allow writes through a null pointer (which
148187
// Rust defines as address zero), so this is not very useful. Only return
149188
// the allocation if the address is non-null.
150189
if let Some(ptr) = NonNull::new(addr1 as *mut u8) {
151-
return Ok(ptr);
190+
let slice = NonNull::slice_from_raw_parts(ptr, count * PAGE_SIZE);
191+
return Ok(slice);
152192
}
153193

154194
// Attempt a second allocation. The first allocation (at address zero) has
155195
// not yet been freed, so if this allocation succeeds it should be at a
156196
// non-zero address.
157-
let mut addr2 = initial_addr;
158-
let r = unsafe { (bt.allocate_pages)(ty, mem_ty, count, &mut addr2) }.to_result();
197+
let mut addr2 = start_address;
198+
let r = unsafe { (bt.allocate_pages)(allocation_type_efi, memory_type, count, &mut addr2) }
199+
.to_result();
159200

160201
// Free the original allocation (ignoring errors).
161202
let _unused = unsafe { (bt.free_pages)(addr1, count) };
@@ -164,7 +205,8 @@ pub fn allocate_pages(ty: AllocateType, mem_ty: MemoryType, count: usize) -> Res
164205
// address zero. Otherwise, return a pointer to the second allocation.
165206
r?;
166207
if let Some(ptr) = NonNull::new(addr2 as *mut u8) {
167-
Ok(ptr)
208+
let slice = NonNull::slice_from_raw_parts(ptr, count * PAGE_SIZE);
209+
Ok(slice)
168210
} else {
169211
Err(Status::OUT_OF_RESOURCES.into())
170212
}

0 commit comments

Comments
 (0)