Skip to content

Commit

Permalink
Merge pull request #25 from pbrucla/heap
Browse files Browse the repository at this point in the history
Heap
  • Loading branch information
Aplet123 authored Nov 16, 2023
2 parents 470c3d6 + 720775b commit ecdab20
Show file tree
Hide file tree
Showing 2 changed files with 177 additions and 31 deletions.
1 change: 1 addition & 0 deletions source/kernel/kernel.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ void main()
{
init_drivers();
terminal_clear();
init_paging();
terminal_update_cursor();
init_idt();
asm volatile("int $0x3");
Expand Down
207 changes: 176 additions & 31 deletions source/kernel/memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,50 +73,195 @@ void pfree(void *ptr)
flush_tlb();
}

typedef struct kalloc_chunk kalloc_chunk;
struct kalloc_chunk {
size_t size;
kalloc_chunk *fwd;
kalloc_chunk *bck;
};
#define KALLOC_ALIGN sizeof(size_t)
#define KALLOC_METADATA sizeof(size_t)
#define KALLOC_MINSIZE (sizeof(kalloc_chunk) + sizeof(size_t))
#define PREV_INUSE 1
#define chunksize(p) ((p)->size & ~(PREV_INUSE))
#define next_chunk(p) ((kalloc_chunk *)((char *)(p) + chunksize(p)))
#define inuse(p) \
(((kalloc_chunk *)((char *)(p) + chunksize(p)))->size & PREV_INUSE)
#define prev_inuse(p) ((p)->size & PREV_INUSE)
#define set_inuse(p) \
(((kalloc_chunk *)((char *)(p) + chunksize(p)))->size |= PREV_INUSE)
#define clear_inuse(p) \
(((kalloc_chunk *)((char *)(p) + chunksize(p)))->size &= ~PREV_INUSE)
#define set_prevsize(p) \
(*(size_t *)((char *)p + chunksize(p) - sizeof(size_t)) = chunksize(p))
#define get_prevsize(p) (*(size_t *)((char *)p - sizeof(size_t)))

// do KASLR later (tm)
char *next_heap_chunk = (char *)0xc0000000U;
void *heap_freelist_head = NULL;
// todo: add __kfree_hook() and __kalloc_hook()
kalloc_chunk *freelist_head = NULL;
char *wilderness = (char *)0xc0000000U;
// keeping track of this prevents us from
// having to iterate the freelist during coalescing
char lastchunk_inuse = 1;

