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.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bit_field"
|
name = "bit_field"
|
||||||
version = "0.10.2"
|
version = "0.10.2"
|
||||||
@ -14,10 +20,21 @@ version = "2.8.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
|
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]]
|
[[package]]
|
||||||
name = "mxrox_kernel"
|
name = "mxrox_kernel"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"spin",
|
||||||
"stable-vec",
|
"stable-vec",
|
||||||
"volatile 0.6.1",
|
"volatile 0.6.1",
|
||||||
"x86_64",
|
"x86_64",
|
||||||
@ -35,6 +52,21 @@ version = "1.0.19"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
|
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]]
|
[[package]]
|
||||||
name = "stable-vec"
|
name = "stable-vec"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
|
@ -6,4 +6,5 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
x86_64 = "0.15.2"
|
x86_64 = "0.15.2"
|
||||||
volatile = "0.6.1"
|
volatile = "0.6.1"
|
||||||
stable-vec = "0.4.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 '$@'
|
nasm -f elf32 '$<' -o '$@'
|
||||||
|
|
||||||
build/kernel.elf: linker.ld build/boot.o build/kernel.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
|
build/kernel.o: build_dir
|
||||||
rustup override set nightly
|
rustup override set nightly
|
||||||
@ -26,8 +26,8 @@ build: build/mxrox.iso
|
|||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf build
|
rm -rf build
|
||||||
# rm -rf target
|
rm -rf target
|
||||||
# rm -rf Cargo.lock
|
# rm -rf Cargo.lock
|
||||||
mkdir build
|
mkdir build
|
||||||
|
|
||||||
run-kernel: build/kernel.elf
|
run-kernel: build/kernel.elf
|
||||||
|
@ -5,7 +5,7 @@ Mixray's small x86_64 OS
|
|||||||
## How to build
|
## How to build
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make clean # removes target/
|
make clean # removes target/, build/
|
||||||
make build # builds iso image (build/mxrox.iso)
|
make build # builds iso image (build/mxrox.iso)
|
||||||
make run # runs iso image in QEMU emulator
|
make run # runs iso image in QEMU emulator
|
||||||
make run-kernel # runs only kernel in QEMU emulator
|
make run-kernel # runs only kernel in QEMU emulator
|
||||||
|
122
boot.s
122
boot.s
@ -1,40 +1,90 @@
|
|||||||
global loader
|
; Declare constants for the multiboot header.
|
||||||
global stack_ptr
|
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
|
; 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
|
||||||
MODULEALIGN equ 1<<0
|
; search for this signature in the first 8 KiB of the kernel file, aligned at a
|
||||||
MEMINFO equ 1<<1
|
; 32-bit boundary. The signature is in its own section so the header can be
|
||||||
FLAGS equ MODULEALIGN | MEMINFO
|
; forced to be within the first 8 KiB of the kernel file.
|
||||||
MAGIC equ 0x1BADB002
|
section .multiboot
|
||||||
CHECKSUM equ -(MAGIC + FLAGS)
|
|
||||||
|
|
||||||
section .mbheader
|
|
||||||
align 4
|
align 4
|
||||||
MultiBootHeader:
|
dd MAGIC
|
||||||
dd MAGIC
|
dd MBFLAGS
|
||||||
dd FLAGS
|
dd CHECKSUM
|
||||||
dd CHECKSUM
|
|
||||||
|
|
||||||
section .text
|
|
||||||
|
|
||||||
STACKSIZE equ 0x4000
|
|
||||||
|
|
||||||
loader:
|
|
||||||
mov esp, stack+STACKSIZE
|
|
||||||
push eax
|
|
||||||
push ebx
|
|
||||||
|
|
||||||
call main
|
|
||||||
|
|
||||||
cli
|
|
||||||
|
|
||||||
hang:
|
|
||||||
hlt
|
|
||||||
jmp hang
|
|
||||||
|
|
||||||
|
; 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
|
section .bss
|
||||||
align 4
|
align 16
|
||||||
stack:
|
stack_bottom:
|
||||||
resb STACKSIZE
|
resb 16384 ; 16 KiB
|
||||||
stack_ptr:
|
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 stable_vec::StableVec;
|
||||||
use vga::{
|
use vga::{
|
||||||
fill_with_color,
|
fill_with_color,
|
||||||
@ -10,6 +11,7 @@ mod vga;
|
|||||||
mod ps2;
|
mod ps2;
|
||||||
mod acpi;
|
mod acpi;
|
||||||
mod thread;
|
mod thread;
|
||||||
|
mod heap;
|
||||||
|
|
||||||
type Vec<T> = StableVec<T>;
|
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);
|
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");
|
show_error("error test");
|
||||||
}
|
}
|
@ -8,7 +8,7 @@ extern "C" fn eh_personality() {}
|
|||||||
|
|
||||||
use core::panic::PanicInfo;
|
use core::panic::PanicInfo;
|
||||||
|
|
||||||
use kernel::{start_kernel, show_error};
|
use kernel::{init_kernel, show_error};
|
||||||
|
|
||||||
mod kernel;
|
mod kernel;
|
||||||
|
|
||||||
@ -19,9 +19,8 @@ fn panic(info: &PanicInfo) -> ! {
|
|||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
extern "C" fn main() -> ! {
|
pub extern "C" fn main() -> ! {
|
||||||
start_kernel();
|
init_kernel();
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user