heap allocator

This commit is contained in:
MeexReay 2025-01-29 12:38:53 +03:00
parent 3e17ebe46e
commit 92881a56da
8 changed files with 265 additions and 46 deletions

32
Cargo.lock generated
View File

@ -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"

View File

@ -6,4 +6,5 @@ edition = "2021"
[dependencies]
x86_64 = "0.15.2"
volatile = "0.6.1"
stable-vec = "0.4.1"
stable-vec = "0.4.1"
spin = "0.9"

View File

@ -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

View File

@ -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
View File

@ -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
View 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);
}
}

View File

@ -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");
}

View File

@ -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 {}
}