From 31493c16d10e42da6fb58a92b55cf73b9d0fa64b Mon Sep 17 00:00:00 2001 From: Eemeli <eemeli.o.lehtonen@utu.fi> Date: Mon, 30 Jan 2023 03:43:12 +0200 Subject: [PATCH] page frame allocator init --- .cargo/config.toml | 7 +- Cargo.toml | 4 - Makefile | 188 ++++++++++-------------- {.cargo => old_cargo_configs}/runner.sh | 0 qemu.mk | 42 ++++++ src/boot/limine/Makefile | 43 ++++++ src/boot/limine/mem.rs | 33 ++++- src/boot/limine/mod.rs | 1 + src/log.rs | 19 +-- src/main.rs | 1 + src/mem.rs | 90 ------------ src/mem/bump.rs | 90 ++++++++++++ src/mem/map.rs | 17 +++ src/mem/mod.rs | 21 +++ src/mem/pfa.rs | 113 ++++++++++++++ src/smp.rs | 2 +- src/term/escape/encode.rs | 84 ++++++++--- src/util/bitmap.rs | 142 ++++++++++++++++++ src/{util.rs => util/fmt.rs} | 0 src/util/mod.rs | 2 + 20 files changed, 655 insertions(+), 244 deletions(-) rename {.cargo => old_cargo_configs}/runner.sh (100%) create mode 100644 qemu.mk create mode 100644 src/boot/limine/Makefile delete mode 100644 src/mem.rs create mode 100644 src/mem/bump.rs create mode 100644 src/mem/map.rs create mode 100644 src/mem/mod.rs create mode 100755 src/mem/pfa.rs create mode 100644 src/util/bitmap.rs rename src/{util.rs => util/fmt.rs} (100%) create mode 100644 src/util/mod.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index c04d9e5..b9360a8 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,8 +1,3 @@ -[alias] -debug = "run -- --debug" - [build] +# this is so that rust-analyzer wouldn't fight me constantly target = "x86_64-unknown-none" - -[target.x86_64-unknown-none] -runner = "./.cargo/runner.sh" diff --git a/Cargo.toml b/Cargo.toml index 96e7ffd..4bcd210 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,9 +13,6 @@ multiboot1 = [] multiboot2 = [] # Pick limine OR bootboot OR multiboot1 OR multiboot2, they conflict with eachother -# [lib] -# crate-type = ["staticlib"] - [dependencies] spin = "0.9.4" volatile = "0.4.5" @@ -29,6 +26,5 @@ rev = "c2fbc349419d4330b80e053019ad2fe504a61764" # version = "0.1.9" # 0.1.9 LimineFile struct has a bug optional = true - [build-dependencies] image = "0.24.5" diff --git a/Makefile b/Makefile index 74ff7dc..4236c51 100644 --- a/Makefile +++ b/Makefile @@ -4,127 +4,99 @@ # @file # @version 0.1 -ARCH ?= x86_64 -#ARCH ?= x86 -PROFILE ?= debug -#PROFILE ?= release -GDB ?= false - -# binary config -NASM ?= nasm -LD ?= ld.lld -OBJCOPY ?= llvm-objcopy -CARGO ?= cargo -#CARGO ?= cargo-clif +# config +ARCH ?= x86_64 +#ARCH ?= x86 +PROFILE ?= debug +#PROFILE ?= release +GDB ?= false +BOOTLOADER ?= limine +KVM ?= true + +# binaries +NASM ?= nasm +LD ?= ld.lld +OBJCOPY ?= llvm-objcopy +CARGO ?= cargo +#CARGO ?= cargo-clif +XORRISO ?= xorriso +JQ ?= jq +QEMU_x86_64 ?= qemu-system-x86_64 +QEMU_x86 ?= qemu-system-i386 +QEMU ?= ${QEMU_${ARCH}} + +# rust targets +RUST_T_x86_64 := x86_64-unknown-none # common directories -TARGET_DIR ?= target -HYPER_DIR := ${TARGET_DIR}/hyperion/${ARCH} -ARCH_DIR := src/arch/${ARCH} -CARGO_DIR = ${TARGET_DIR}/${RUST_T_${ARCH}}/${PROFILE} - -# hyperion kernel lib -RUST_T_x86_64 := x86_64-unknown-none -RUST_F_debug := -RUST_F_release:= --release -CARGO_FLAGS ?= -CARGO_FLAGS += ${RUST_F_${PROFILE}} -CARGO_FLAGS += --target=${RUST_T_${ARCH}} -KERNEL_LIB := ${CARGO_DIR}/libhyperion.a -KERNEL_SRC := $(filter-out %: ,$(file < ${CARGO_DIR}/libhyperion.d)) -${KERNEL_LIB} : ${KERNEL_SRC} Makefile Cargo.toml Cargo.lock - @echo "\n\033[32m--[[ building Hyperion lib ]]--\033[0m" +TARGET_DIR ?= target +HYPER_DIR := ${TARGET_DIR}/hyperion/${BOOTLOADER}/${ARCH} +ARCH_DIR := src/arch/${ARCH} +BOOT_DIR := src/boot +CARGO_DIR := ${TARGET_DIR}/${RUST_T_${ARCH}}/${PROFILE} +ISO_DIR := ${HYPER_DIR}/iso +ISO_TESTING_DIR := ${HYPER_DIR}/iso-testing + +# artefacts +HYPERION := ${HYPER_DIR}/hyperion.iso +HYPERION_TESTING := ${HYPER_DIR}/hyperion-testing.iso + +# rust/cargo +RUST_F_debug := +RUST_F_release := --release +CARGO_FLAGS ?= +CARGO_FLAGS += ${RUST_F_${PROFILE}} +CARGO_FLAGS += --target=${RUST_T_${ARCH}} +CARGO_FLAGS += --package=hyperion +KERNEL := ${CARGO_DIR}/hyperion +KERNEL_TESTING := ${KERNEL}-testing +KERNEL_SRC := $(filter-out %: ,$(file < ${CARGO_DIR}/hyperion.d)) + +# gdb +GDB_FLAGS ?= +GDB_FLAGS += --eval-command="target remote localhost:1234" +GDB_FLAGS += --eval-command="symbol-file ${KERNEL}" + +# hyperion kernel compilation +${KERNEL}: ${KERNEL_SRC} Makefile Cargo.toml Cargo.lock + @echo "\n\033[32m--[[ building Hyperion ]]--\033[0m" ${CARGO} build ${CARGO_FLAGS} + @touch ${KERNEL} + +${KERNEL_TESTING}: ${KERNEL_SRC} Makefile Cargo.toml Cargo.lock + @echo "\n\033[32m--[[ building Hyperion-Testing ]]--\033[0m" + @${CARGO} test --no-run # first one prints human readable errors + ${CARGO} test --no-run --message-format=json ${CARGO_FLAGS} | \ + jq -r "select(.profile.test == true) | .filenames[]" | \ + xargs -I % cp "%" ${KERNEL_TESTING} + @touch ${KERNEL_TESTING} + +# ISO generation +include ./${BOOT_DIR}/${BOOTLOADER}/Makefile -# hyperion boot code -# BOOT_SRC := ${ARCH_DIR}/start.asm -# BOOT_OBJ := ${HYPER_DIR}/start.o -# NASM_F_x86_64 := elf64 -# NASM_F_x86 := elf32 -# NASM_FLAGS ?= -# NASM_FLAGS += ${BOOT_SRC} -# NASM_FLAGS += -o ${BOOT_OBJ} -# NASM_FLAGS += -f ${NASM_F_${ARCH}} -# ${BOOT_OBJ} : ${BOOT_SRC} Makefile -# @echo "\n\033[32m--[[ building Hyperion boot ]]--\033[0m" -# mkdir -p ${HYPER_DIR} -# ${NASM} ${NASM_FLAGS} - -# hyperion kernel elf -LD_SCRIPT := ${ARCH_DIR}/link.ld -KERNEL_ELF := ${HYPER_DIR}/hyperion -KERNEL_DEPS := ${KERNEL_LIB} #${BOOT_OBJ} -LD_M_x86_64 := elf_x86_64 -LD_M_x86 := elf_i386 -LD_FLAGS ?= -#LD_FLAGS += --whole-archive -LD_FLAGS += ${KERNEL_DEPS} -LD_FLAGS += -o ${KERNEL_ELF} -LD_FLAGS += --gc-sections -LD_FLAGS += -T ${LD_SCRIPT} -LD_FLAGS += -m ${LD_M_${ARCH}} -${KERNEL_ELF} : ${KERNEL_DEPS} ${LD_SCRIPT} Makefile - @echo "\n\033[32m--[[ building Hyperion kernel ]]--\033[0m" - mkdir -p ${HYPER_DIR} - ${LD} ${LD_FLAGS} -# evil hack to satisfy qemu and grub: -# the entry format has to be x86 not x86_64 - ${OBJCOPY} -O elf32-i386 ${KERNEL_ELF} - -# hyperion iso -HYPERION := ${HYPER_DIR}/hyperion.iso -ISO_DIR := ${HYPER_DIR}/iso -BOOT_DIR := ${ISO_DIR}/boot -GRUB_DIR := ${BOOT_DIR}/grub -${HYPERION} : ${KERNEL_ELF} cfg/grub.cfg Makefile - @echo "\n\033[32m--[[ building Hyperion iso ]]--\033[0m" - mkdir -p ${GRUB_DIR} - cp cfg/grub.cfg ${GRUB_DIR} - cp ${KERNEL_ELF} ${BOOT_DIR}/ - grub-mkrescue /usr/lib/grub/i386-pc -o $@ ${ISO_DIR} +# ISO running +include ./qemu.mk # build alias -build : ${KERNEL_ELF} - -# qemu direct kernel boot alias -QEMU_x86_64 ?= qemu-system-x86_64 -QEMU_x86 ?= qemu-system-i386 -QEMU_FLAGS ?= -QEMU_FLAGS += -serial stdio -QEMU_FLAGS += -s -ifeq (${GDB},true) -QEMU_FLAGS += -S -endif -QEMU_FLAGS += -enable-kvm -QEMU_FLAGS += -d cpu_reset,guest_errors -#QEMU_FLAGS += -M pc-i440fx-7.2 -#QEMU_FLAGS += -device VGA,vgamem_mb=64 -QEMU_FLAGS += -vga std -QEMU_KERNEL := -kernel ${KERNEL_ELF} -append qemu -qemu : ${KERNEL_ELF} - ${QEMU_${ARCH}} ${QEMU_FLAGS} ${QEMU_KERNEL} - -# qemu iso boot alias -#QEMU_FLAGS += -bios ${QEMU_OVMF} -QEMU_OVMF ?= /usr/share/ovmf/x64/OVMF.fd -QEMU_ISO := -drive format=raw,file=${HYPERION} -qemu_iso : ${HYPERION} - ${QEMU_${ARCH}} ${QEMU_FLAGS} ${QEMU_ISO} +build: ${KERNEL} + +# bootable iso alias +iso: ${HYPERION} + +reset-cargo-deps: + rm ${CARGO_DIR}/hyperion.d # connect gdb to qemu -GDB_FLAGS ?= -GDB_FLAGS += --eval-command="target remote localhost:1234" -GDB_FLAGS += --eval-command="symbol-file ${KERNEL_ELF}" gdb: gdb ${GDB_FLAGS} # objdump -objdump : ${KERNEL_ELF} - objdump -D ${KERNEL_ELF} +objdump : ${KERNEL} + objdump -D ${KERNEL} -readelf : ${KERNEL_ELF} - readelf --all ${KERNEL_ELF} +readelf : ${KERNEL} + readelf --all ${KERNEL} -.PHONY : build qemu objdump readelf +.PHONY : build iso reset-cargo-deps run test gdb objdump readelf # end diff --git a/.cargo/runner.sh b/old_cargo_configs/runner.sh similarity index 100% rename from .cargo/runner.sh rename to old_cargo_configs/runner.sh diff --git a/qemu.mk b/qemu.mk new file mode 100644 index 0000000..51ff39d --- /dev/null +++ b/qemu.mk @@ -0,0 +1,42 @@ +QEMU_FLAGS ?= +ifeq (${KVM},true) +QEMU_FLAGS += -enable-kvm +endif +ifeq (${GDB},true) +QEMU_FLAGS += -s -S +endif +QEMU_FLAGS += -machine q35 +QEMU_FLAGS += -cpu qemu64,+rdrand,+rdseed +QEMU_FLAGS += -smp 8 +QEMU_FLAGS += -m 256m +QEMU_FLAGS += -M smm=off +#QEMU_FLAGS += -d int,guest_errors,cpu_reset +QEMU_FLAGS += -d int,guest_errors +QEMU_FLAGS += -no-reboot +QEMU_FLAGS += -serial stdio +#QEMU_OVMF ?= /usr/share/ovmf/x64/OVMF.fd +#QEMU_FLAGS += -bios ${QEMU_OVMF} +QEMU_RUN_FLAGS ?= +QEMU_RUN_FLAGS += ${QEMU_FLAGS} +QEMU_TEST_FLAGS ?= +QEMU_TEST_FLAGS += ${QEMU_FLAGS} +QEMU_TEST_FLAGS += -device isa-debug-exit,iobase=0xf4,iosize=0x04 +QEMU_TEST_FLAGS += -display none +QEMU_KERNEL := -kernel ${KERNEL} -append qemu +QEMU_DRIVE := -drive format=raw,file + +# TODO: multiboot1 direct kernel boot + +# qemu normal run +run: ${HYPERION} + @echo "\n\033[32m--[[ running Hyperion in QEMU ]]--\033[0m" + ${QEMU} ${QEMU_RUN_FLAGS} ${QEMU_DRIVE}=${HYPERION} + +# run tests in qemu +test: ${HYPERION_TESTING} + @echo "\n\033[32m--[[ running Hyperion-Testing in QEMU ]]--\033[0m" + ${QEMU} ${QEMU_TEST_FLAGS} ${QEMU_DRIVE}=${HYPERION_TESTING};\ + [ $$? -ne 33 ] && exit 1;\ + exit 0 + + diff --git a/src/boot/limine/Makefile b/src/boot/limine/Makefile new file mode 100644 index 0000000..56a6592 --- /dev/null +++ b/src/boot/limine/Makefile @@ -0,0 +1,43 @@ +LIM_GIT := "https://github.com/limine-bootloader/limine.git" +LIM_DIR := ${TARGET_DIR}/limine +LIM_FILES := ${LIM_DIR}/limine-cd-efi.bin ${LIM_DIR}/limine-cd.bin ${LIM_DIR}/limine.sys +LIM_CFG := cfg/limine.cfg + +XORRISO_FLAGS ?= +XORRISO_FLAGS += -as mkisofs +XORRISO_FLAGS += -b limine-cd.bin +XORRISO_FLAGS += -no-emul-boot +XORRISO_FLAGS += -boot-load-size 4 +XORRISO_FLAGS += -boot-info-table +XORRISO_FLAGS += --efi-boot limine-cd-efi.bin +XORRISO_FLAGS += -efi-boot-part +XORRISO_FLAGS += --efi-boot-image +XORRISO_FLAGS += --protective-msdos-label + +# clone limine +${LIM_FILES}: + @echo "\n\033[32m--[[ cloning Limine ]]--\033[0m" + git clone ${LIM_GIT} --depth=1 --branch v3.0-branch-binary ${LIM_DIR} + cd ${LIM_DIR}; make + +# create ISO +${HYPERION}: ${KERNEL} ${LIM_CFG} ${LIM_FILES} Makefile + @echo "\n\033[32m--[[ creating Hyperion ISO ]]--\033[0m" + mkdir -p ${ISO_DIR} + cp ${LIM_CFG} ${LIM_FILES} ${ISO_DIR} + cp ${KERNEL} ${ISO_DIR}/hyperion + + ${XORRISO} ${XORRISO_FLAGS} ${ISO_DIR} -o ${HYPERION} + + ./${LIM_DIR}/limine-deploy ${HYPERION} + +# create testing ISO +${HYPERION_TESTING}: ${KERNEL_TESTING} ${LIM_CFG} ${LIM_FILES} Makefile + @echo "\n\033[32m--[[ creating Hyperion-Testing ISO ]]--\033[0m" + mkdir -p ${ISO_TESTING_DIR} + cp ${LIM_CFG} ${LIM_FILES} ${ISO_TESTING_DIR} + cp ${KERNEL_TESTING} ${ISO_TESTING_DIR}/hyperion + + ${XORRISO} ${XORRISO_FLAGS} ${ISO_TESTING_DIR} -o ${HYPERION_TESTING} + + ./${LIM_DIR}/limine-deploy ${HYPERION_TESTING} diff --git a/src/boot/limine/mem.rs b/src/boot/limine/mem.rs index e8e41f5..40c8350 100644 --- a/src/boot/limine/mem.rs +++ b/src/boot/limine/mem.rs @@ -1,20 +1,16 @@ -use crate::mem::Memmap; -use limine::{LimineMemmapRequest, LimineMemoryMapEntryType}; +use crate::{debug, mem::map::Memmap}; +use limine::{LimineMemmapEntry, LimineMemmapRequest, LimineMemoryMapEntryType, NonNullPtr}; +use spin::Lazy; // pub fn memmap() -> impl Iterator<Item = Memmap> { - static REQ: LimineMemmapRequest = LimineMemmapRequest::new(0); - const DEFAULT_MEMMAP: Memmap = Memmap { base: u64::MAX, len: 0u64, }; - REQ.get_response() - .get() - .into_iter() - .flat_map(|a| a.memmap()) + memiter() .scan(DEFAULT_MEMMAP, |acc, memmap| { // TODO: zero init reclaimable regions if let LimineMemoryMapEntryType::Usable @@ -34,3 +30,24 @@ pub fn memmap() -> impl Iterator<Item = Memmap> { }) .flatten() } + +pub fn memtotal() -> u64 { + static TOTAL: Lazy<u64> = Lazy::new(|| { + memiter() + .filter(|memmap| { + memmap.typ != LimineMemoryMapEntryType::Reserved + && memmap.typ != LimineMemoryMapEntryType::Framebuffer + }) + .map(|memmap| memmap.len) + .sum::<u64>() + }); + *TOTAL +} + +fn memiter() -> impl Iterator<Item = &'static NonNullPtr<LimineMemmapEntry>> { + static REQ: LimineMemmapRequest = LimineMemmapRequest::new(0); + REQ.get_response() + .get() + .into_iter() + .flat_map(|a| a.memmap()) +} diff --git a/src/boot/limine/mod.rs b/src/boot/limine/mod.rs index 483e3e2..c5657a8 100644 --- a/src/boot/limine/mod.rs +++ b/src/boot/limine/mod.rs @@ -4,6 +4,7 @@ use crate::{arch, kernel_main}; // pub use mem::memmap; +pub use mem::memtotal; pub use term::_print; // diff --git a/src/log.rs b/src/log.rs index 2d7bfa9..3bf9fc5 100644 --- a/src/log.rs +++ b/src/log.rs @@ -95,19 +95,20 @@ pub fn _print_log(level: LogLevel, module: &str, args: Arguments) { // print!("[{level:?}]: ") // } else { let level = match level { - LogLevel::None => " NONE ", - LogLevel::Error => "\x1b[38;2;255;85;85m ERROR ", - LogLevel::Warn => "\x1b[38;2;255;255;85m WARN ", - LogLevel::Info => "\x1b[38;2;85;255;85m INFO ", - LogLevel::Debug => "\x1b[38;2;85;255;255m DEBUG ", - LogLevel::Trace => "\x1b[38;2;255;85;255m TRACE ", - }; + LogLevel::None => " NONE ".into(), + LogLevel::Error => " ERROR ".true_red(), + LogLevel::Warn => " WARN ".true_yellow(), + LogLevel::Info => " INFO ".true_green(), + LogLevel::Debug => " DEBUG ".true_cyan(), + LogLevel::Trace => " TRACE ".true_magenta(), + } + .with_reset(false); print!( "{}{level} {} {}: {args}", '['.true_grey(), - module.true_grey(), - ']'.true_grey(), + module.true_grey().with_reset(false), + ']'.reset_after(), ) // } } diff --git a/src/main.rs b/src/main.rs index 7dbe95c..f478141 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,7 @@ #![feature(allocator_api)] #![feature(nonnull_slice_from_raw_parts)] #![feature(exclusive_range_pattern)] +#![feature(let_chains)] #![test_runner(crate::testfw::test_runner)] #![reexport_test_harness_main = "test_main"] diff --git a/src/mem.rs b/src/mem.rs deleted file mode 100644 index af0077c..0000000 --- a/src/mem.rs +++ /dev/null @@ -1,90 +0,0 @@ -use crate::{boot, debug, error, util::NumberPostfix}; -use core::{ - alloc::{GlobalAlloc, Layout}, - ptr::null_mut, - sync::atomic::{AtomicU64, Ordering}, -}; -use spin::Mutex; - -// - -pub fn init() { - let mut usable = 0; - - for Memmap { base, len } in boot::memmap() { - usable += len; - debug!("base: {base:#X} len: {len:#X} ({}B)", len.postfix_binary()); - - ALLOC.memory.store(base, Ordering::SeqCst); - *ALLOC.remaining.lock() = len; - } - - debug!("Usable system memory: {}B", usable.postfix_binary()); -} - -// - -pub struct Memmap { - pub base: u64, - pub len: u64, -} - -// - -#[global_allocator] -static ALLOC: BumpAlloc = BumpAlloc { - memory: AtomicU64::new(0), - remaining: Mutex::new(0), -}; - -struct BumpAlloc { - memory: AtomicU64, - remaining: Mutex<u64>, -} - -unsafe impl GlobalAlloc for BumpAlloc { - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - let memory = self.memory.load(Ordering::SeqCst); - let mut remaining = self.remaining.lock(); - - let top = memory + *remaining; - let Some(tmp) = top.checked_sub(layout.size() as u64) else { - error!("OUT OF MEMORY"); - error!( - "ALLOC: size: {} align: {} top: {top} memory: {memory} remaining: {remaining}", - layout.size(), - layout.align() - ); - return null_mut(); - }; - let new_top = tmp / layout.align() as u64 * layout.align() as u64; - let reservation = top - new_top; - - if let Some(left) = remaining.checked_sub(reservation) { - *remaining = left; - (memory + left) as _ - } else { - error!("OUT OF MEMORY"); - error!( - "ALLOC: size: {} align: {} top: {top} new: {new_top} memory: {memory} remaining: {remaining}", - layout.size(), - layout.align() - ); - null_mut() - } - } - - unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) { - // BUMP alloc is stupid and won't free the memory - } -} - -#[cfg(test)] -mod tests { - use alloc::vec::Vec; - - #[test_case] - fn test_alloc() { - core::hint::black_box((0..64).map(|i| i * 2).collect::<Vec<_>>()); - } -} diff --git a/src/mem/bump.rs b/src/mem/bump.rs new file mode 100644 index 0000000..cd0b47c --- /dev/null +++ b/src/mem/bump.rs @@ -0,0 +1,90 @@ +use super::map::Memmap; +use crate::{boot, error}; +use core::{ + alloc::{GlobalAlloc, Layout}, + ptr::null_mut, +}; +use spin::{Mutex, Once}; + +// + +const MAX_BUMP_ALLOC: u64 = 2u64.pow(16); // 64KiB + +// + +pub fn init() { + let mut map = boot::memmap() + .min_by_key(|Memmap { len, .. }| *len) + .expect("No memory"); + + map.len = map.len.min(MAX_BUMP_ALLOC); + + ALLOC.inner.call_once(|| BumpAllocInner { + remaining: Mutex::new(map.len), + map, + }); +} + +pub fn map() -> Option<Memmap> { + ALLOC.inner.get().map(|i| i.map) +} + +// + +#[global_allocator] +static ALLOC: BumpAllocator = BumpAllocator { inner: Once::new() }; + +// + +struct BumpAllocator { + inner: Once<BumpAllocInner>, +} + +struct BumpAllocInner { + map: Memmap, + remaining: Mutex<u64>, +} + +// + +unsafe impl GlobalAlloc for BumpAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + let Some(inner) = self.inner.get() else { + error!("Allocator used before init"); + return null_mut(); + }; + + let memory = inner.map.base; + let mut remaining = inner.remaining.lock(); + + let top = memory + *remaining; + let Some(tmp) = top.checked_sub(layout.size() as u64) else { + error!("OUT OF MEMORY"); + error!( + "ALLOC: size: {} align: {} top: {top} memory: {memory} remaining: {remaining}", + layout.size(), + layout.align() + ); + return null_mut(); + }; + let new_top = tmp / layout.align() as u64 * layout.align() as u64; + let reservation = top - new_top; + + if let Some(left) = remaining.checked_sub(reservation) { + *remaining = left; + (memory + left) as _ + } else { + error!("OUT OF MEMORY"); + error!( + "ALLOC: size: {} align: {} top: {top} new: {new_top} memory: {memory} remaining: {remaining}", + layout.size(), + layout.align() + ); + null_mut() + } + } + + unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) { + // BUMP alloc is stupid and won't free the memory + } +} diff --git a/src/mem/map.rs b/src/mem/map.rs new file mode 100644 index 0000000..6681f94 --- /dev/null +++ b/src/mem/map.rs @@ -0,0 +1,17 @@ +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub struct Memmap { + pub base: u64, + pub len: u64, +} + +// + +#[cfg(test)] +mod tests { + use alloc::vec::Vec; + + #[test_case] + fn test_alloc() { + core::hint::black_box((0..64).map(|i| i * 2).collect::<Vec<_>>()); + } +} diff --git a/src/mem/mod.rs b/src/mem/mod.rs new file mode 100644 index 0000000..08297bb --- /dev/null +++ b/src/mem/mod.rs @@ -0,0 +1,21 @@ +use crate::{boot, debug, mem::map::Memmap, util::fmt::NumberPostfix}; + +// + +pub mod map; + +// allocator +pub mod bump; +pub mod pfa; + +// + +pub fn init() { + let usable = boot::memmap().map(|Memmap { len, .. }| len).sum::<u64>(); + let total = boot::memtotal(); + debug!("Usable system memory: {}B", usable.postfix_binary()); + debug!("Total system memory: {}B", total.postfix_binary()); + + bump::init(); + pfa::init(); +} diff --git a/src/mem/pfa.rs b/src/mem/pfa.rs new file mode 100755 index 0000000..442644a --- /dev/null +++ b/src/mem/pfa.rs @@ -0,0 +1,113 @@ +use super::map::Memmap; +use crate::{ + boot, debug, + log::{test_log_level, LogLevel}, + mem::bump, + util::{bitmap::Bitmap, fmt::NumberPostfix}, +}; +use core::slice; +use spin::{Mutex, Once}; +use x86_64::{align_down, align_up}; + +// + +const PAGE_SIZE: u64 = 2u64.pow(12); // 4KiB pages + +// const PAGE_SIZE: u64 = 2u64.pow(21); // 2MiB pages + +static PFA: Once<Mutex<PageFrameAllocator>> = Once::new(); + +// + +pub fn init() { + let mem_bottom = boot::memmap() + .map(|Memmap { base, .. }| base) + .min() + .expect("No memory"); + + let mem_top = boot::memmap() + .map(|Memmap { base, len }| base + len) + .max() + .expect("No memory"); + + // size in bytes + let bitmap_size = (mem_top - mem_bottom) / PAGE_SIZE / 8 + 1; + let bitmap_data = boot::memmap() + .find(|Memmap { len, .. }| *len >= bitmap_size) + .expect("No place to store PageFrameAllocator bitmap") + .base; + + // SAFETY: this bitmap is going to be initialized before it is read from + let bitmap = unsafe { slice::from_raw_parts_mut(bitmap_data as _, bitmap_size as _) }; + let mut bitmap = Bitmap::new(bitmap); + bitmap.fill(true); // initialized here + + let bottom_page = align_up(mem_bottom, PAGE_SIZE) / PAGE_SIZE; + + // free up some pages + for Memmap { mut base, mut len } in boot::memmap() { + if let Some(map) = bump::map() && map.base == base { + // skip the BumpAllocator spot + base += map.base; + len -= map.len; + } + if base == bitmap_data { + // skip the bitmap allocation spot + base += bitmap_data; + len -= bitmap_size; + } + + let mut bottom = align_up(base, PAGE_SIZE); + let mut top = align_down(base + len, PAGE_SIZE); + + if bottom >= top { + continue; + } + + debug!( + "Free pages: {:#0X?} ({}B)", + bottom..top, + (top - bottom).postfix_binary() + ); + + bottom /= PAGE_SIZE; + top /= PAGE_SIZE; + bottom -= bottom_page; + top -= bottom_page; + + for page in bottom..top { + #[cfg(debug_assertions)] + bitmap.set(page as _, false).unwrap(); + #[cfg(not(debug_assertions))] + let _ = bitmap.set(page as _, false); + } + } + + let free = bitmap.iter_false().count() as u64 * PAGE_SIZE; + let used = 0; + debug!("Free pages: ({}B)", free.postfix_binary()); + + PFA.call_once(|| { + Mutex::new(PageFrameAllocator { + bitmap, + free, + used, + bottom_page, + }) + }); +} + +// + +pub struct PageFrameAllocator { + bitmap: Bitmap<'static>, + free: u64, + used: u64, + bottom_page: u64, +} + +// + +impl PageFrameAllocator { + pub fn free_page(&mut self, addr: u64) {} +} diff --git a/src/smp.rs b/src/smp.rs index aa926d2..4922dbd 100644 --- a/src/smp.rs +++ b/src/smp.rs @@ -1,4 +1,4 @@ -use crate::{arch, boot, debug}; +use crate::{boot, debug}; use core::fmt::{self, Display, Formatter}; // diff --git a/src/term/escape/encode.rs b/src/term/escape/encode.rs index 6eec9ea..a216d3a 100644 --- a/src/term/escape/encode.rs +++ b/src/term/escape/encode.rs @@ -2,51 +2,64 @@ use core::fmt; // -pub trait EscapeEncoder { - fn with_escape_code<'a>(&'a self, code: &'a str) -> EncodedPart<'a, Self> { - EncodedPart { code, data: self } +pub trait EscapeEncoder +where + Self: Sized, +{ + fn with_escape_code<'a>(self, code: &'a str) -> EncodedPart<'a, Self> { + EncodedPart { + code, + data: self, + reset: true, + } } - fn true_red(&self) -> EncodedPart<Self> { + fn true_red(self) -> EncodedPart<'static, Self> { self.with_escape_code("\x1B[38;2;255;0;0m") } - fn true_green(&self) -> EncodedPart<Self> { + fn true_green(self) -> EncodedPart<'static, Self> { self.with_escape_code("\x1B[38;2;0;255;0m") } - fn true_blue(&self) -> EncodedPart<Self> { + fn true_blue(self) -> EncodedPart<'static, Self> { self.with_escape_code("\x1B[38;2;0;0;255m") } - fn true_cyan(&self) -> EncodedPart<Self> { + fn true_cyan(self) -> EncodedPart<'static, Self> { self.with_escape_code("\x1B[38;2;0;255;255m") } - fn true_magenta(&self) -> EncodedPart<Self> { + fn true_magenta(self) -> EncodedPart<'static, Self> { self.with_escape_code("\x1B[38;2;255;0;255m") } - fn true_yellow(&self) -> EncodedPart<Self> { + fn true_yellow(self) -> EncodedPart<'static, Self> { self.with_escape_code("\x1B[38;2;255;255;0m") } - fn true_black(&self) -> EncodedPart<Self> { + fn true_black(self) -> EncodedPart<'static, Self> { self.with_escape_code("\x1B[38;2;0;0;0m") } - fn true_white(&self) -> EncodedPart<Self> { + fn true_white(self) -> EncodedPart<'static, Self> { self.with_escape_code("\x1B[38;2;255;255;255m") } - fn true_grey(&self) -> EncodedPart<Self> { + fn true_grey(self) -> EncodedPart<'static, Self> { self.with_escape_code("\x1B[38;2;128;128;128m") } + + fn reset_after(self) -> EncodedPart<'static, Self> { + self.with_escape_code("") + } } -pub struct EncodedPart<'a, T: ?Sized> { +#[derive(Clone, Copy)] +pub struct EncodedPart<'a, T: Sized> { code: &'a str, - data: &'a T, + reset: bool, + data: T, } // @@ -57,22 +70,57 @@ pub struct EncodedPart<'a, T: ?Sized> { impl<T> EscapeEncoder for T {} -impl<'a, T> fmt::Display for EncodedPart<'a, T> +impl<T> EncodedPart<'_, T> { + pub fn with_reset(mut self, reset: bool) -> Self { + self.reset = reset; + self + } + + fn write_end(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.reset { + write!(f, "\x1B[m")?; + } + Ok(()) + } +} + +impl Default for EncodedPart<'static, ()> { + fn default() -> Self { + Self { + code: "", + data: (), + reset: true, + } + } +} + +impl<T> From<T> for EncodedPart<'_, T> { + fn from(value: T) -> Self { + Self { + code: "", + data: value, + reset: false, + } + } +} + +impl<T> fmt::Display for EncodedPart<'_, T> where T: fmt::Display, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}{}\x1B[m", self.code, self.data) + write!(f, "{}{}", self.code, self.data)?; + self.write_end(f) } } -impl<'a, T> fmt::Debug for EncodedPart<'a, T> +impl<T> fmt::Debug for EncodedPart<'_, T> where T: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.code)?; self.data.fmt(f)?; - write!(f, "\x1B[m") + self.write_end(f) } } diff --git a/src/util/bitmap.rs b/src/util/bitmap.rs new file mode 100644 index 0000000..1ff797d --- /dev/null +++ b/src/util/bitmap.rs @@ -0,0 +1,142 @@ +#[derive(Debug, Default)] +pub struct Bitmap<'a> { + data: &'a mut [u8], +} + +// + +impl<'a> Bitmap<'a> { + pub fn new(data: &'a mut [u8]) -> Self { + Self { data } + } + + #[must_use] + pub fn len(&self) -> usize { + self.data.len() * 8 + } + + #[must_use] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + // pub fn resize(&mut self, bits: usize) { + // let bytes = bits / 8 + 1; + // self.data.resize(bytes, 0); + // } + + pub fn fill(&mut self, val: bool) { + self.data.fill(if val { 0xFF } else { 0x0 }) + } + + #[must_use] + pub fn get(&self, n: usize) -> Option<bool> { + let (byte, _, mask) = self.bp(n); + let byte = *self.data.get(byte)?; + + Some(byte & mask != 0) + } + + #[must_use] + pub fn set(&mut self, n: usize, val: bool) -> Option<()> { + let (byte, bit, mask) = self.bp(n); + let byte = self.data.get_mut(byte)?; + + // reset the bit + *byte &= !mask; + + // set the bit + *byte |= (val as u8) << bit; + + Some(()) + } + + /// iterator over indexes of 1 bits + pub fn iter_true(&self) -> impl Iterator<Item = usize> + '_ { + self.iter_bytes() + .enumerate() + .filter(|(_, byte)| *byte != 0) + .flat_map(|(i, byte)| { + (0..8) + .enumerate() + .filter(move |(_, bit)| byte & (1 << *bit) != 0) + .map(move |(j, _)| i * 8 + j) + }) + } + + /// iterator over indexes of 0 bits + pub fn iter_false(&self) -> impl Iterator<Item = usize> + '_ { + self.iter_bytes() + .enumerate() + .filter(|(_, byte)| *byte != 0xFF) + .flat_map(|(i, byte)| { + (0..8) + .enumerate() + .filter(move |(_, bit)| byte & (1 << *bit) == 0) + .map(move |(j, _)| i * 8 + j) + }) + } + + pub fn iter(&self) -> impl Iterator<Item = bool> + '_ { + self.iter_bytes() + .flat_map(|byte| (0..8).map(move |i| byte & (1 << i) != 0)) + } + + pub fn iter_bytes(&self) -> impl Iterator<Item = u8> + '_ { + self.data.iter().copied() + } + + #[must_use] + fn bp(&self, n: usize) -> (usize, usize, u8) { + let bit = n % 8; + (n / 8, bit, 1 << bit) + } +} + +// + +#[cfg(test)] +mod tests { + use super::Bitmap; + + #[test_case] + fn test_bitmap_iter_true() { + let mut bitmap = [0; 10]; + let mut bitmap = Bitmap::new(&mut bitmap); + + assert_eq!(bitmap.set(5, true), Some(())); + assert_eq!(bitmap.set(7, true), Some(())); + assert_eq!(bitmap.set(9, true), Some(())); + assert_eq!(bitmap.set(53, true), Some(())); + assert_eq!(bitmap.set(79, true), Some(())); + assert_eq!(bitmap.set(89, true), None); + + let mut iter = bitmap.iter_true(); + assert_eq!(iter.next(), Some(5)); + assert_eq!(iter.next(), Some(7)); + assert_eq!(iter.next(), Some(9)); + assert_eq!(iter.next(), Some(53)); + assert_eq!(iter.next(), Some(79)); + assert_eq!(iter.next(), None); + } + + #[test_case] + fn test_bitmap_iter_false() { + let mut bitmap = [0xFF; 10]; + let mut bitmap = Bitmap::new(&mut bitmap); + assert_eq!(bitmap.set(5, false), Some(())); + assert_eq!(bitmap.set(7, false), Some(())); + assert_eq!(bitmap.set(9, false), Some(())); + assert_eq!(bitmap.set(53, false), Some(())); + assert_eq!(bitmap.set(79, false), Some(())); + assert_eq!(bitmap.set(89, false), None); + + let mut iter = bitmap.iter_false(); + assert_eq!(iter.next(), Some(5)); + assert_eq!(iter.next(), Some(7)); + assert_eq!(iter.next(), Some(9)); + assert_eq!(iter.next(), Some(53)); + assert_eq!(iter.next(), Some(79)); + assert_eq!(iter.next(), None); + } +} diff --git a/src/util.rs b/src/util/fmt.rs similarity index 100% rename from src/util.rs rename to src/util/fmt.rs diff --git a/src/util/mod.rs b/src/util/mod.rs new file mode 100644 index 0000000..3ca20ab --- /dev/null +++ b/src/util/mod.rs @@ -0,0 +1,2 @@ +pub mod bitmap; +pub mod fmt; -- GitLab