r_efi_alloc/
alloc.rs

1//! UEFI Memory Allocators
2//!
3//! This module provides a memory allocator that integrates with the UEFI pool
4//! allocator. It exports an `Allocator` type that wraps a System-Table
5//! together with a UEFI memory type and forwards memory requests to the UEFI
6//! pool allocator.
7//!
8//! The allocator implements the `core::alloc::Allocator` API defined by the
9//! rust standard library. Furthermore, as an alternative to this unstable
10//! standard library trait, raw alloc and dealloc functions are provided that
11//! map to their equivalents from the `raw` module.
12//!
13//! The `core::alloc::Allocator` trait is only implemented if the
14//! `allocator_api` feature is enabled. This requires a nightly / unstable
15//! compiler. If the feature is not enabled, only the raw interface is
16//! available.
17
18use r_efi::efi;
19
20/// Memory Allocator
21///
22/// This crate implements a rust memory allocator that forwards requests to the
23/// UEFI pool allocator. It takes a System-Table as input, as well as the
24/// memory type to use as backing, and then forwards all memory allocation
25/// requests to the `AllocatePool()` UEFI system.
26///
27/// The `core::alloc::Allocator` trait is implemented for this allocator.
28/// Hence, this allocator can also be used to back the global memory-allocator
29/// of `liballoc` (or `libstd`). See the `Global` type for an implementation of
30/// the global allocator, based on this type.
31pub struct Allocator {
32    system_table: *mut efi::SystemTable,
33    memory_type: efi::MemoryType,
34}
35
36impl Allocator {
37    /// Create Allocator from UEFI System-Table
38    ///
39    /// This creates a new Allocator object from a UEFI System-Table pointer
40    /// and the memory-type to use for allocations. That is, all allocations on
41    /// this object will be tunnelled through the `AllocatePool` API on the
42    /// given System-Table. Allocations will always use the memory type given
43    /// as `memtype`.
44    ///
45    /// Note that this interface is unsafe, since the caller must guarantee
46    /// that the System-Table is valid for as long as the Allocator is.
47    /// Furthermore, the caller must guarantee validity of the
48    /// system-table-interface. The latter is usually guaranteed by the
49    /// provider of the System-Table. The former is usually just a matter of
50    /// tearing down the allocator before returning from your application
51    /// entry-point.
52    pub unsafe fn from_system_table(
53        st: *mut efi::SystemTable,
54        memtype: efi::MemoryType,
55    ) -> Allocator {
56        Allocator {
57            system_table: st,
58            memory_type: memtype,
59        }
60    }
61
62    /// Allocate Memory from UEFI Boot-Services
63    ///
64    /// Use the UEFI `allocate_pool` boot-services to request a block of memory
65    /// satisfying the given memory layout. The memory type tied to this
66    /// allocator object is used.
67    ///
68    /// This returns a null-pointer if the allocator could not serve the
69    /// request (which on UEFI implies out-of-memory). Otherwise, a non-null
70    /// pointer to the aligned block is returned.
71    ///
72    /// Safety
73    /// ------
74    ///
75    /// To ensure safety of this interface, the caller must guarantee:
76    ///
77    ///  * The allocation size must not be 0. The function will panic
78    ///    otherwise.
79    ///
80    ///  * The returned pointer is not necessarily the same pointer as returned
81    ///    by `allocate_pool` of the boot-services. A caller must not assume
82    ///    this when forwarding the pointer to other allocation services
83    ///    outside of this module.
84    pub unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 {
85        crate::raw::alloc(self.system_table, layout, self.memory_type)
86    }
87
88    /// Deallocate Memory from UEFI Boot-Services
89    ///
90    /// Use the UEFI `free_pool` boot-services to release a block of memory
91    /// previously allocated through `alloc()`.
92    ///
93    /// Safety
94    /// ------
95    ///
96    /// To ensure safety of this interface, the caller must guarantee:
97    ///
98    ///  * The memory block must be the same as previously returned by a call
99    ///    to `alloc()`. Every memory block must be released exactly once.
100    ///
101    ///  * The passed layout must match the layout used to allocate the memory
102    ///    block.
103    pub unsafe fn dealloc(&self, ptr: *mut u8, layout: core::alloc::Layout) {
104        crate::raw::dealloc(self.system_table, ptr, layout)
105    }
106}
107
108#[cfg(feature = "allocator_api")]
109unsafe impl core::alloc::Allocator for Allocator {
110    fn allocate(
111        &self,
112        layout: core::alloc::Layout,
113    ) -> Result<core::ptr::NonNull<[u8]>, core::alloc::AllocError> {
114        let size = layout.size();
115
116        let ptr = if size > 0 {
117            unsafe {
118                crate::raw::alloc(self.system_table, layout, self.memory_type)
119            }
120        } else {
121            layout.dangling().as_ptr() as *mut _
122        };
123
124        if ptr.is_null() {
125            Err(core::alloc::AllocError)
126        } else {
127            Ok(
128                core::ptr::NonNull::new(
129                    core::ptr::slice_from_raw_parts(ptr, size) as *mut _,
130                ).unwrap(),
131            )
132        }
133    }
134
135    unsafe fn deallocate(
136        &self,
137        ptr: core::ptr::NonNull<u8>,
138        layout: core::alloc::Layout,
139    ) {
140        if layout.size() != 0 {
141            crate::raw::dealloc(self.system_table, ptr.as_ptr(), layout)
142        }
143    }
144}