Skip to content
Snippets Groups Projects
Unverified Commit 10152b83 authored by Martin Kröning's avatar Martin Kröning :crab: Committed by GitHub
Browse files

Merge pull request #278 from duanyu-yu/alloc

Add alloc support
parents 644dad2e 3b79e130
No related branches found
No related tags found
No related merge requests found
......@@ -14,12 +14,24 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1926655ba000b19e21f0402be09a1d52d318c8a8a68622870bfb7af2a71315cd"
[[package]]
name = "allocator-api2"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
[[package]]
name = "anyhow"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bit_field"
version = "0.10.2"
......@@ -61,9 +73,9 @@ checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216"
[[package]]
name = "deranged"
version = "0.3.9"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3"
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
dependencies = [
"powerfmt",
]
......@@ -78,6 +90,12 @@ dependencies = [
"void",
]
[[package]]
name = "exclusive_cell"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5b9e9908e50b47ebbc3d6fd66ed295b997c270e8d2312a035bcc62722a160ef"
[[package]]
name = "fdt"
version = "0.1.5"
......@@ -129,7 +147,9 @@ version = "0.4.5"
dependencies = [
"align-address",
"align-data",
"allocator-api2",
"cc",
"exclusive_cell",
"fdt",
"goblin 0.8.0",
"hermit-dtb",
......@@ -140,6 +160,7 @@ dependencies = [
"nasm-rs",
"riscv",
"sbi",
"spinning_top",
"sptr",
"uart_16550",
"uefi",
......@@ -159,6 +180,16 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "955be5d0ca0465caf127165acb47964f911e2bc26073e865deb8be7189302faf"
[[package]]
name = "lock_api"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.20"
......@@ -304,6 +335,12 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29cb0870400aca7e4487e8ec1e93f9d4288da763cb1da2cedc5102e62b6522ad"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "scroll"
version = "0.11.0"
......@@ -316,6 +353,15 @@ version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ab8598aa408498679922eff7fa985c25d58a90771bd6be794434c5277eab1a6"
[[package]]
name = "spinning_top"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300"
dependencies = [
"lock_api",
]
[[package]]
name = "sptr"
version = "0.3.2"
......@@ -346,9 +392,9 @@ dependencies = [
[[package]]
name = "time"
version = "0.3.30"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5"
checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e"
dependencies = [
"deranged",
"powerfmt",
......
......@@ -28,6 +28,11 @@ align-data = "0.1"
hermit-dtb = { version = "0.1" }
goblin = { version = "0.8", default-features = false, features = ["elf64"] }
[target.'cfg(target_os = "none")'.dependencies]
allocator-api2 = { version = "0.2", default-features = false }
exclusive_cell = "0.1"
spinning_top = "0.3"
[target.'cfg(target_os = "uefi")'.dependencies]
uefi = "0.26"
uefi-services = "0.23"
......
//! A bootstrap allocator based on a statically allocated buffer.
/// A pointer range that can only be compared against.
mod ptr_range {
use core::ops::Range;
use core::ptr::NonNull;
/// A pointer range that can only be compared against.
pub struct PtrRange<T> {
inner: Range<NonNull<T>>,
}
// SAFETY: We never dereference, but only compare, pointers.
unsafe impl<T> Send for PtrRange<T> {}
unsafe impl<T> Sync for PtrRange<T> {}
impl<T> PtrRange<T> {
/// Returns `true` if the pointer range contains `ptr`.
pub fn contains(&self, ptr: NonNull<T>) -> bool {
self.inner.contains(&ptr)
}
}
impl<T> From<Range<NonNull<T>>> for PtrRange<T> {
fn from(value: Range<NonNull<T>>) -> Self {
Self { inner: value }
}
}
}
use core::mem::MaybeUninit;
use core::ops::Range;
use core::ptr::NonNull;
use allocator_api2::alloc::{AllocError, Allocator, Layout};
use exclusive_cell::ExclusiveCell;
use self::ptr_range::PtrRange;
/// A bootstrap allocator.
///
/// This allocator is generic over the internal allocator and can only be created once.
/// The bootstrap allocator provides the internal allocator with static memory.
///
/// This allocator tracks, which static memory it was using initially.
/// It can be queried whether a pointer belongs to it.
pub struct BootstrapAllocator<A> {
ptr_range: PtrRange<u8>,
allocator: A,
}
impl<A> Default for BootstrapAllocator<A>
where
A: From<&'static mut [MaybeUninit<u8>]>,
{
fn default() -> Self {
let mem = {
const SIZE: usize = 4 * 1024;
const BYTE: MaybeUninit<u8> = MaybeUninit::uninit();
/// The actual memory of the boostrap allocator.
static MEM: ExclusiveCell<[MaybeUninit<u8>; SIZE]> = ExclusiveCell::new([BYTE; SIZE]);
MEM.take().unwrap()
};
let ptr_range = {
let Range { start, end } = mem.as_mut_ptr_range();
let start = NonNull::new(start).unwrap().cast::<u8>();
let end = NonNull::new(end).unwrap().cast::<u8>();
PtrRange::from(start..end)
};
let allocator = A::from(mem);
Self {
ptr_range,
allocator,
}
}
}
impl<A> BootstrapAllocator<A> {
/// Returns `true` if the pointer belonged to the static memory of this allocator.
pub fn manages(&self, ptr: NonNull<u8>) -> bool {
self.ptr_range.contains(ptr)
}
}
unsafe impl<A> Allocator for BootstrapAllocator<A>
where
A: Allocator,
{
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
self.allocator.allocate(layout)
}
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
debug_assert!(self.manages(ptr));
unsafe { self.allocator.deallocate(ptr, layout) }
}
}
//! A bump allocator.
//!
//! This is a simple allocator design which can only allocate and not deallocate.
use core::cell::Cell;
use core::mem::MaybeUninit;
use core::ptr::NonNull;
use allocator_api2::alloc::{AllocError, Allocator, Layout};
/// A simple, `!Sync` implementation of a bump allocator.
///
/// This allocator manages the provided memory.
pub struct BumpAllocator {
mem: Cell<&'static mut [MaybeUninit<u8>]>,
}
unsafe impl Allocator for BumpAllocator {
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
let ptr: *mut [MaybeUninit<u8>] = self.allocate_slice(layout)?;
Ok(NonNull::new(ptr as *mut [u8]).unwrap())
}
unsafe fn deallocate(&self, _ptr: NonNull<u8>, _layout: Layout) {}
}
impl BumpAllocator {
fn allocate_slice(&self, layout: Layout) -> Result<&'static mut [MaybeUninit<u8>], AllocError> {
let mem = self.mem.take();
let align_offset = mem.as_ptr().align_offset(layout.align());
let mid = layout.size() + align_offset;
if mid > mem.len() {
self.mem.set(mem);
Err(AllocError)
} else {
let (alloc, remaining) = mem.split_at_mut(mid);
self.mem.set(remaining);
Ok(&mut alloc[align_offset..])
}
}
}
impl From<&'static mut [MaybeUninit<u8>]> for BumpAllocator {
fn from(mem: &'static mut [MaybeUninit<u8>]) -> Self {
Self {
mem: Cell::new(mem),
}
}
}
//! Implementation of the Hermit Allocator in the loader
mod bootstrap;
mod bump;
use core::ptr;
use core::ptr::NonNull;
use allocator_api2::alloc::{AllocError, Allocator, GlobalAlloc, Layout};
use spinning_top::Spinlock;
use self::bootstrap::BootstrapAllocator;
use self::bump::BumpAllocator;
/// The global system allocator for Hermit.
struct GlobalAllocator {
/// The bootstrap allocator, which is available immediately.
///
/// It allows allocations before the heap has been initalized.
bootstrap_allocator: Option<BootstrapAllocator<BumpAllocator>>,
}
impl GlobalAllocator {
const fn empty() -> Self {
Self {
bootstrap_allocator: None,
}
}
fn align_layout(layout: Layout) -> Layout {
let size = layout.size();
let align = layout.align();
Layout::from_size_align(size, align).unwrap()
}
fn allocate(&mut self, layout: Layout) -> Result<NonNull<u8>, AllocError> {
let layout = Self::align_layout(layout);
self.bootstrap_allocator
.get_or_insert_with(Default::default)
.allocate(layout)
// FIXME: Use NonNull::as_mut_ptr once `slice_ptr_get` is stabilized
// https://github.com/rust-lang/rust/issues/74265
.map(|ptr| NonNull::new(ptr.as_ptr() as *mut u8).unwrap())
}
unsafe fn deallocate(&mut self, ptr: NonNull<u8>, layout: Layout) {
let layout = Self::align_layout(layout);
let bootstrap_allocator = self.bootstrap_allocator.as_ref().unwrap();
assert!(bootstrap_allocator.manages(ptr));
unsafe {
bootstrap_allocator.deallocate(ptr, layout);
}
}
}
pub struct LockedAllocator(Spinlock<GlobalAllocator>);
impl LockedAllocator {
/// Creates an empty allocator. All allocate calls will return `None`.
pub const fn empty() -> LockedAllocator {
LockedAllocator(Spinlock::new(GlobalAllocator::empty()))
}
}
/// To avoid false sharing, the global memory allocator align
/// all requests to a cache line.
unsafe impl GlobalAlloc for LockedAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
self.0
.lock()
.allocate(layout)
.ok()
.map_or(ptr::null_mut(), |allocation| allocation.as_ptr())
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
unsafe {
self.0
.lock()
.deallocate(NonNull::new_unchecked(ptr), layout)
}
}
}
#[global_allocator]
static ALLOCATOR: LockedAllocator = LockedAllocator::empty();
#[cfg(all(test, not(target_os = "none")))]
mod tests {
use core::mem;
use super::*;
#[test]
fn empty() {
let mut allocator = GlobalAllocator::empty();
let layout = Layout::from_size_align(1, 1).unwrap();
// we have 4 kbyte static memory
assert!(allocator.allocate(layout.clone()).is_ok());
let layout = Layout::from_size_align(0x1000, mem::align_of::<usize>());
let addr = allocator.allocate(layout.unwrap());
assert!(addr.is_err());
}
}
......@@ -8,6 +8,8 @@
#[macro_use]
mod macros;
#[cfg(target_os = "none")]
mod allocator;
mod arch;
mod console;
mod log;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment