From 552221876705d98d07409de245f25661963d3ffa Mon Sep 17 00:00:00 2001 From: Tomas Date: Tue, 29 Apr 2025 20:10:59 +0200 Subject: [PATCH] Implemented a simple kernel heap --- kernel/include/kmalloc.h | 25 ++++++ kernel/include/vmm.h | 13 ---- kernel/src/kernel.c | 7 +- kernel/src/kmalloc.c | 162 +++++++++++++++++++++++++++++++++++++++ kernel/src/vmm.c | 72 ----------------- 5 files changed, 190 insertions(+), 89 deletions(-) create mode 100644 kernel/include/kmalloc.h delete mode 100644 kernel/include/vmm.h create mode 100644 kernel/src/kmalloc.c delete mode 100644 kernel/src/vmm.c diff --git a/kernel/include/kmalloc.h b/kernel/include/kmalloc.h new file mode 100644 index 0000000..628cfbc --- /dev/null +++ b/kernel/include/kmalloc.h @@ -0,0 +1,25 @@ +#ifndef _KALLOC_H +#define _KALLOC_H + +#include + +// TODO: Set a better minimum that just a random number +#define KMALLOC_MIN_PAGES 4 + +// kmalloc() singly-linked list node +struct kmalloc_node { + size_t size; + int free; + struct kmalloc_node* next; +}; + +extern struct kmalloc_node* kmalloc_head; + +int kmalloc_init(void); +void* kmalloc(size_t size); +void* kzalloc(size_t size); +void* kcalloc(size_t size, size_t count); +void* krealloc(size_t size, void* ptr); +void kfree(void* ptr); + +#endif diff --git a/kernel/include/vmm.h b/kernel/include/vmm.h deleted file mode 100644 index 53a6785..0000000 --- a/kernel/include/vmm.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef _VMM_H -#define _VMM_H - -#include -#include - -extern bitmap_t vmm_bitmap; - -int vmm_init(void); -void* vmm_alloc(size_t pages); -void vmm_free(void* ptr, size_t pages); - -#endif diff --git a/kernel/src/kernel.c b/kernel/src/kernel.c index 593cce0..871a2c5 100644 --- a/kernel/src/kernel.c +++ b/kernel/src/kernel.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -7,7 +8,6 @@ #include #include #include -#include __noreturn void kmain(void* ptr, uint32_t magic) { @@ -24,17 +24,16 @@ void kmain(void* ptr, uint32_t magic) { // Memory manager init if (pmm_init() != 0) panic("Failed to initalize Physical Memory Manager."); - if (vmm_init() != 0) panic("Failed to initalize Virtual Memory Manager."); + if (kmalloc_init() != 0) panic("Failed to initialize kernel heap."); // VGA TTY if (tty_init() != 0) panic("Failed to initalize TTY driver."); tty_puts("kern.\n\n"); - // Timer if (timer_init() != 0) panic("Failed to initialize timer."); - // TODO: Syscalls // TODO: Scheduler + // TODO: Syscalls // TODO: Interprocess communication // TODO: Tasking // TODO: Load & run /sbin/init diff --git a/kernel/src/kmalloc.c b/kernel/src/kmalloc.c new file mode 100644 index 0000000..1da7d71 --- /dev/null +++ b/kernel/src/kmalloc.c @@ -0,0 +1,162 @@ +#include +#include +#include +#include +#include + +#include + +struct kmalloc_node* kmalloc_head = NULL; +size_t kmalloc_pages; +size_t kmalloc_base; + +int kmalloc_init(void) { + kmalloc_base = KERNEL_HEAP_BASE; + kmalloc_pages = KMALLOC_MIN_PAGES; + + for (size_t i = 0; i < kmalloc_pages; i++) { + uint32_t paddr = (uint32_t)pmm_alloc(); + + if (paddr == 0) { + kmalloc_pages = i; + break; + } + + pag_map(kmalloc_base + i * PAGE_SIZE, paddr, 0); + } + + kmalloc_head = (struct kmalloc_node*)kmalloc_base; + + if (kmalloc_pages < KMALLOC_MIN_PAGES) return 1; + + kmalloc_head->size = (kmalloc_pages * PAGE_SIZE) - sizeof(*kmalloc_head); + kmalloc_head->free = 1; + kmalloc_head->next = NULL; + + debugf("[kmalloc] kmalloc() initialized.\n"); + + return 0; +} + +void kmalloc_split(struct kmalloc_node* node, size_t size) { + if (node->size < size + sizeof(struct kmalloc_node)) return; + + struct kmalloc_node* remainder = + (struct kmalloc_node*)((uint32_t)node + size + sizeof(struct kmalloc_node)); + remainder->size = node->size - size - sizeof(struct kmalloc_node); + remainder->free = 1; + remainder->next = node->next; + + node->size = size; + node->free = 0; + node->next = remainder; +} + +void kmalloc_merge() { + struct kmalloc_node* current = kmalloc_head; + + while (current != NULL && current->next != NULL) { + if (current->free && current->next->free) { + current->size += current->next->size + sizeof(struct kmalloc_node); + current->next = current->next->next; + break; + } + + current = current->next; + } +} + +void* kmalloc(size_t size) { + if (size == 0) return NULL; // Why would you... + + struct kmalloc_node* current = kmalloc_head; + + while ((current->size <= size || !current->free) && current->next != NULL) { + current = current->next; + } + + if (current->size == size) current->free = 0; + else if (current->size > size) kmalloc_split(current, size); + else { // Allocate more pages + size_t found_size = current->free ? current->size : 0; + size_t page_count = 1; + found_size += PAGE_SIZE; + + while (found_size < size + sizeof(struct kmalloc_node)) { + page_count++; + found_size += PAGE_SIZE; + } + + uint32_t vaddr = kmalloc_base + kmalloc_pages * PAGE_SIZE; + for (size_t i = 0; i < page_count; i++) { + // WARN: This could be a memory leak! It doesn't free after allocating :/ + uint32_t paddr = (uint32_t)pmm_alloc(); + if (paddr == 0) return NULL; + + pag_map(vaddr + i * PAGE_SIZE, paddr, 0); + kmalloc_pages++; + } + + if (current->free) { + current->size += PAGE_SIZE * page_count; + } else { + current->next = (struct kmalloc_node*)vaddr; + current = current->next; + + current->size = PAGE_SIZE * page_count; + current->free = 1; + current->next = NULL; + } + + return kmalloc(size); + } + + return (void*)current + sizeof(struct kmalloc_node); +} + +void* kzalloc(size_t size) { + void* ptr = kmalloc(size); + if (ptr == NULL) return NULL; + memset(ptr, 0, size); + return ptr; +} + +void* kcalloc(size_t size, size_t count) { + size_t siz = size * count; + void* ptr = kmalloc(siz); + if (ptr == NULL) return NULL; + memset(ptr, 0, siz); + return ptr; +} + +void* krealloc(size_t size, void* ptr) { + void* new = kmalloc(size); + + struct kmalloc_node* current = kmalloc_head; + while (current != NULL) { + if ((void*)current + sizeof(struct kmalloc_node) == ptr) break; + current = current->next; + } + + // Tried to realloc non-existent buffer + if (current == NULL) return NULL; + if (size > current->size) size = current->size; + memcpy(ptr, new, size); + kfree(ptr); + + return new; +} + +void kfree(void* ptr) { + struct kmalloc_node* current = kmalloc_head; + + while (current != NULL) { + if ((void*)current + sizeof(struct kmalloc_node) == ptr) { + current->free = 1; + kmalloc_merge(); + break; + } + current = current->next; + } +} + diff --git a/kernel/src/vmm.c b/kernel/src/vmm.c deleted file mode 100644 index 49b3a02..0000000 --- a/kernel/src/vmm.c +++ /dev/null @@ -1,72 +0,0 @@ -#include -#include -#include -#include - -#include - -bitmap_t vmm_bitmap; - -int vmm_init(void) { - vmm_bitmap.ready = 0; - vmm_bitmap.membase = KERNEL_HEAP_BASE; - vmm_bitmap.blocks = dceil(KERNEL_HEAP_SIZE, BLOCK_SIZE); - vmm_bitmap.bytes = dceil(vmm_bitmap.blocks, 8); - uint32_t bitmap_loc = 0xC6000000; - - size_t page_count = dceil(vmm_bitmap.bytes, PAGE_SIZE); - for (size_t i = 0; i < page_count; i++) { - void* fr = pmm_alloc(); - if (fr == NULL) return 1; - pag_map(bitmap_loc + i * PAGE_SIZE, (uint32_t)fr, 0); - } - - // Set all pages as - vmm_bitmap.map = (void*)bitmap_loc; - bmap_set_all(&vmm_bitmap, 0); - - bmap_set(&vmm_bitmap, 0, 1); - bmap_set(&vmm_bitmap, 8, 1); - - debugf( - "[vmm] Virtual memory manager initialized.\n" - "\tBitmap manages %d blocks\n" - "\tBitmap is at 0x%08x in virtual memory\n" - "\tBitmap is %d bytes over %d pages\n", - vmm_bitmap.blocks, vmm_bitmap.map, - vmm_bitmap.bytes, page_count - ); - - vmm_bitmap.ready = 1; - return 0; -} - -void* vmm_alloc(size_t pages) { - size_t idx = bmap_get_area(&vmm_bitmap, pages, 0); - if (idx == BMAP_NOT_FOUND) return NULL; - void* ptr = bmap_ptr(&vmm_bitmap, idx); - - bmap_set_area(&vmm_bitmap, idx * BLOCK_SIZE, pages * PAGE_SIZE, 1); - - for (size_t i = 0; i < pages; i++) { - void* fr = pmm_alloc(); - if (fr == NULL) return NULL; - pag_map((uint32_t)ptr + i * PAGE_SIZE, (uint32_t)fr, 0); - } - - return ptr; -} - -void vmm_free(void* ptr, size_t pages) { - for (size_t i = 0; i < pages; i++) { - uint32_t vaddr = (uint32_t)ptr + i * PAGE_SIZE; - uint32_t paddr = (uint32_t)pag_virt_to_phys((uint32_t)ptr); - - pmm_free((void*)paddr); - pag_umap(vaddr); - } - - size_t base = ((size_t)ptr - KERNEL_BASE) / BLOCK_SIZE; - bmap_set_area(&vmm_bitmap, base, pages, 0); -} -