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}