diff --git a/src/obkrnl/Cargo.toml b/src/obkrnl/Cargo.toml index 2e45e3a8..c2df0746 100644 --- a/src/obkrnl/Cargo.toml +++ b/src/obkrnl/Cargo.toml @@ -8,3 +8,5 @@ anstyle = { version = "1.0.8", default-features = false } macros = { path = "../macros" } obconf = { path = "../obconf" } obvirt = { path = "../obvirt" } +spin = { version = "0.9.8", features = ["spin_mutex"], default-features = false } +talc = { version = "4.4.1", default-features = false } diff --git a/src/obkrnl/src/main.rs b/src/obkrnl/src/main.rs index f9ea6b33..f0b3f90a 100644 --- a/src/obkrnl/src/main.rs +++ b/src/obkrnl/src/main.rs @@ -5,6 +5,7 @@ use crate::config::set_boot_env; use crate::malloc::KernelHeap; use alloc::string::String; use core::arch::asm; +use core::mem::zeroed; use core::panic::PanicInfo; use obconf::BootEnv; @@ -26,7 +27,7 @@ extern crate alloc; /// /// See PS4 kernel entry point for a reference. #[allow(dead_code)] -#[cfg_attr(not(test), no_mangle)] +#[cfg_attr(target_os = "none", no_mangle)] extern "C" fn _start(env: &'static BootEnv) -> ! { // SAFETY: This is safe because we called it as the first thing here. unsafe { set_boot_env(env) }; @@ -67,6 +68,10 @@ fn panic(i: &PanicInfo) -> ! { crate::panic::panic(); } +// SAFETY: STAGE1_HEAP is a mutable static so it valid for reads and writes. This will be safe as +// long as no one access STAGE1_HEAP. #[allow(dead_code)] #[cfg_attr(target_os = "none", global_allocator)] -static mut KERNEL_HEAP: KernelHeap = KernelHeap::new(); +static mut KERNEL_HEAP: KernelHeap = + unsafe { KernelHeap::new(STAGE1_HEAP.as_mut_ptr(), STAGE1_HEAP.len()) }; +static mut STAGE1_HEAP: [u8; 1024 * 1024] = unsafe { zeroed() }; diff --git a/src/obkrnl/src/malloc/mod.rs b/src/obkrnl/src/malloc/mod.rs index 4a63e0c9..c9c571d4 100644 --- a/src/obkrnl/src/malloc/mod.rs +++ b/src/obkrnl/src/malloc/mod.rs @@ -1,20 +1,42 @@ use core::alloc::{GlobalAlloc, Layout}; +use core::ptr::{null_mut, NonNull}; +use talc::{ClaimOnOom, Span, Talc}; /// Implementation of [`GlobalAlloc`] for objects belong to kernel space. -pub struct KernelHeap {} +/// +/// This allocator has 2 stages. The first stage will allocate a memory from a static buffer (AKA +/// arena). This stage will be primary used for bootstrapping the kernel. The second stage will be +/// activated once the required subsystems has been initialized. +pub struct KernelHeap { + stage1: spin::Mutex>, +} impl KernelHeap { - pub const fn new() -> Self { - Self {} + /// # Safety + /// The specified memory must be valid for reads and writes and it must be exclusively available + /// to [`KernelHeap`]. + pub const unsafe fn new(stage1: *mut u8, len: usize) -> Self { + let stage1 = Talc::new(unsafe { ClaimOnOom::new(Span::from_base_size(stage1, len)) }); + + Self { + stage1: spin::Mutex::new(stage1), + } } } unsafe impl GlobalAlloc for KernelHeap { - unsafe fn alloc(&self, _: Layout) -> *mut u8 { - todo!() + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + // SAFETY: GlobalAlloc::alloc required layout to be non-zero. + self.stage1 + .lock() + .malloc(layout) + .map(|v| v.as_ptr()) + .unwrap_or(null_mut()) } - unsafe fn dealloc(&self, _: *mut u8, _: Layout) { - todo!() + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + // SAFETY: GlobalAlloc::dealloc required ptr to be the same one that returned from our + // GlobalAlloc::alloc and layout to be the same one that passed to it. + self.stage1.lock().free(NonNull::new_unchecked(ptr), layout); } }