diff options
author | Raghuram Subramani <raghus2247@gmail.com> | 2025-01-28 04:53:10 -0500 |
---|---|---|
committer | Raghuram Subramani <raghus2247@gmail.com> | 2025-01-28 09:13:21 -0500 |
commit | c307bc6ad3b058027162c3db0b22f65f98665648 (patch) | |
tree | c57da2d99d14bc2c05d830526cf9b8f34508bdd0 | |
parent | 690d5f698a14554901d6f661f23695a4dca09a7c (diff) |
kmalloc: Initial (extremely buggy) implementation from liballoc
-rw-r--r-- | kernel/CMakeLists.txt | 1 | ||||
-rw-r--r-- | kernel/include/libk/kmalloc.h | 47 | ||||
-rw-r--r-- | kernel/include/libk/stdio.h | 1 | ||||
-rw-r--r-- | kernel/kernel/kernel.c | 20 | ||||
-rw-r--r-- | kernel/libk/kmalloc.c | 580 | ||||
-rw-r--r-- | kernel/libk/printk.c | 14 | ||||
-rw-r--r-- | kernel/mm/virtual_mm/virtual_mm.c | 44 |
7 files changed, 670 insertions, 37 deletions
diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 059600d..fb997be 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -18,6 +18,7 @@ set(SRC libk/printf.c libk/printk.c libk/strlen.c + libk/kmalloc.c mm/memory_map.c mm/physical_mm/memory_map.c diff --git a/kernel/include/libk/kmalloc.h b/kernel/include/libk/kmalloc.h new file mode 100644 index 0000000..f760ee1 --- /dev/null +++ b/kernel/include/libk/kmalloc.h @@ -0,0 +1,47 @@ +/* + * bubbl + * Copyright (C) 2025 Raghuram Subramani <raghus2247@gmail.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __libk_kmalloc_h +#define __libk_kmalloc_h + +#include <stddef.h> + +void *kmalloc(size_t); +void *krealloc(void *, size_t); +void *kcalloc(size_t, size_t); +void kfree(void *); + +/** 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. +}; + +#endif diff --git a/kernel/include/libk/stdio.h b/kernel/include/libk/stdio.h index 2f33980..f944fe4 100644 --- a/kernel/include/libk/stdio.h +++ b/kernel/include/libk/stdio.h @@ -37,5 +37,6 @@ int vsprintf(char *str, const char *fmt, va_list ap); int vsnprintf(char *str, size_t len, const char *fmt, va_list ap); void printk(const char *from, const char *msg, ...); +void printk_raw(const char *msg, ...); #endif diff --git a/kernel/kernel/kernel.c b/kernel/kernel/kernel.c index 8754aa2..4579a5d 100644 --- a/kernel/kernel/kernel.c +++ b/kernel/kernel/kernel.c @@ -18,18 +18,16 @@ #include <boot/gdt.h> +#include <drivers/serial.h> +#include <drivers/vga_text_buffer.h> +#include <kernel/halt.h> +#include <libk/kmalloc.h> +#include <libk/stdio.h> #include <mm/memory_map.h> #include <mm/multiboot.h> #include <mm/physical_mm.h> #include <mm/virtual_mm.h> -#include <kernel/halt.h> - -#include <libk/stdio.h> - -#include <drivers/serial.h> -#include <drivers/vga_text_buffer.h> - void kernel_main(uint32_t magic, multiboot_info_t *multiboot_info) { @@ -46,8 +44,12 @@ kernel_main(uint32_t magic, multiboot_info_t *multiboot_info) physical_mm_init(); virtual_mm_initialize(); - void *starting_address = virtual_mm_alloc_pages(1); - virtual_mm_free_pages(starting_address, 2); + int *a; + for (int i = 0; i < 3; i++) { + a = kmalloc(1); + printk("debug", "%d: Kmalloc allocated: 0x%x", i, a); + *a = 79; + } printk("\nKernel", "Started."); diff --git a/kernel/libk/kmalloc.c b/kernel/libk/kmalloc.c new file mode 100644 index 0000000..69e77ba --- /dev/null +++ b/kernel/libk/kmalloc.c @@ -0,0 +1,580 @@ +/* + + * bubbl + * Copyright (C) 2025 Raghuram Subramani <raghus2247@gmail.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <kernel/spinlock.h> +#include <libk/kmalloc.h> +#include <libk/stdio.h> +#include <mm/virtual_mm.h> +#include <stdatomic.h> +#include <stdint.h> + +static atomic_flag s_liballoc_lock; + +#define liballoc_alloc virtual_mm_alloc_pages +#define liballoc_free virtual_mm_free_pages + +#define printf printk_raw + +int +liballoc_lock(void) +{ + spinlock_acquire(&s_liballoc_lock); + return 0; +} + +int +liballoc_unlock(void) +{ + spinlock_release(&s_liballoc_lock); + 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 + +#ifdef DEBUG +unsigned int l_allocated = 0; //< The real amount of memory allocated. +unsigned int l_inuse = 0; //< The amount of memory in use (kmalloc'ed). +#endif + +static int l_initialized = 0; //< Flag to indicate initialization. +static int l_pageSize = 4096; //< Individual page size +static unsigned 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 unsigned int +getexp(unsigned int size) +{ + if (size < (1 << MINEXP)) { +#ifdef DEBUG + printf("getexp returns -1 for %i less than MINEXP\n", size); +#endif + return -1; // Smaller than the quantum. + } + + int shift = MINEXP; + + while (shift < MAXEXP) { + if (((unsigned int) 1 << shift) > size) + break; + shift += 1; + } + +#ifdef DEBUG + printf("getexp returns %i (%i bytes) for %i size\n", + shift - 1, + (1 << (shift - 1)), + size); +#endif + + 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; +} + +#ifdef DEBUG +static void +dump_array(void) +{ + int i = 0; + struct boundary_tag *tag = NULL; + + printf("------ Free pages array ---------\n"); + printf("System memory allocated: %i\n", l_allocated); + printf("Memory in used (kmalloc'ed): %i\n", l_inuse); + + for (i = 0; i < MAXEXP; i++) { + printf("%.2i(%i): ", i, l_completePages[i]); + + tag = l_freePages[i]; + while (tag != NULL) { + if (tag->split_left != NULL) + printf("*"); + printf("%i", tag->real_size); + if (tag->split_right != NULL) + printf("*"); + + printf(" "); + tag = tag->next; + } + printf("\n"); + } + + printf("'*' denotes a split to the left/right of a tag\n"); +} +#endif + +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 *) ((unsigned int) 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 < 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; + +#ifdef DEBUG + printf("Resource allocated %x of %i pages (%i bytes) for %i size.\n", + tag, + pages, + pages * l_pageSize, + size); + + l_allocated += pages * l_pageSize; + + printf("Total memory usage = %i KB\n", (int) ((l_allocated / (1024)))); +#endif + + return tag; +} + +void * +kmalloc(size_t size) +{ + int index; + void *ptr; + struct boundary_tag *tag = NULL; + + liballoc_lock(); + + if (l_initialized == 0) { +#ifdef DEBUG + printf("%s\n", "liballoc initializing."); +#endif + 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))) { +#ifdef DEBUG + printf("Tag search found %i >= %i\n", + (tag->real_size - sizeof(struct boundary_tag)), + (size + sizeof(struct boundary_tag))); +#endif + 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. + +#ifdef DEBUG + printf("Found tag with %i bytes available (requested %i bytes, leaving %i), " + "which has exponent: %i (%i bytes)\n", + tag->real_size - sizeof(struct boundary_tag), + size, + tag->real_size - size - sizeof(struct boundary_tag), + index, + 1 << index); +#endif + + 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<<MINEXP))*/) { + int childIndex = getexp(remainder); + + if (childIndex >= 0) { +#ifdef DEBUG + printf("Seems to be splittable: %i >= 2^%i .. %i\n", + remainder, + childIndex, + (1 << childIndex)); +#endif + + struct boundary_tag *new_tag = split_tag(tag); + (void) new_tag; + +#ifdef DEBUG + printf("Old tag has become %i bytes, new tag is now %i bytes (%i exp)\n", + tag->real_size, + new_tag->real_size, + new_tag->index); +#endif + } + } + + ptr = (void *) ((unsigned int) tag + sizeof(struct boundary_tag)); + +#ifdef DEBUG + l_inuse += size; + printf("kmalloc: %x, %i, %i\n", + ptr, + (int) l_inuse / 1024, + (int) l_allocated / 1024); + dump_array(); +#endif + + liballoc_unlock(); + return ptr; +} + +void +kfree(void *ptr) +{ + int index; + struct boundary_tag *tag; + + if (ptr == NULL) + return; + + liballoc_lock(); + + tag = (struct boundary_tag *) ((unsigned int) ptr + - sizeof(struct boundary_tag)); + + if (tag->magic != LIBALLOC_MAGIC) { + liballoc_unlock(); // release the lock + return; + } + +#ifdef DEBUG + l_inuse -= tag->size; + printf("free: %x, %i, %i\n", + ptr, + (int) l_inuse / 1024, + (int) l_allocated / 1024); +#endif + + // MELT LEFT... + while ((tag->split_left != NULL) && (tag->split_left->index >= 0)) { +#ifdef DEBUG + printf("Melting tag left into available memory. Left was %i, becomes %i " + "(%i)\n", + tag->split_left->real_size, + tag->split_left->real_size + tag->real_size, + tag->split_left->real_size); +#endif + tag = melt_left(tag); + remove_tag(tag); + } + + // MELT RIGHT... + while ((tag->split_right != NULL) && (tag->split_right->index >= 0)) { +#ifdef DEBUG + printf("Melting tag right into available memory. This was was %i, becomes " + "%i (%i)\n", + tag->real_size, + tag->split_right->real_size + tag->real_size, + tag->split_right->real_size); +#endif + 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 < l_pageCount) + pages = l_pageCount; + + liballoc_free(tag, pages); + +#ifdef DEBUG + l_allocated -= pages * l_pageSize; + printf("Resource freeing %x of %i pages\n", tag, pages); + dump_array(); +#endif + + liballoc_unlock(); + return; + } + + l_completePages[index] += 1; // Increase the count of complete pages. + } + + // .......... + + insert_tag(tag, index); +#ifdef DEBUG + printf("Returning tag with %i bytes (requested %i bytes), which has " + "exponent: %i\n", + tag->real_size, + tag->size, + index); + dump_array(); +#endif + + liballoc_unlock(); +} + +void * +kcalloc(size_t nobj, size_t size) +{ + int real_size; + void *p; + + real_size = nobj * size; + + p = kmalloc(real_size); + + liballoc_memset(p, 0, real_size); + + return p; +} + +void * +krealloc(void *p, size_t size) +{ + void *ptr; + struct boundary_tag *tag; + size_t real_size; + + if (size == 0) { + kfree(p); + return NULL; + } + if (p == NULL) + return kmalloc(size); + + liballoc_lock(); // lockit + tag = (struct boundary_tag *) ((unsigned int) p + - sizeof(struct boundary_tag)); + real_size = tag->size; + liballoc_unlock(); + + if (real_size > size) + real_size = size; + + ptr = kmalloc(size); + liballoc_memcpy(ptr, p, real_size); + kfree(p); + + return ptr; +} diff --git a/kernel/libk/printk.c b/kernel/libk/printk.c index ee84fa4..f97fa1c 100644 --- a/kernel/libk/printk.c +++ b/kernel/libk/printk.c @@ -38,3 +38,17 @@ printk(const char *from, const char *msg, ...) serial_write_string(str); serial_write_string("\033[0m\n"); } + +void +printk_raw(const char *msg, ...) +{ + /* TODO: Dynamic Memory Allocation */ + char str[256]; + + va_list ap; + va_start(ap, msg); + vsnprintf(str, sizeof(str), msg, ap); + va_end(ap); + + serial_write_string(str); +} diff --git a/kernel/mm/virtual_mm/virtual_mm.c b/kernel/mm/virtual_mm/virtual_mm.c index 4039597..4acf7c7 100644 --- a/kernel/mm/virtual_mm/virtual_mm.c +++ b/kernel/mm/virtual_mm/virtual_mm.c @@ -86,26 +86,31 @@ virtual_mm_initialize(void) virtual_mm_enable_paging(); } -void -virtual_mm_map_page(void *physical_address, void *virtual_address) +ALWAYS_INLINE uint32_t * +get_or_make_table(uint32_t *pd_entry) { - uint32_t *pd_entry = ¤t_page_directory[GET_PD_INDEX(virtual_address)]; - - uint32_t *table = 0; - /* If the pd_entry isn't present, allocate a block for it, zero the table, - * and set the pd_entry's frame to the table's address. */ + uint32_t *table; if (!PDE_IS_PRESENT(pd_entry)) { table = physical_mm_allocate_block(); if (!table) ASSERT_NOT_REACHED(); for (uint32_t i = 0; i < 1024; i++) - table[i] = 0; + table[i] = 0x0; *pd_entry = PDE_FRAME((uint32_t) table) | PDE_PRESENT(1) | PDE_WRITABLE(1); } else table = (uint32_t *) PDE_GET_TABLE(pd_entry); + return table; +} + +void +virtual_mm_map_page(void *physical_address, void *virtual_address) +{ + uint32_t *pd_entry = ¤t_page_directory[GET_PD_INDEX(virtual_address)]; + uint32_t *table = get_or_make_table(pd_entry); + uint32_t *pt_entry = &table[GET_PT_INDEX(virtual_address)]; if (PTE_IS_PRESENT(pt_entry)) { printk("debug", "Mapping previously mapped memory: 0x%x", pt_entry); @@ -129,28 +134,10 @@ virtual_mm_unmap_page(void *virtual_address) table = (uint32_t *) PDE_GET_TABLE(pd_entry); uint32_t *pt_entry = &table[GET_PT_INDEX(virtual_address)]; + printk("debug", "Freeing: 0x%x", pt_entry); *pt_entry = 0; } -ALWAYS_INLINE uint32_t * -get_or_make_table(uint32_t *pd_entry) -{ - uint32_t *table; - if (!PDE_IS_PRESENT(pd_entry)) { - table = physical_mm_allocate_block(); - if (!table) - ASSERT_NOT_REACHED(); - - for (uint32_t i = 0; i < 1024; i++) - table[i] = 0x0; - - *pd_entry = PDE_FRAME((uint32_t) table) | PDE_PRESENT(1) | PDE_WRITABLE(1); - } else - table = (uint32_t *) PDE_GET_TABLE(pd_entry); - - return table; -} - uint32_t virtual_mm_find_free_virtual_addresses(uint32_t n) { @@ -173,7 +160,8 @@ virtual_mm_find_free_virtual_addresses(uint32_t n) pt_index++) { /* If we overflow, switch to the consecutive page directory entry */ if (pt_index == PAGE_TABLE_SIZE) { - if (++pd_index == PAGE_DIRECTORY_SIZE) + pd_index++; + if (pd_index == PAGE_DIRECTORY_SIZE) return 0; /* Ran out of pd_entries */ pd_entry = ¤t_page_directory[pd_index]; |