164 lines
3.1 KiB
C
164 lines
3.1 KiB
C
#include <mem.h>
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <debug.h>
|
|
#include <string.h>
|
|
|
|
typedef struct chunk_st
|
|
{
|
|
struct chunk_st *p, *n;
|
|
uint8_t flags;
|
|
size_t size;
|
|
uint8_t data[];
|
|
} chunk_t;
|
|
#define CHUNK_FREE 0x1
|
|
|
|
uintptr_t heap_max = KERNEL_HEAP_S;
|
|
uintptr_t brk = KERNEL_HEAP_S;
|
|
|
|
chunk_t *heap = 0;
|
|
|
|
void *ksbrk(size_t size)
|
|
{
|
|
// This can only be used to increase the size of the kernel heap
|
|
// for now...
|
|
while(brk < heap_max + size)
|
|
{
|
|
vmm_set_page(0, brk, (uintptr_t)pmm_alloc(), PAGE_PRESENT | PAGE_WRITE);
|
|
brk += PAGE_SIZE;
|
|
}
|
|
heap_max += size;
|
|
return (void *)heap_max;
|
|
}
|
|
|
|
void merge_chunk(chunk_t *c)
|
|
{
|
|
// Join free chunks together if they are next to each other
|
|
|
|
if(c->n && c->n->flags & CHUNK_FREE) // Merge with next
|
|
{
|
|
// Update size
|
|
c->size += c->n->size + sizeof(chunk_t);
|
|
// Update list
|
|
c->n = c->n->n;
|
|
if(c->n)
|
|
c->n->p = c;
|
|
}
|
|
if(c->p && c->p->flags & CHUNK_FREE) // Merge with previous
|
|
{
|
|
// Update size
|
|
c->p->size += c->size + sizeof(chunk_t);
|
|
// Update list
|
|
c->p->n = c->n;
|
|
if(c->n)
|
|
c->n->p = c->p;
|
|
}
|
|
}
|
|
|
|
void split_chunk(chunk_t *c, size_t size)
|
|
{
|
|
// Split a chunk into one of given size and one of with the rest
|
|
|
|
if(c->size <= size + sizeof(chunk_t))
|
|
return;
|
|
|
|
// Split off as much as isn't needed
|
|
chunk_t *c2 = incptr(c->data, size);
|
|
c2->size = c->size - size - sizeof(chunk_t);
|
|
c2->flags = CHUNK_FREE;
|
|
|
|
// Insert into list
|
|
c2->n = c->n;
|
|
if(c2->n) c2->n->p = c2;
|
|
c->n = c2;
|
|
c2->p = c;
|
|
|
|
// Change size of first chunk
|
|
c->size = size;
|
|
}
|
|
|
|
void kfree(void *a)
|
|
{
|
|
if((uintptr_t)a < KERNEL_HEAP_S || (uintptr_t)a > heap_max)
|
|
{
|
|
debug_error("PANIC: kfree - Tried to free %x\n", a);
|
|
for(;;);
|
|
}
|
|
|
|
// Find chunk header and mark as free
|
|
chunk_t *c = incptr(a, -sizeof(chunk_t));
|
|
c->flags = CHUNK_FREE;
|
|
merge_chunk(c);
|
|
}
|
|
|
|
void *kmalloc(size_t size)
|
|
{
|
|
chunk_t *c = heap, *p = 0;
|
|
while(c)
|
|
{
|
|
// Linear search of list for free chunk
|
|
|
|
if((c->flags & CHUNK_FREE) && c->size >= size)
|
|
{
|
|
// A large enough free chunk has been found
|
|
// adjust and return
|
|
split_chunk(c, size);
|
|
c->flags = 0;
|
|
return (void *)c->data;
|
|
}
|
|
p = c;
|
|
c = c->n;
|
|
}
|
|
|
|
// No free chunk of right size found
|
|
// increase heap size
|
|
if(p)
|
|
{
|
|
c = incptr(p, p->size + sizeof(chunk_t));
|
|
} else {
|
|
c = heap = (chunk_t *)heap_max;
|
|
}
|
|
|
|
void *end = ksbrk(size + sizeof(chunk_t));
|
|
// Add a new chunk
|
|
c->size = (uintptr_t)end - (uintptr_t)c->data;
|
|
c->flags = CHUNK_FREE;
|
|
c->n = c->p = 0;
|
|
c->p = p;
|
|
if(p) c->n = p->n;
|
|
if(p) p->n = c;
|
|
// And try again...
|
|
return kmalloc(size);
|
|
}
|
|
|
|
void *kcalloc(size_t count, size_t size)
|
|
{
|
|
void *ptr = kmalloc(count*size);
|
|
memset(ptr, 0, count*size);
|
|
return ptr;
|
|
}
|
|
|
|
void *krealloc(void *ptr, size_t size)
|
|
{
|
|
void *new = kmalloc(size);
|
|
if(ptr)
|
|
{
|
|
memcpy(new, ptr, size);
|
|
kfree(ptr);
|
|
}
|
|
return new;
|
|
}
|
|
|
|
void heap_print()
|
|
{
|
|
// Print all heap chunks
|
|
|
|
chunk_t *c = heap;
|
|
debug_info("Heap map:\n");
|
|
while(c)
|
|
{
|
|
debug("s:%x f:%x\n", c->size, c->flags);
|
|
c = c->n;
|
|
}
|
|
}
|