2026-02-08 23:51:37 +00:00
|
|
|
#include <stdint.h>
|
|
|
|
|
#include <stddef.h>
|
|
|
|
|
#include "drivers/uart.h"
|
|
|
|
|
#include "memory.h"
|
2026-02-09 19:33:11 +00:00
|
|
|
#include "interrupts.h"
|
2026-02-08 23:51:37 +00:00
|
|
|
|
2026-02-09 18:55:51 +00:00
|
|
|
HeapHeader *heap_free_list;
|
2026-02-09 00:13:00 +00:00
|
|
|
extern uint8_t _heap_start[]; // named in the linker script
|
2026-02-08 23:51:37 +00:00
|
|
|
|
2026-02-09 18:55:51 +00:00
|
|
|
void page_init()
|
|
|
|
|
{
|
2026-02-08 23:51:37 +00:00
|
|
|
kprint("Initialising page allocator.\n");
|
2026-02-09 00:13:00 +00:00
|
|
|
uintptr_t start = ((uintptr_t)_heap_start + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
|
2026-02-08 23:51:37 +00:00
|
|
|
uintptr_t end = 0x88000000; // Default QEMU RAM limit
|
|
|
|
|
|
2026-02-09 18:55:51 +00:00
|
|
|
for (uintptr_t addr = start; addr + PAGE_SIZE <= end; addr += PAGE_SIZE)
|
|
|
|
|
{
|
2026-02-08 23:51:37 +00:00
|
|
|
page_free((void *)addr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-09 18:55:51 +00:00
|
|
|
void page_free(void *addr)
|
|
|
|
|
{
|
|
|
|
|
if (addr == NULL)
|
|
|
|
|
return;
|
2026-02-08 23:51:37 +00:00
|
|
|
|
|
|
|
|
struct Page *p = (struct Page *)addr;
|
|
|
|
|
p->next = free_list;
|
|
|
|
|
free_list = p;
|
2026-02-09 00:13:00 +00:00
|
|
|
}
|
|
|
|
|
|
2026-02-09 18:55:51 +00:00
|
|
|
void *page_alloc()
|
|
|
|
|
{
|
|
|
|
|
if (free_list == NULL)
|
|
|
|
|
{
|
2026-02-09 00:13:00 +00:00
|
|
|
kpanic("No free pages!");
|
|
|
|
|
}
|
|
|
|
|
struct Page *p = free_list;
|
|
|
|
|
free_list = free_list->next;
|
|
|
|
|
|
2026-02-09 18:55:51 +00:00
|
|
|
// zero out the page
|
|
|
|
|
for (int i = 0; i < (PAGE_SIZE); i++)
|
|
|
|
|
{
|
2026-02-09 00:13:00 +00:00
|
|
|
((uint8_t *)p)[i] = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (void *)p;
|
|
|
|
|
}
|
2026-02-09 00:13:11 +00:00
|
|
|
|
2026-02-09 18:55:51 +00:00
|
|
|
void *kmalloc(size_t size)
|
|
|
|
|
{
|
|
|
|
|
if (size == 0)
|
|
|
|
|
return NULL;
|
2026-02-09 02:21:48 +00:00
|
|
|
|
|
|
|
|
// Align size to 8 bytes
|
|
|
|
|
size = (size + 7) & ~7;
|
|
|
|
|
|
2026-02-09 18:55:51 +00:00
|
|
|
if (heap_free_list == NULL)
|
|
|
|
|
{
|
|
|
|
|
HeapHeader *header = (HeapHeader *)page_alloc();
|
2026-02-09 02:21:48 +00:00
|
|
|
header->size = PAGE_SIZE - sizeof(HeapHeader);
|
|
|
|
|
header->is_free = 1;
|
|
|
|
|
header->next = NULL;
|
2026-02-09 15:33:11 +00:00
|
|
|
header->prev = NULL;
|
2026-02-09 18:55:51 +00:00
|
|
|
|
2026-02-09 02:21:48 +00:00
|
|
|
heap_free_list = header;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HeapHeader *current = heap_free_list;
|
|
|
|
|
HeapHeader *prev = NULL;
|
|
|
|
|
|
2026-02-09 18:55:51 +00:00
|
|
|
while (current != NULL)
|
|
|
|
|
{
|
|
|
|
|
if (current->is_free && current->size >= size)
|
|
|
|
|
{
|
2026-02-09 02:21:48 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
prev = current;
|
|
|
|
|
current = current->next;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-09 18:55:51 +00:00
|
|
|
if (current == NULL)
|
|
|
|
|
{
|
|
|
|
|
HeapHeader *header = (HeapHeader *)page_alloc();
|
2026-02-09 02:21:48 +00:00
|
|
|
header->size = PAGE_SIZE - sizeof(HeapHeader);
|
|
|
|
|
header->is_free = 1;
|
2026-02-09 03:28:54 +00:00
|
|
|
header->next = heap_free_list;
|
2026-02-09 15:33:11 +00:00
|
|
|
header->prev = NULL;
|
2026-02-09 18:55:51 +00:00
|
|
|
if (heap_free_list)
|
|
|
|
|
{
|
2026-02-09 15:33:11 +00:00
|
|
|
heap_free_list->prev = header;
|
|
|
|
|
}
|
2026-02-09 03:28:54 +00:00
|
|
|
heap_free_list = header;
|
2026-02-09 02:21:48 +00:00
|
|
|
current = header;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-09 18:55:51 +00:00
|
|
|
if (!current)
|
|
|
|
|
{
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2026-02-09 02:21:48 +00:00
|
|
|
|
|
|
|
|
current->is_free = 0;
|
|
|
|
|
|
|
|
|
|
size_t min_split_size = sizeof(HeapHeader) + 16;
|
2026-02-09 18:55:51 +00:00
|
|
|
if (current->size >= size + min_split_size)
|
|
|
|
|
{
|
2026-02-09 02:21:48 +00:00
|
|
|
HeapHeader *new_header = (HeapHeader *)((uint8_t *)current + sizeof(HeapHeader) + size);
|
2026-02-09 18:55:51 +00:00
|
|
|
if ((uintptr_t)new_header < 0x80000000 || (uintptr_t)new_header > 0x88000000)
|
|
|
|
|
{
|
|
|
|
|
kpanic("Splitting created invalid pointer!");
|
|
|
|
|
}
|
2026-02-09 02:21:48 +00:00
|
|
|
new_header->size = current->size - size - sizeof(HeapHeader);
|
|
|
|
|
new_header->is_free = 1;
|
|
|
|
|
|
2026-02-09 15:33:11 +00:00
|
|
|
new_header->next = current->next;
|
|
|
|
|
new_header->prev = current;
|
2026-02-09 18:55:51 +00:00
|
|
|
if (current->next)
|
|
|
|
|
{
|
2026-02-09 15:33:11 +00:00
|
|
|
current->next->prev = new_header;
|
|
|
|
|
}
|
2026-02-09 02:21:48 +00:00
|
|
|
current->size = size;
|
|
|
|
|
current->next = new_header;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-09 18:55:51 +00:00
|
|
|
return (void *)((char *)current + sizeof(HeapHeader));
|
2026-02-09 02:21:48 +00:00
|
|
|
}
|
|
|
|
|
|
2026-02-09 18:55:51 +00:00
|
|
|
void kfree(void *ptr)
|
|
|
|
|
{
|
|
|
|
|
if (ptr == NULL)
|
|
|
|
|
return;
|
2026-02-09 02:21:48 +00:00
|
|
|
|
|
|
|
|
HeapHeader *header = (HeapHeader *)((uint8_t *)ptr - sizeof(HeapHeader));
|
2026-02-09 18:55:51 +00:00
|
|
|
if (header->is_free)
|
|
|
|
|
{
|
2026-02-09 02:21:48 +00:00
|
|
|
kpanic("Double free detected!");
|
|
|
|
|
}
|
|
|
|
|
header->is_free = 1;
|
|
|
|
|
|
2026-02-09 18:55:51 +00:00
|
|
|
kcoalesce(header);
|
2026-02-09 02:21:48 +00:00
|
|
|
}
|
|
|
|
|
|
2026-02-09 18:55:51 +00:00
|
|
|
void kcoalesce(HeapHeader *header)
|
|
|
|
|
{
|
|
|
|
|
if (!header || !header->is_free)
|
|
|
|
|
return;
|
2026-02-09 20:59:56 +00:00
|
|
|
// jump backward
|
2026-02-09 18:55:51 +00:00
|
|
|
while (header->prev && header->prev->is_free)
|
|
|
|
|
{
|
2026-02-09 15:33:11 +00:00
|
|
|
header = header->prev;
|
|
|
|
|
}
|
2026-02-09 18:55:51 +00:00
|
|
|
// merge forward
|
|
|
|
|
while (header->next && header->next->is_free)
|
|
|
|
|
{
|
2026-02-09 15:33:11 +00:00
|
|
|
uintptr_t current_end = (uintptr_t)header + sizeof(HeapHeader) + header->size;
|
2026-02-09 18:55:51 +00:00
|
|
|
if (current_end == (uintptr_t)header->next)
|
|
|
|
|
{
|
2026-02-09 15:33:11 +00:00
|
|
|
header->size += sizeof(HeapHeader) + header->next->size;
|
|
|
|
|
header->next = header->next->next;
|
2026-02-09 18:55:51 +00:00
|
|
|
if (header->next)
|
|
|
|
|
{
|
2026-02-09 15:33:11 +00:00
|
|
|
header->next->prev = header;
|
|
|
|
|
}
|
2026-02-09 18:55:51 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2026-02-09 15:33:11 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2026-02-09 02:21:48 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-09 18:55:51 +00:00
|
|
|
void test_memory_integrity()
|
|
|
|
|
{
|
|
|
|
|
kprint("Running Integrity Test...\n");
|
|
|
|
|
uint64_t *a = (uint64_t *)kmalloc(16);
|
|
|
|
|
uint64_t *b = (uint64_t *)kmalloc(16);
|
2026-02-09 02:21:48 +00:00
|
|
|
|
2026-02-09 18:55:51 +00:00
|
|
|
*a = 0x1122334455667788;
|
|
|
|
|
*b = 0x99AABBCCDDEEFF00;
|
|
|
|
|
|
|
|
|
|
if (*a == 0x1122334455667788)
|
|
|
|
|
{
|
|
|
|
|
kprint("Integrity Pass!\n");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
kprint("CORRUPTION DETECTED!\n");
|
|
|
|
|
}
|
|
|
|
|
kfree(a);
|
|
|
|
|
kfree(b);
|
2026-02-09 03:28:54 +00:00
|
|
|
}
|
|
|
|
|
|
2026-02-09 18:55:51 +00:00
|
|
|
void test_memory_alignment()
|
|
|
|
|
{
|
2026-02-09 03:28:54 +00:00
|
|
|
kprint("Running Alignment Test...\n");
|
2026-02-09 18:55:51 +00:00
|
|
|
for (int i = 1; i <= 64; i++)
|
|
|
|
|
{
|
2026-02-09 03:28:54 +00:00
|
|
|
void *ptr = kmalloc(i);
|
2026-02-09 18:55:51 +00:00
|
|
|
if (((uintptr_t)ptr % 8) != 0)
|
|
|
|
|
{
|
2026-02-09 03:28:54 +00:00
|
|
|
kprint("Misaligned allocation detected!\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
kfree(ptr);
|
|
|
|
|
}
|
|
|
|
|
kprint("All allocations are properly aligned!\n");
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-09 18:55:51 +00:00
|
|
|
void test_memory_stress()
|
|
|
|
|
{
|
|
|
|
|
kprintf("Starting Stress Test...\n");
|
2026-02-09 03:28:54 +00:00
|
|
|
heap_stats();
|
2026-02-09 20:59:56 +00:00
|
|
|
void *ptrs[100] = {0};
|
|
|
|
|
uint32_t seed = 0xACE2026;
|
2026-02-09 03:28:54 +00:00
|
|
|
|
2026-02-09 18:55:51 +00:00
|
|
|
for (int i = 0; i < 1000; i++)
|
|
|
|
|
{
|
|
|
|
|
int idx = (seed >> 16) % 50;
|
|
|
|
|
if (ptrs[idx] == NULL)
|
|
|
|
|
{
|
|
|
|
|
size_t size = (seed % 256) + 1;
|
|
|
|
|
ptrs[idx] = kmalloc(size);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
kfree(ptrs[idx]);
|
|
|
|
|
ptrs[idx] = NULL;
|
|
|
|
|
}
|
|
|
|
|
// Simple LCG to "randomize" seed
|
|
|
|
|
seed = (seed * 1103515245 + 12345) & 0x7fffffff;
|
|
|
|
|
}
|
|
|
|
|
kprintf("Stress Test Finished. Check heap_stats() for sanity.\n");
|
2026-02-09 15:33:11 +00:00
|
|
|
kprintf("HeadHeader size: %d bytes\n", sizeof(HeapHeader));
|
2026-02-09 18:55:51 +00:00
|
|
|
heap_stats();
|
2026-02-09 15:33:11 +00:00
|
|
|
kprintf("Cleaning up remaining allocations...\n");
|
2026-02-09 18:55:51 +00:00
|
|
|
for (int i = 0; i < 100; i++)
|
|
|
|
|
{
|
|
|
|
|
if (ptrs[i] != NULL)
|
|
|
|
|
{
|
2026-02-09 15:33:11 +00:00
|
|
|
kfree(ptrs[i]);
|
|
|
|
|
ptrs[i] = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
heap_stats();
|
2026-02-09 03:28:54 +00:00
|
|
|
}
|
|
|
|
|
|
2026-02-09 18:55:51 +00:00
|
|
|
void heap_stats()
|
|
|
|
|
{
|
|
|
|
|
size_t free_size = 0;
|
|
|
|
|
size_t used_size = 0;
|
|
|
|
|
size_t free_blocks = 0;
|
|
|
|
|
size_t used_blocks = 0;
|
2026-02-09 03:28:54 +00:00
|
|
|
|
2026-02-09 18:55:51 +00:00
|
|
|
HeapHeader *current = heap_free_list;
|
2026-02-09 03:28:54 +00:00
|
|
|
|
2026-02-09 18:55:51 +00:00
|
|
|
while (current != NULL)
|
|
|
|
|
{
|
|
|
|
|
if (current->is_free)
|
|
|
|
|
{
|
|
|
|
|
free_size += current->size;
|
|
|
|
|
free_blocks++;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
used_size += current->size;
|
|
|
|
|
used_blocks++;
|
|
|
|
|
}
|
|
|
|
|
current = current->next;
|
|
|
|
|
}
|
2026-02-09 03:28:54 +00:00
|
|
|
|
2026-02-09 18:55:51 +00:00
|
|
|
kprint("--- Kernel Heap Stats ---\n");
|
2026-02-09 17:56:49 +00:00
|
|
|
kprintf("Page Size: %d bytes", PAGE_SIZE);
|
|
|
|
|
kprintf("HeapHeader size: %d bytes", sizeof(HeapHeader));
|
|
|
|
|
kprintf("Pages allocated: %d", (used_size + free_size + used_blocks * sizeof(HeapHeader) + free_blocks * sizeof(HeapHeader)) / PAGE_SIZE);
|
2026-02-09 18:55:51 +00:00
|
|
|
kprintf("Used: %x bytes in %d blocks", used_size, used_blocks);
|
2026-02-09 17:56:49 +00:00
|
|
|
kprintf("Used (with overhead): %x bytes", used_size + used_blocks * sizeof(HeapHeader));
|
|
|
|
|
kprintf("Free: %x bytes in %d blocks", free_size, free_blocks);
|
2026-02-09 18:55:51 +00:00
|
|
|
kprintf("Total metadata overhead: %d bytes", ((used_blocks + free_blocks) * sizeof(HeapHeader)));
|
|
|
|
|
kprint("-------------------------\n");
|
2026-02-08 23:51:37 +00:00
|
|
|
}
|