From 57be90da84416e7ee429b30b9b019aa45d1857f1 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Sat, 6 Sep 2025 00:54:38 +0200 Subject: [PATCH 1/6] kernel.ld: slight rewrite, use memory regions for ram instead of static offset --- kern/kernel.ld | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/kern/kernel.ld b/kern/kernel.ld index af3fd7c..17e1ee2 100644 --- a/kern/kernel.ld +++ b/kern/kernel.ld @@ -1,14 +1,13 @@ OUTPUT_ARCH( "riscv" ) ENTRY( _entry ) /* See: entry.S */ +MEMORY +{ + RAM (rwx) : ORIGIN = 0x80000000, LENGTH = 128M +} + SECTIONS { - /* - * ensure that entry.S / _entry is at 0x80000000, - * where qemu's -kernel jumps. - */ - . = 0x80000000; - /* * This section contains the code. This is, the machine language instructions * that will be executed by the processor. In here we will find symbols @@ -35,7 +34,7 @@ SECTIONS /* Define symbol etext to be the current location. */ PROVIDE(etext = .); - } :text + } > RAM :text /* * This contains any data that is marked as read only. @@ -47,7 +46,7 @@ SECTIONS *(.srodata*) /* do not need to distinguish this from .rodata */ . = ALIGN(16); *(.rodata*) - } + } > RAM /* * This section contains initialized global and static variables. @@ -58,7 +57,7 @@ SECTIONS *(.sdata*) /* do not need to distinguish this from .data */ . = ALIGN(16); *(.data*) - } + } > RAM /* * Contains all uninitialized global and static var iables. These are usually @@ -71,10 +70,12 @@ SECTIONS *(.sbss*) /* do not need to distinguish this from .bss */ . = ALIGN(16); *(.bss*) - } + } > RAM /* Define symbol end as current location, note that this is not aligned, see vm.c */ PROVIDE(kernel_end = .); + PROVIDE(__heap_start = ALIGN(32)); + PROVIDE(__heap_end = ORIGIN(RAM) + LENGTH(RAM)); } PHDRS { From 7301205e1f819a7600ad2a3630b698c84695357f Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Sat, 6 Sep 2025 02:09:48 +0200 Subject: [PATCH 2/6] Add newline to panic macro --- kern/libkern/panic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kern/libkern/panic.h b/kern/libkern/panic.h index 4f2e3f4..3eb3b32 100644 --- a/kern/libkern/panic.h +++ b/kern/libkern/panic.h @@ -1,7 +1,7 @@ #ifndef KERNEL_PANIC_H #define KERNEL_PANIC_H -#define PANIC(fmt, ...) __panic("[Panic @ %s:%d %s] " fmt, __FILE__, __LINE__, __func__, ##__VA_ARGS__) +#define PANIC(fmt, ...) __panic("[Panic @ %s:%d %s] " fmt "\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__) void __panic(const char *restrict fmt, ...); From e762502c33d444beb1d2990417b4114b706d9fa0 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Sat, 6 Sep 2025 02:36:04 +0200 Subject: [PATCH 3/6] Include stdio in assert.h --- kern/libkern/assert.h | 1 + 1 file changed, 1 insertion(+) diff --git a/kern/libkern/assert.h b/kern/libkern/assert.h index 7cb7a27..3489342 100644 --- a/kern/libkern/assert.h +++ b/kern/libkern/assert.h @@ -1,4 +1,5 @@ #include +#include #define assert(cond) \ do { \ From 21d55031d9c70bbec51c0ca89596dbb4c1b4d5f9 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Sat, 6 Sep 2025 02:36:29 +0200 Subject: [PATCH 4/6] memory.h: sweep algorithm for checking regions --- kern/libkern/memory.c | 37 +++++++++++++++---------------------- kern/libkern/memory.h | 7 +++---- 2 files changed, 18 insertions(+), 26 deletions(-) diff --git a/kern/libkern/memory.c b/kern/libkern/memory.c index 47ac873..72871fa 100644 --- a/kern/libkern/memory.c +++ b/kern/libkern/memory.c @@ -1,28 +1,21 @@ +#include #include -#include -#include +#include -#define MAX_PROBE_SIZE (256 * 1024 * 1024) // Probe up to 256 MiB max -#define PROBE_STEP 0x1000 // Probe every 4 KiB page +int memory_sweep(const uintptr_t start, const uintptr_t end) { + assert_msg(start < end, "Start needs to be before end."); + uintptr_t sweeper = start; -size_t probe_memory(void) { - volatile u32 *addr; - u32 test_pattern = 0xA5A5A5A5; - size_t detected = 0; - - for (size_t offset = 4096 * 16; offset < MAX_PROBE_SIZE; offset += PROBE_STEP) { - addr = (volatile u32 *)(KERNBASE + offset); - - u32 old = *addr; - *addr = test_pattern; - - if (*addr != test_pattern) { - break; // Memory not readable/writable here, stop probing - } - - *addr = old; // restore original data - detected = offset + PROBE_STEP; + while (sweeper < end) { + *(uint64_t *)sweeper = 0xFAFAFAFABCBCBCBC; + sweeper += sizeof(uint64_t); } - return detected; + sweeper -= sizeof(uint64_t); + while (sweeper != start) { + assert(*(uint64_t *)sweeper == 0xFAFAFAFABCBCBCBC); + sweeper -= sizeof(uint64_t); + } + + return EXIT_SUCCESS; } diff --git a/kern/libkern/memory.h b/kern/libkern/memory.h index c8d7d36..a61d701 100644 --- a/kern/libkern/memory.h +++ b/kern/libkern/memory.h @@ -8,9 +8,8 @@ #define PHYSTOP (KERNBASE + 128 * 1024 * 1024) /** - * Returns size in bytes of detected RAM In qemu, it requires a trap handler to - * handle the interrupt when accessing unavailable memory. + * Set and fetch routine for checking memory. */ -size_t probe_memory(void); +int memory_sweep(const uintptr_t start, const uintptr_t end); -#endif // MEMORY_KARNEL_H +#endif // MEMORY_KERNEL_H From 90c63ab41eee7ada08634f93b664b7608cff9104 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Sat, 6 Sep 2025 02:37:09 +0200 Subject: [PATCH 5/6] Buddy allocator --- Makefile | 1 + kern/libkern/buddy.c | 139 +++++++++++++++++++++++++++++++++++++++++++ kern/libkern/buddy.h | 22 +++++++ 3 files changed, 162 insertions(+) create mode 100644 kern/libkern/buddy.c create mode 100644 kern/libkern/buddy.h diff --git a/Makefile b/Makefile index f1c4355..cc4c6a3 100644 --- a/Makefile +++ b/Makefile @@ -64,6 +64,7 @@ KERNEL_OBJ := \ kern/libkern/spinlock.o \ kern/libkern/mini-printf.o \ kern/libkern/stdio.o \ + kern/libkern/buddy.o \ kern/libkern/badrand.o kern/kernel.elf: $(KERNEL_OBJ) diff --git a/kern/libkern/buddy.c b/kern/libkern/buddy.c new file mode 100644 index 0000000..3b49bb0 --- /dev/null +++ b/kern/libkern/buddy.c @@ -0,0 +1,139 @@ +#include +#include +#include +#include +#include + +#define MAX_ORDER 22 // 2^22 * min_block_size = max allocatable block +#define MIN_BLOCK_SIZE 32 // Smallest allocatable block + +typedef struct free_block { + struct free_block *next; +} free_block_t; + +typedef struct { + uint8_t order; +} block_header_t; + +static free_block_t *free_lists[MAX_ORDER + 1]; + +uintptr_t heap_start = (uintptr_t)&__heap_start; +uintptr_t heap_end = (uintptr_t)&__heap_end; + +// Utility: round up to next power of two +static size_t next_pow2(size_t x) { + if (x <= 1) + return 1; + x--; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; +#if UINTPTR_MAX > 0xffffffff + x |= x >> 32; +#endif + return x + 1; +} + +// Find order for size +static int size_to_order(size_t size) { + size_t needed = (size < MIN_BLOCK_SIZE) ? MIN_BLOCK_SIZE : size; + size_t block_size = next_pow2(needed); + int order = 0; + while ((MIN_BLOCK_SIZE << order) < block_size) { + order++; + } + return order; +} + +// Initialize allocator +void buddy_init(uintptr_t start, uintptr_t end) { + heap_start = (start + MIN_BLOCK_SIZE - 1) & ~(MIN_BLOCK_SIZE - 1); + heap_end = end & ~(MIN_BLOCK_SIZE - 1); + + // Clear free lists + for (int i = 0; i <= MAX_ORDER; i++) free_lists[i] = NULL; + + // Put the whole region as one big block + size_t total_size = heap_end - heap_start; + int order = MAX_ORDER; + while ((MIN_BLOCK_SIZE << order) > total_size && order > 0) order--; + + free_block_t *block = (free_block_t *)heap_start; + block->next = NULL; + free_lists[order] = block; +} + +// Allocate memory +void *buddy_alloc(size_t size) { + int order = size_to_order(size); + + for (int i = order; i <= MAX_ORDER; i++) { + if (free_lists[i]) { + // Remove block from this list + free_block_t *block = free_lists[i]; + free_lists[i] = block->next; + + // Split down to requested order + while (i > order) { + i--; + uintptr_t buddy_addr = (uintptr_t)block + (MIN_BLOCK_SIZE << i); + free_block_t *buddy = (free_block_t *)buddy_addr; + buddy->next = free_lists[i]; + free_lists[i] = buddy; + } + + /* + * Here, we install a header into the block. This will allow us to use free, without + * specifying size, since it can read the order from one byte before the block start. + */ + { + block_header_t *hdr = (block_header_t *)block; + hdr->order = order; + return (void *)(hdr + 1); // return pointer after header + // return (void *)block; // Regular block return for reference + } + } + } + return NULL; // Out of memory +} + +// Free memory +int buddy_free(void *ptr) { + block_header_t *hdr = (block_header_t *)ptr - 1; + int order = hdr->order; + + assert_msg(order != MAX_ORDER, "The buddy freelist header seems to have been corrupted."); + + uintptr_t addr = (uintptr_t)ptr; + + while (order < MAX_ORDER) { + uintptr_t buddy_addr = addr ^ (MIN_BLOCK_SIZE << order); + + // Check if buddy is in free list + free_block_t **prev = &free_lists[order]; + free_block_t *cur = free_lists[order]; + while (cur) { + if ((uintptr_t)cur == buddy_addr) { + // Remove buddy from list + *prev = cur->next; + // Merge + if (buddy_addr < addr) + addr = buddy_addr; + order++; + goto try_merge; + } + prev = &cur->next; + cur = cur->next; + } + break; // Buddy not free, stop + try_merge:; + } + + free_block_t *block = (free_block_t *)addr; + block->next = free_lists[order]; + free_lists[order] = block; + + return order; +} diff --git a/kern/libkern/buddy.h b/kern/libkern/buddy.h new file mode 100644 index 0000000..a6cf8a4 --- /dev/null +++ b/kern/libkern/buddy.h @@ -0,0 +1,22 @@ +#ifndef BUDDY_H +#define BUDDY_H + +#include +#include + +extern uintptr_t heap_start; +extern uintptr_t heap_end; + +extern char __heap_start; +extern char __heap_end; + +void buddy_init(uintptr_t start, uintptr_t end); +void *buddy_alloc(size_t size); +int buddy_free(void *ptr); + +/* Returns total heap memory managed by buddy allocator in bytes */ +static inline size_t buddy_total_bytes(void) { + return (uintptr_t)&__heap_end - (uintptr_t)&__heap_start; +} + +#endif // BUDDY_H From 8316c9f6aeeb977eaee7a442f66d071a3e1cd617 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Sat, 6 Sep 2025 02:40:05 +0200 Subject: [PATCH 6/6] Remove original kalloc free-list memory allocator entirely for now --- Makefile | 1 - kern/kalloc.c | 77 --------------------------------------------------- kern/kalloc.h | 33 ---------------------- kern/start.c | 7 +++-- 4 files changed, 5 insertions(+), 113 deletions(-) delete mode 100644 kern/kalloc.c delete mode 100644 kern/kalloc.h diff --git a/Makefile b/Makefile index cc4c6a3..3fbba85 100644 --- a/Makefile +++ b/Makefile @@ -55,7 +55,6 @@ quickstart: KERNEL_OBJ := \ kern/entry.o \ kern/start.o \ - kern/kalloc.o \ kern/libkern/string.o \ kern/libkern/proc.o \ kern/libkern/uart.o \ diff --git a/kern/kalloc.c b/kern/kalloc.c deleted file mode 100644 index dead607..0000000 --- a/kern/kalloc.c +++ /dev/null @@ -1,77 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -// Physical memory allocator, for user processes, -// kernel stacks, page-table pages, -// and pipe buffers. Allocates whole 4096-byte pages. - -/** Free list of physical pages. */ -void freerange(void *physaddr_start, void *physaddr_end); - -/** First address after kernel. Provided kernel.ld */ -extern char kernel_end[]; - -/** A run is a node in the free list. */ -struct Run { - struct Run *next; -}; - -/** Kernel memory allocator. */ -struct { - spinlock_t lock; - struct Run *freelist; -} kmem; - -void kalloc_init() { - spinlock_init(&kmem.lock); - freerange(kernel_end, (void *)PHYSTOP); -} - -void freerange(void *physaddr_start, void *physaddr_end) { - char *p; - p = (char *)PGROUNDUP((u64)physaddr_start); - for (; p + PGSIZE <= (char *)physaddr_end; p += PGSIZE) kfree(p); -} - -void kfree(void *pa) { - struct Run *r; - - // Assert that page is a ligned to a page boundary and that its correctly - // sized - if (((u64)pa % PGSIZE) != 0 || (char *)pa < kernel_end || (u64)pa >= PHYSTOP) - PANIC("kfree"); - - // TODO: Kconfig this - // Fill with junk to catch dangling refs. - memset(pa, 1, PGSIZE); - - r = (struct Run *)pa; - - spin_lock(&kmem.lock); - r->next = kmem.freelist; - kmem.freelist = r; - spin_unlock(&kmem.lock); -} - -void *kalloc(void) { - struct Run *r; - - spin_lock(&kmem.lock); - - r = kmem.freelist; - - if (r) - kmem.freelist = r->next; - - spin_unlock(&kmem.lock); - - if (r) - memset((char *)r, 5, PGSIZE); // fill with junk - - return (void *)r; -} diff --git a/kern/kalloc.h b/kern/kalloc.h deleted file mode 100644 index 592d814..0000000 --- a/kern/kalloc.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef KALLOC_KERNEL_H -#define KALLOC_KERNEL_H - -/** - * Kernel memory allocator - * - * Allocate one 4096-byte page of physical memory. - * Returns a pointer that the kernel can use. - * Returns 0 if the memory cannot be allocated. - * See: kalloc.c - */ -void *kalloc(void); - -/** - * Kernel memory allocator - * - * Free the page of physical memory pointed at by pa, - * which normally should have been returned by a - * call to kalloc(). (The exception is when - * initializing the allocator; see kinit above.) - * See: kalloc.c - */ -void kfree(void *); - -/** - * Initialize kernel memory allocator - * - * Called by main() on the way to the kernel's main loop. - * See: kalloc.c - */ -void kalloc_init(void); - -#endif // KALLOC_KERNEL_H diff --git a/kern/start.c b/kern/start.c index 0e4b264..c892765 100644 --- a/kern/start.c +++ b/kern/start.c @@ -1,6 +1,7 @@ +#include #include +#include #include -#include #include #include #include @@ -8,6 +9,7 @@ #include #include #include +#include #include /** @@ -39,7 +41,8 @@ void start() { if (id == 0) { /* Here we will do a bunch of initialization steps */ - kalloc_init(); + memory_sweep(heap_start, heap_end); + buddy_init(heap_start, heap_end); spinlock_init(&sl); for (int i = 0; i < banner_len; i++) uart_putc(banner[i]); __sync_synchronize();