heap allocator
This commit is contained in:
parent
3e17ebe46e
commit
92881a56da
32
Cargo.lock
generated
32
Cargo.lock
generated
@ -2,6 +2,12 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "bit_field"
|
||||
version = "0.10.2"
|
||||
@ -14,10 +20,21 @@ version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mxrox_kernel"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"spin",
|
||||
"stable-vec",
|
||||
"volatile 0.6.1",
|
||||
"x86_64",
|
||||
@ -35,6 +52,21 @@ version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable-vec"
|
||||
version = "0.4.1"
|
||||
|
@ -7,3 +7,4 @@ edition = "2021"
|
||||
x86_64 = "0.15.2"
|
||||
volatile = "0.6.1"
|
||||
stable-vec = "0.4.1"
|
||||
spin = "0.9"
|
6
Makefile
6
Makefile
@ -9,7 +9,7 @@ build/boot.o: boot.s build_dir
|
||||
nasm -f elf32 '$<' -o '$@'
|
||||
|
||||
build/kernel.elf: linker.ld build/boot.o build/kernel.o
|
||||
i686-elf-ld -m elf_i386 -nostdlib -o '$@' -T $^
|
||||
i686-elf-ld -m elf_i386 -o '$@' -T $^
|
||||
|
||||
build/kernel.o: build_dir
|
||||
rustup override set nightly
|
||||
@ -26,8 +26,8 @@ build: build/mxrox.iso
|
||||
|
||||
clean:
|
||||
rm -rf build
|
||||
# rm -rf target
|
||||
# rm -rf Cargo.lock
|
||||
rm -rf target
|
||||
# rm -rf Cargo.lock
|
||||
mkdir build
|
||||
|
||||
run-kernel: build/kernel.elf
|
||||
|
@ -5,7 +5,7 @@ Mixray's small x86_64 OS
|
||||
## How to build
|
||||
|
||||
```bash
|
||||
make clean # removes target/
|
||||
make clean # removes target/, build/
|
||||
make build # builds iso image (build/mxrox.iso)
|
||||
make run # runs iso image in QEMU emulator
|
||||
make run-kernel # runs only kernel in QEMU emulator
|
||||
|
122
boot.s
122
boot.s
@ -1,40 +1,90 @@
|
||||
global loader
|
||||
global stack_ptr
|
||||
; Declare constants for the multiboot header.
|
||||
MBALIGN equ 1 << 0 ; align loaded modules on page boundaries
|
||||
MEMINFO equ 1 << 1 ; provide memory map
|
||||
MBFLAGS equ MBALIGN | MEMINFO ; this is the Multiboot 'flag' field
|
||||
MAGIC equ 0x1BADB002 ; 'magic number' lets bootloader find the header
|
||||
CHECKSUM equ -(MAGIC + MBFLAGS) ; checksum of above, to prove we are multiboot
|
||||
|
||||
extern main
|
||||
|
||||
MODULEALIGN equ 1<<0
|
||||
MEMINFO equ 1<<1
|
||||
FLAGS equ MODULEALIGN | MEMINFO
|
||||
MAGIC equ 0x1BADB002
|
||||
CHECKSUM equ -(MAGIC + FLAGS)
|
||||
|
||||
section .mbheader
|
||||
; Declare a multiboot header that marks the program as a kernel. These are magic
|
||||
; values that are documented in the multiboot standard. The bootloader will
|
||||
; search for this signature in the first 8 KiB of the kernel file, aligned at a
|
||||
; 32-bit boundary. The signature is in its own section so the header can be
|
||||
; forced to be within the first 8 KiB of the kernel file.
|
||||
section .multiboot
|
||||
align 4
|
||||
MultiBootHeader:
|
||||
dd MAGIC
|
||||
dd FLAGS
|
||||
dd CHECKSUM
|
||||
|
||||
section .text
|
||||
|
||||
STACKSIZE equ 0x4000
|
||||
|
||||
loader:
|
||||
mov esp, stack+STACKSIZE
|
||||
push eax
|
||||
push ebx
|
||||
|
||||
call main
|
||||
|
||||
cli
|
||||
|
||||
hang:
|
||||
hlt
|
||||
jmp hang
|
||||
dd MAGIC
|
||||
dd MBFLAGS
|
||||
dd CHECKSUM
|
||||
|
||||
; The multiboot standard does not define the value of the stack pointer register
|
||||
; (esp) and it is up to the kernel to provide a stack. This allocates room for a
|
||||
; small stack by creating a symbol at the bottom of it, then allocating 16384
|
||||
; bytes for it, and finally creating a symbol at the top. The stack grows
|
||||
; downwards on x86. The stack is in its own section so it can be marked nobits,
|
||||
; which means the kernel file is smaller because it does not contain an
|
||||
; uninitialized stack. The stack on x86 must be 16-byte aligned according to the
|
||||
; System V ABI standard and de-facto extensions. The compiler will assume the
|
||||
; stack is properly aligned and failure to align the stack will result in
|
||||
; undefined behavior.
|
||||
section .bss
|
||||
align 4
|
||||
stack:
|
||||
resb STACKSIZE
|
||||
stack_ptr:
|
||||
align 16
|
||||
stack_bottom:
|
||||
resb 16384 ; 16 KiB
|
||||
stack_top:
|
||||
|
||||
; The linker script specifies _start as the entry point to the kernel and the
|
||||
; bootloader will jump to this position once the kernel has been loaded. It
|
||||
; doesn't make sense to return from this function as the bootloader is gone.
|
||||
; Declare _start as a function symbol with the given symbol size.
|
||||
section .text
|
||||
global _start:function (_start.end - _start)
|
||||
_start:
|
||||
; The bootloader has loaded us into 32-bit protected mode on a x86
|
||||
; machine. Interrupts are disabled. Paging is disabled. The processor
|
||||
; state is as defined in the multiboot standard. The kernel has full
|
||||
; control of the CPU. The kernel can only make use of hardware features
|
||||
; and any code it provides as part of itself. There's no printf
|
||||
; function, unless the kernel provides its own <stdio.h> header and a
|
||||
; printf implementation. There are no security restrictions, no
|
||||
; safeguards, no debugging mechanisms, only what the kernel provides
|
||||
; itself. It has absolute and complete power over the
|
||||
; machine.
|
||||
|
||||
; To set up a stack, we set the esp register to point to the top of our
|
||||
; stack (as it grows downwards on x86 systems). This is necessarily done
|
||||
; in assembly as languages such as C cannot function without a stack.
|
||||
mov esp, stack_top
|
||||
|
||||
; This is a good place to initialize crucial processor state before the
|
||||
; high-level kernel is entered. It's best to minimize the early
|
||||
; environment where crucial features are offline. Note that the
|
||||
; processor is not fully initialized yet: Features such as floating
|
||||
; point instructions and instruction set extensions are not initialized
|
||||
; yet. The GDT should be loaded here. Paging should be enabled here.
|
||||
; C++ features such as global constructors and exceptions will require
|
||||
; runtime support to work as well.
|
||||
|
||||
; Enter the high-level kernel. The ABI requires the stack is 16-byte
|
||||
; aligned at the time of the call instruction (which afterwards pushes
|
||||
; the return pointer of size 4 bytes). The stack was originally 16-byte
|
||||
; aligned above and we've since pushed a multiple of 16 bytes to the
|
||||
; stack since (pushed 0 bytes so far) and the alignment is thus
|
||||
; preserved and the call is well defined.
|
||||
; note, that if you are building on Windows, C functions may have "_" prefix in assembly: _kernel_main
|
||||
extern main
|
||||
call main
|
||||
|
||||
; If the system has nothing more to do, put the computer into an
|
||||
; infinite loop. To do that:
|
||||
; 1) Disable interrupts with cli (clear interrupt enable in eflags).
|
||||
; They are already disabled by the bootloader, so this is not needed.
|
||||
; Mind that you might later enable interrupts and return from
|
||||
; kernel_main (which is sort of nonsensical to do).
|
||||
; 2) Wait for the next interrupt to arrive with hlt (halt instruction).
|
||||
; Since they are disabled, this will lock up the computer.
|
||||
; 3) Jump to the hlt instruction if it ever wakes up due to a
|
||||
; non-maskable interrupt occurring or due to system management mode.
|
||||
cli
|
||||
.hang: hlt
|
||||
jmp .hang
|
||||
.end:
|
133
src/kernel/heap.rs
Normal file
133
src/kernel/heap.rs
Normal file
@ -0,0 +1,133 @@
|
||||
use core::alloc::{GlobalAlloc, Layout};
|
||||
use core::ptr::null_mut;
|
||||
use core::mem;
|
||||
use core::cell::UnsafeCell;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
struct FreeListNode {
|
||||
size: usize,
|
||||
next: *mut FreeListNode,
|
||||
}
|
||||
|
||||
pub struct FreeListAllocator {
|
||||
head: UnsafeCell<*mut FreeListNode>,
|
||||
heap_start: AtomicUsize,
|
||||
heap_end: AtomicUsize,
|
||||
}
|
||||
|
||||
unsafe impl Send for FreeListAllocator {}
|
||||
unsafe impl Sync for FreeListAllocator {}
|
||||
|
||||
impl FreeListAllocator {
|
||||
pub const fn new() -> Self {
|
||||
FreeListAllocator {
|
||||
head: UnsafeCell::new(null_mut()),
|
||||
heap_start: AtomicUsize::new(0),
|
||||
heap_end: AtomicUsize::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn init(&self, heap_start: usize, heap_size: usize) {
|
||||
self.heap_start.store(heap_start, Ordering::Relaxed) ;
|
||||
self.heap_end.store(heap_start + heap_size, Ordering::Relaxed) ;
|
||||
|
||||
let first_node = heap_start as *mut FreeListNode;
|
||||
first_node.write(FreeListNode { size: heap_size, next: null_mut() });
|
||||
|
||||
*self.head.get() = first_node;
|
||||
}
|
||||
|
||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||
let mut prev: *mut FreeListNode = null_mut();
|
||||
let mut current = *self.head.get();
|
||||
|
||||
while !current.is_null() {
|
||||
let node = &mut *current;
|
||||
|
||||
let alloc_start = (current as usize + mem::size_of::<FreeListNode>() + layout.align() - 1)
|
||||
& !(layout.align() - 1);
|
||||
let alloc_end = alloc_start + layout.size();
|
||||
|
||||
if alloc_end > self.heap_end.load(Ordering::Relaxed) || alloc_end > (current as usize + node.size) {
|
||||
prev = current;
|
||||
current = node.next;
|
||||
continue;
|
||||
}
|
||||
|
||||
let remaining_size = (current as usize + node.size) - alloc_end;
|
||||
if remaining_size > mem::size_of::<FreeListNode>() {
|
||||
let new_node = alloc_end as *mut FreeListNode;
|
||||
new_node.write(FreeListNode {
|
||||
size: remaining_size,
|
||||
next: node.next,
|
||||
});
|
||||
|
||||
if prev.is_null() {
|
||||
*self.head.get() = new_node;
|
||||
} else {
|
||||
(*prev).next = new_node;
|
||||
}
|
||||
} else {
|
||||
if prev.is_null() {
|
||||
*self.head.get() = node.next;
|
||||
} else {
|
||||
(*prev).next = node.next;
|
||||
}
|
||||
}
|
||||
|
||||
return alloc_start as *mut u8;
|
||||
}
|
||||
|
||||
null_mut()
|
||||
}
|
||||
|
||||
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
|
||||
let free_node = ptr as *mut FreeListNode;
|
||||
free_node.write(FreeListNode {
|
||||
size: layout.size(),
|
||||
next: *self.head.get(),
|
||||
});
|
||||
|
||||
*self.head.get() = free_node;
|
||||
}
|
||||
}
|
||||
|
||||
#[global_allocator]
|
||||
static ALLOCATOR: FreeListAllocator = FreeListAllocator::new();
|
||||
|
||||
unsafe impl GlobalAlloc for FreeListAllocator {
|
||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||
self.alloc(layout)
|
||||
}
|
||||
|
||||
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
|
||||
self.dealloc(ptr, layout)
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 {
|
||||
for i in 0..n {
|
||||
unsafe {
|
||||
*dest.add(i) = *src.add(i);
|
||||
}
|
||||
}
|
||||
dest
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn memset(dest: *mut u8, value: i32, n: usize) -> *mut u8 {
|
||||
let byte = value as u8;
|
||||
for i in 0..n {
|
||||
unsafe {
|
||||
*dest.add(i) = byte;
|
||||
}
|
||||
}
|
||||
dest
|
||||
}
|
||||
|
||||
pub fn init_heap(heap_start: usize, heap_size: usize) {
|
||||
unsafe {
|
||||
ALLOCATOR.init(heap_start, heap_size);
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
use heap::init_heap;
|
||||
use stable_vec::StableVec;
|
||||
use vga::{
|
||||
fill_with_color,
|
||||
@ -10,6 +11,7 @@ mod vga;
|
||||
mod ps2;
|
||||
mod acpi;
|
||||
mod thread;
|
||||
mod heap;
|
||||
|
||||
type Vec<T> = StableVec<T>;
|
||||
|
||||
@ -18,6 +20,8 @@ pub fn show_error(message: &str) {
|
||||
put_string_by_index(0, message, VGA_COLOR_BLACK, VGA_COLOR_RED);
|
||||
}
|
||||
|
||||
pub fn start_kernel() {
|
||||
pub fn init_kernel() {
|
||||
init_heap(16400, 16384);
|
||||
|
||||
show_error("error test");
|
||||
}
|
@ -8,7 +8,7 @@ extern "C" fn eh_personality() {}
|
||||
|
||||
use core::panic::PanicInfo;
|
||||
|
||||
use kernel::{start_kernel, show_error};
|
||||
use kernel::{init_kernel, show_error};
|
||||
|
||||
mod kernel;
|
||||
|
||||
@ -19,9 +19,8 @@ fn panic(info: &PanicInfo) -> ! {
|
||||
loop {}
|
||||
}
|
||||
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn main() -> ! {
|
||||
start_kernel();
|
||||
pub extern "C" fn main() -> ! {
|
||||
init_kernel();
|
||||
loop {}
|
||||
}
|
Loading…
Reference in New Issue
Block a user