From 8b9dfad445a2fdf7458390adb1b45f22416c9210 Mon Sep 17 00:00:00 2001 From: Sainnhe Park Date: Thu, 13 Jan 2022 16:19:54 +0800 Subject: [PATCH] Heap Allocation --- .cargo/config.toml | 2 +- Cargo.lock | 34 +++++++++++++++++++++++ Cargo.toml | 1 + src/allocator.rs | 54 ++++++++++++++++++++++++++++++++++++ src/lib.rs | 9 ++++++ src/main.rs | 27 ++++++++++++++++++ tests/heap_allocation.rs | 60 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 186 insertions(+), 1 deletion(-) create mode 100644 src/allocator.rs create mode 100644 tests/heap_allocation.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index 14d634e..d4dc69f 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -3,7 +3,7 @@ target = "x86_64-anos.json" [unstable] build-std-features = ["compiler-builtins-mem"] -build-std = ["core", "compiler_builtins"] +build-std = ["core", "compiler_builtins", "alloc"] [target.'cfg(target_os = "none")'] runner = "bootimage runner" diff --git a/Cargo.lock b/Cargo.lock index af7486b..e231e1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,7 @@ version = "0.1.0" dependencies = [ "bootloader", "lazy_static", + "linked_list_allocator", "pc-keyboard", "pic8259", "spin", @@ -43,6 +44,24 @@ dependencies = [ "spin", ] +[[package]] +name = "linked_list_allocator" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "549ce1740e46b291953c4340adcd74c59bcf4308f4cac050fd33ba91b7168f4a" +dependencies = [ + "spinning_top", +] + +[[package]] +name = "lock_api" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" +dependencies = [ + "scopeguard", +] + [[package]] name = "pc-keyboard" version = "0.5.1" @@ -58,12 +77,27 @@ dependencies = [ "x86_64", ] +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + [[package]] name = "spin" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spinning_top" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75adad84ee84b521fb2cca2d4fd0f1dab1d8d026bda3c5bea4ca63b5f9f9293c" +dependencies = [ + "lock_api", +] + [[package]] name = "uart_16550" version = "0.2.16" diff --git a/Cargo.toml b/Cargo.toml index 19e7dc5..c2d2510 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ x86_64 = "0.14.7" uart_16550 = "0.2.0" pic8259 = "0.10.1" pc-keyboard = "0.5.0" +linked_list_allocator = "0.9.0" [dependencies.lazy_static] version = "1.0" diff --git a/src/allocator.rs b/src/allocator.rs new file mode 100644 index 0000000..8067411 --- /dev/null +++ b/src/allocator.rs @@ -0,0 +1,54 @@ +use alloc::alloc::{GlobalAlloc, Layout}; +use core::ptr::null_mut; +use linked_list_allocator::LockedHeap; +use x86_64::{ + structures::paging::{ + mapper::MapToError, FrameAllocator, Mapper, Page, PageTableFlags, Size4KiB, + }, + VirtAddr, +}; + +pub const HEAP_START: usize = 0x_4444_4444_0000; +pub const HEAP_SIZE: usize = 100 * 1024; + +#[global_allocator] +static ALLOCATOR: LockedHeap = LockedHeap::empty(); + +pub fn init_heap( + mapper: &mut impl Mapper, + frame_allocator: &mut impl FrameAllocator, +) -> Result<(), MapToError> { + let page_range = { + let heap_start = VirtAddr::new(HEAP_START as u64); + let heap_end = heap_start + HEAP_SIZE - 1u64; + let heap_start_page = Page::containing_address(heap_start); + let heap_end_page = Page::containing_address(heap_end); + Page::range_inclusive(heap_start_page, heap_end_page) + }; + + for page in page_range { + let frame = frame_allocator + .allocate_frame() + .ok_or(MapToError::FrameAllocationFailed)?; + let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; + unsafe { mapper.map_to(page, frame, flags, frame_allocator)?.flush() }; + } + + unsafe { + ALLOCATOR.lock().init(HEAP_START, HEAP_SIZE); + } + + Ok(()) +} + +pub struct Dummy; + +unsafe impl GlobalAlloc for Dummy { + unsafe fn alloc(&self, _layout: Layout) -> *mut u8 { + null_mut() + } + + unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) { + panic!("dealloc should be never called") + } +} diff --git a/src/lib.rs b/src/lib.rs index bff9277..24195c4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,9 @@ #![test_runner(crate::test_runner)] #![reexport_test_harness_main = "test_main"] #![feature(abi_x86_interrupt)] +#![feature(alloc_error_handler)] + +extern crate alloc; use core::panic::PanicInfo; pub mod gdt; @@ -11,6 +14,7 @@ pub mod interrupts; pub mod memory; pub mod serial; pub mod vga_buffer; +pub mod allocator; pub trait Testable { fn run(&self) -> (); @@ -91,6 +95,11 @@ pub fn hlt_loop() -> ! { } } +#[alloc_error_handler] +fn alloc_error_handler(layout: alloc::alloc::Layout) -> ! { + panic!("allocation error: {:?}", layout); +} + pub fn init() { gdt::init(); interrupts::init_idt(); diff --git a/src/main.rs b/src/main.rs index 5e16bf0..69053d6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,14 +4,18 @@ #![test_runner(anos::test_runner)] #![reexport_test_harness_main = "test_main"] +extern crate alloc; + use anos::println; use bootloader::{entry_point, BootInfo}; use core::panic::PanicInfo; +use alloc::{boxed::Box, rc::Rc, vec, vec::Vec}; entry_point!(kernel_main); fn kernel_main(boot_info: &'static BootInfo) -> ! { use anos::memory::{self, BootInfoFrameAllocator}; + use anos::allocator; use x86_64::{structures::paging::Page, VirtAddr}; println!("Hello World{}", "!"); @@ -27,6 +31,29 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! { let page_ptr: *mut u64 = page.start_address().as_mut_ptr(); unsafe { page_ptr.offset(400).write_volatile(0x_f021_f077_f065_f04e) }; + allocator::init_heap(&mut mapper, &mut frame_allocator).expect("heap initialization failed"); + + let heap_value = Box::new(41); + println!("heap_value at {:p}", heap_value); + + let mut vec = Vec::new(); + for i in 0..500 { + vec.push(i); + } + println!("vec at {:p}", vec.as_slice()); + + let reference_counted = Rc::new(vec![1, 2, 3]); + let cloned_reference = reference_counted.clone(); + println!( + "current reference count is {}", + Rc::strong_count(&cloned_reference) + ); + core::mem::drop(reference_counted); + println!( + "reference count is {} now", + Rc::strong_count(&cloned_reference) + ); + // 触发一个中断 x86_64::instructions::interrupts::int3(); println!("It didn't crash!"); diff --git a/tests/heap_allocation.rs b/tests/heap_allocation.rs new file mode 100644 index 0000000..558511b --- /dev/null +++ b/tests/heap_allocation.rs @@ -0,0 +1,60 @@ +#![no_std] +#![no_main] +#![feature(custom_test_frameworks)] +#![test_runner(anos::test_runner)] +#![reexport_test_harness_main = "test_main"] + +extern crate alloc; + +use alloc::{boxed::Box, vec::Vec}; +use anos::allocator::HEAP_SIZE; +use bootloader::{entry_point, BootInfo}; +use core::panic::PanicInfo; + +entry_point!(main); + +fn main(boot_info: &'static BootInfo) -> ! { + use anos::allocator; + use anos::memory::{self, BootInfoFrameAllocator}; + use x86_64::VirtAddr; + + anos::init(); + let phys_mem_offset = VirtAddr::new(boot_info.physical_memory_offset); + let mut mapper = unsafe { memory::init(phys_mem_offset) }; + let mut frame_allocator = unsafe { BootInfoFrameAllocator::init(&boot_info.memory_map) }; + allocator::init_heap(&mut mapper, &mut frame_allocator).expect("heap initialization failed"); + + test_main(); + loop {} +} + +#[test_case] +fn simple_allocation() { + let heap_value_1 = Box::new(41); + let heap_value_2 = Box::new(13); + assert_eq!(*heap_value_1, 41); + assert_eq!(*heap_value_2, 13); +} + +#[test_case] +fn large_vec() { + let n = 1000; + let mut vec = Vec::new(); + for i in 0..n { + vec.push(i); + } + assert_eq!(vec.iter().sum::(), (n - 1) * n / 2); +} + +#[test_case] +fn many_boxes() { + for i in 0..HEAP_SIZE { + let x = Box::new(i); + assert_eq!(*x, i); + } +} + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + anos::test_panic_handler(info) +}