mirror of
https://github.com/phil-opp/blog_os.git
synced 2025-12-16 14:27:49 +00:00
Compare commits
2 Commits
post-12-ne
...
code-heap-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1eb8f0f2c7 | ||
|
|
398ca5357c |
@@ -1,8 +1,11 @@
|
||||
#![cfg_attr(not(test), no_std)]
|
||||
#![cfg_attr(not(test), no_main)] // disable all Rust-level entry points
|
||||
#![cfg_attr(test, allow(unused_imports))]
|
||||
#![feature(alloc_error_handler)]
|
||||
|
||||
use blog_os::memory::allocator::DummyAllocator;
|
||||
use blog_os::{exit_qemu, serial_println};
|
||||
use core::alloc::Layout;
|
||||
use core::panic::PanicInfo;
|
||||
|
||||
/// This function is the entry point, since the linker looks for a function
|
||||
@@ -31,3 +34,11 @@ fn panic(info: &PanicInfo) -> ! {
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[global_allocator]
|
||||
static ALLOCATOR: DummyAllocator = DummyAllocator;
|
||||
|
||||
#[alloc_error_handler]
|
||||
fn out_of_memory(layout: Layout) -> ! {
|
||||
panic!("out of memory: allocation for {:?} failed", layout);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
#![no_std]
|
||||
#![cfg_attr(not(test), no_main)]
|
||||
#![cfg_attr(test, allow(dead_code, unused_macros, unused_imports))]
|
||||
#![feature(alloc_error_handler)]
|
||||
|
||||
use blog_os::memory::allocator::DummyAllocator;
|
||||
use blog_os::{exit_qemu, serial_println};
|
||||
use core::alloc::Layout;
|
||||
use core::panic::PanicInfo;
|
||||
|
||||
#[cfg(not(test))]
|
||||
@@ -32,3 +35,11 @@ fn panic(info: &PanicInfo) -> ! {
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[global_allocator]
|
||||
static ALLOCATOR: DummyAllocator = DummyAllocator;
|
||||
|
||||
#[alloc_error_handler]
|
||||
fn out_of_memory(layout: Layout) -> ! {
|
||||
panic!("out of memory: allocation for {:?} failed", layout);
|
||||
}
|
||||
|
||||
@@ -2,8 +2,11 @@
|
||||
#![no_std]
|
||||
#![cfg_attr(not(test), no_main)]
|
||||
#![cfg_attr(test, allow(dead_code, unused_macros, unused_imports))]
|
||||
#![feature(alloc_error_handler)]
|
||||
|
||||
use blog_os::memory::allocator::DummyAllocator;
|
||||
use blog_os::{exit_qemu, serial_println};
|
||||
use core::alloc::Layout;
|
||||
use core::panic::PanicInfo;
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
@@ -45,6 +48,14 @@ fn panic(info: &PanicInfo) -> ! {
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[global_allocator]
|
||||
static ALLOCATOR: DummyAllocator = DummyAllocator;
|
||||
|
||||
#[alloc_error_handler]
|
||||
fn out_of_memory(layout: Layout) -> ! {
|
||||
panic!("out of memory: allocation for {:?} failed", layout);
|
||||
}
|
||||
|
||||
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
|
||||
|
||||
lazy_static! {
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
#![cfg_attr(not(test), no_std)]
|
||||
#![cfg_attr(not(test), no_main)]
|
||||
#![cfg_attr(test, allow(unused_imports))]
|
||||
#![feature(alloc_error_handler)]
|
||||
|
||||
use blog_os::memory::allocator::DummyAllocator;
|
||||
use blog_os::{exit_qemu, serial_println};
|
||||
use core::alloc::Layout;
|
||||
use core::panic::PanicInfo;
|
||||
|
||||
#[cfg(not(test))]
|
||||
@@ -21,3 +24,11 @@ fn panic(_info: &PanicInfo) -> ! {
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[global_allocator]
|
||||
static ALLOCATOR: DummyAllocator = DummyAllocator;
|
||||
|
||||
#[alloc_error_handler]
|
||||
fn out_of_memory(layout: Layout) -> ! {
|
||||
panic!("out of memory: allocation for {:?} failed", layout);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#![cfg_attr(not(test), no_std)]
|
||||
#![feature(abi_x86_interrupt)]
|
||||
#![feature(alloc)]
|
||||
#![feature(const_fn)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
pub mod gdt;
|
||||
pub mod interrupts;
|
||||
|
||||
43
src/main.rs
43
src/main.rs
@@ -1,9 +1,18 @@
|
||||
#![cfg_attr(not(test), no_std)]
|
||||
#![cfg_attr(not(test), no_main)]
|
||||
#![cfg_attr(test, allow(unused_imports))]
|
||||
#![feature(alloc)]
|
||||
#![feature(alloc_error_handler)]
|
||||
|
||||
use blog_os::println;
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use blog_os::{
|
||||
memory::allocator::{BumpAllocator, LinkedListAllocator, LockedAllocator, BucketAllocator},
|
||||
println,
|
||||
};
|
||||
use bootloader::{entry_point, BootInfo};
|
||||
use core::alloc::Layout;
|
||||
use core::panic::PanicInfo;
|
||||
|
||||
entry_point!(kernel_main);
|
||||
@@ -24,13 +33,21 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! {
|
||||
let mut mapper = unsafe { memory::init(boot_info.physical_memory_offset) };
|
||||
let mut frame_allocator = memory::init_frame_allocator(&boot_info.memory_map);
|
||||
|
||||
// map a previously unmapped page
|
||||
let page = Page::containing_address(VirtAddr::new(0xdeadbeaf000));
|
||||
memory::create_example_mapping(page, &mut mapper, &mut frame_allocator);
|
||||
let heap_start = VirtAddr::new(HEAP_START);
|
||||
let heap_end = VirtAddr::new(HEAP_END);
|
||||
memory::map_heap(heap_start, heap_end, &mut mapper, &mut frame_allocator)
|
||||
.expect("map_heap failed");
|
||||
|
||||
// write the string `New!` to the screen through the new mapping
|
||||
let page_ptr: *mut u64 = page.start_address().as_mut_ptr();
|
||||
unsafe { page_ptr.offset(400).write_volatile(0x_f021_f077_f065_f04e) };
|
||||
ALLOCATOR.lock().underlying().add_memory(heap_start, HEAP_END - HEAP_START);
|
||||
|
||||
//let mut x = Vec::with_capacity(1000);
|
||||
let mut x = Vec::new();
|
||||
for i in 0..1000 {
|
||||
x.push(i);
|
||||
}
|
||||
println!("{:?}", *ALLOCATOR.lock());
|
||||
println!("with vec of size {}: {}", x.len(), x.iter().sum::<i32>());
|
||||
println!("with formular: {}", 999 * 1000 / 2);
|
||||
|
||||
println!("It did not crash!");
|
||||
blog_os::hlt_loop();
|
||||
@@ -43,3 +60,15 @@ fn panic(info: &PanicInfo) -> ! {
|
||||
println!("{}", info);
|
||||
blog_os::hlt_loop();
|
||||
}
|
||||
|
||||
const HEAP_START: u64 = 0o_001_000_000_0000;
|
||||
const HEAP_END: u64 = HEAP_START + 10 * 0x1000;
|
||||
|
||||
#[global_allocator]
|
||||
static ALLOCATOR: LockedAllocator<BucketAllocator<LinkedListAllocator>> =
|
||||
LockedAllocator::new(BucketAllocator::new(LinkedListAllocator::empty()));
|
||||
|
||||
#[alloc_error_handler]
|
||||
fn out_of_memory(layout: Layout) -> ! {
|
||||
panic!("out of memory: allocation for {:?} failed", layout);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
use bootloader::bootinfo::{MemoryMap, MemoryRegionType};
|
||||
use x86_64::{
|
||||
structures::paging::{
|
||||
FrameAllocator, MappedPageTable, Mapper, MapperAllSizes, Page, PageTable, PhysFrame,
|
||||
Size4KiB,
|
||||
mapper, FrameAllocator, MappedPageTable, Mapper, MapperAllSizes, Page, PageTable,
|
||||
PhysFrame, Size4KiB,
|
||||
},
|
||||
PhysAddr, VirtAddr,
|
||||
};
|
||||
|
||||
pub mod allocator;
|
||||
|
||||
/// Initialize a new MappedPageTable.
|
||||
///
|
||||
/// This function is unsafe because the caller must guarantee that the
|
||||
@@ -59,19 +61,37 @@ unsafe fn active_level_4_table(physical_memory_offset: u64) -> &'static mut Page
|
||||
&mut *page_table_ptr // unsafe
|
||||
}
|
||||
|
||||
/// Creates an example mapping for the given page to frame `0xb8000`.
|
||||
pub fn create_example_mapping(
|
||||
page: Page,
|
||||
pub fn map_heap(
|
||||
heap_start: VirtAddr,
|
||||
heap_end: VirtAddr,
|
||||
mapper: &mut impl Mapper<Size4KiB>,
|
||||
frame_allocator: &mut impl FrameAllocator<Size4KiB>,
|
||||
) {
|
||||
) -> Result<(), MapHeapError> {
|
||||
use x86_64::structures::paging::PageTableFlags as Flags;
|
||||
|
||||
let frame = PhysFrame::containing_address(PhysAddr::new(0xb8000));
|
||||
let flags = Flags::PRESENT | Flags::WRITABLE;
|
||||
let start_page = Page::containing_address(heap_start);
|
||||
let end_page = Page::containing_address(heap_end - 1u64);
|
||||
|
||||
let map_to_result = unsafe { mapper.map_to(page, frame, flags, frame_allocator) };
|
||||
map_to_result.expect("map_to failed").flush();
|
||||
for page in Page::range_inclusive(start_page, end_page) {
|
||||
let frame = frame_allocator.allocate_frame();
|
||||
let frame = frame.ok_or(MapHeapError::FrameAllocationFailed)?;
|
||||
unsafe { mapper.map_to(page, frame, flags, frame_allocator)?.flush() };
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MapHeapError {
|
||||
FrameAllocationFailed,
|
||||
MapToError(mapper::MapToError),
|
||||
}
|
||||
|
||||
impl From<mapper::MapToError> for MapHeapError {
|
||||
fn from(err: mapper::MapToError) -> Self {
|
||||
MapHeapError::MapToError(err)
|
||||
}
|
||||
}
|
||||
|
||||
/// A FrameAllocator that always returns `None`.
|
||||
|
||||
49
src/memory/allocator.rs
Normal file
49
src/memory/allocator.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
pub use bump::BumpAllocator;
|
||||
pub use dummy::DummyAllocator;
|
||||
pub use linked_list::LinkedListAllocator;
|
||||
pub use bucket::BucketAllocator;
|
||||
|
||||
use core::alloc::{GlobalAlloc, Layout};
|
||||
use spin::{Mutex, MutexGuard};
|
||||
|
||||
mod bump;
|
||||
mod dummy;
|
||||
mod linked_list;
|
||||
mod bucket;
|
||||
|
||||
pub struct LockedAllocator<T> {
|
||||
allocator: Mutex<T>,
|
||||
}
|
||||
|
||||
impl<T> LockedAllocator<T> {
|
||||
pub const fn new(allocator: T) -> Self {
|
||||
Self {
|
||||
allocator: Mutex::new(allocator),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> LockedAllocator<T> {
|
||||
pub fn lock(&self) -> MutexGuard<T> {
|
||||
self.allocator.lock()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T> GlobalAlloc for LockedAllocator<T>
|
||||
where
|
||||
T: MutGlobalAlloc,
|
||||
{
|
||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||
self.allocator.lock().alloc(layout)
|
||||
}
|
||||
|
||||
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
|
||||
self.allocator.lock().dealloc(ptr, layout)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait MutGlobalAlloc {
|
||||
fn alloc(&mut self, layout: Layout) -> *mut u8;
|
||||
|
||||
fn dealloc(&mut self, ptr: *mut u8, layout: Layout);
|
||||
}
|
||||
126
src/memory/allocator/bucket.rs
Normal file
126
src/memory/allocator/bucket.rs
Normal file
@@ -0,0 +1,126 @@
|
||||
use super::MutGlobalAlloc;
|
||||
use core::alloc::Layout;
|
||||
use core::mem::size_of;
|
||||
use core::fmt::{self, Debug};
|
||||
use core::cmp;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BucketAllocator<A> where A: MutGlobalAlloc + Debug {
|
||||
underlying: A,
|
||||
buckets: [Bucket; 10],
|
||||
}
|
||||
|
||||
impl<A> BucketAllocator<A> where A: MutGlobalAlloc + Debug {
|
||||
pub const fn new(underlying: A) -> Self {
|
||||
Self {
|
||||
underlying,
|
||||
buckets: [
|
||||
Bucket::new(size_of::<Region>()),
|
||||
Bucket::new(16),
|
||||
Bucket::new(32),
|
||||
Bucket::new(64),
|
||||
Bucket::new(128),
|
||||
Bucket::new(256),
|
||||
Bucket::new(512),
|
||||
Bucket::new(1024),
|
||||
Bucket::new(2048),
|
||||
Bucket::new(4096),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn underlying(&mut self) -> &mut A {
|
||||
&mut self.underlying
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Bucket {
|
||||
size: usize,
|
||||
head: Option<&'static mut Region>,
|
||||
}
|
||||
|
||||
impl Bucket {
|
||||
const fn new(size: usize) -> Self {
|
||||
Bucket {
|
||||
size,
|
||||
head: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn layout(&self) -> Layout {
|
||||
Layout::from_size_align(self.size, self.size).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Bucket {
|
||||
fn fmt(&self, f: &mut fmt::Formatter)-> fmt::Result {
|
||||
let mut regions = 0;
|
||||
let mut current = &self.head;
|
||||
while let Some(region) = current {
|
||||
current = ®ion.next;
|
||||
regions += 1;
|
||||
}
|
||||
f.debug_struct("Bucket").field("size", &self.size).field("regions", ®ions).finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Region {
|
||||
next: Option<&'static mut Region>,
|
||||
}
|
||||
|
||||
impl Region {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
next: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_mut_u8(&'static mut self) -> *mut u8 {
|
||||
self as *mut Region as *mut u8
|
||||
}
|
||||
|
||||
unsafe fn from_mut_u8(ptr: *mut u8) -> &'static mut Self {
|
||||
(ptr as *mut Region).write(Region::new());
|
||||
&mut *(ptr as *mut Region)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> BucketAllocator<A> where A: MutGlobalAlloc + Debug {
|
||||
fn get_bucket_index(&self, layout: &Layout) -> Option<usize> {
|
||||
let required_bucket_size = cmp::max(layout.size(), layout.align());
|
||||
match self.buckets.binary_search_by(|bucket| bucket.size.cmp(&required_bucket_size)) {
|
||||
Ok(index) => Some(index),
|
||||
Err(index) if index < self.buckets.len() => Some(index),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> MutGlobalAlloc for BucketAllocator<A> where A: MutGlobalAlloc + Debug {
|
||||
fn alloc(&mut self, layout: Layout) -> *mut u8 {
|
||||
if let Some(bucket_index) = self.get_bucket_index(&layout) {
|
||||
let bucket = &mut self.buckets[bucket_index];
|
||||
if let Some(head) = bucket.head.take() {
|
||||
let next = head.next.take();
|
||||
bucket.head = next;
|
||||
return head.as_mut_u8();
|
||||
} else {
|
||||
self.underlying.alloc(bucket.layout())
|
||||
}
|
||||
} else {
|
||||
self.underlying.alloc(layout)
|
||||
}
|
||||
}
|
||||
|
||||
fn dealloc(&mut self, ptr: *mut u8, layout: Layout) {
|
||||
if let Some(bucket_index) = self.get_bucket_index(&layout) {
|
||||
let bucket = &mut self.buckets[bucket_index];
|
||||
let region = unsafe {Region::from_mut_u8(ptr)};
|
||||
region.next = bucket.head.take();
|
||||
bucket.head = Some(region);
|
||||
} else {
|
||||
self.underlying.dealloc(ptr, layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
38
src/memory/allocator/bump.rs
Normal file
38
src/memory/allocator/bump.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
use super::MutGlobalAlloc;
|
||||
use core::alloc::Layout;
|
||||
use x86_64::align_up;
|
||||
|
||||
pub struct BumpAllocator {
|
||||
heap_start: u64,
|
||||
heap_end: u64,
|
||||
next: u64,
|
||||
}
|
||||
|
||||
impl BumpAllocator {
|
||||
pub const fn new(heap_start: u64, heap_end: u64) -> Self {
|
||||
Self {
|
||||
heap_start,
|
||||
heap_end,
|
||||
next: heap_start,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MutGlobalAlloc for BumpAllocator {
|
||||
fn alloc(&mut self, layout: Layout) -> *mut u8 {
|
||||
let alloc_start = align_up(self.next, layout.align() as u64);
|
||||
let alloc_end = alloc_start.saturating_add(layout.size() as u64);
|
||||
|
||||
if alloc_end >= self.heap_end {
|
||||
// out of memory
|
||||
return 0 as *mut u8;
|
||||
}
|
||||
|
||||
self.next = alloc_end;
|
||||
alloc_start as *mut u8
|
||||
}
|
||||
|
||||
fn dealloc(&mut self, _ptr: *mut u8, _layout: Layout) {
|
||||
panic!("BumpAllocator::dealloc called");
|
||||
}
|
||||
}
|
||||
16
src/memory/allocator/dummy.rs
Normal file
16
src/memory/allocator/dummy.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
use core::alloc::{GlobalAlloc, Layout};
|
||||
|
||||
/// A dummy allocator that panics on every `alloc` or `dealloc` call.
|
||||
pub struct DummyAllocator;
|
||||
|
||||
unsafe impl GlobalAlloc for DummyAllocator {
|
||||
/// Always panics.
|
||||
unsafe fn alloc(&self, _layout: Layout) -> *mut u8 {
|
||||
panic!("DummyAllocator::alloc called");
|
||||
}
|
||||
|
||||
/// Always panics.
|
||||
unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {
|
||||
panic!("DummyAllocator::dealloc called");
|
||||
}
|
||||
}
|
||||
95
src/memory/allocator/linked_list.rs
Normal file
95
src/memory/allocator/linked_list.rs
Normal file
@@ -0,0 +1,95 @@
|
||||
use super::MutGlobalAlloc;
|
||||
use core::alloc::Layout;
|
||||
use core::mem;
|
||||
use x86_64::{align_up, VirtAddr};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LinkedListAllocator {
|
||||
head: Region,
|
||||
}
|
||||
|
||||
impl LinkedListAllocator {
|
||||
pub const fn empty() -> Self {
|
||||
let head = Region {
|
||||
size: 0,
|
||||
next: None,
|
||||
};
|
||||
Self { head }
|
||||
}
|
||||
|
||||
pub unsafe fn new(heap_start: VirtAddr, heap_size: u64) -> Self {
|
||||
let mut allocator = Self::empty();
|
||||
allocator.add_memory(heap_start, heap_size);
|
||||
allocator
|
||||
}
|
||||
|
||||
pub fn add_memory(&mut self, start: VirtAddr, size: u64) {
|
||||
let aligned = start.align_up(mem::size_of::<Region>() as u64);
|
||||
let mut region = Region {
|
||||
size: size - (aligned - start),
|
||||
next: None
|
||||
};
|
||||
mem::swap(&mut self.head.next, &mut region.next);
|
||||
|
||||
let region_ptr: *mut Region = aligned.as_mut_ptr();
|
||||
unsafe { region_ptr.write(region) };
|
||||
self.head.next = Some(unsafe { &mut *region_ptr });
|
||||
}
|
||||
}
|
||||
|
||||
impl MutGlobalAlloc for LinkedListAllocator {
|
||||
fn alloc(&mut self, layout: Layout) -> *mut u8 {
|
||||
let size = align_up(layout.size() as u64, mem::size_of::<Region>() as u64);
|
||||
|
||||
let mut current = &mut self.head;
|
||||
loop {
|
||||
let next = match current.next {
|
||||
Some(ref mut next) => next,
|
||||
None => break,
|
||||
};
|
||||
let next_start = VirtAddr::new(*next as *mut Region as u64);
|
||||
let next_end = next_start + next.size;
|
||||
|
||||
let alloc_start = next_start.align_up(layout.align() as u64);
|
||||
let alloc_end = alloc_start + size;
|
||||
|
||||
// check if Region large enough
|
||||
if alloc_end <= next_end {
|
||||
// remove Region from list
|
||||
let next_next = next.next.take();
|
||||
current.next = next_next;
|
||||
// insert remaining Region to list
|
||||
self.add_memory(alloc_end, next_end - alloc_end);
|
||||
// return allocated memory
|
||||
return alloc_start.as_mut_ptr();
|
||||
}
|
||||
|
||||
// continue with next element
|
||||
//
|
||||
// This is basically `current = next`, but we need a new `match` expression because
|
||||
// the compiler can't figure the lifetimes out when we use the `next` binding
|
||||
// from above.
|
||||
current = match current.next {
|
||||
Some(ref mut next) => next,
|
||||
None => unreachable!(),
|
||||
};
|
||||
}
|
||||
|
||||
// no large enough Region found
|
||||
0 as *mut u8
|
||||
}
|
||||
|
||||
fn dealloc(&mut self, ptr: *mut u8, layout: Layout) {
|
||||
let size = align_up(layout.size() as u64, mem::size_of::<Region>() as u64);
|
||||
self.add_memory(VirtAddr::new(ptr as u64), size);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Region {
|
||||
size: u64,
|
||||
next: Option<&'static mut Region>,
|
||||
}
|
||||
|
||||
|
||||
// TODO recycle alignment
|
||||
Reference in New Issue
Block a user