33//! If the `global_allocator` feature is enabled, the [`Allocator`] will be used
44//! as the global Rust allocator.
55//!
6- //! # Usage
7- //!
8- //! Call the `init` function with a reference to the boot services table.
9- //! Failure to do so before calling a memory allocating function will panic.
10- //!
11- //! Call the `exit_boot_services` function before exiting UEFI boot services.
12- //! Failure to do so will turn subsequent allocation into undefined behaviour.
6+ //! This allocator can only be used while boot services are active. If boot
7+ //! services are not active, `alloc` will return a null pointer, and `dealloc`
8+ //! will panic.
139
1410use core:: alloc:: { GlobalAlloc , Layout } ;
15- use core:: ffi:: c_void;
16- use core:: ptr;
17- use core:: sync:: atomic:: { AtomicPtr , AtomicU32 , Ordering } ;
11+ use core:: ptr:: { self , NonNull } ;
12+ use core:: sync:: atomic:: { AtomicU32 , Ordering } ;
1813
14+ use crate :: boot;
1915use crate :: mem:: memory_map:: MemoryType ;
2016use crate :: proto:: loaded_image:: LoadedImage ;
21- use crate :: table:: boot:: BootServices ;
2217use crate :: table:: { Boot , SystemTable } ;
2318
24- /// Reference to the system table, used to call the boot services pool memory
25- /// allocation functions.
26- ///
27- /// The pointer is only safe to dereference if UEFI boot services have not been
28- /// exited by the host application yet.
29- static SYSTEM_TABLE : AtomicPtr < c_void > = AtomicPtr :: new ( ptr:: null_mut ( ) ) ;
19+ /// Deprecated; this function is now a no-op.
20+ #[ deprecated = "this function is now a no-op" ]
21+ #[ allow( unused_unsafe, clippy:: missing_safety_doc) ]
22+ pub unsafe fn init ( _: & mut SystemTable < Boot > ) { }
3023
31- /// The memory type used for pool memory allocations.
32- static MEMORY_TYPE : AtomicU32 = AtomicU32 :: new ( MemoryType :: LOADER_DATA . 0 ) ;
24+ /// Deprecated; this function is now a no-op.
25+ #[ deprecated = "this function is now a no-op" ]
26+ #[ allow( clippy:: missing_const_for_fn) ]
27+ pub fn exit_boot_services ( ) { }
3328
34- /// Initializes the allocator.
35- ///
36- /// # Safety
29+ /// Get the memory type to use for allocation.
3730///
38- /// This function is unsafe because you _must_ make sure that exit_boot_services
39- /// will be called when UEFI boot services will be exited.
40- pub unsafe fn init ( system_table : & mut SystemTable < Boot > ) {
41- SYSTEM_TABLE . store ( system_table. as_ptr ( ) . cast_mut ( ) , Ordering :: Release ) ;
31+ /// The first time this is called, the data type of the loaded image will be
32+ /// retrieved. That value is cached in a static and reused on subsequent
33+ /// calls. If the memory type of the loaded image cannot be retrieved for some
34+ /// reason, a default of `LOADER_DATA` is used.
35+ fn get_memory_type ( ) -> MemoryType {
36+ // Initialize to a `RESERVED` to indicate the actual value hasn't been set yet.
37+ static MEMORY_TYPE : AtomicU32 = AtomicU32 :: new ( MemoryType :: RESERVED . 0 ) ;
4238
43- let boot_services = system_table. boot_services ( ) ;
44- if let Ok ( loaded_image) =
45- boot_services. open_protocol_exclusive :: < LoadedImage > ( boot_services. image_handle ( ) )
46- {
47- MEMORY_TYPE . store ( loaded_image. data_type ( ) . 0 , Ordering :: Release ) ;
39+ let memory_type = MEMORY_TYPE . load ( Ordering :: Acquire ) ;
40+ if memory_type == MemoryType :: RESERVED . 0 {
41+ let memory_type = if let Ok ( loaded_image) =
42+ boot:: open_protocol_exclusive :: < LoadedImage > ( boot:: image_handle ( ) )
43+ {
44+ loaded_image. data_type ( )
45+ } else {
46+ MemoryType :: LOADER_DATA
47+ } ;
48+ MEMORY_TYPE . store ( memory_type. 0 , Ordering :: Release ) ;
49+ memory_type
50+ } else {
51+ MemoryType ( memory_type)
4852 }
4953}
5054
51- /// Access the boot services
52- fn boot_services ( ) -> * const BootServices {
53- let ptr = SYSTEM_TABLE . load ( Ordering :: Acquire ) ;
54- let system_table =
55- unsafe { SystemTable :: from_ptr ( ptr) } . expect ( "The system table handle is not available" ) ;
56- system_table. boot_services ( )
57- }
58-
59- /// Notify the allocator library that boot services are not safe to call anymore
60- ///
61- /// You must arrange for this function to be called on exit from UEFI boot services
62- pub fn exit_boot_services ( ) {
63- SYSTEM_TABLE . store ( ptr:: null_mut ( ) , Ordering :: Release ) ;
64- }
65-
6655/// Allocator which uses the UEFI pool allocation functions.
6756///
6857/// Only valid for as long as the UEFI boot services are available.
6958#[ derive( Debug ) ]
7059pub struct Allocator ;
7160
7261unsafe impl GlobalAlloc for Allocator {
73- /// Allocate memory using [`BootServices ::allocate_pool`]. The allocation is
62+ /// Allocate memory using [`boot ::allocate_pool`]. The allocation is
7463 /// of type [`MemoryType::LOADER_DATA`] for UEFI applications, [`MemoryType::BOOT_SERVICES_DATA`]
7564 /// for UEFI boot drivers and [`MemoryType::RUNTIME_SERVICES_DATA`] for UEFI runtime drivers.
7665 unsafe fn alloc ( & self , layout : Layout ) -> * mut u8 {
66+ if !boot:: are_boot_services_active ( ) {
67+ return ptr:: null_mut ( ) ;
68+ }
69+
7770 let size = layout. size ( ) ;
7871 let align = layout. align ( ) ;
79- let memory_type = MemoryType ( MEMORY_TYPE . load ( Ordering :: Acquire ) ) ;
80-
81- let boot_services = & * boot_services ( ) ;
72+ let memory_type = get_memory_type ( ) ;
8273
8374 if align > 8 {
8475 // The requested alignment is greater than 8, but `allocate_pool` is
8576 // only guaranteed to provide eight-byte alignment. Allocate extra
8677 // space so that we can return an appropriately-aligned pointer
8778 // within the allocation.
88- let full_alloc_ptr =
89- if let Ok ( ptr) = boot_services. allocate_pool ( memory_type, size + align) {
90- ptr. as_ptr ( )
91- } else {
92- return ptr:: null_mut ( ) ;
93- } ;
79+ let full_alloc_ptr = if let Ok ( ptr) = boot:: allocate_pool ( memory_type, size + align) {
80+ ptr. as_ptr ( )
81+ } else {
82+ return ptr:: null_mut ( ) ;
83+ } ;
9484
9585 // Calculate the offset needed to get an aligned pointer within the
9686 // full allocation. If that offset is zero, increase it to `align`
@@ -115,20 +105,24 @@ unsafe impl GlobalAlloc for Allocator {
115105 // The requested alignment is less than or equal to eight, and
116106 // `allocate_pool` always provides eight-byte alignment, so we can
117107 // use `allocate_pool` directly.
118- boot_services
119- . allocate_pool ( memory_type, size)
108+ boot:: allocate_pool ( memory_type, size)
120109 . map ( |ptr| ptr. as_ptr ( ) )
121110 . unwrap_or ( ptr:: null_mut ( ) )
122111 }
123112 }
124113
125- /// Deallocate memory using [`BootServices ::free_pool`].
114+ /// Deallocate memory using [`boot ::free_pool`].
126115 unsafe fn dealloc ( & self , mut ptr : * mut u8 , layout : Layout ) {
127116 if layout. align ( ) > 8 {
128117 // Retrieve the pointer to the full allocation that was packed right
129118 // before the aligned allocation in `alloc`.
130119 ptr = ( ptr as * const * mut u8 ) . sub ( 1 ) . read ( ) ;
131120 }
132- ( * boot_services ( ) ) . free_pool ( ptr) . unwrap ( ) ;
121+
122+ // OK to unwrap: `ptr` is required to be a valid allocation by the trait API.
123+ let ptr = NonNull :: new ( ptr) . unwrap ( ) ;
124+
125+ // Warning: this will panic after exiting boot services.
126+ boot:: free_pool ( ptr) . unwrap ( ) ;
133127 }
134128}
0 commit comments