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