diff --git a/docker/scripts/build.sh b/docker/scripts/build.sh index f90a75f..7176eeb 100644 --- a/docker/scripts/build.sh +++ b/docker/scripts/build.sh @@ -63,6 +63,7 @@ fi echo "[5/6] Build ISO staging tree" mkdir -p build/isodir/boot cp build/exodoom build/isodir/boot/exodoom +cp wads/freedoom2.wad build/isodir/boot/freedoom2.wad cp src/grub.cfg build/isodir/boot/grub/grub.cfg echo "[6/6] Create ISO -> build/exodoom.iso" diff --git a/src/grub.cfg b/src/grub.cfg index afce220..60ab66d 100644 --- a/src/grub.cfg +++ b/src/grub.cfg @@ -27,4 +27,5 @@ menuentry "exo" { set gfxpayload=keep multiboot /boot/exodoom + module /boot/freedoom2.wad } diff --git a/src/kernel.c b/src/kernel.c index fa2b1f4..619aac0 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -3,21 +3,7 @@ #include "serial.h" #include "memory.h" #include "mmap.h" - -//IDT and Interrupt includes -#include "idt.h" -#include "pic.h" -#include "pit.h" -#include "sleep.h" -#include "fb.h" -#include "fb_console.h" - -//IRQ0 stub from assembly -extern void irq0_stub(); - -#ifdef TESTING -extern int run_tests(void); -#endif +#include "page_alloc.h" static inline void qemu_exit(uint32_t code) { __asm__ volatile ("outl %0, %1" : : "a"(code), "Nd"(0xF4)); @@ -27,9 +13,11 @@ void kernel_main(uint32_t mb_info_addr) { serial_init(); struct multiboot_info* mb = (struct multiboot_info*)mb_info_addr; + serial_print("Kernel Booted\n"); mmap_init(mb); + memory_init(); #ifdef TESTING @@ -39,7 +27,29 @@ void kernel_main(uint32_t mb_info_addr) { serial_print("Memory subsystem initialized\n"); serial_print("Allocator base: "); serial_print_hex(memory_base_address()); + serial_print("\n"); + + page_alloc_init(mb); + +#ifdef DEBUG + void* p1 = alloc_page(); + void* p2 = alloc_page(); + // p2 is intentionally not freed here because this is a short-lived smoke test. + + serial_print("Allocated page 1: "); + serial_print_hex((uint32_t)p1); + serial_print("\n"); + + serial_print("Allocated page 2: "); + serial_print_hex((uint32_t)p2); serial_print("\n"); + + free_page(p1); + serial_print("Freed page 1\n"); + + free_page(p1); +#endif + serial_flush(); idt_init(); diff --git a/src/page_alloc.c b/src/page_alloc.c new file mode 100644 index 0000000..a38725d --- /dev/null +++ b/src/page_alloc.c @@ -0,0 +1,150 @@ +#include +#include +#include "page_alloc.h" +#include "memory.h" +#include "serial.h" +#include "mmap.h" + +#define PAGE_SIZE 4096 + +static uintptr_t managed_base = 0; +static uint32_t total_pages = 0; +static uint8_t* bitmap = 0; + +extern char _load_start[]; + +static void bitmap_set(uint32_t index) { + bitmap[index / 8] |= (1u << (index % 8)); +} + +static void bitmap_clear(uint32_t index) { + bitmap[index / 8] &= ~(1u << (index % 8)); +} + +static int bitmap_test(uint32_t index) { + return (bitmap[index / 8] >> (index % 8)) & 1u; +} + +static uintptr_t align_up_uintptr(uintptr_t value, uintptr_t align) { + return (value + align - 1) & ~(align - 1); +} + +static uintptr_t align_down_uintptr(uintptr_t value, uintptr_t align) { + return value & ~(align - 1); +} + +static void reserve_region(uintptr_t start, uintptr_t end) { + start = align_down_uintptr(start, PAGE_SIZE); + end = align_up_uintptr(end, PAGE_SIZE); + + for (uintptr_t addr = start; addr < end; addr += PAGE_SIZE) { + if (addr < managed_base) continue; + + uint32_t index = (uint32_t)((addr - managed_base) / PAGE_SIZE); + if (index < total_pages) { + bitmap_set(index); + } + } +} + +void page_alloc_init(struct multiboot_info* mb) { + if (!(mb->flags & MULTIBOOT_INFO_FLAG_MMAP)) { + serial_print("page_alloc: no mmap available\n"); + return; + } + + if (bitmap != 0) { + return; + } + + uint32_t count = 0; + const mmap_region_t* regions = mmap_get_regions(&count); + + for (uint32_t i = 0; i < count; i++) { + if (regions[i].type == MULTIBOOT_MMAP_AVAILABLE && + regions[i].base >= 0x100000 && + regions[i].length >= PAGE_SIZE) { + + managed_base = align_up_uintptr((uintptr_t)regions[i].base, PAGE_SIZE); + uintptr_t region_end = (uintptr_t)(regions[i].base + regions[i].length); + uintptr_t usable_len = region_end - managed_base; + + total_pages = (uint32_t)(usable_len / PAGE_SIZE); + + uint32_t bitmap_bytes = (total_pages + 7) / 8; + bitmap = (uint8_t*)kmalloc(bitmap_bytes); + + for (uint32_t j = 0; j < bitmap_bytes; j++) { + bitmap[j] = 0; + } + + uintptr_t reserve_start = managed_base; + uintptr_t reserve_end = (uintptr_t)memory_base_address(); + + reserve_region(reserve_start, reserve_end); + + serial_print("page_alloc: kernel/heap reserved\n"); + + if ((mb->flags & MULTIBOOT_INFO_FLAG_MODS) && mb->mods_count > 0) { + struct multiboot_module* mods = + (struct multiboot_module*)mb->mods_addr; + + for (uint32_t i = 0; i < mb->mods_count; i++) { + reserve_region((uintptr_t)mods[i].mod_start, + (uintptr_t)mods[i].mod_end); + } + + serial_print("page_alloc: modules reserved\n"); +} + serial_print("page_alloc: initialized\n"); + return; + } + } + + serial_print("page_alloc: no usable region found\n"); +} + +void* alloc_page(void) { + if (bitmap == 0 || total_pages == 0) { + serial_print("alloc_page: allocator not initialized\n"); + return 0; + } + + for (uint32_t i = 0; i < total_pages; i++) { + if (!bitmap_test(i)) { + bitmap_set(i); + return (void*)(managed_base + ((uintptr_t)i * PAGE_SIZE)); + } + } + + serial_print("alloc_page: out of pages\n"); + return 0; +} + +void free_page(void* addr) { + if (bitmap == 0 || total_pages == 0) { + serial_print("free_page: allocator not initialized\n"); + return; + } + + uintptr_t page = (uintptr_t)addr; + + if (page < managed_base || ((page - managed_base) % PAGE_SIZE) != 0) { + serial_print("free_page: invalid page address\n"); + return; + } + + uint32_t index = (uint32_t)((page - managed_base) / PAGE_SIZE); + + if (index >= total_pages) { + serial_print("free_page: page out of range\n"); + return; + } + + if (!bitmap_test(index)) { + serial_print("free_page: double free detected\n"); + return; + } + + bitmap_clear(index); +} diff --git a/src/page_alloc.h b/src/page_alloc.h new file mode 100644 index 0000000..36dd38c --- /dev/null +++ b/src/page_alloc.h @@ -0,0 +1,13 @@ +#ifndef PAGE_ALLOC_H +#define PAGE_ALLOC_H + +#include +#include "multiboot.h" + +// Initializes the bitmap allocator using the first eligible usable memory region. +// NOTE: currently only the first large usable region is managed. +void page_alloc_init(struct multiboot_info* mb); +void* alloc_page(void); +void free_page(void* addr); + +#endif diff --git a/wads/freedoom2.wad b/wads/freedoom2.wad new file mode 100644 index 0000000..a02643d Binary files /dev/null and b/wads/freedoom2.wad differ