r_efi_alloc/global.rs
1//! Global Allocator Bridge
2//!
3//! This module provides a bridge between the global-allocator interface of
4//! the rust standard library and the allocators of this crate. The stabilized
5//! interface of the rust compiler and standard-library to the global allocator
6//! is provided by the `core::alloc::GlobalAlloc` trait and the
7//! `global_allocator` attribute. The types provided by this module implement
8//! this trait and can be used to register a global allocator.
9//!
10//! Only one crate in every dependency graph can use the `global_allocator`
11//! attribute to mark one static variable as the global allocator of the entire
12//! application. The type of it must implement `GlobalAlloc`. Note that this
13//! attribute can only be used in the crate-root, not in sub-modules.
14//!
15//! UEFI is, however, not a natural fit for the global-allocator trait. On UEFI
16//! systems, access to all system APIs is done through the system table, which
17//! is passed as argument to the application entry-point. Therefore, it is up
18//! to the implementor of the entry-point to set up the global state inherent
19//! to rust's global allocator.
20//!
21//! # Examples
22//!
23//! The following UEFI application simply registers an allocator with its
24//! system-table and then invokes `uefi_run()`. The latter can then operate
25//! under the assumption that an allocator is available and ready. Once the
26//! function returns, the allocator is automatically torn down.
27//!
28//! This is a typical use of the `r-efi-alloc` crate. Only applications that
29//! actually exit the boot-services, or access UEFI outside of regular UEFI
30//! application and driver environments will have to use the custom allocator
31//! interfaces.
32//!
33//! ```ignore
34//! #![no_main]
35//! #![no_std]
36//!
37//! use r_efi::efi;
38//! use r_efi_alloc::{alloc::Allocator, global::Bridge};
39//!
40//! #[global_allocator]
41//! static GLOBAL_ALLOCATOR: Bridge = Bridge::new();
42//!
43//! #[no_mangle]
44//! pub extern "C" fn efi_main(
45//! h: efi::Handle,
46//! st: *mut efi::SystemTable,
47//! ) -> efi::Status {
48//! unsafe {
49//! let mut allocator = Allocator::from_system_table(st, efi::LOADER_DATA);
50//! let _attachment = GLOBAL_ALLOCATOR.attach(&mut allocator);
51//!
52//! efi_run(h, st)
53//! }
54//! }
55//!
56//! pub fn efi_run(h: efi::Handle, st: *mut efi::SystemTable) -> efi::Status {
57//! ...
58//! }
59//! ```
60
61use core::sync::atomic;
62
63/// Bridge for Global Allocators
64///
65/// This bridge connects static allocator variables to the dynamic UEFI
66/// allocator interfaces. The bridge object implements the `GlobalAlloc`
67/// interface and can thus be marked as `global_allocator`.
68///
69/// The need for a bridge arises from the fact that UEFI requires access to
70/// the system-table to allocate memory, and the system-table is only available
71/// as argument to the entry-point. Hence, this bridge represents a dynamic
72/// link between the global allocator and a runtime allocator created by the
73/// application.
74///
75/// The main API of the bridge is the `attach()` function, which allows to
76/// attach an allocator to the bridge, which is thereon used for allocations.
77/// Only a single allocator can be attached to a bridge at a time, and any
78/// global allocations will fail if no allocator is attached.
79///
80/// The `attach()` operation returns an object that represents the attachment.
81/// To release it, the attachment object has to be dropped. Note that the
82/// caller must ensure that any global allocator is released before an
83/// allocator attachment is released.
84pub struct Bridge {
85 attachment: atomic::AtomicPtr<crate::alloc::Allocator>,
86}
87
88/// Bridge Attachment
89///
90/// This type represents the attachment of an allocator to a bridge. It is
91/// returned by the `attach()` operation of a bridge. This type has no exposed
92/// API other than a custom `drop()` implementation, which releases the
93/// attachment.
94pub struct Attachment<'alloc, 'bridge> {
95 allocator: &'alloc mut crate::alloc::Allocator,
96 bridge: &'bridge Bridge,
97}
98
99impl Bridge {
100 /// Create Bridge
101 ///
102 /// The Bridge type represents the global allocator. Since the latter
103 /// cannot be instantiated at compile-time (on UEFI the system-table
104 /// address can only be resolved at runtime, since it is passed as argument
105 /// to the entry point), it is implemented as a bridge between the actual
106 /// allocator object and the global allocator. By default, the bridge
107 /// object has no allocator linked. Any allocation requests will thusly
108 /// yield an allocation error.
109 ///
110 /// To make use of a bridge, you have to instantiate an allocator object
111 /// and attach it via the `attach()` method.
112 ///
113 /// You can create as many bridges as you like. However, to mark a bridge
114 /// as global allocator, you have to make it a global, static variable and
115 /// annotate it with `#[global_allocator]`. Only one such variable is
116 /// allowed to exist in any crate tree, and it must be declared in the root
117 /// module of a given crate.
118 pub const fn new() -> Bridge {
119 Bridge {
120 attachment: atomic::AtomicPtr::new(core::ptr::null_mut()),
121 }
122 }
123
124 unsafe fn raw_attach(&self, ptr: *mut crate::alloc::Allocator) -> Option<()> {
125 // Set @ptr as the attachment on this bridge. This only succeeds if
126 // there is not already an attachment set.
127 // We use a compare_exchange() to change the attachment if it was NULL.
128 // We use Release semantics, so any stores to your allocator are
129 // visible once the attachment is written. On error, no ordering
130 // guarantees are given, since this interface is not meant to be a
131 // programmatic query.
132 // Note that the Release pairs with the Acquire in the GlobalAlloc
133 // trait below.
134 //
135 // This interface is unsafe since the caller must guarantee to detach
136 // the bridge before it is destroyed. There are no runtime guarantees
137 // given by this interface, it is all left to the caller.
138 let p = self.attachment.compare_exchange(
139 core::ptr::null_mut(),
140 ptr,
141 atomic::Ordering::Release,
142 atomic::Ordering::Relaxed,
143 );
144
145 if p.is_ok() {
146 Some(())
147 } else {
148 None
149 }
150 }
151
152 unsafe fn raw_detach(&self, ptr: *mut crate::alloc::Allocator) {
153 // Detach @ptr from this bridge. The caller must guarantee @ptr is
154 // already attached to the bridge. This function will panic if @ptr is
155 // not the current attachment.
156 //
157 // We use compare_exchange() to replace the old attachment with NULL.
158 // If it was not NULL, we panic. No ordering guarantees are required,
159 // since there is no dependent state.
160 let p = self.attachment.compare_exchange(
161 ptr,
162 core::ptr::null_mut(),
163 atomic::Ordering::Relaxed,
164 atomic::Ordering::Relaxed,
165 );
166 assert!(p.is_ok());
167 }
168
169 /// Attach an allocator
170 ///
171 /// This attaches the allocator given as @allocator to the bridge. If there
172 /// is an allocator attached already, this will yield `None`. Otherwise, an
173 /// attachment is returned that represents this link. Dropping the
174 /// attachment will detach the allocator from the bridge.
175 ///
176 /// As long as an allocator is attached to a bridge, allocations through
177 /// this bridge (via rust's `GlobalAlloc` trait) will be served by this
178 /// allocator.
179 ///
180 /// This is an unsafe interface. It is the caller's responsibility to
181 /// guarantee that the attachment survives all outstanding allocations.
182 /// That is, any allocated memory must be released before detaching the
183 /// allocator.
184 pub unsafe fn attach<'alloc, 'bridge>(
185 &'bridge self,
186 allocator: &'alloc mut crate::alloc::Allocator,
187 ) -> Option<Attachment<'alloc, 'bridge>> {
188 match self.raw_attach(allocator) {
189 None => None,
190 Some(()) => Some(Attachment {
191 allocator: allocator,
192 bridge: self,
193 }),
194 }
195 }
196}
197
198impl<'alloc, 'bridge> Drop for Attachment<'alloc, 'bridge> {
199 fn drop(&mut self) {
200 unsafe {
201 self.bridge.raw_detach(self.allocator);
202 }
203 }
204}
205
206// This implements GlobalAlloc for our bridge. This trait is used by the rust
207// ecosystem to serve global memory allocations. For this to work, you must
208// have a bridge as static variable annotated as `#[global_allocator]`.
209//
210// We simply forward all allocation requests to the attached allocator. If the
211// allocator is NULL, we fail the allocations.
212//
213// Note that the bridge interface must guarantee that an attachment survives
214// all allocations. That is, you must drop/deallocate all memory before
215// dropping your attachment. See the description of the bridge interface for
216// details.
217unsafe impl core::alloc::GlobalAlloc for Bridge {
218 unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 {
219 let allocator = self.attachment.load(atomic::Ordering::Acquire);
220
221 if allocator.is_null() {
222 return core::ptr::null_mut();
223 }
224
225 (&*allocator).alloc(layout)
226 }
227
228 unsafe fn dealloc(&self, ptr: *mut u8, layout: core::alloc::Layout) {
229 let allocator = self.attachment.load(atomic::Ordering::Acquire);
230
231 assert!(!allocator.is_null());
232
233 (&*allocator).dealloc(ptr, layout)
234 }
235}