12
12
//! Failure to do so will turn subsequent allocation into undefined behaviour.
13
13
14
14
use core:: alloc:: { GlobalAlloc , Layout } ;
15
- use core:: ptr:: { self , NonNull } ;
15
+ use core:: ffi:: c_void;
16
+ use core:: ptr;
17
+ use core:: sync:: atomic:: { AtomicPtr , AtomicU32 , Ordering } ;
16
18
17
19
use crate :: proto:: loaded_image:: LoadedImage ;
18
20
use crate :: table:: boot:: { BootServices , MemoryType } ;
21
+ use crate :: table:: { Boot , SystemTable } ;
19
22
20
- /// Reference to the boot services table, used to call the pool memory allocation functions.
23
+ /// Reference to the system table, used to call the boot services pool memory
24
+ /// allocation functions.
21
25
///
22
- /// The inner pointer is only safe to dereference if UEFI boot services have not been
26
+ /// The pointer is only safe to dereference if UEFI boot services have not been
23
27
/// exited by the host application yet.
24
- static mut BOOT_SERVICES : Option < NonNull < BootServices > > = None ;
28
+ static SYSTEM_TABLE : AtomicPtr < c_void > = AtomicPtr :: new ( ptr :: null_mut ( ) ) ;
25
29
26
30
/// The memory type used for pool memory allocations.
27
- /// TODO: Use OnceCell when stablilized.
28
- static mut MEMORY_TYPE : MemoryType = MemoryType :: LOADER_DATA ;
31
+ static MEMORY_TYPE : AtomicU32 = AtomicU32 :: new ( MemoryType :: LOADER_DATA . 0 ) ;
29
32
30
33
/// Initializes the allocator.
31
34
///
32
35
/// # Safety
33
36
///
34
37
/// This function is unsafe because you _must_ make sure that exit_boot_services
35
38
/// will be called when UEFI boot services will be exited.
36
- pub unsafe fn init ( boot_services : & BootServices ) {
37
- BOOT_SERVICES = NonNull :: new ( boot_services as * const _ as * mut _ ) ;
39
+ pub unsafe fn init ( system_table : & mut SystemTable < Boot > ) {
40
+ SYSTEM_TABLE . store ( system_table . as_ptr ( ) . cast_mut ( ) , Ordering :: Release ) ;
38
41
42
+ let boot_services = system_table. boot_services ( ) ;
39
43
if let Ok ( loaded_image) =
40
44
boot_services. open_protocol_exclusive :: < LoadedImage > ( boot_services. image_handle ( ) )
41
45
{
42
- MEMORY_TYPE = loaded_image. data_type ( )
46
+ MEMORY_TYPE . store ( loaded_image. data_type ( ) . 0 , Ordering :: Release ) ;
43
47
}
44
48
}
45
49
46
50
/// Access the boot services
47
- fn boot_services ( ) -> NonNull < BootServices > {
48
- unsafe { BOOT_SERVICES . expect ( "Boot services are unavailable or have been exited" ) }
51
+ fn boot_services ( ) -> * const BootServices {
52
+ let ptr = SYSTEM_TABLE . load ( Ordering :: Acquire ) ;
53
+ let system_table =
54
+ unsafe { SystemTable :: from_ptr ( ptr) } . expect ( "The system table handle is not available" ) ;
55
+ system_table. boot_services ( )
49
56
}
50
57
51
58
/// Notify the allocator library that boot services are not safe to call anymore
52
59
///
53
60
/// You must arrange for this function to be called on exit from UEFI boot services
54
61
pub fn exit_boot_services ( ) {
55
- unsafe {
56
- BOOT_SERVICES = None ;
57
- }
62
+ SYSTEM_TABLE . store ( ptr:: null_mut ( ) , Ordering :: Release ) ;
58
63
}
59
64
60
65
/// Allocator which uses the UEFI pool allocation functions.
@@ -70,20 +75,21 @@ unsafe impl GlobalAlloc for Allocator {
70
75
unsafe fn alloc ( & self , layout : Layout ) -> * mut u8 {
71
76
let size = layout. size ( ) ;
72
77
let align = layout. align ( ) ;
78
+ let memory_type = MemoryType ( MEMORY_TYPE . load ( Ordering :: Acquire ) ) ;
79
+
80
+ let boot_services = & * boot_services ( ) ;
73
81
74
82
if align > 8 {
75
83
// The requested alignment is greater than 8, but `allocate_pool` is
76
84
// only guaranteed to provide eight-byte alignment. Allocate extra
77
85
// space so that we can return an appropriately-aligned pointer
78
86
// within the allocation.
79
- let full_alloc_ptr = if let Ok ( ptr) = boot_services ( )
80
- . as_ref ( )
81
- . allocate_pool ( MEMORY_TYPE , size + align)
82
- {
83
- ptr
84
- } else {
85
- return ptr:: null_mut ( ) ;
86
- } ;
87
+ let full_alloc_ptr =
88
+ if let Ok ( ptr) = boot_services. allocate_pool ( memory_type, size + align) {
89
+ ptr
90
+ } else {
91
+ return ptr:: null_mut ( ) ;
92
+ } ;
87
93
88
94
// Calculate the offset needed to get an aligned pointer within the
89
95
// full allocation. If that offset is zero, increase it to `align`
@@ -108,9 +114,8 @@ unsafe impl GlobalAlloc for Allocator {
108
114
// The requested alignment is less than or equal to eight, and
109
115
// `allocate_pool` always provides eight-byte alignment, so we can
110
116
// use `allocate_pool` directly.
111
- boot_services ( )
112
- . as_ref ( )
113
- . allocate_pool ( MEMORY_TYPE , size)
117
+ boot_services
118
+ . allocate_pool ( memory_type, size)
114
119
. unwrap_or ( ptr:: null_mut ( ) )
115
120
}
116
121
}
@@ -122,7 +127,7 @@ unsafe impl GlobalAlloc for Allocator {
122
127
// before the aligned allocation in `alloc`.
123
128
ptr = ( ptr as * const * mut u8 ) . sub ( 1 ) . read ( ) ;
124
129
}
125
- boot_services ( ) . as_ref ( ) . free_pool ( ptr) . unwrap ( ) ;
130
+ ( * boot_services ( ) ) . free_pool ( ptr) . unwrap ( ) ;
126
131
}
127
132
}
128
133
0 commit comments