//! UEFI Memory Allocators //! //! This module provides a memory allocator that integrates with the UEFI pool //! allocator. It exports an `Allocator` type that wraps a System-Table //! together with a UEFI memory type and forwards memory requests to the UEFI //! pool allocator. //! //! The allocator implements the `core::alloc::Allocator` API defined by the //! rust standard library. Furthermore, as an alternative to this unstable //! standard library trait, raw alloc and dealloc functions are provided that //! map to their equivalents from the `raw` module. //! //! The `core::alloc::Allocator` trait is only implemented if the //! `allocator_api` feature is enabled. This requires a nightly / unstable //! compiler. If the feature is not enabled, only the raw interface is //! available. use r_efi::efi; /// Memory Allocator /// /// This crate implements a rust memory allocator that forwards requests to the /// UEFI pool allocator. It takes a System-Table as input, as well as the /// memory type to use as backing, and then forwards all memory allocation /// requests to the `AllocatePool()` UEFI system. /// /// The `core::alloc::Allocator` trait is implemented for this allocator. /// Hence, this allocator can also be used to back the global memory-allocator /// of `liballoc` (or `libstd`). See the `Global` type for an implementation of /// the global allocator, based on this type. pub struct Allocator { system_table: *mut efi::SystemTable, memory_type: efi::MemoryType, } impl Allocator { /// Create Allocator from UEFI System-Table /// /// This creates a new Allocator object from a UEFI System-Table pointer /// and the memory-type to use for allocations. That is, all allocations on /// this object will be tunnelled through the `AllocatePool` API on the /// given System-Table. Allocations will always use the memory type given /// as `memtype`. /// /// Note that this interface is unsafe, since the caller must guarantee /// that the System-Table is valid for as long as the Allocator is. /// Furthermore, the caller must guarantee validity of the /// system-table-interface. The latter is usually guaranteed by the /// provider of the System-Table. The former is usually just a matter of /// tearing down the allocator before returning from your application /// entry-point. pub unsafe fn from_system_table( st: *mut efi::SystemTable, memtype: efi::MemoryType, ) -> Allocator { Allocator { system_table: st, memory_type: memtype, } } /// Allocate Memory from UEFI Boot-Services /// /// Use the UEFI `allocate_pool` boot-services to request a block of memory /// satisfying the given memory layout. The memory type tied to this /// allocator object is used. /// /// This returns a null-pointer if the allocator could not serve the /// request (which on UEFI implies out-of-memory). Otherwise, a non-null /// pointer to the aligned block is returned. /// /// Safety /// ------ /// /// To ensure safety of this interface, the caller must guarantee: /// /// * The allocation size must not be 0. The function will panic /// otherwise. /// /// * The returned pointer is not necessarily the same pointer as returned /// by `allocate_pool` of the boot-services. A caller must not assume /// this when forwarding the pointer to other allocation services /// outside of this module. pub unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 { crate::raw::alloc(self.system_table, layout, self.memory_type) } /// Deallocate Memory from UEFI Boot-Services /// /// Use the UEFI `free_pool` boot-services to release a block of memory /// previously allocated through `alloc()`. /// /// Safety /// ------ /// /// To ensure safety of this interface, the caller must guarantee: /// /// * The memory block must be the same as previously returned by a call /// to `alloc()`. Every memory block must be released exactly once. /// /// * The passed layout must match the layout used to allocate the memory /// block. pub unsafe fn dealloc(&self, ptr: *mut u8, layout: core::alloc::Layout) { crate::raw::dealloc(self.system_table, ptr, layout) } } #[cfg(feature = "allocator_api")] unsafe impl core::alloc::Allocator for Allocator { fn allocate( &self, layout: core::alloc::Layout, ) -> Result, core::alloc::AllocError> { let size = layout.size(); let ptr = if size > 0 { unsafe { crate::raw::alloc(self.system_table, layout, self.memory_type) } } else { layout.dangling().as_ptr() as *mut _ }; if ptr.is_null() { Err(core::alloc::AllocError) } else { Ok( core::ptr::NonNull::new( core::ptr::slice_from_raw_parts(ptr, size) as *mut _, ).unwrap(), ) } } unsafe fn deallocate( &self, ptr: core::ptr::NonNull, layout: core::alloc::Layout, ) { if layout.size() != 0 { crate::raw::dealloc(self.system_table, ptr.as_ptr(), layout) } } }