From a0542573369437efb1091cd1c26af51eeea1f36e Mon Sep 17 00:00:00 2001 From: kamkow1 Date: Fri, 16 Jan 2026 18:50:40 +0100 Subject: [PATCH] Port liballoc to userspace --- init/init.c | 19 +- libmsl/alloc/.gitignore | 1 + libmsl/alloc/liballoc.c | 406 ++++++++++++++++++++++++++++++++++++++++ libmsl/alloc/liballoc.h | 93 +++++++++ libmsl/alloc/src.mk | 3 + libmsl/init/__premain.c | 2 + libmsl/m/proc.h | 1 + libmsl/m/status.h | 6 + libmsl/src.mk | 1 + 9 files changed, 522 insertions(+), 10 deletions(-) create mode 100644 libmsl/alloc/.gitignore create mode 100644 libmsl/alloc/liballoc.c create mode 100644 libmsl/alloc/liballoc.h create mode 100644 libmsl/alloc/src.mk create mode 100644 libmsl/m/status.h diff --git a/init/init.c b/init/init.c index fae5550..43ff8b6 100644 --- a/init/init.c +++ b/init/init.c @@ -1,5 +1,7 @@ +#include #include #include +#include #include #include #include @@ -16,17 +18,14 @@ void mythread (void) { } } -void make_thread (void* fn) { - size_t stack_pages = 256; - size_t stack_size = PAGE_SIZE * stack_pages; +int make_thread (void (*fn) (void)) { + size_t stack_size = 256 * PAGE_SIZE; + void* stack = malloc (stack_size); + if (stack == NULL) + return -SR_OOM_ERROR; - uintptr_t out_paddr; - int mem_rid = proc_create_resource_mem (100, stack_pages, RV_PRIVATE, &out_paddr); - proc_map (out_paddr, PROC_MAP_BASE, stack_pages, PM_PRESENT | PM_RW | PM_USER); - memset ((void*)PROC_MAP_BASE, 0, stack_size); - - uintptr_t vstack_top = PROC_MAP_BASE + stack_size; - proc_spawn_thread (vstack_top, stack_size, fn); + uintptr_t stack_top = (uintptr_t)stack + stack_size; + return proc_spawn_thread (stack_top, stack_size, (void*)fn); } void app_main (void) { diff --git a/libmsl/alloc/.gitignore b/libmsl/alloc/.gitignore new file mode 100644 index 0000000..5761abc --- /dev/null +++ b/libmsl/alloc/.gitignore @@ -0,0 +1 @@ +*.o diff --git a/libmsl/alloc/liballoc.c b/libmsl/alloc/liballoc.c new file mode 100644 index 0000000..eca0573 --- /dev/null +++ b/libmsl/alloc/liballoc.c @@ -0,0 +1,406 @@ +/* liballoc breaks when optimized too aggressively, for eg. clang's -Oz */ +#pragma clang optimize off + +#include +#include + +static int liballoc_mutex; +static uintptr_t liballoc_map_base = PROC_MAP_BASE; +static int mem_rid_base = 10000; + +void liballoc_init (void) { liballoc_mutex = proc_create_resource_mutex (100, RV_PRIVATE); } + +int liballoc_lock (void) { + proc_mutex_lock (liballoc_mutex); + return 0; +} + +int liballoc_unlock (void) { + proc_mutex_unlock (liballoc_mutex); + return 0; +} + +void* liballoc_alloc (int pages) { + uintptr_t current_base = liballoc_map_base; + uintptr_t out_paddr; + + int mem_rid = proc_create_resource_mem (mem_rid_base++, pages, RV_PRIVATE, &out_paddr); + if (mem_rid < 0) + return NULL; + + proc_map (out_paddr, current_base, pages, PM_PRESENT | PM_RW | PM_USER); + + uintptr_t old_base = current_base; + current_base += pages * PAGE_SIZE; + + return (void*)old_base; +} + +int liballoc_free (void* ptr, int pages) { return 0; } + +/** Durand's Ridiculously Amazing Super Duper Memory functions. */ + +// #define DEBUG + +#define LIBALLOC_MAGIC 0xc001c0de +#define MAXCOMPLETE 5 +#define MAXEXP 32 +#define MINEXP 8 + +#define MODE_BEST 0 +#define MODE_INSTANT 1 + +#define MODE MODE_BEST + +struct boundary_tag* l_freePages[MAXEXP]; //< Allowing for 2^MAXEXP blocks +int l_completePages[MAXEXP]; //< Allowing for 2^MAXEXP blocks + +static int l_initialized = 0; //< Flag to indicate initialization. +static int l_pageSize = PAGE_SIZE; //< Individual page size +static int l_pageCount = 16; //< Minimum number of pages to allocate. + +// *********** HELPER FUNCTIONS ******************************* + +/** Returns the exponent required to manage 'size' amount of memory. + * + * Returns n where 2^n <= size < 2^(n+1) + */ +static inline int getexp (unsigned int size) { + if (size < (1 << MINEXP)) { + return -1; // Smaller than the quantum. + } + + int shift = MINEXP; + + while (shift < MAXEXP) { + if ((1 << shift) > size) + break; + shift += 1; + } + + return shift - 1; +} + +static void* liballoc_memset (void* s, int c, size_t n) { + size_t i; + for (i = 0; i < n; i++) + ((char*)s)[i] = c; + + return s; +} + +static void* liballoc_memcpy (void* s1, const void* s2, size_t n) { + char* cdest; + char* csrc; + unsigned int* ldest = (unsigned int*)s1; + unsigned int* lsrc = (unsigned int*)s2; + + while (n >= sizeof (unsigned int)) { + *ldest++ = *lsrc++; + n -= sizeof (unsigned int); + } + + cdest = (char*)ldest; + csrc = (char*)lsrc; + + while (n > 0) { + *cdest++ = *csrc++; + n -= 1; + } + + return s1; +} + +static inline void insert_tag (struct boundary_tag* tag, int index) { + int realIndex; + + if (index < 0) { + realIndex = getexp (tag->real_size - sizeof (struct boundary_tag)); + if (realIndex < MINEXP) + realIndex = MINEXP; + } else + realIndex = index; + + tag->index = realIndex; + + if (l_freePages[realIndex] != NULL) { + l_freePages[realIndex]->prev = tag; + tag->next = l_freePages[realIndex]; + } + + l_freePages[realIndex] = tag; +} + +static inline void remove_tag (struct boundary_tag* tag) { + if (l_freePages[tag->index] == tag) + l_freePages[tag->index] = tag->next; + + if (tag->prev != NULL) + tag->prev->next = tag->next; + if (tag->next != NULL) + tag->next->prev = tag->prev; + + tag->next = NULL; + tag->prev = NULL; + tag->index = -1; +} + +static inline struct boundary_tag* melt_left (struct boundary_tag* tag) { + struct boundary_tag* left = tag->split_left; + + left->real_size += tag->real_size; + left->split_right = tag->split_right; + + if (tag->split_right != NULL) + tag->split_right->split_left = left; + + return left; +} + +static inline struct boundary_tag* absorb_right (struct boundary_tag* tag) { + struct boundary_tag* right = tag->split_right; + + remove_tag (right); // Remove right from free pages. + + tag->real_size += right->real_size; + + tag->split_right = right->split_right; + if (right->split_right != NULL) + right->split_right->split_left = tag; + + return tag; +} + +static inline struct boundary_tag* split_tag (struct boundary_tag* tag) { + unsigned int remainder = tag->real_size - sizeof (struct boundary_tag) - tag->size; + + struct boundary_tag* new_tag = + (struct boundary_tag*)((uintptr_t)tag + sizeof (struct boundary_tag) + tag->size); + + new_tag->magic = LIBALLOC_MAGIC; + new_tag->real_size = remainder; + + new_tag->next = NULL; + new_tag->prev = NULL; + + new_tag->split_left = tag; + new_tag->split_right = tag->split_right; + + if (new_tag->split_right != NULL) + new_tag->split_right->split_left = new_tag; + tag->split_right = new_tag; + + tag->real_size -= new_tag->real_size; + + insert_tag (new_tag, -1); + + return new_tag; +} + +// *************************************************************** + +static struct boundary_tag* allocate_new_tag (unsigned int size) { + unsigned int pages; + unsigned int usage; + struct boundary_tag* tag; + + // This is how much space is required. + usage = size + sizeof (struct boundary_tag); + + // Perfect amount of space + pages = usage / l_pageSize; + if ((usage % l_pageSize) != 0) + pages += 1; + + // Make sure it's >= the minimum size. + if (pages < (unsigned int)l_pageCount) + pages = l_pageCount; + + tag = (struct boundary_tag*)liballoc_alloc (pages); + + if (tag == NULL) + return NULL; // uh oh, we ran out of memory. + + tag->magic = LIBALLOC_MAGIC; + tag->size = size; + tag->real_size = pages * l_pageSize; + tag->index = -1; + + tag->next = NULL; + tag->prev = NULL; + tag->split_left = NULL; + tag->split_right = NULL; + + return tag; +} + +void* malloc (size_t size) { + int index; + void* ptr; + struct boundary_tag* tag = NULL; + + liballoc_lock (); + + if (l_initialized == 0) { + for (index = 0; index < MAXEXP; index++) { + l_freePages[index] = NULL; + l_completePages[index] = 0; + } + l_initialized = 1; + } + + index = getexp (size) + MODE; + if (index < MINEXP) + index = MINEXP; + + // Find one big enough. + tag = l_freePages[index]; // Start at the front of the list. + while (tag != NULL) { + // If there's enough space in this tag. + if ((tag->real_size - sizeof (struct boundary_tag)) >= (size + sizeof (struct boundary_tag))) { + break; + } + + tag = tag->next; + } + + // No page found. Make one. + if (tag == NULL) { + if ((tag = allocate_new_tag (size)) == NULL) { + liballoc_unlock (); + return NULL; + } + + index = getexp (tag->real_size - sizeof (struct boundary_tag)); + } else { + remove_tag (tag); + + if ((tag->split_left == NULL) && (tag->split_right == NULL)) + l_completePages[index] -= 1; + } + + // We have a free page. Remove it from the free pages list. + + tag->size = size; + + // Removed... see if we can re-use the excess space. + + unsigned int remainder = + tag->real_size - size - sizeof (struct boundary_tag) * 2; // Support a new tag + remainder + + if (((int)(remainder) > 0) /*&& ( (tag->real_size - remainder) >= (1<= 0) { + struct boundary_tag* new_tag = split_tag (tag); + + (void)new_tag; + } + } + + ptr = (void*)((uintptr_t)tag + sizeof (struct boundary_tag)); + + liballoc_unlock (); + return ptr; +} + +void free (void* ptr) { + int index; + struct boundary_tag* tag; + + if (ptr == NULL) + return; + + liballoc_lock (); + + tag = (struct boundary_tag*)((uintptr_t)ptr - sizeof (struct boundary_tag)); + + if (tag->magic != LIBALLOC_MAGIC) { + liballoc_unlock (); // release the lock + return; + } + + // MELT LEFT... + while ((tag->split_left != NULL) && (tag->split_left->index >= 0)) { + tag = melt_left (tag); + remove_tag (tag); + } + + // MELT RIGHT... + while ((tag->split_right != NULL) && (tag->split_right->index >= 0)) { + tag = absorb_right (tag); + } + + // Where is it going back to? + index = getexp (tag->real_size - sizeof (struct boundary_tag)); + if (index < MINEXP) + index = MINEXP; + + // A whole, empty block? + if ((tag->split_left == NULL) && (tag->split_right == NULL)) { + if (l_completePages[index] == MAXCOMPLETE) { + // Too many standing by to keep. Free this one. + unsigned int pages = tag->real_size / l_pageSize; + + if ((tag->real_size % l_pageSize) != 0) + pages += 1; + if (pages < (unsigned int)l_pageCount) + pages = l_pageCount; + + liballoc_free (tag, pages); + + liballoc_unlock (); + return; + } + + l_completePages[index] += 1; // Increase the count of complete pages. + } + + // .......... + + insert_tag (tag, index); + + liballoc_unlock (); +} + +void* calloc (size_t nobj, size_t size) { + int real_size; + void* p; + + real_size = nobj * size; + + p = malloc (real_size); + + liballoc_memset (p, 0, real_size); + + return p; +} + +void* realloc (void* p, size_t size) { + void* ptr; + struct boundary_tag* tag; + int real_size; + + if (size == 0) { + free (p); + return NULL; + } + if (p == NULL) + return malloc (size); + + if (&liballoc_lock != NULL) + liballoc_lock (); // lockit + tag = (struct boundary_tag*)((uintptr_t)p - sizeof (struct boundary_tag)); + real_size = tag->size; + if (&liballoc_unlock != NULL) + liballoc_unlock (); + + if ((size_t)real_size > size) + real_size = size; + + ptr = malloc (size); + liballoc_memcpy (ptr, p, real_size); + free (p); + + return ptr; +} diff --git a/libmsl/alloc/liballoc.h b/libmsl/alloc/liballoc.h new file mode 100644 index 0000000..9c52eee --- /dev/null +++ b/libmsl/alloc/liballoc.h @@ -0,0 +1,93 @@ +#ifndef _LIBALLOC_H +#define _LIBALLOC_H + +#include +#include + +#define _ALLOC_SKIP_DEFINE + +// If we are told to not define our own size_t, then we +// skip the define. +#ifndef _ALLOC_SKIP_DEFINE + +#ifndef _HAVE_SIZE_T +#define _HAVE_SIZE_T +typedef unsigned int size_t; +#endif + +#ifndef NULL +#define NULL 0 +#endif + +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** This is a boundary tag which is prepended to the + * page or section of a page which we have allocated. It is + * used to identify valid memory blocks that the + * application is trying to free. + */ +struct boundary_tag { + unsigned int magic; //< It's a kind of ... + unsigned int size; //< Requested size. + unsigned int real_size; //< Actual size. + int index; //< Location in the page table. + + struct boundary_tag* split_left; //< Linked-list info for broken pages. + struct boundary_tag* split_right; //< The same. + + struct boundary_tag* next; //< Linked list info. + struct boundary_tag* prev; //< Linked list info. +}; + +/** This function is supposed to lock the memory data structures. It + * could be as simple as disabling interrupts or acquiring a spinlock. + * It's up to you to decide. + * + * \return 0 if the lock was acquired successfully. Anything else is + * failure. + */ +extern int liballoc_lock (void); + +/** This function unlocks what was previously locked by the liballoc_lock + * function. If it disabled interrupts, it enables interrupts. If it + * had acquiried a spinlock, it releases the spinlock. etc. + * + * \return 0 if the lock was successfully released. + */ +extern int liballoc_unlock (void); + +/** This is the hook into the local system which allocates pages. It + * accepts an integer parameter which is the number of pages + * required. The page size was set up in the liballoc_init function. + * + * \return NULL if the pages were not allocated. + * \return A pointer to the allocated memory. + */ +extern void* liballoc_alloc (int); + +/** This frees previously allocated memory. The void* parameter passed + * to the function is the exact same value returned from a previous + * liballoc_alloc call. + * + * The integer value is the number of pages to free. + * + * \return 0 if the memory was successfully freed. + */ +extern int liballoc_free (void*, int); + +void* malloc (size_t); //< The standard function. +void* realloc (void*, size_t); //< The standard function. +void* calloc (size_t, size_t); //< The standard function. +void free (void*); //< The standard function. + +void liballoc_init (void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libmsl/alloc/src.mk b/libmsl/alloc/src.mk new file mode 100644 index 0000000..17322b7 --- /dev/null +++ b/libmsl/alloc/src.mk @@ -0,0 +1,3 @@ +c += alloc/liballoc.c + +o += alloc/liballoc.o diff --git a/libmsl/init/__premain.c b/libmsl/init/__premain.c index f929442..be92166 100644 --- a/libmsl/init/__premain.c +++ b/libmsl/init/__premain.c @@ -1,3 +1,4 @@ +#include #include #include @@ -15,6 +16,7 @@ static void clear_bss (void) { void __premain (void) { clear_bss (); + liballoc_init (); app_main (); diff --git a/libmsl/m/proc.h b/libmsl/m/proc.h index 686fb12..307ca2e 100644 --- a/libmsl/m/proc.h +++ b/libmsl/m/proc.h @@ -27,5 +27,6 @@ int proc_mutex_lock (int mutex_rid); int proc_mutex_unlock (int mutex_rid); int proc_spawn_thread (uintptr_t vstack_top, size_t stack_size, void* entry); int proc_sched (void); +int proc_translate_resource_mem (uintptr_t vaddr); #endif // _LIBMSL_M_PROC_H diff --git a/libmsl/m/status.h b/libmsl/m/status.h new file mode 100644 index 0000000..b28ae70 --- /dev/null +++ b/libmsl/m/status.h @@ -0,0 +1,6 @@ +#ifndef _LIBMSL_M_STATUS_H +#define _LIBMSL_M_STATUS_H + +#include + +#endif // _LIBMSL_M_STATUS_H diff --git a/libmsl/src.mk b/libmsl/src.mk index 2d7c2af..e2e4e8b 100644 --- a/libmsl/src.mk +++ b/libmsl/src.mk @@ -2,3 +2,4 @@ include $(platform)/src.mk include init/src.mk include m/src.mk include string/src.mk +include alloc/src.mk