// heap chunks are:
// 4 byte metadata for chunk size
// size (aligned to 4 bytes) bytes for chunk data
void *kalloc(size_t size)
{
if (size == 0) {
if (size == 0)
return NULL;
// alignment and header
size_t aligned = (size + KALLOC_ALIGN - 1) & ~(KALLOC_ALIGN - 1);
aligned += KALLOC_METADATA;
// check for overflow
if (aligned < size)
return NULL;
size = aligned;
if (size < KALLOC_MINSIZE)
size = KALLOC_MINSIZE;
// search for free chunk
// note: binning would not only make this faster but also more space
// efficient because we currently just take the first large enough
// chunk, even if there is a closer sized chunk to our request farther along
kalloc_chunk *cur = freelist_head;
while (cur != NULL) {
if (chunksize(cur) < size) {
cur = cur->fwd;
continue;
}
kalloc_chunk *bck = cur->bck;
kalloc_chunk *fwd = cur->fwd;
size_t remainder = chunksize(cur) - size;
// split off separate chunk if remainder large enough
if (remainder >= KALLOC_MINSIZE) {
kalloc_chunk *split = (kalloc_chunk *)((char *)cur + size);
split->size = remainder | PREV_INUSE;
split->fwd = cur->fwd;
split->bck = cur->bck;
bck = fwd = split;
}
cur->size = size | prev_inuse(cur);
// adjust freelist
if (cur->fwd != NULL)
cur->fwd->bck = bck;
if (cur->bck != NULL)
cur->bck->fwd = fwd;
else
freelist_head = fwd;

if (next_chunk(cur) >= wilderness)
lastchunk_inuse = 1;
return (char *)cur + KALLOC_METADATA;
}
// alignment
if (size % 4 != 0) {
size += 4 - size % 4;
// free chunk not found, so allocate from wilderness
kalloc_chunk *chunk = (kalloc_chunk *)wilderness;
// if wilderness is page aligned, that page may not
// be allocated, so remaining_page must be zero
size_t remaining_page = (0x1000 - ((size_t)wilderness % 0x1000)) % 0x1000;
if (size > remaining_page) {
size_t unalloc_size = size - remaining_page;
char *next_page = wilderness + remaining_page;
for (;;) {
palloc(next_page, PAGE_PRESENT | PAGE_RW);
if (unalloc_size <= 0x1000)
break;
next_page += 0x1000;
unalloc_size -= 0x1000;
}
}
chunk->size = size | lastchunk_inuse;
lastchunk_inuse = 1;
wilderness += size;
return (char *)chunk + KALLOC_METADATA;
}

void kfree(void *ptr)
{
if (ptr == NULL)
return;
kalloc_chunk *cur = (kalloc_chunk *)((char *)ptr - KALLOC_METADATA);
int coalesced = 0;
// backwards coalescing
if (!prev_inuse(cur)) {
kalloc_chunk *prev = (kalloc_chunk *)((char *)cur - get_prevsize(cur));
prev->size += cur->size;
cur = prev;
set_prevsize(cur);
coalesced = 1;
}
// metadata
size += 4;
if (heap_freelist_head == NULL) {
char *base = next_heap_chunk;
size_t remaining_page =
(0x1000 - ((size_t)next_heap_chunk % 0x1000)) % 0x1000;
if (size > remaining_page) {
size_t unalloc_size = size - remaining_page;
char *next_page = next_heap_chunk + unalloc_size;
while (1) {
palloc(next_page, PAGE_PRESENT | PAGE_RW);
next_page += 0x1000;
if (unalloc_size <= 0x1000) {
break;
} else {
unalloc_size -= 0x1000;
}
kalloc_chunk *next = next_chunk(cur);
if (next >= wilderness)
lastchunk_inuse = 0;
else {
clear_inuse(cur);
// forwards coalescing
int next_inuse;
if (next_chunk(next) >= wilderness)
next_inuse = lastchunk_inuse;
else
next_inuse = inuse(next);
if (!next_inuse) {
cur->size += chunksize(next);
cur->fwd = next->fwd;
if (cur->fwd != NULL)
cur->fwd->bck = cur;
// only set bck if haven't coalesced backwards
if (!coalesced) {
cur->bck = next->bck;
if (cur->bck != NULL)
cur->bck->fwd = cur;
else
freelist_head = cur;
}
set_prevsize(cur);
return;
}
}
// only insert into freelist if haven't coalesced
if (coalesced)
return;
cur->fwd = freelist_head;
freelist_head = cur;
cur->bck = NULL;
set_prevsize(cur);
}

*(size_t *)base = size;
next_heap_chunk += size;
// adjust for metadata
return base + 4;
// debugging
void print_freelist()
{
kalloc_chunk *cur;

printf("\nfreelist {\n");
cur = freelist_head;
while (cur != NULL) {
printf(" ");
terminal_put64(cur);
printf(": ");
terminal_put64(cur->size);
printf(",\n");
cur = cur->fwd;
}
printf("}\n\n");
}

void kfree(void *ptr) {}
void print_chunks()
{
kalloc_chunk *cur;

printf("\nchunks {\n");
cur = (kalloc_chunk *)0xc0000000U;
while (cur < wilderness) {
printf(" ");
terminal_put64(cur);
printf(": ");
terminal_put64(cur->size);
printf(",\n");
if ((cur->size & ~1) < KALLOC_MINSIZE) {
printf(" ERROR\n");
break;
}
cur = (char *)cur + (cur->size & ~1);
}
printf("}\n\n");
}

void init_paging()
{
Expand Down

0 comments on commit ecdab20

Please sign in to comment.