You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

138 lines
4.2 KiB
Rust

// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2022 Andre Richter <andre.o.richter@gmail.com>
//! Heap allocation.
use crate::{
backtrace, bsp, common, debug, info,
memory::{Address, Virtual},
synchronization,
synchronization::IRQSafeNullLock,
};
use alloc::alloc::{GlobalAlloc, Layout};
use linked_list_allocator::Heap as LinkedListHeap;
//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------
/// A heap allocator that can be lazyily initialized.
pub struct HeapAllocator {
inner: IRQSafeNullLock<LinkedListHeap>,
}
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
#[global_allocator]
static KERNEL_HEAP_ALLOCATOR: HeapAllocator = HeapAllocator::new();
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
#[inline(always)]
fn debug_print_alloc_dealloc(operation: &'static str, ptr: *mut u8, layout: Layout) {
let size = layout.size();
let (size_h, size_unit) = common::size_human_readable_ceil(size);
let addr = Address::<Virtual>::new(ptr as usize);
debug!(
"Kernel Heap: {}\n \
Size: {:#x} ({} {})\n \
Start: {}\n \
End excl: {}\n\n \
{}",
operation,
size,
size_h,
size_unit,
addr,
addr + size,
backtrace::Backtrace
);
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
use synchronization::interface::Mutex;
#[alloc_error_handler]
fn alloc_error_handler(layout: Layout) -> ! {
panic!("Allocation error: {:?}", layout)
}
/// Return a reference to the kernel's heap allocator.
pub fn kernel_heap_allocator() -> &'static HeapAllocator {
&KERNEL_HEAP_ALLOCATOR
}
impl HeapAllocator {
/// Create an instance.
pub const fn new() -> Self {
Self {
inner: IRQSafeNullLock::new(LinkedListHeap::empty()),
}
}
/// Print the current heap usage.
pub fn print_usage(&self) {
let (used, free) = KERNEL_HEAP_ALLOCATOR
.inner
.lock(|inner| (inner.used(), inner.free()));
if used >= 1024 {
let (used_h, used_unit) = common::size_human_readable_ceil(used);
info!(" Used: {} Byte ({} {})", used, used_h, used_unit);
} else {
info!(" Used: {} Byte", used);
}
if free >= 1024 {
let (free_h, free_unit) = common::size_human_readable_ceil(free);
info!(" Free: {} Byte ({} {})", free, free_h, free_unit);
} else {
info!(" Free: {} Byte", free);
}
}
}
unsafe impl GlobalAlloc for HeapAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let result = KERNEL_HEAP_ALLOCATOR
.inner
.lock(|inner| inner.allocate_first_fit(layout).ok());
match result {
None => core::ptr::null_mut(),
Some(allocation) => {
let ptr = allocation.as_ptr();
debug_print_alloc_dealloc("Allocation", ptr, layout);
ptr
}
}
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
KERNEL_HEAP_ALLOCATOR
.inner
.lock(|inner| inner.deallocate(core::ptr::NonNull::new_unchecked(ptr), layout));
debug_print_alloc_dealloc("Free", ptr, layout);
}
}
/// Query the BSP for the heap region and initialize the kernel's heap allocator with it.
pub fn kernel_init_heap_allocator() {
let region = bsp::memory::mmu::virt_heap_region();
KERNEL_HEAP_ALLOCATOR.inner.lock(|inner| unsafe {
inner.init(region.start_addr().as_usize() as *mut u8, region.size())
});
}