From: Matthias Kruk Date: Wed, 18 Dec 2019 06:38:18 +0000 (+0900) Subject: Add heap implementation for use in drivers and system services X-Git-Url: https://git.corax.cc/?a=commitdiff_plain;h=a6ae26fd6000723ec6d9c1376aaa92d24c294bc7;p=corax Add heap implementation for use in drivers and system services --- diff --git a/kernel/include/kheap.h b/kernel/include/kheap.h new file mode 100644 index 0000000..d91bbb8 --- /dev/null +++ b/kernel/include/kheap.h @@ -0,0 +1,22 @@ +#ifndef __KHEAP_H +#define __KHEAP_H + +#include +#include +#include +#include + +typedef struct { + spinlock_t h_lock; + void *h_base; + void *h_brk; + size_t h_size; + size_t h_free; +} kheap_t; + +int kheap_init(kheap_t*, size_t); +int kheap_alloc(kheap_t*, size_t, void**); +int kheap_free(kheap_t*, void*); + + +#endif /* __KHEAP_H */ diff --git a/kernel/klibc/Makefile b/kernel/klibc/Makefile index f4f88a0..f2bb415 100644 --- a/kernel/klibc/Makefile +++ b/kernel/klibc/Makefile @@ -1,5 +1,5 @@ OUTPUT = klibc.a -OBJECTS = string.o spinlock.o mutex.o posixcall.o coraxcall.o +OBJECTS = string.o spinlock.o mutex.o posixcall.o coraxcall.o heap.o PHONY = clean INCLUDES = -I../include -I../../include -I../.. CFLAGS = -m32 -Wall -nostdlib -nodefaultlibs -nostartfiles -ffreestanding $(INCLUDES) diff --git a/kernel/klibc/heap.c b/kernel/klibc/heap.c new file mode 100644 index 0000000..6c06c95 --- /dev/null +++ b/kernel/klibc/heap.c @@ -0,0 +1,168 @@ +#include +#include +#include +#include +#include +#include +#include + +#define FLAG_USED (1 << 0) + +struct heap_node { + struct heap_node *next; + size_t size; + size_t flags; +}; + +int kheap_init(kheap_t *heap, size_t size) +{ + struct heap_node *obj; + int ret_val; + + ret_val = -EINVAL; + + if(heap) { + heap->h_base = sbrk(0); + heap->h_brk = sbrk((ssize_t)size); + + if(heap->h_base != (void*)-1 && heap->h_brk != (void*)-1) { + heap->h_size = (size_t)heap->h_brk - (size_t)heap->h_base; + heap->h_lock = 0; + + obj = (struct heap_node*)heap->h_base; + obj->next = NULL; + obj->size = heap->h_size - sizeof(*obj); + obj->flags = 0; + + heap->h_free = heap->h_size - sizeof(*obj); + + ret_val = 0; + } else { + ret_val = -ENOMEM; + } + } + + return(ret_val); +} + +static int _kheap_grow(kheap_t *heap, const size_t size) +{ + struct heap_node *last; + struct heap_node *new; + int ret_val; + size_t new_size; + + ret_val = -ENOMEM; + + /* old brk is the start of the new heap object */ + new = (struct heap_node*)heap->h_brk; + /* attempt to grow the heap */ + heap->h_brk = sbrk((ssize_t)size); + + new_size = (size_t)heap->h_brk - (size_t)new; + + if(new_size > sizeof(*new)) { + /* find the last node */ + for(last = (struct heap_node*)heap->h_base; + last->next; last = last->next); + + new->next = NULL; + new->size = new_size - sizeof(*new); + new->flags = 0; + + last->next = new; + heap->h_size += new_size; + heap->h_free += new->size; + ret_val = 0; + } + + return(ret_val); +} + +void* heap_alloc(kheap_t *heap, size_t size) +{ + struct heap_node *cur; + void *ret_val; + int err; + + ret_val = NULL; + err = 0; + + spinlock_lock(&(heap->h_lock)); + + if(heap->h_free < size) { + err = _kheap_grow(heap, size); + } + + if(!err) { + for(cur = (struct heap_node*)heap->h_base; + cur; cur = cur->next) { + if(cur->flags & FLAG_USED) { + continue; + } + + if(cur->size < size) { + continue; + } + + /* get a pointer to the available memory */ + ret_val = (void*)(cur + 1); + + cur->flags |= FLAG_USED; + + /* split the heap node, if possible */ + if(cur->size > (size + sizeof(struct heap_node))) { + struct heap_node *new; + + new = (struct heap_node*)(ret_val + size); + new->size = cur->size - size - sizeof(*new); + new->next = cur->next; + new->flags = 0; + + cur->size = size; + cur->next = new; + + heap->h_free -= sizeof(*new); + } + + memset(ret_val, 0, size); + heap->h_free -= size; + + break; + } + } + + spinlock_unlock(&(heap->h_lock)); + + return(ret_val); +} + +void heap_free(kheap_t *heap, void *ptr) +{ + struct heap_node *cur; + + spinlock_lock(&(heap->h_lock)); + + for(cur = (struct heap_node*)heap->h_base; + cur; + cur = cur->next) { + void *base; + + base = (void*)(cur + 1); + + if(base <= ptr && (base + cur->size) > ptr) { + if(!(cur->flags & FLAG_USED)) { + /* double free */ + } else { + cur->flags ^= FLAG_USED; + heap->h_free += cur->size; + } + + break; + } + } + + spinlock_unlock(&(heap->h_lock)); + + return; +}