Skip to content

Commit 391132b

Browse files
committed
feat: implement heap allocation
1 parent 34ffa7f commit 391132b

File tree

10 files changed

+204
-16
lines changed

10 files changed

+204
-16
lines changed

.cargo/config.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ target = "bare-metal-target.json"
33

44
[unstable]
55
build-std-features = ["compiler-builtins-mem"]
6-
build-std = ["core", "compiler_builtins"]
6+
build-std = ["core", "compiler_builtins", "alloc"]
77

88
[target.'cfg(target_os = "none")']
99
runner = "bootimage runner"

Cargo.lock

Lines changed: 41 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ x86_64 = "0.14.2"
1111
uart_16550 = "0.2.0"
1212
pic8259 = "0.10.1"
1313
pc-keyboard = "0.7.0"
14+
linked_list_allocator = "0.9.0"
1415

1516
[dependencies.lazy_static]
1617
version = "1.0"
@@ -41,4 +42,4 @@ harness = false # disable test runner from default and custom test framewo
4142

4243
[[test]]
4344
name = "stack_overflow"
44-
harness = false
45+
harness = false

src/allocator.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
use alloc::alloc::{GlobalAlloc, Layout};
2+
use core::ptr::null_mut;
3+
use linked_list_allocator::LockedHeap;
4+
use x86_64::{
5+
VirtAddr,
6+
structures::paging::{
7+
FrameAllocator, Mapper, Page, PageTableFlags, Size4KiB, mapper::MapToError,
8+
},
9+
};
10+
11+
pub const HEAP_START: usize = 0x_4444_4444_0000;
12+
pub const HEAP_SIZE: usize = 100 * 1024; // 100 KiB
13+
14+
pub fn init_heap(
15+
mapper: &mut impl Mapper<Size4KiB>,
16+
frame_allocator: &mut impl FrameAllocator<Size4KiB>,
17+
) -> Result<(), MapToError<Size4KiB>> {
18+
let page_range = {
19+
let heap_start = VirtAddr::new(HEAP_START as u64);
20+
let heap_end = heap_start + HEAP_SIZE - 1u64;
21+
let heap_start_page = Page::containing_address(heap_start);
22+
let heap_end_page = Page::containing_address(heap_end);
23+
24+
Page::range_inclusive(heap_start_page, heap_end_page)
25+
};
26+
27+
for page in page_range {
28+
let frame = frame_allocator
29+
.allocate_frame()
30+
.ok_or(MapToError::FrameAllocationFailed)?;
31+
32+
let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE;
33+
34+
unsafe {
35+
mapper.map_to(page, frame, flags, frame_allocator)?.flush();
36+
};
37+
}
38+
39+
unsafe {
40+
ALLOCATOR.lock().init(HEAP_START, HEAP_SIZE);
41+
}
42+
43+
Ok(())
44+
}
45+
46+
#[global_allocator]
47+
static ALLOCATOR: LockedHeap = LockedHeap::empty();
48+
49+
pub struct Dummy;
50+
51+
unsafe impl GlobalAlloc for Dummy {
52+
unsafe fn alloc(&self, _layout: Layout) -> *mut u8 {
53+
null_mut()
54+
}
55+
56+
unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {
57+
panic!("dealloc should be never called")
58+
}
59+
}

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77

88
use core::panic::PanicInfo;
99

10+
extern crate alloc;
11+
12+
pub mod allocator;
1013
pub mod gdt;
1114
pub mod interrupts;
1215
pub mod memory;

src/main.rs

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,19 @@
44
#![test_runner(osdev_rust::test_runner)]
55
#![reexport_test_harness_main = "test_main"]
66

7+
use alloc::{boxed::Box, rc::Rc, vec, vec::Vec};
78
use bootloader::{BootInfo, entry_point};
89
use core::panic::PanicInfo;
910
use osdev_rust::println;
1011

12+
extern crate alloc;
13+
1114
entry_point!(kernel_main);
1215

