Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docker/scripts/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions src/grub.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ menuentry "exo" {

set gfxpayload=keep
multiboot /boot/exodoom
module /boot/freedoom2.wad
}
40 changes: 25 additions & 15 deletions src/kernel.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -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
Expand All @@ -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();
Expand Down
150 changes: 150 additions & 0 deletions src/page_alloc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
#include <stdint.h>
#include <stddef.h>
#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);
}
13 changes: 13 additions & 0 deletions src/page_alloc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#ifndef PAGE_ALLOC_H
#define PAGE_ALLOC_H

#include <stdint.h>
#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
Binary file added wads/freedoom2.wad
Binary file not shown.
Loading