drivers/standard_library/
memory.rs1use core::{
2 cell::RefCell,
3 ffi::c_void,
4 mem::MaybeUninit,
5 ptr::{NonNull, null_mut},
6};
7
8use libc::{
9 _SC_PAGE_SIZE, MAP_32BIT, MAP_ANONYMOUS, MAP_FAILED, MAP_PRIVATE, PROT_EXEC, PROT_READ,
10 PROT_WRITE, mmap, mremap, munmap, sysconf,
11};
12use linked_list_allocator::Heap;
13use memory::{Capabilities, Layout, ManagerTrait};
14use synchronization::blocking_mutex::{CriticalSectionMutex, Mutex};
15
16const INITIAL_HEAP_SIZE: usize = 1024 * 1024; struct Region {
20 pub heap: Heap,
21 pub capabilities: Capabilities,
22 pub slice: &'static mut [MaybeUninit<u8>],
23}
24
25pub struct MemoryManager {
26 regions: CriticalSectionMutex<RefCell<[Region; 2]>>,
27}
28
29impl Default for MemoryManager {
30 fn default() -> Self {
31 Self::new()
32 }
33}
34
35impl MemoryManager {
36 pub const fn new() -> Self {
37 MemoryManager {
38 regions: Mutex::new(RefCell::new([
39 Region {
40 heap: Heap::empty(),
41 capabilities: Capabilities::new(false, false),
42 slice: &mut [],
43 },
44 Region {
45 heap: Heap::empty(),
46 capabilities: Capabilities::new(true, false),
47 slice: &mut [],
48 },
49 ])),
50 }
51 }
52}
53
54impl Drop for MemoryManager {
55 fn drop(&mut self) {
56 self.regions.lock(|regions| {
57 let mut regions = regions.borrow_mut();
58 for region in regions.iter_mut() {
59 if !region.slice.is_empty() {
60 unsafe {
61 unmap(region.slice.as_mut_ptr(), region.slice.len());
62 }
63 }
64 }
65 });
66 }
67}
68
69impl ManagerTrait for MemoryManager {
70 unsafe fn allocate(&self, capabilities: Capabilities, layout: Layout) -> Option<NonNull<u8>> {
71 unsafe {
72 self.regions.lock(|regions| {
73 let mut regions = regions.try_borrow_mut().ok()?;
74
75 let capable_regions = regions
77 .iter_mut()
78 .filter(|region| region.capabilities.is_superset_of(capabilities));
79
80 for region in capable_regions {
82 let result = region.heap.allocate_first_fit(layout).ok();
83 if result.is_some() {
84 return result;
85 }
86 }
87
88 let capable_regions = regions
89 .iter_mut()
90 .filter(|region| region.capabilities.is_superset_of(capabilities));
91
92 for region in capable_regions {
94 if expand(region, layout.size()) {
96 let result = region.heap.allocate_first_fit(layout).ok();
98 if result.is_some() {
99 return result;
100 }
101 }
102 }
103
104 None
105 })
106 }
107 }
108
109 unsafe fn deallocate(&self, pointer: NonNull<u8>, layout: Layout) {
110 unsafe {
111 self.regions.lock(|regions| {
112 let mut regions = regions.borrow_mut();
113
114 let region = regions
115 .iter_mut()
116 .find(|region| is_slice_within_region(region, pointer, layout));
117
118 if let Some(region) = region {
119 region.heap.deallocate(pointer, layout);
120 } else {
121 panic!("Pointer not found in any region");
122 }
123 });
124 }
125 }
126
127 unsafe fn get_used(&self) -> usize {
128 self.regions.lock(|regions| {
129 let regions = regions.borrow();
130 let mut used = 0;
131 for region in regions.iter() {
132 used += region.heap.used();
133 }
134 used
135 })
136 }
137
138 unsafe fn get_free(&self) -> usize {
139 self.regions.lock(|regions| {
140 let regions = regions.borrow();
141 let mut free = 0;
142 for region in regions.iter() {
143 free += region.heap.free();
144 }
145 free
146 })
147 }
148
149 fn get_page_size(&self) -> usize {
150 get_page_size()
151 }
152}
153
154unsafe fn is_slice_within_region(region: &Region, pointer: NonNull<u8>, layout: Layout) -> bool {
155 let start = region.slice.as_ptr() as usize;
156 let end = start + region.slice.len();
157 let pointer_start = pointer.as_ptr() as usize;
158 let pointer_end = pointer_start + layout.size();
159
160 pointer_start >= start && pointer_end <= end
162}
163
164unsafe fn expand(region: &mut Region, tried_size: usize) -> bool {
165 unsafe {
166 let page_size = get_page_size();
167 if region.slice.is_empty() {
169 let size = round_page_size(tried_size.max(INITIAL_HEAP_SIZE), page_size);
170 let new_slice = map(size, region.capabilities);
171 let new_size = new_slice.len();
172 region
173 .heap
174 .init(new_slice.as_mut_ptr() as *mut u8, new_size);
175 region.slice = new_slice;
176 return true;
177 }
178 let region_old_size = region.slice.len();
180 let new_size = region_old_size + tried_size;
181 let new_size = round_page_size(new_size, page_size);
182
183 remap(&mut region.slice, new_size);
184
185 let difference = new_size - region_old_size;
186
187 region.heap.extend(difference);
188
189 true
190 }
191}
192
193fn get_page_size() -> usize {
194 unsafe { sysconf(_SC_PAGE_SIZE) as usize }
195}
196
197const fn round_page_size(size: usize, page_size: usize) -> usize {
198 (size + page_size - 1) & !(page_size - 1) }
200
201unsafe fn map(size: usize, capabilities: Capabilities) -> &'static mut [MaybeUninit<u8>] {
202 unsafe {
203 let page_size = get_page_size();
204 let size = round_page_size(size, page_size);
205
206 let capabilities = if capabilities.get_executable() {
207 PROT_READ | PROT_WRITE | PROT_EXEC
208 } else {
209 PROT_READ | PROT_WRITE
210 };
211
212 let pointer = mmap(
213 null_mut(),
214 size,
215 capabilities,
216 MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT,
217 -1,
218 0,
219 );
220
221 if pointer == MAP_FAILED {
222 panic!("Failed to allocate memory");
223 }
224
225 core::slice::from_raw_parts_mut(pointer as *mut MaybeUninit<u8>, size)
226 }
227}
228
229unsafe fn remap(slice: &mut &'static mut [MaybeUninit<u8>], new_size: usize) {
230 unsafe {
231 let page_size = get_page_size();
232 let new_size = round_page_size(new_size, page_size);
233
234 let old_size = slice.len();
235
236 let pointer = mremap(slice.as_mut_ptr() as *mut c_void, old_size, new_size, 0);
237
238 if (pointer == MAP_FAILED) || (pointer != slice.as_mut_ptr() as *mut c_void) {
239 panic!("Failed to reallocate memory");
240 }
241
242 *slice = core::slice::from_raw_parts_mut(slice.as_mut_ptr(), new_size);
243 }
244}
245
246unsafe fn unmap(pointer: *mut MaybeUninit<u8>, size: usize) {
247 unsafe {
248 munmap(pointer as *mut c_void, size);
249 }
250}
251
252#[cfg(test)]
253mod tests {
254 use super::*;
255
256 use core::ptr::NonNull;
257 use memory::instantiate_global_allocator;
258
259 instantiate_global_allocator!(MemoryManager);
260
261 #[test]
262 fn test_global_allocator() {
263 let test_manager = MemoryManager::new();
265
266 let layout = Layout::from_size_align(128, 8).unwrap();
268 let capabilities = Capabilities::new(false, false);
269
270 unsafe {
271 let allocation = test_manager.allocate(capabilities, layout);
272 assert!(allocation.is_some());
273
274 if let Some(pointer) = allocation {
275 *pointer.as_ptr() = 42;
277 assert_eq!(*pointer.as_ptr(), 42);
278
279 test_manager.deallocate(pointer, layout);
281 }
282 }
283 }
284
285 #[test]
286 fn test_get_used_free() {
287 unsafe {
288 let manager = MemoryManager::new();
289
290 let layout = Layout::from_size_align(128, 8).unwrap();
292 let capabilities = Capabilities::new(false, false);
293
294 let allocation = manager.allocate(capabilities, layout);
295 assert!(allocation.is_some());
296
297 let used = manager.get_used();
299 let free = manager.get_free();
300 assert_eq!(used, layout.size());
301 assert_eq!(free, INITIAL_HEAP_SIZE - layout.size());
302
303 if let Some(pointer) = allocation {
305 manager.deallocate(pointer, layout);
306 }
307 }
308 }
309
310 #[test]
311 fn test_memory_manager_initialization() {
312 unsafe {
313 let manager = MemoryManager::new();
314
315 let layout = Layout::from(Layout::from_size_align(128, 8).unwrap());
317 let capabilities = Capabilities::new(false, false);
318 let allocation = manager.allocate(capabilities, layout);
319 assert!(allocation.is_some());
320 if let Some(pointer) = allocation {
321 manager.deallocate(pointer, layout);
323 }
324
325 let capabilities = Capabilities::new(true, false);
326 let allocation = manager.allocate(capabilities, layout);
327 assert!(allocation.is_some());
328 if let Some(pointer) = allocation {
329 manager.deallocate(pointer, layout);
331 }
332
333 manager.regions.lock(|regions| {
335 let regions_reference = regions.borrow();
336 assert!(!regions_reference[0].slice.is_empty());
337 assert!(!regions_reference[1].slice.is_empty());
338 assert!(!regions_reference[0].capabilities.get_executable());
339 assert!(regions_reference[1].capabilities.get_executable());
340 });
341 }
342 }
343
344 #[test]
345 fn test_allocate_deallocate() {
346 unsafe {
347 let manager = MemoryManager::new();
348
349 let layout = Layout::from_size_align(128, 8).unwrap();
351 let capabilities = Capabilities::new(false, false);
352
353 let allocation = manager.allocate(capabilities, layout);
354 assert!(allocation.is_some());
355
356 if let Some(pointer) = allocation {
358 manager.deallocate(pointer, layout);
359 }
360 }
361 }
362
363 #[test]
364 fn test_executable_memory() {
365 unsafe {
366 let manager = MemoryManager::new();
367
368 let layout = Layout::from_size_align(128, 8).unwrap();
370 let capabilities = Capabilities::new(true, false);
371
372 let allocation = manager.allocate(capabilities, layout);
373 assert!(allocation.is_some());
374
375 if let Some(pointer) = allocation {
377 manager.deallocate(pointer, layout);
378 }
379 }
380 }
381
382 #[test]
383 fn test_memory_expansion() {
384 unsafe {
385 let manager = MemoryManager::new();
386
387 let _page_size = get_page_size();
390 let layout = Layout::from_size_align(INITIAL_HEAP_SIZE, 8).unwrap();
391 let capabilities: Capabilities = Capabilities::new(false, false);
392
393 let allocation1 = manager.allocate(capabilities, layout);
394 assert!(allocation1.is_some());
395
396 let allocation2 = manager.allocate(capabilities, layout);
398 assert!(allocation2.is_some());
399
400 if let Some(pointer) = allocation1 {
402 manager.deallocate(pointer, layout);
403 }
404 if let Some(pointer) = allocation2 {
405 manager.deallocate(pointer, layout);
406 }
407 }
408 }
409
410 #[test]
411 fn test_is_slice_within_region() {
412 unsafe {
413 let manager = MemoryManager::new();
414
415 let layout = Layout::from_size_align(128, 8).unwrap();
417 let capabilities = Capabilities::new(false, false);
418
419 let allocation = manager.allocate(capabilities, layout);
420 assert!(allocation.is_some());
421
422 manager.regions.lock(|regions| {
423 let regions_reference = regions.borrow();
424 if let Some(pointer) = allocation {
425 assert!(is_slice_within_region(
427 ®ions_reference[0],
428 pointer,
429 layout
430 ));
431
432 let invalid_pointer = NonNull::new(0xdeadbeef as *mut u8).unwrap();
434 assert!(!is_slice_within_region(
435 ®ions_reference[0],
436 invalid_pointer,
437 layout
438 ));
439 }
440 });
441
442 if let Some(pointer) = allocation {
444 manager.deallocate(pointer, layout);
445 }
446 }
447 }
448
449 #[test]
450 fn test_page_size_rounding() {
451 let page_size = get_page_size();
452
453 assert_eq!(round_page_size(1, page_size), page_size);
455 assert_eq!(round_page_size(page_size, page_size), page_size);
456 assert_eq!(round_page_size(page_size + 1, page_size), page_size * 2);
457 assert_eq!(round_page_size(page_size * 2, page_size), page_size * 2);
458 }
459}