Implements stage 1 kernel heap (#956)

This commit is contained in:
Putta Khunchalee 2024-08-31 21:41:08 +07:00 committed by GitHub
parent da46138ba6
commit 8bd35ba9ff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 38 additions and 9 deletions

View File

@ -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 }

View File

@ -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() };

View File

@ -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<Talc<ClaimOnOom>>,
}
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);
}
}