From f1c9f285dfceb42cd5f30c2dc9a6136dca8639ca Mon Sep 17 00:00:00 2001 From: Nicholas Bishop Date: Sun, 21 Jul 2024 09:39:43 -0400 Subject: [PATCH] Add uefi::boot module The initial version contains allocate_pages/allocate_pool and the corresponding `free` functions. Unlike the `BootServices` methods, these functions all use `NonNull` for consistency. --- uefi-test-runner/src/boot/memory.rs | 33 ++++++++++ uefi/CHANGELOG.md | 2 + uefi/src/boot.rs | 99 +++++++++++++++++++++++++++++ uefi/src/lib.rs | 1 + 4 files changed, 135 insertions(+) create mode 100644 uefi/src/boot.rs diff --git a/uefi-test-runner/src/boot/memory.rs b/uefi-test-runner/src/boot/memory.rs index 17e7bc1e3..d0c676dd6 100644 --- a/uefi-test-runner/src/boot/memory.rs +++ b/uefi-test-runner/src/boot/memory.rs @@ -1,3 +1,4 @@ +use uefi::boot; use uefi::table::boot::{AllocateType, BootServices, MemoryMap, MemoryMapMut, MemoryType}; use alloc::vec::Vec; @@ -5,6 +6,9 @@ use alloc::vec::Vec; pub fn test(bt: &BootServices) { info!("Testing memory functions"); + test_allocate_pages_freestanding(); + test_allocate_pool_freestanding(); + allocate_pages(bt); vec_alloc(); alloc_alignment(); @@ -12,6 +16,35 @@ pub fn test(bt: &BootServices) { memory_map(bt); } +fn test_allocate_pages_freestanding() { + let num_pages = 1; + let ptr = + boot::allocate_pages(AllocateType::AnyPages, MemoryType::LOADER_DATA, num_pages).unwrap(); + let addr = ptr.as_ptr() as usize; + assert_eq!(addr % 4096, 0, "Page pointer is not page-aligned"); + + // Verify the page can be written to. + { + let ptr = ptr.as_ptr(); + unsafe { ptr.write_volatile(0xff) }; + unsafe { ptr.add(4095).write_volatile(0xff) }; + } + + unsafe { boot::free_pages(ptr, num_pages) }.unwrap(); +} + +fn test_allocate_pool_freestanding() { + let ptr = boot::allocate_pool(MemoryType::LOADER_DATA, 10).unwrap(); + + // Verify the allocation can be written to. + { + let ptr = ptr.as_ptr(); + unsafe { ptr.write_volatile(0xff) }; + unsafe { ptr.add(9).write_volatile(0xff) }; + } + unsafe { boot::free_pool(ptr) }.unwrap(); +} + fn allocate_pages(bt: &BootServices) { info!("Allocating some pages of memory"); diff --git a/uefi/CHANGELOG.md b/uefi/CHANGELOG.md index bcff19b22..ab20920a2 100644 --- a/uefi/CHANGELOG.md +++ b/uefi/CHANGELOG.md @@ -3,6 +3,8 @@ ## Added - `uefi::system` is a new module that provides freestanding functions for accessing fields of the global system table. +- `uefi::boot` is a new module that provides freestanding functions for + boot services using the global system table. - `uefi::runtime` is a new module that provides freestanding functions for runtime services using the global system table. - Add standard derives for `ConfigTableEntry`. diff --git a/uefi/src/boot.rs b/uefi/src/boot.rs new file mode 100644 index 000000000..3df02d827 --- /dev/null +++ b/uefi/src/boot.rs @@ -0,0 +1,99 @@ +//! UEFI boot services. +//! +//! These functions will panic if called after exiting boot services. + +use crate::data_types::PhysicalAddress; +use core::ptr::{self, NonNull}; +use uefi::{table, Result, StatusExt}; + +#[cfg(doc)] +use uefi::Status; + +pub use uefi::table::boot::AllocateType; +pub use uefi_raw::table::boot::MemoryType; + +fn boot_services_raw_panicking() -> NonNull { + let st = table::system_table_raw_panicking(); + // SAFETY: valid per requirements of `set_system_table`. + let st = unsafe { st.as_ref() }; + NonNull::new(st.boot_services).expect("boot services are not active") +} + +/// Allocates memory pages from the system. +/// +/// UEFI OS loaders should allocate memory of the type `LoaderData`. +/// +/// # Errors +/// +/// * [`Status::OUT_OF_RESOURCES`]: allocation failed. +/// * [`Status::INVALID_PARAMETER`]: `mem_ty` is [`MemoryType::PERSISTENT_MEMORY`], +/// [`MemoryType::UNACCEPTED`], or in the range [`MemoryType::MAX`]`..=0x6fff_ffff`. +/// * [`Status::NOT_FOUND`]: the requested pages could not be found. +pub fn allocate_pages(ty: AllocateType, mem_ty: MemoryType, count: usize) -> Result> { + let bt = boot_services_raw_panicking(); + let bt = unsafe { bt.as_ref() }; + + let (ty, mut addr) = match ty { + AllocateType::AnyPages => (0, 0), + AllocateType::MaxAddress(addr) => (1, addr), + AllocateType::Address(addr) => (2, addr), + }; + let addr = + unsafe { (bt.allocate_pages)(ty, mem_ty, count, &mut addr) }.to_result_with_val(|| addr)?; + let ptr = addr as *mut u8; + Ok(NonNull::new(ptr).expect("allocate_pages must not return a null pointer if successful")) +} + +/// Frees memory pages allocated by [`allocate_pages`]. +/// +/// # Safety +/// +/// The caller must ensure that no references into the allocation remain, +/// and that the memory at the allocation is not used after it is freed. +/// +/// # Errors +/// +/// * [`Status::NOT_FOUND`]: `ptr` was not allocated by [`allocate_pages`]. +/// * [`Status::INVALID_PARAMETER`]: `ptr` is not page aligned or is otherwise invalid. +pub unsafe fn free_pages(ptr: NonNull, count: usize) -> Result { + let bt = boot_services_raw_panicking(); + let bt = unsafe { bt.as_ref() }; + + let addr = ptr.as_ptr() as PhysicalAddress; + unsafe { (bt.free_pages)(addr, count) }.to_result() +} + +/// Allocates from a memory pool. The pointer will be 8-byte aligned. +/// +/// # Errors +/// +/// * [`Status::OUT_OF_RESOURCES`]: allocation failed. +/// * [`Status::INVALID_PARAMETER`]: `mem_ty` is [`MemoryType::PERSISTENT_MEMORY`], +/// [`MemoryType::UNACCEPTED`], or in the range [`MemoryType::MAX`]`..=0x6fff_ffff`. +pub fn allocate_pool(mem_ty: MemoryType, size: usize) -> Result> { + let bt = boot_services_raw_panicking(); + let bt = unsafe { bt.as_ref() }; + + let mut buffer = ptr::null_mut(); + let ptr = + unsafe { (bt.allocate_pool)(mem_ty, size, &mut buffer) }.to_result_with_val(|| buffer)?; + + Ok(NonNull::new(ptr).expect("allocate_pool must not return a null pointer if successful")) +} + +/// Frees memory allocated by [`allocate_pool`]. +/// +/// # Safety +/// +/// The caller must ensure that no references into the allocation remain, +/// and that the memory at the allocation is not used after it is freed. +/// +/// # Errors +/// +/// * [`Status::INVALID_PARAMETER`]: `ptr` is invalid. +pub unsafe fn free_pool(ptr: NonNull) -> Result { + let bt = boot_services_raw_panicking(); + let bt = unsafe { bt.as_ref() }; + + unsafe { (bt.free_pool)(ptr.as_ptr()) }.to_result() +} diff --git a/uefi/src/lib.rs b/uefi/src/lib.rs index 378be3b8d..4dc526893 100644 --- a/uefi/src/lib.rs +++ b/uefi/src/lib.rs @@ -104,6 +104,7 @@ extern crate uefi_raw; #[macro_use] pub mod data_types; pub mod allocator; +pub mod boot; #[cfg(feature = "alloc")] pub mod fs; pub mod helpers;