3
3
//! If the `global_allocator` feature is enabled, the [`Allocator`] will be used
4
4
//! as the global Rust allocator.
5
5
//!
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.
13
9
14
10
use 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 } ;
18
13
14
+ use crate :: boot;
19
15
use crate :: mem:: memory_map:: MemoryType ;
20
16
use crate :: proto:: loaded_image:: LoadedImage ;
21
- use crate :: table:: boot:: BootServices ;
22
17
use crate :: table:: { Boot , SystemTable } ;
23
18
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 > ) { }
30
23
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 ( ) { }
33
28
34
- /// Initializes the allocator.
35
- ///
36
- /// # Safety
29
+ /// Get the memory type to use for allocation.
37
30
///
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 ) ;
42
38
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)
48
52
}
49
53
}
50
54
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
-
66
55
/// Allocator which uses the UEFI pool allocation functions.
67
56
///
68
57
/// Only valid for as long as the UEFI boot services are available.
69
58
#[ derive( Debug ) ]
70
59
pub struct Allocator ;
71
60
72
61
unsafe impl GlobalAlloc for Allocator {
73
- /// Allocate memory using [`BootServices ::allocate_pool`]. The allocation is
62
+ /// Allocate memory using [`boot ::allocate_pool`]. The allocation is
74
63
/// of type [`MemoryType::LOADER_DATA`] for UEFI applications, [`MemoryType::BOOT_SERVICES_DATA`]
75
64
/// for UEFI boot drivers and [`MemoryType::RUNTIME_SERVICES_DATA`] for UEFI runtime drivers.
76
65
unsafe fn alloc ( & self , layout : Layout ) -> * mut u8 {
66
+ if !boot:: are_boot_services_active ( ) {
67
+ return ptr:: null_mut ( ) ;
68
+ }
69
+
77
70
let size = layout. size ( ) ;
78
71
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 ( ) ;
82
73
83
74
if align > 8 {
84
75
// The requested alignment is greater than 8, but `allocate_pool` is
85
76
// only guaranteed to provide eight-byte alignment. Allocate extra
86
77
// space so that we can return an appropriately-aligned pointer
87
78
// 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
+ } ;
94
84
95
85
// Calculate the offset needed to get an aligned pointer within the
96
86
// full allocation. If that offset is zero, increase it to `align`
@@ -115,20 +105,24 @@ unsafe impl GlobalAlloc for Allocator {
115
105
// The requested alignment is less than or equal to eight, and
116
106
// `allocate_pool` always provides eight-byte alignment, so we can
117
107
// use `allocate_pool` directly.
118
- boot_services
119
- . allocate_pool ( memory_type, size)
108
+ boot:: allocate_pool ( memory_type, size)
120
109
. map ( |ptr| ptr. as_ptr ( ) )
121
110
. unwrap_or ( ptr:: null_mut ( ) )
122
111
}
123
112
}
124
113
125
- /// Deallocate memory using [`BootServices ::free_pool`].
114
+ /// Deallocate memory using [`boot ::free_pool`].
126
115
unsafe fn dealloc ( & self , mut ptr : * mut u8 , layout : Layout ) {
127
116
if layout. align ( ) > 8 {
128
117
// Retrieve the pointer to the full allocation that was packed right
129
118
// before the aligned allocation in `alloc`.
130
119
ptr = ( ptr as * const * mut u8 ) . sub ( 1 ) . read ( ) ;
131
120
}
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 ( ) ;
133
127
}
134
128
}
0 commit comments