diff --git a/nucleus/src/main.rs b/nucleus/src/main.rs index 54feb19..c3198d3 100644 --- a/nucleus/src/main.rs +++ b/nucleus/src/main.rs @@ -11,8 +11,10 @@ #![no_main] #![feature(asm)] #![feature(global_asm)] +#![feature(allocator_api)] #![feature(ptr_internals)] #![feature(format_args_nl)] +#![feature(nonnull_slice_from_raw_parts)] #![feature(custom_test_frameworks)] #![test_runner(crate::tests::test_runner)] #![reexport_test_harness_main = "test_main"] diff --git a/nucleus/src/mm/bump_allocator.rs b/nucleus/src/mm/bump_allocator.rs new file mode 100644 index 0000000..6df157c --- /dev/null +++ b/nucleus/src/mm/bump_allocator.rs @@ -0,0 +1,92 @@ +/* + * SPDX-License-Identifier: BlueOak-1.0.0 + * Copyright (c) Berkus Decker + */ + +// @todo Use alloc-fmt crate for logging in allocators + +use { + crate::println, + core::{ + alloc::{AllocError, AllocRef, Layout}, + cell::Cell, + ptr::NonNull, + }, +}; + +pub struct BumpAllocator { + next: Cell, + pool_end: usize, + name: &'static str, +} + +unsafe impl AllocRef for BumpAllocator { + /// Allocate a memory block from the pool. + fn alloc(&self, layout: Layout) -> Result, AllocError> { + let start = crate::mm::aligned_addr_unchecked(self.next.get(), layout.align()); + let end = start + layout.size(); + + println!( + "[i] {}:\n Allocating Start {:#010x} End {:#010x}", + self.name, start, end + ); + + if end > self.pool_end { + return Err(AllocError); + } + self.next.set(end); + + println!( + "[i] {}:\n Allocated Addr {:#010x} Size {:#x}", + self.name, + start, + layout.size() + ); + + Ok(NonNull::slice_from_raw_parts( + unsafe { NonNull::new_unchecked(start as *mut u8) }, + layout.size(), + )) + } + + /// A bump allocator doesn't care about releasing memory. + unsafe fn dealloc(&self, _ptr: NonNull, _layout: Layout) {} +} + +impl BumpAllocator { + /// Create a named bump allocator between start and end addresses. + pub const fn new(pool_start: usize, pool_end: usize, name: &'static str) -> Self { + Self { + next: Cell::new(pool_start), + pool_end, + name, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + // Validate allocator allocates from the provided address range + // Validate allocation fails when range is exhausted + #[test_case] + fn test_allocates_within_init_range() { + let allocator = BumpAllocator::new(256, 512, "Test allocator 1"); + let result1 = allocator.alloc(unsafe { Layout::from_size_align_unchecked(128, 1) }); + assert!(result1.is_ok()); + let result2 = allocator.alloc(unsafe { Layout::from_size_align_unchecked(128, 32) }); + println!("{:?}", result2); + assert!(result2.is_ok()); + let result3 = allocator.alloc(unsafe { Layout::from_size_align_unchecked(1, 1) }); + assert!(result3.is_err()); + } + // Creating with end <= start sshould fail + // @todo return Result<> from new? + #[test_case] + fn test_bad_allocator() { + let bad_allocator = BumpAllocator::new(512, 256, "Test allocator 2"); + let result1 = bad_allocator.alloc(unsafe { Layout::from_size_align_unchecked(1, 1) }); + assert!(result1.is_err()); + } +} diff --git a/nucleus/src/mm/mod.rs b/nucleus/src/mm/mod.rs index 7ec2e8e..981f1d7 100644 --- a/nucleus/src/mm/mod.rs +++ b/nucleus/src/mm/mod.rs @@ -3,6 +3,9 @@ * Copyright (c) Berkus Decker */ +pub mod bump_allocator; +pub use bump_allocator::BumpAllocator; + /// Align address downwards. /// /// Returns the greatest x with alignment `align` so that x <= addr.