Files
pepperOS/src/mem/kheap.c
T
2026-04-01 09:15:59 +02:00

189 lines
5.3 KiB
C

/*
* @author xamidev <xamidev@riseup.net>
* @brief Kernel heap
* @license GPL-3.0-only
*/
#include <mem/kheap.h>
#include <mem/paging.h>
#include <mem/pmm.h>
#include <stddef.h>
#include <kernel.h>
#include <sched/process.h>
#include <config.h>
extern uint64_t kernel_phys_base;
extern uint64_t kernel_virt_base;
uintptr_t kheap_start;
static struct heap_block* head = NULL;
static uintptr_t end;
// Kernel root table (level 4)
extern uint64_t *kernel_pml4;
/*
* kheap_init - Kernel heap initialization
*
* This function physically allocates and maps enough pages
* of memory for KHEAP_SIZE, which is defined in config.h.
*
* It then creates one big heap block, which will be the
* base for a linked list.
*/
void kheap_init()
{
kheap_start = ALIGN_UP(kernel_virt_base + KERNEL_SIZE, PAGE_SIZE);
size_t heap_pages = ALIGN_UP(KHEAP_SIZE, PAGE_SIZE) / PAGE_SIZE;
DEBUG("Mapping %d kernel heap pages at 0x%p", heap_pages, kheap_start);
uintptr_t current_addr = kheap_start;
// Map/alloc enough pages for heap (KHEAP_SIZE)
for (size_t i=0; i<heap_pages; i++) {
uintptr_t phys = pmm_alloc();
if (phys == 0) {
panic(NULL, "Not enough memory available to initialize kernel heap.");
}
paging_map_page(kernel_pml4, current_addr, phys, PTE_PRESENT | PTE_WRITABLE);
current_addr += PAGE_SIZE;
}
end = current_addr;
// Give linked list head its properties
head = (struct heap_block*)kheap_start;
head->size = (end-kheap_start) - sizeof(struct heap_block);
head->free = true;
head->next = NULL;
DEBUG("Kernel heap initialized, head=0x%p, size=%u bytes", head, head->size);
}
/*
* kmalloc - Kernel memory allocation
* @size: number of bytes to allocate
*
* Looks for a big enough free block and marks it
* as taken. Each block of memory is preceded by
* the linked list header.
*
* Return:
* <ptr> - Pointer to at least <size> bytes of usable memory
* NULL - No more memory, or no valid size given
*/
void* kmalloc(size_t size)
{
// No size, no memory allocated!
if (!size) return NULL;
size = ALIGN(size);
struct heap_block* curr = head;
while (curr) {
// Is block free and big enough for us?
if (curr->free && curr->size >= size) {
// We split the block if it is big enough
if (curr->size >= size + sizeof(struct heap_block) + 16) {
struct heap_block* split = (struct heap_block*)((uintptr_t)curr + sizeof(struct heap_block) + size);
split->size = curr->size - size - sizeof(struct heap_block);
split->free = true;
split->next = curr->next;
curr->next = split;
curr->size = size;
}
// Found a good block, we return it
curr->free = false;
return (void*)((uintptr_t)curr + sizeof(struct heap_block));
}
// Continue browsing the list if nothing good was found yet
curr = curr->next;
}
// No growing. If we're here it means the initial pool
// wasn't sufficient. Too bad.
DEBUG("Kernel heap is OUT OF MEMORY!");
// if we were terrorists maybe we should panic
// or just wait for others to free stuff?
return NULL;
}
/*
* kfree - Kernel memory freeing
* @ptr: pointer to memory region to free
*
* Marks the memory block beginning at <ptr>
* as free. Also merges adjacent free blocks
* to lessen fragmentation.
*/
void kfree(void* ptr)
{
// Nothing to free
if (!ptr) return;
// Set it free!
struct heap_block* block = (struct heap_block*)((uintptr_t)ptr - sizeof(struct heap_block));
block->free = true;
// merge adjacent free blocks (coalescing)
struct heap_block* curr = head;
while (curr && curr->next) {
if (curr->free && curr->next->free) {
curr->size += sizeof(*curr) + curr->next->size;
curr->next = curr->next->next;
continue;
}
curr = curr->next;
}
}
/*
* kalloc_stack - Stack memory allocation
*
* Allocates a memory region of at least PROCESS_STACK_SIZE,
* to be used as a stack for a process. The pointer returned
* points to the end of the region, as the stack grows downwards.
*
* Return:
* <ptr> - Pointer to a region after at least PROCESS_STACK_SIZE bytes of usable memory
* NULL - No more memory
*/
void* kalloc_stack()
{
uint8_t* ptr = kmalloc(PROCESS_STACK_SIZE); // As it's out of kmalloc, stack is already mapped into kernel space
return ptr ? ptr+PROCESS_STACK_SIZE : NULL;
}
/*
* kheap_info - Display heap info
*
* This function writes the size of the heap (total),
* the number of allocated bytes, and the number of
* free bytes to the standard output.
*/
void kheap_info()
{
uint64_t free_bytes = 0;
struct heap_block* curr = (struct heap_block*)kheap_start;
while (curr) {
if (curr->free == true) {
free_bytes += curr->size;
}
curr = curr->next;
}
uint64_t total = end-kheap_start;
printf("total=% 8u bytes (%u kB)\r\n"
"alloc=% 8u bytes (%u kB)\r\n"
" free=% 8u bytes (%u kB)\r\n",
total, (total)/1000,
total-free_bytes, (total-free_bytes)/1000,
free_bytes, free_bytes/1000);
}