summaryrefslogtreecommitdiffstats
path: root/src/memory.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/memory.rs196
1 files changed, 196 insertions, 0 deletions
diff --git a/src/memory.rs b/src/memory.rs
new file mode 100644
index 0000000..d223f69
--- /dev/null
+++ b/src/memory.rs
@@ -0,0 +1,196 @@
1use crate::uefi::*;
2use core::alloc::{GlobalAlloc, Layout};
3use core::mem;
4use core::ptr;
5
6const MAX_MAP_ENTRIES: usize = 512;
7
8static mut MEMORY_MAP: [MemoryDescriptor; MAX_MAP_ENTRIES] = unsafe { mem::zeroed() };
9static mut MAP_ENTRIES: usize = 0;
10
11/// The map key returned by GetMemoryMap, needed for ExitBootServices.
12static mut MAP_KEY: usize = 0;
13
14/// Static working buffer for GetMemoryMap (64 KB, way more than enough).
15/// Lives in .bss; no pool allocation, so no map-key staleness.
16static mut MAP_WORK_BUF: [u8; 65536] = [0u8; 65536];
17
18// ---------------------------------------------------------------------------
19// Bump allocator — no deallocation, just pointer advancement.
20// Used as Rust's #[global_allocator] once the heap region is set up.
21// ---------------------------------------------------------------------------
22struct BumpAllocator;
23
24unsafe impl Sync for BumpAllocator {}
25
26unsafe impl GlobalAlloc for BumpAllocator {
27 unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
28 let align = layout.align().max(16);
29 let size = layout.size();
30 unsafe {
31 let a = (HEAP_CURSOR + align - 1) & !(align - 1);
32 let e = a + size;
33 if e > HEAP_END || e < a {
34 return ptr::null_mut();
35 }
36 HEAP_CURSOR = e;
37 a as *mut u8
38 }
39 }
40
41 unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {
42 // Bump allocator — cannot free individual allocations.
43 // Memory is reclaimed on reboot. Good enough for now.
44 unsafe {
45 core::ptr::read_volatile(&_ptr);
46 }
47 let _ = _layout;
48 }
49}
50
51#[global_allocator]
52static ALLOCATOR: BumpAllocator = BumpAllocator;
53
54/// Heap bump allocator bounds (`static mut` accessed only inside
55/// `GlobalAlloc` impl, which is already `unsafe`).
56static mut HEAP_START: usize = 0;
57static mut HEAP_END: usize = 0;
58static mut HEAP_CURSOR: usize = 0;
59
60/// Capture the UEFI memory map into the static buffer. Uses a pre-allocated
61/// .bss working buffer so no pool allocation (and thus no memory-map change)
62/// occurs between GetMemoryMap and ExitBootServices.
63pub fn capture_memory_map(bs: &BootServices) -> Result<usize, Status> {
64 let mut map_key: usize = 0;
65 let mut desc_size: usize = 0;
66 let mut desc_ver: u32 = 0;
67
68 // Probe with null to get required size + descriptor size
69 let mut buf_size: usize = 0;
70 let status = unsafe {
71 (bs.get_memory_map)(
72 &mut buf_size,
73 ptr::null_mut(),
74 &mut map_key,
75 &mut desc_size,
76 &mut desc_ver,
77 )
78 };
79 if status != EFI_BUFFER_TOO_SMALL {
80 // Fallback: allocate a sensible size
81 buf_size = 64 * 1024;
82 }
83 if desc_size == 0 {
84 desc_size = mem::size_of::<MemoryDescriptor>();
85 }
86
87 // Round up to desc_size alignment
88 let buf_size = (buf_size + desc_size - 1) / desc_size * desc_size;
89
90 // If our static buffer is too small, fall back to pool (rare)
91 if buf_size > unsafe { MAP_WORK_BUF.len() } {
92 return Err(EFI_BUFFER_TOO_SMALL);
93 }
94
95 let mut actual_size = buf_size;
96 let status = unsafe {
97 (bs.get_memory_map)(
98 &mut actual_size,
99 &raw mut MAP_WORK_BUF[0] as *mut MemoryDescriptor,
100 &mut map_key,
101 &mut desc_size,
102 &mut desc_ver,
103 )
104 };
105
106 if status == EFI_BUFFER_TOO_SMALL {
107 // The map grew between the probe and the real call. Try a larger
108 // static buffer (can't grow here without pool, so return error to
109 // let the caller retry if they can allocate pool).
110 // For our 64 KB buffer this should never happen on OVMF.
111 return Err(EFI_BUFFER_TOO_SMALL);
112 }
113 if status != EFI_SUCCESS {
114 return Err(status);
115 }
116
117 // Copy descriptors into our indexed array
118 let count = actual_size / desc_size;
119 let count = count.min(MAX_MAP_ENTRIES);
120 unsafe {
121 for i in 0..count {
122 let p = (&raw const MAP_WORK_BUF[..]) as *const u8;
123 let off = i * desc_size;
124 let src = p.add(off) as *const MemoryDescriptor;
125 MEMORY_MAP[i] = ptr::read(src);
126 }
127 MAP_ENTRIES = count;
128 MAP_KEY = map_key;
129 }
130
131 // Do NOT free anything — the static buffer stays. Map key is still
132 // valid because no pool allocation/free happened after GetMemoryMap.
133
134 init_bump_allocator();
135 Ok(map_key)
136}
137
138/// Retry ExitBootServices with a fresh memory map.
139pub fn refresh_and_exit(bs: &BootServices, image_handle: Handle) -> Result<usize, Status> {
140 let key = capture_memory_map(bs)?;
141 let status = unsafe { (bs.exit_boot_services)(image_handle, key) };
142 if status == EFI_SUCCESS {
143 Ok(key)
144 } else {
145 Err(status)
146 }
147}
148
149fn init_bump_allocator() {
150 unsafe {
151 let mut best_start = 0usize;
152 let mut best_size = 0usize;
153 for i in 0..MAP_ENTRIES {
154 let d = &MEMORY_MAP[i];
155 if d.kind == MEMORY_TYPE_CONVENTIONAL {
156 let sz = d.number_of_pages as usize * 4096;
157 if sz > best_size {
158 best_start = d.physical_start as usize;
159 best_size = sz;
160 }
161 }
162 }
163 if best_size > 2 * 1024 * 1024 {
164 let s = best_start + 2 * 1024 * 1024;
165 let e = best_start + best_size;
166 if s < e {
167 HEAP_START = s;
168 HEAP_END = e;
169 HEAP_CURSOR = s;
170 }
171 }
172 }
173}
174
175#[allow(dead_code)]
176pub fn allocate(size: usize) -> *mut u8 {
177 unsafe {
178 let a = (HEAP_CURSOR + 15) & !15;
179 let e = a + size;
180 if e > HEAP_END || e < a {
181 return ptr::null_mut();
182 }
183 HEAP_CURSOR = e;
184 a as *mut u8
185 }
186}
187
188#[allow(dead_code)]
189pub fn get_map() -> (&'static [MemoryDescriptor], usize) {
190 unsafe {
191 (
192 core::slice::from_raw_parts(&MEMORY_MAP[0], MAP_ENTRIES),
193 MAP_KEY,
194 )
195 }
196}