drivers/standard_library/
memory.rs

1use 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
16// Initial heap size and growth constants
17const INITIAL_HEAP_SIZE: usize = 1024 * 1024; // 1MB
18
19struct 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                // Find the first region that can satisfy the allocation request
76                let capable_regions = regions
77                    .iter_mut()
78                    .filter(|region| region.capabilities.is_superset_of(capabilities));
79
80                // Try to allocate from the first capable region
81                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                // If no region could satisfy the request, try to expand existing regions
93                for region in capable_regions {
94                    // Try to expand the region to fit the requested layout
95                    if expand(region, layout.size()) {
96                        // Try to allocate again after expanding
97                        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    // Check if the pointer range is completely contained within the region
161    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 the region is empty, allocate a new one
168        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        // If the region is not empty, try to expand it
179        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) // Round up to the nearest page size
199}
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        // Create a separate instance for testing to avoid reentrancy issues with the global allocator
264        let test_manager = MemoryManager::new();
265
266        // Allocate some memory using the test manager directly
267        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                // Use the allocated memory (e.g., write to it)
276                *pointer.as_ptr() = 42;
277                assert_eq!(*pointer.as_ptr(), 42);
278
279                // Deallocate the memory
280                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            // Allocate some memory
291            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            // Check used and free memory
298            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            // Deallocate the memory
304            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            // Perform an initial allocation to trigger initialization
316            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                // Deallocate the memory
322                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                // Deallocate the memory
330                manager.deallocate(pointer, layout);
331            }
332
333            // Check that regions are initialized
334            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            // Allocate some memory
350            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            // Deallocate the memory
357            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            // Allocate some executable memory
369            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            // Deallocate the memory
376            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            // Allocate a chunk of memory close to the region size
388            // to trigger expansion
389            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            // Try another allocation that should trigger expansion
397            let allocation2 = manager.allocate(capabilities, layout);
398            assert!(allocation2.is_some());
399
400            // Deallocate everything
401            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            // Allocate some memory
416            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                    // Check that the pointer is within the region
426                    assert!(is_slice_within_region(
427                        &regions_reference[0],
428                        pointer,
429                        layout
430                    ));
431
432                    // Create a pointer outside the region
433                    let invalid_pointer = NonNull::new(0xdeadbeef as *mut u8).unwrap();
434                    assert!(!is_slice_within_region(
435                        &regions_reference[0],
436                        invalid_pointer,
437                        layout
438                    ));
439                }
440            });
441
442            // Deallocate
443            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        // Test various sizes
454        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}