Skip to main content

osiris/
mem.rs

1//! This module provides access to the global memory allocator.
2
3use crate::hal;
4use crate::mem::pfa::PAGE_SIZE;
5use crate::mem::vmm::{AddressSpacelike, Backing, Perms, Region};
6use crate::sync::spinlock::SpinLocked;
7use alloc::Allocator;
8use core::ptr::NonNull;
9use hal::mem::PhysAddr;
10
11pub mod alloc;
12pub mod pfa;
13pub mod vmm;
14
15#[allow(dead_code)]
16pub const BITS_PER_PTR: usize = core::mem::size_of::<usize>() * 8;
17
18unsafe extern "C" {
19    unsafe static __stack_top: u8;
20}
21
22/// The global memory allocator.
23pub(crate) static GLOBAL_ALLOCATOR: SpinLocked<alloc::bestfit::BestFitAllocator> =
24    SpinLocked::new(alloc::bestfit::BestFitAllocator::new());
25
26/// Initialize the memory allocator.
27///
28/// `regions` - The memory node module of device tree codegen file.
29///
30/// Returns an error if the memory allocator could not be initialized.
31pub fn init_memory() -> vmm::AddressSpace {
32    let stack_top = &raw const __stack_top as usize;
33    if let Err(e) = pfa::init_pfa(PhysAddr::new(stack_top)) {
34        // TODO: Get this from the DeviceTree.
35        panic!("failed to initialize PFA. Error: {e}");
36    }
37
38    // TODO: Configure via env / DT. heap_pgs is mapped, the rest reserved for stacks.
39    let total_pgs = 64;
40    let heap_pgs = 8; // 32 KB heap
41
42    let mut kaddr_space = vmm::AddressSpace::new(total_pgs).unwrap_or_else(|e| {
43        panic!("failed to create kernel address space. Error: {e}");
44    });
45
46    let begin = kaddr_space
47        .map(Region::new(
48            None,
49            heap_pgs * PAGE_SIZE,
50            Backing::Zeroed,
51            Perms::all(),
52        ))
53        .unwrap_or_else(|e| {
54            panic!("failed to map kernel address space. Error: {e}");
55        });
56
57    {
58        let mut allocator = GLOBAL_ALLOCATOR.lock();
59
60        let range = begin..(begin + heap_pgs * PAGE_SIZE);
61        if let Err(e) = unsafe { allocator.add_range(&range) } {
62            panic!("failed to add range to allocator. Error: {e}");
63        }
64    }
65
66    kaddr_space
67}
68
69/// Allocate a memory block. Normally Box<T> or SizedPool<T> should be used instead of this function.
70///
71/// `size` - The size of the memory block to allocate.
72/// `align` - The alignment of the memory block.
73///
74/// Returns a pointer to the allocated memory block if the allocation was successful, or `None` if the allocation failed.
75pub fn malloc(size: usize, align: usize) -> Option<NonNull<u8>> {
76    let mut allocator = GLOBAL_ALLOCATOR.lock();
77    // Safety: The global allocator is valid for the lifetime of the program.
78    unsafe { allocator.malloc(size, align, None).ok() }
79}
80
81/// Free a memory block.
82///
83/// `ptr` - The pointer to the memory block.
84/// `size` - The size of the memory block.
85///
86/// # Safety
87///
88/// The caller must ensure that the pointer is from a previous call to `malloc` and that the size is still the same.
89pub unsafe fn free(ptr: NonNull<u8>, size: usize) {
90    let mut allocator = GLOBAL_ALLOCATOR.lock();
91    unsafe { allocator.free(ptr, size) };
92}
93
94/// Returns a metrics snapshot of the global kernel heap.
95#[cfg(any(feature = "metrics", metrics))]
96pub(crate) fn global_metrics() -> alloc::Metrics {
97    GLOBAL_ALLOCATOR.lock().metrics()
98}
99
100/// Aligns a size to be a multiple of the u128 alignment.
101///
102/// `size` - The size to align.
103///
104/// Returns the aligned size.
105pub fn align_up(size: usize) -> usize {
106    if size >= (usize::MAX - align_of::<u128>()) {
107        return usize::MAX;
108    }
109
110    let align = align_of::<u128>();
111    (size + align - 1) & !(align - 1)
112}