diff --git a/kernel/include/kernel/kmalloc.h b/kernel/include/kernel/kmalloc.h index 9e678918..aab8f764 100644 --- a/kernel/include/kernel/kmalloc.h +++ b/kernel/include/kernel/kmalloc.h @@ -7,4 +7,5 @@ void kmalloc_dump_nodes(); void* kmalloc_eternal(size_t); void* kmalloc(size_t); +void* kmalloc(size_t, size_t); void kfree(void*); diff --git a/kernel/kernel/kmalloc.cpp b/kernel/kernel/kmalloc.cpp index eb34fa28..ddeb97b6 100644 --- a/kernel/kernel/kmalloc.cpp +++ b/kernel/kernel/kmalloc.cpp @@ -1,60 +1,71 @@ -#include +#include +#include #include -#include -#include +#include #include #define MB (1 << 20) -#define ALIGN (alignof(max_align_t)) +/* + +Kmalloc holds a bitmap of free/allocated chunks + +When allocating n chunks, kmalloc will put the number of chunks +to address, and return pointer to the byte after the stored size + +*/ + +static constexpr uintptr_t s_kmalloc_base = 0x00200000; +static constexpr size_t s_kmalloc_size = 1 * MB; +static constexpr uintptr_t s_kmalloc_end = s_kmalloc_base + s_kmalloc_size; + +static constexpr uintptr_t s_kmalloc_eternal_base = s_kmalloc_end; +static constexpr size_t s_kmalloc_eternal_size = 1 * MB; +static constexpr uintptr_t s_kmalloc_eternal_end = s_kmalloc_eternal_base + s_kmalloc_eternal_size; + +static constexpr size_t s_kmalloc_default_align = alignof(max_align_t); +static constexpr size_t s_kmalloc_chunk_size = s_kmalloc_default_align; +static constexpr size_t s_kmalloc_chunks_per_size = sizeof(size_t) * 8 / s_kmalloc_chunk_size; +static constexpr size_t s_kmalloc_total_chunks = s_kmalloc_size / s_kmalloc_chunk_size; +static uint8_t s_kmalloc_bitmap[s_kmalloc_total_chunks / 8] { 0 }; extern "C" uintptr_t g_kernel_end; -/* - #### KMALLOC ################ -*/ -struct kmalloc_node +static bool is_kmalloc_chunk_used(size_t index) { - uintptr_t addr; - size_t size : sizeof(size_t) * 8 - 1; - size_t free : 1; -}; -static kmalloc_node* s_kmalloc_node_head = nullptr; -static size_t s_kmalloc_node_count; + ASSERT(index < s_kmalloc_total_chunks); + return s_kmalloc_bitmap[index / 8] & (1 << (index % 8)); +} -static uintptr_t s_kmalloc_node_base = 0x00200000; -static size_t s_kmalloc_max_nodes = 1000; +static uintptr_t chunk_address(size_t index) +{ + ASSERT(index < s_kmalloc_total_chunks); + return s_kmalloc_base + s_kmalloc_chunk_size * index; +} -static uintptr_t s_kmalloc_base = s_kmalloc_node_base + s_kmalloc_max_nodes * sizeof(kmalloc_node); -static size_t s_kmalloc_size = 1 * MB; -static uintptr_t s_kmalloc_end = s_kmalloc_base + s_kmalloc_size; - -static size_t s_kmalloc_available = 0; -static size_t s_kmalloc_allocated = 0; - -/* - #### KMALLOC ETERNAL ######## -*/ -static uintptr_t s_kmalloc_eternal_ptr = 0; - -static uintptr_t s_kmalloc_eternal_base = s_kmalloc_end; -static size_t s_kmalloc_eternal_size = 1 * MB; -static uintptr_t s_kmalloc_eternal_end = s_kmalloc_eternal_base + s_kmalloc_eternal_size; -/* - ############################# -*/ - -static bool s_initialized = false; +static void free_chunks(size_t start) +{ + ASSERT(s_kmalloc_chunks_per_size <= start && start < s_kmalloc_total_chunks); + start -= s_kmalloc_chunks_per_size; + size_t size = *(size_t*)chunk_address(start); + for (size_t i = 0; i < size; i++) + s_kmalloc_bitmap[(start + i) / 8] &= ~(1 << ((start + i) % 8)); +} void kmalloc_initialize() { if (!(g_multiboot_info->flags & (1 << 6))) Kernel::Panic("Kmalloc: Bootloader didn't provide a memory map"); - if (g_kernel_end > s_kmalloc_node_base) + if (g_kernel_end > s_kmalloc_base) Kernel::Panic("Kmalloc: Kernel end is over kmalloc base"); + dprintln("initializing kmalloc"); + + dprintln("kmalloc {} -> {}", (void*)s_kmalloc_base, (void*)s_kmalloc_end); + dprintln("kmalloc eternal {} -> {}", (void*)s_kmalloc_eternal_base, (void*)s_kmalloc_eternal_end); + // Validate kmalloc memory bool valid = false; for (size_t i = 0; i < g_multiboot_info->mmap_length;) @@ -77,198 +88,80 @@ void kmalloc_initialize() if (!valid) { Kernel::Panic("Kmalloc: Could not find {}.{} MB of memory", - (s_kmalloc_eternal_end - s_kmalloc_node_base) / MB, - (s_kmalloc_eternal_end - s_kmalloc_node_base) % MB + (s_kmalloc_eternal_end - s_kmalloc_base) / MB, + (s_kmalloc_eternal_end - s_kmalloc_base) % MB ); } - - s_kmalloc_node_count = 1; - s_kmalloc_node_head = (kmalloc_node*)s_kmalloc_node_base; - - s_kmalloc_allocated = 0; - s_kmalloc_available = s_kmalloc_size; - - kmalloc_node& head = s_kmalloc_node_head[0]; - head.addr = s_kmalloc_base; - head.size = s_kmalloc_size; - head.free = true; - - s_kmalloc_eternal_ptr = s_kmalloc_eternal_base; - - s_initialized = true; } void kmalloc_dump_nodes() { - if (!s_initialized) - Kernel::Panic("kmalloc not initialized!"); - - dprintln("Kmalloc memory available {}.{} MB", s_kmalloc_available / MB, s_kmalloc_available % MB); - dprintln("Kmalloc memory allocated {}.{} MB", s_kmalloc_allocated / MB, s_kmalloc_allocated % MB); - dprintln("Using {}/{} nodes", s_kmalloc_node_count, s_kmalloc_max_nodes); - for (size_t i = 0; i < s_kmalloc_node_count; i++) - { - kmalloc_node& node = s_kmalloc_node_head[i]; - dprintln(" ({3}) {}, node at {}, free: {}, size: {}", i, (void*)&node, (void*)node.addr, node.free, node.size); - } -} - -void* kmalloc_eternal(size_t size) -{ - if (!s_initialized) - Kernel::Panic("kmalloc not initialized!"); - - if (size % ALIGN) - size += ALIGN - (size % ALIGN); - - if (s_kmalloc_eternal_ptr % ALIGN) - Kernel::Panic("Unaligned ptr in kmalloc_eternal"); - - if (s_kmalloc_eternal_ptr + size > s_kmalloc_eternal_end) - { - dprintln("\e[33mKmalloc eternal: Could not allocate {} bytes\e[0m", size); - return nullptr; - } - - void* result = (void*)s_kmalloc_eternal_ptr; - s_kmalloc_eternal_ptr += size; - return result; + dprintln("kmalloc dump: {8b}{8b}{8b}{8b}{8b}{8b}{8b}{8b}", + s_kmalloc_bitmap[7], s_kmalloc_bitmap[6], s_kmalloc_bitmap[5], s_kmalloc_bitmap[4], + s_kmalloc_bitmap[3], s_kmalloc_bitmap[2], s_kmalloc_bitmap[1], s_kmalloc_bitmap[0] + ); } void* kmalloc(size_t size) { - if (!s_initialized) - Kernel::Panic("kmalloc not initialized!"); - - if (size % ALIGN) - size += ALIGN - (size % ALIGN); - - // Search for node with free memory and big enough size - size_t valid_node_index = -1; - for (size_t i = 0; i < s_kmalloc_node_count; i++) - { - kmalloc_node& current = s_kmalloc_node_head[i]; - if (current.free && current.size >= size) - { - valid_node_index = i; - break; - } - } - - if (valid_node_index == size_t(-1)) - { - dprintln("\e[33mKmalloc: Could not allocate {} bytes\e[0m", size); - return nullptr; - } - - kmalloc_node& valid_node = s_kmalloc_node_head[valid_node_index]; - - // If node's size happens to match requested size, - // just flip free bit and return the address - if (valid_node.size == size) - { - valid_node.free = false; - if (valid_node.addr % ALIGN) - Kernel::Panic("Unaligned ptr in kmalloc"); - return (void*)valid_node.addr; - } - - if (s_kmalloc_node_count == s_kmalloc_max_nodes) - { - dprintln("\e[33mKmalloc: Out of kmalloc nodes\e[0m"); - return nullptr; - } - - // Shift every node after valid_node one place to right - for (size_t i = s_kmalloc_node_count - 1; i > valid_node_index; i--) - s_kmalloc_node_head[i + 1] = s_kmalloc_node_head[i]; - - // Create new node after the valid node - s_kmalloc_node_count++; - kmalloc_node& new_node = s_kmalloc_node_head[valid_node_index + 1]; - new_node.addr = valid_node.addr + size; - new_node.size = valid_node.size - size; - new_node.free = true; - - // Update the valid node - valid_node.size = size; - valid_node.free = false; - - s_kmalloc_allocated += size; - s_kmalloc_available -= size; - - if (valid_node.addr % ALIGN) - Kernel::Panic("Unaligned ptr in kmalloc"); - return (void*)valid_node.addr; + return kmalloc(size, s_kmalloc_default_align); } -void kfree(void* addr) +void* kmalloc(size_t size, size_t align) { - if (!s_initialized) Kernel::Panic("kmalloc not initialized!"); + if (size == 0) + return nullptr; - if (addr == nullptr) - return; - - // TODO: use binary search etc. - - size_t node_index = -1; - for (size_t i = 0; i < s_kmalloc_node_count; i++) + if (align == 0) + align = s_kmalloc_chunk_size; + + if (align < s_kmalloc_chunk_size || align % s_kmalloc_chunk_size) { - if (s_kmalloc_node_head[i].addr == (uintptr_t)addr) + size_t new_align = BAN::Math::lcm(align, s_kmalloc_chunk_size); + dwarnln("kmalloc asked to align to {}, aliging to {} instead", align, new_align); + align = new_align; + } + + size_t needed_chunks = (size - 1) / s_kmalloc_chunk_size + 1 + s_kmalloc_chunks_per_size; + for (size_t i = 0; i < s_kmalloc_total_chunks - needed_chunks; i++) + { + if (chunk_address(i + s_kmalloc_chunks_per_size) % align) + continue; + + bool free = true; + for (size_t j = 0; j < needed_chunks; j++) { - node_index = i; - break; + if (is_kmalloc_chunk_used(i + j)) + { + free = false; + i += j; + break; + } + } + if (free) + { + *(size_t*)chunk_address(i) = needed_chunks; + for (size_t j = 0; j < needed_chunks; j++) + s_kmalloc_bitmap[(i + j) / 8] |= (1 << ((i + j) % 8)); + return (void*)chunk_address(i + s_kmalloc_chunks_per_size); } } - if (node_index == size_t(-1)) - { - dprintln("\e[33mKmalloc: Attempting to free unallocated pointer {}\e[0m", addr); - return; - } - - // Mark this node as free - kmalloc_node* node = &s_kmalloc_node_head[node_index]; - node->free = true; - - size_t size = node->size; - - // If node before this node is free, merge them - if (node_index > 0) - { - kmalloc_node& prev = s_kmalloc_node_head[node_index - 1]; - - if (prev.free) - { - prev.size += node->size; - - s_kmalloc_node_count--; - for (size_t i = node_index; i < s_kmalloc_node_count; i++) - s_kmalloc_node_head[i] = s_kmalloc_node_head[i + 1]; - - node_index--; - node = &s_kmalloc_node_head[node_index]; - } - } - - // If node after this node is free, merge them - if (node_index < s_kmalloc_node_count - 1) - { - kmalloc_node& next = s_kmalloc_node_head[node_index + 1]; - - if (next.free) - { - node->size += next.size; - - s_kmalloc_node_count--; - for (size_t i = node_index; i < s_kmalloc_node_count; i++) - s_kmalloc_node_head[i + 1] = s_kmalloc_node_head[i + 2]; - - node_index--; - node = &s_kmalloc_node_head[node_index]; - } - } - - s_kmalloc_allocated -= size; - s_kmalloc_available += size; + dwarnln("Could not allocate {} bytes", size); + return nullptr; } + +void kfree(void* address) +{ + if (!address) + return; + ASSERT(((uintptr_t)address % s_kmalloc_chunk_size) == 0); + ASSERT(s_kmalloc_base <= (uintptr_t)address && (uintptr_t)address < s_kmalloc_end); + + size_t first_chunk = ((uintptr_t)address - s_kmalloc_base) / s_kmalloc_chunk_size - s_kmalloc_chunks_per_size; + ASSERT(is_kmalloc_chunk_used(first_chunk)); + + size_t size = *(size_t*)chunk_address(first_chunk); + for (size_t i = 0; i < size; i++) + s_kmalloc_bitmap[(first_chunk + i) / 8] &= ~(1 << ((first_chunk + i) % 8)); +} \ No newline at end of file