1316
fn kernel_main(boot_info: &'static BootInfo) -> ! {
14-
use osdev_rust::memory;
15-
use osdev_rust::memory::BootInfoFrameAllocator;
16-
use x86_64::{VirtAddr, structures::paging::Page};
17+
use osdev_rust::allocator;
18+
use osdev_rust::memory::{self, BootInfoFrameAllocator};
19+
use x86_64::VirtAddr;
1720

1821
println!("Hello World!");
1922

@@ -23,14 +26,31 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! {
2326
let mut mapper = unsafe { memory::init(phys_mem_offset) };
2427
let mut frame_allocator = unsafe { BootInfoFrameAllocator::init(&boot_info.memory_map) };
2528

26-
let page = Page::containing_address(VirtAddr::new(0xdeadbeef000));
27-
memory::create_example_mapping(page, &mut mapper, &mut frame_allocator);
29+
allocator::init_heap(&mut mapper, &mut frame_allocator).expect("heap initialization failed");
30+
31+
let heap_value = Box::new(42);
32+
println!("heap_value at {:p}", heap_value);
33+
34+
let mut vec = Vec::new();
2835

29-
let page_ptr: *mut u64 = page.start_address().as_mut_ptr();
30-
unsafe {
31-
page_ptr.offset(400).write_volatile(0x_f021_f077_f065_f04e);
36+
for i in 0..500 {
37+
vec.push(i);
3238
}
3339

40+
println!("vec at {:p}", vec.as_slice());
41+
42+
let reference_counted = Rc::new(vec![1, 2, 3]);
43+
let cloned_reference = reference_counted.clone();
44+
println!(
45+
"current reference count: {}",
46+
Rc::strong_count(&cloned_reference),
47+
);
48+
core::mem::drop(reference_counted);
49+
println!(
50+
"reference count is {} now",
51+
Rc::strong_count(&cloned_reference)
52+
);
53+
3454
#[cfg(test)]
3555
test_main();
3656

tests/basic_boot.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use core::panic::PanicInfo;
88

99
use osdev_rust::println;
1010

11-
#[no_mangle]
11+
#[unsafe(no_mangle)]
1212
pub extern "C" fn _start() -> ! {
1313
test_main();
1414

tests/heap_allocation.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#![no_std]
2+
#![no_main]
3+
#![feature(custom_test_frameworks)]
4+
#![test_runner(osdev_rust::test_runner)]
5+
#![reexport_test_harness_main = "test_main"]
6+
7+
extern crate alloc;
8+
9+
use alloc::{boxed::Box, vec::Vec};
10+
use bootloader::{BootInfo, entry_point};
11+
use core::panic::PanicInfo;
12+
use osdev_rust::allocator::HEAP_SIZE;
13+
14+
entry_point!(main);
15+
16+
fn main(boot_info: &'static BootInfo) -> ! {
17+
use osdev_rust::allocator;
18+
use osdev_rust::memory::{self, BootInfoFrameAllocator};
19+
use x86_64::VirtAddr;
20+
21+
osdev_rust::init();
22+
23+
let phys_mem_offset = VirtAddr::new(boot_info.physical_memory_offset);
24+
let mut mapper = unsafe { memory::init(phys_mem_offset) };
25+
let mut frame_allocator = unsafe { BootInfoFrameAllocator::init(&boot_info.memory_map) };
26+
allocator::init_heap(&mut mapper, &mut frame_allocator).expect("heap initialization failed");
27+
28+
test_main();
29+
loop {}
30+
}
31+
32+
#[panic_handler]
33+
fn panic(info: &PanicInfo) -> ! {
34+
osdev_rust::test_panic_handler(info);
35+
}
36+
37+
#[test_case]
38+
fn simple_allocation() {
39+
let heap_value_1 = Box::new(42);
40+
let heap_value_2 = Box::new(43);
41+
42+
assert_eq!(*heap_value_1, 42);
43+
assert_eq!(*heap_value_2, 43);
44+
}
45+
46+
#[test_case]
47+
fn large_vec() {
48+
let n = 1000;
49+
let mut vec = Vec::new();
50+
51+
for i in 0..n {
52+
vec.push(i);
53+
}
54+
55+
assert_eq!(vec.iter().sum::<u64>(), (n - 1) * n / 2);
56+
}
57+
58+
#[test_case]
59+
fn many_boxes() {
60+
for i in 0..HEAP_SIZE {
61+
let x = Box::new(i);
62+
assert_eq!(*x, i);
63+
}
64+
}

tests/should_panic.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55

66
use core::panic::PanicInfo;
77

8-
use osdev_rust::{exit_qemu, serial_print, serial_println, QemuExitCode};
8+
use osdev_rust::{QemuExitCode, exit_qemu, serial_print, serial_println};
99

10-
#[no_mangle]
10+
#[unsafe(no_mangle)]
1111
pub extern "C" fn _start() -> ! {
1212
should_fail();
1313
serial_println!("[test did not panic]");

tests/stack_overflow.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
use core::panic::PanicInfo;
66
use lazy_static::lazy_static;
77
use osdev_rust::{
8-
exit_qemu, gdt::DOUBLE_FAULT_IST_INDEX, serial_print, serial_println, test_panic_handler,
9-
QemuExitCode,
8+
QemuExitCode, exit_qemu, gdt::DOUBLE_FAULT_IST_INDEX, serial_print, serial_println,
9+
test_panic_handler,
1010
};
1111
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
1212

13-
#[no_mangle]
13+
#[unsafe(no_mangle)]
1414
pub extern "C" fn _start() -> ! {
1515
serial_print!("stack_overflow::stack_overflow...\t");
1616

0 commit comments

Comments
 (0)