diff --git a/Cargo.lock b/Cargo.lock index bbfcaea..b5101f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,6 +24,7 @@ version = "0.1.0" dependencies = [ "bootloader 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "linked_list_allocator 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "pc-keyboard 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "pic8259_simple 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -60,6 +61,14 @@ dependencies = [ "spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "linked_list_allocator" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "nodrop" version = "0.1.13" @@ -167,6 +176,7 @@ dependencies = [ "checksum cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be" "checksum cpuio 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "22b8e308ccfc5acf3b82f79c0eac444cf6114cb2ac67a230ca6c177210068daa" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +"checksum linked_list_allocator 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "47314ec1d29aa869ee7cb5a5be57be9b1055c56567d59c3fb6689926743e0bea" "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" "checksum pc-keyboard 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fff50ab09ba31bcebc0669f4e64c0952fae1acdca9e6e0587e68e4e8443808ac" "checksum pic8259_simple 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc64b2fd10828da8521b6cdabe0679385d7d2a3a6d4c336b819d1fa31ba35c72" diff --git a/Cargo.toml b/Cargo.toml index 8ee1e2c..2076b13 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" [dependencies] bootloader = { version = "0.8.1", features = ["map_physical_memory"]} +linked_list_allocator = "0.6.4" pc-keyboard = "0.3.1" pic8259_simple = "0.1.1" spin = "0.5.2" diff --git a/src/allocator.rs b/src/allocator.rs new file mode 100644 index 0000000..0e0c4e1 --- /dev/null +++ b/src/allocator.rs @@ -0,0 +1,56 @@ +use alloc::alloc::{GlobalAlloc, Layout}; +use core::ptr::null_mut; +use x86_64:: { + structures::paging:: { + FrameAllocator, + mapper::MapToError, + Mapper, + Page, + PageTableFlags, + Size4KiB, + }, + VirtAddr, +}; + +pub const HEAP_START: usize = 0x_4444_4444_0000; +pub const HEAP_SIZE: usize = 100 * 1024; // 100 KiB + +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 never be called") + } +} + +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 head_end = heap_start + HEAP_SIZE - 1u64; + let heap_start_page = Page::containing_address(heap_start); + let heap_end_page = Page::containing_address(head_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 { + super::ALLOCATOR.lock().init(HEAP_START, HEAP_SIZE); + } + + Ok(()) +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index b7c96da..420ebe5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,15 @@ #![no_std] #![cfg_attr(test, no_main)] +#![feature(alloc_error_handler)] #![feature(custom_test_frameworks)] #![feature(abi_x86_interrupt)] #![test_runner(crate::test_runner)] #![reexport_test_harness_main = "test_main"] +extern crate alloc; + use core::panic::PanicInfo; +use linked_list_allocator::LockedHeap; #[cfg(test)] use bootloader::{entry_point, BootInfo}; @@ -13,6 +17,15 @@ use bootloader::{entry_point, BootInfo}; #[cfg(test)] entry_point!(test_kernel_main); +#[global_allocator] +static ALLOCATOR: LockedHeap = LockedHeap::empty(); + +#[alloc_error_handler] +fn alloc_error_handler(layout: alloc::alloc::Layout) -> ! { + panic!("allocation error: {:?}", layout) +} + +pub mod allocator; pub mod gdt; pub mod interrupts; pub mod macros; diff --git a/src/main.rs b/src/main.rs index 3d6760c..b827ed9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,14 @@ #![test_runner(blog_os::test_runner)] #![reexport_test_harness_main = "test_main"] +extern crate alloc; + +use alloc::{ + boxed::Box, + vec, + vec::Vec, + rc::Rc, +}; use core::panic::PanicInfo; use blog_os::println; @@ -26,24 +34,38 @@ fn panic(info: &PanicInfo) -> ! { entry_point!(kernel_main); fn kernel_main(boot_info: &'static BootInfo) -> ! { - use blog_os::memory; - use x86_64::{structures::paging::Page, VirtAddr}; + use blog_os::allocator; + use blog_os::memory::{self, BootInfoFrameAllocator}; + use x86_64::VirtAddr; println!("Hello World{}", "!"); blog_os::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 { memory::BootInfoFrameAllocator::init(&boot_info.memory_map) }; + let mut mapper = unsafe { memory::init(VirtAddr::new(boot_info.physical_memory_offset)) }; + let mut frame_allocator = unsafe { + BootInfoFrameAllocator::init(&boot_info.memory_map) + }; - let page = Page::containing_address(VirtAddr::new(0xdeadbeef000)); - memory::create_example_mapping(page, &mut mapper, &mut frame_allocator); + allocator::init_heap(&mut mapper, &mut frame_allocator) + .expect("heap initialization failed"); - let page_ptr: *mut u64 = page.start_address().as_mut_ptr(); - unsafe { - page_ptr.offset(400).write_volatile(0x_f021_f077_f065_f04e); + // allocate a number on the heap + let heap_value = Box::new(41); + println!("heap_value at {:p}", heap_value); + + // create a dynamically sized vector + let mut vec = Vec::new(); + for i in 0..500 { + vec.push(i); } + println!("vec at {:p}", vec.as_slice()); + + // create a reference counted vector -> will be freed when count reaches 0 + 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)); #[cfg(test)] test_main(); diff --git a/tests/heap_allocation.rs b/tests/heap_allocation.rs new file mode 100644 index 0000000..d8b2bd0 --- /dev/null +++ b/tests/heap_allocation.rs @@ -0,0 +1,69 @@ +#![no_std] +#![no_main] +#![feature(custom_test_frameworks)] +#![test_runner(blog_os::test_runner)] +#![reexport_test_harness_main = "test_main"] + +extern crate alloc; + +use alloc::boxed::Box; +use alloc::vec::Vec; +use blog_os::{serial_print, serial_println}; +use bootloader::{entry_point, BootInfo}; +use core::panic::PanicInfo; + +entry_point!(main); + +fn main(boot_info: &'static BootInfo) -> ! { + use blog_os::allocator; + use blog_os::memory::{self, BootInfoFrameAllocator}; + use x86_64::VirtAddr; + + blog_os::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 {} +} + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + blog_os::test_panic_handler(info) +} + +#[test_case] +fn simple_allocation() { + serial_print!("simple allocation... "); + let heap_value = Box::new(41); + assert_eq!(*heap_value, 41); + serial_println!("[ok]"); +} + +#[test_case] +fn large_vec() { + serial_print!("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); + serial_println!("[ok]"); +} + +#[test_case] +fn many_boxes() { + serial_print!("many_boxes... "); + for i in 0..10_000 { + let x = Box::new(i); + assert_eq!(*x, i); + } + serial_println!("[ok]"); +}