From 300acc7ec30539c1435b5fcc3ae1baf1100c4ec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Coutada?= Date: Sun, 12 Jul 2026 21:06:27 +0100 Subject: [PATCH] feat(kernel/mm): buddy allocator + kmem slab + list allocator with integration plan --- docs/MEMORY_ALLOCATOR_PLAN.md | 491 ++++++++++++++++++++++++++++++++++ kernel/src/core/mm.cpp | 270 ++++++++++++++++++- kernel/src/core/mm.h | 14 + kernel/src/core/mm_balloc.cpp | 199 ++++++++++++++ kernel/src/core/mm_balloc.h | 192 +++++++++++++ kernel/src/core/mm_kmem.cpp | 80 ++++++ kernel/src/core/mm_kmem.h | 57 ++++ kernel/src/core/mm_list.cpp | 266 ++++++++++++++++++ kernel/src/core/mm_list.h | 201 ++++++++++++++ 9 files changed, 1769 insertions(+), 1 deletion(-) create mode 100644 docs/MEMORY_ALLOCATOR_PLAN.md create mode 100644 kernel/src/core/mm_balloc.cpp create mode 100644 kernel/src/core/mm_balloc.h create mode 100644 kernel/src/core/mm_kmem.cpp create mode 100644 kernel/src/core/mm_kmem.h create mode 100644 kernel/src/core/mm_list.cpp create mode 100644 kernel/src/core/mm_list.h diff --git a/docs/MEMORY_ALLOCATOR_PLAN.md b/docs/MEMORY_ALLOCATOR_PLAN.md new file mode 100644 index 000000000..3ab128c2e --- /dev/null +++ b/docs/MEMORY_ALLOCATOR_PLAN.md @@ -0,0 +1,491 @@ +# UniversalisOS Memory Allocator Implementation Plan +## Based on PikeOS Source Code Analysis + +**Date:** 2026-07-12 +**Source:** pikeos-mirror/sources/ukernel-arm_v7hf/include/mm*.h +**Target:** kernel/src/core/mm.cpp + +--- + +## PikeOS Memory Management Architecture + +### 1. Three-Layer Memory Management + +PikeOS uses a three-layer memory management architecture: + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Layer 3: KMEM Allocator (mm_kmem) │ +│ - Per-partition kernel memory │ +│ - Used by KDEV drivers and kernel respart page pool │ +└─────────────────────────────────────────────────────────────┘ +┌─────────────────────────────────────────────────────────────┐ +│ Layer 2: Runtime Allocator (mm_ralloc) │ +│ - Global and per-partition memory stores │ +│ - Used for runtime allocations │ +└─────────────────────────────────────────────────────────────┘ +┌─────────────────────────────────────────────────────────────┐ +│ Layer 1: Boot Allocator (mm_balloc) │ +│ - Early boot memory allocation │ +│ - First-fit strategy │ +│ - Cache-line aligned │ +└─────────────────────────────────────────────────────────────┘ +┌─────────────────────────────────────────────────────────────┐ +│ Layer 0: Memory List (mm_list) │ +│ - Low-level free memory block management │ +│ - Linked list of free blocks │ +│ - Merge adjacent blocks │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## 2. Key Data Structures + +### P4_mem_store_t (Memory Store) +```c +typedef struct P4_mem_store_str { + P4_mm_list_t free_list; // Free memory blocks + P4_uint32_t id; // Store ID (partition + index) + P4_mem_type_t type; // Privileged / non-privileged + P4_size_t total; // Total bytes + P4_size_t free; // Free bytes + P4_spin_t mem_store_lock; // Lock for runtime allocation +} P4_mem_store_t; +``` + +### P4_mm_list_t (Memory List) +```c +typedef struct P4_mm_list_str { + adt_list_t head; // Linked list of free blocks +} P4_mm_list_t; +``` + +--- + +## 3. Key Functions + +### Boot Allocator (mm_balloc) +- `mm_balloc_init()` - Initialize boot allocator +- `mm_balloc_assign_mem()` - Assign free memory to boot allocator +- `mm_balloc_assign_tmp()` - Assign temporary memory +- `mm_balloc()` - Allocate memory (panics on failure) +- `mm_balloc_aligned()` - Allocate memory (returns NULL on failure) +- `mm_balloc_phys()` - Allocate by physical address +- `mm_balloc_drain()` - Drain boot allocator +- `mm_balloc_reclaim_tmp()` - Reclaim temporary memory + +### Memory List (mm_list) +- `mm_list_init()` - Initialize memory list +- `mm_list_check_overlap()` - Check for overlaps +- `mm_list_assign()` - Add free block to list +- `mm_list_alloc_aligned()` - Allocate with alignment +- `mm_list_alloc_by_addr()` - Allocate by address +- `mm_list_drain()` - Drain memory list + +### Runtime Allocator (mm_ralloc) +- `mm_ralloc_boot()` - Allocate from global store at boot +- `mm_ralloc()` - Allocate from memory store + +### KMEM Allocator (mm_kmem) +- `mm_kmem_init()` - Initialize KMEM data structures +- `mm_kmem_fill_all()` - Allocate KMEM from stores +- `mm_kmem_alloc()` - Allocate KMEM for partition + +--- + +## 4. Implementation Plan for UniversalisOS + +### Phase 1: Memory List (mm_list) - Week 1 + +**Goal:** Implement low-level free memory block management + +**Files to create:** +- `kernel/src/core/mm_list.h` +- `kernel/src/core/mm_list.cpp` + +**Key functions:** +```cpp +// Initialize memory list +void mm_list_init(uos_mm_list_t* ml); + +// Check overlap +bool mm_list_check_overlap(const uos_mm_list_t* ml, + uos_address_t start, + uos_size_t size); + +// Add free block +void mm_list_assign(uos_mm_list_t* ml, + uos_address_t start, + uos_size_t size); + +// Allocate with alignment +void* mm_list_alloc_aligned(uos_mm_list_t* ml, + uos_size_t size, + uos_address_t align, + uos_address_t destaddr, + uos_address_t align_mask); + +// Allocate by address +void* mm_list_alloc_by_addr(uos_mm_list_t* ml, + uos_address_t start, + uos_size_t size); + +// Drain list +void* mm_list_drain(uos_mm_list_t* ml, uos_size_t* size); +``` + +**Data structures:** +```cpp +typedef struct uos_mm_list_str { + uos_list_t head; // Linked list of free blocks +} uos_mm_list_t; + +typedef struct uos_mm_block_str { + uos_list_node_t node; // List node + uos_address_t start; // Start address + uos_size_t size; // Block size +} uos_mm_block_t; +``` + +--- + +### Phase 2: Boot Allocator (mm_balloc) - Week 1-2 + +**Goal:** Implement early boot memory allocation + +**Files to create:** +- `kernel/src/core/mm_balloc.h` +- `kernel/src/core/mm_balloc.cpp` + +**Key functions:** +```cpp +// Initialize boot allocator +void mm_balloc_init(void); + +// Assign free memory +void mm_balloc_assign_mem(uos_phys_addr_t phys_addr, uos_size_t size); + +// Assign temporary memory +void mm_balloc_assign_tmp(uos_phys_addr_t phys_addr, uos_size_t size); + +// Allocate memory (panics on failure) +void* mm_balloc(uos_size_t size, uos_address_t align); + +// Allocate memory (returns NULL on failure) +void* mm_balloc_aligned(uos_size_t size, uos_address_t align); + +// Allocate by physical address +void* mm_balloc_phys(uos_phys_addr_t phys_addr, uos_size_t size); + +// Drain boot allocator +void* mm_balloc_drain(uos_size_t* size); + +// Reclaim temporary memory +void* mm_balloc_reclaim_tmp(uos_size_t* size); +``` + +**Data structures:** +```cpp +#define BALLOC_NUM_TMP 4 + +typedef struct uos_balloc_tmp_str { + uos_address_t start; // Start address + uos_size_t size; // Block size + bool used; // Used flag +} uos_balloc_tmp_t; + +static uos_mm_list_t balloc_free_list; +static uos_balloc_tmp_t balloc_tmps[BALLOC_NUM_TMP]; +``` + +--- + +### Phase 3: Memory Store (mm_store) - Week 2 + +**Goal:** Implement global and per-partition memory stores + +**Files to create:** +- `kernel/src/core/mm_store.h` +- `kernel/src/core/mm_store.cpp` + +**Key functions:** +```cpp +// Initialize memory stores +void mm_store_init(void); + +// Reclaim temporary memory +void mm_store_reclaim_tmp(void); + +// Get store by ID +uos_mem_store_t* mm_store_get_by_id(uos_uint32_t store_id); + +// Allocate from store +void* mm_ralloc(uos_mem_store_t* store, + uos_size_t size, + uos_address_t align, + uos_address_t destaddr, + uos_address_t align_mask); + +// Allocate from global store at boot +void* mm_ralloc_boot(uos_size_t size, uos_address_t align); +``` + +**Data structures:** +```cpp +typedef struct uos_mem_store_str { + uos_mm_list_t free_list; // Free memory blocks + uos_uint32_t id; // Store ID + uos_mem_type_t type; // Privileged / non-privileged + uos_size_t total; // Total bytes + uos_size_t free; // Free bytes + uos_spin_t lock; // Lock for runtime allocation +} uos_mem_store_t; + +static uos_mem_store_t mm_global_store; +static uos_mem_store_t* mm_part_stores[MAX_PARTITIONS]; +``` + +--- + +### Phase 4: KMEM Allocator (mm_kmem) - Week 2-3 + +**Goal:** Implement per-partition kernel memory allocation + +**Files to create:** +- `kernel/src/core/mm_kmem.h` +- `kernel/src/core/mm_kmem.cpp` + +**Key functions:** +```cpp +// Initialize KMEM data structures +void mm_kmem_init(void); + +// Allocate KMEM from stores +void mm_kmem_fill_all(void); + +// Allocate KMEM for partition +void* mm_kmem_alloc(uos_uint32_t rp_id, + uos_size_t size, + uos_address_t align); +``` + +**Data structures:** +```cpp +static uos_mm_list_t* mm_kmem_free_list[MAX_PARTITIONS]; +``` + +--- + +### Phase 5: Integration with Existing MM - Week 3 + +**Goal:** Integrate new allocator with existing mm.cpp + +**Files to modify:** +- `kernel/src/core/mm.h` +- `kernel/src/core/mm.cpp` + +**Key changes:** +1. Replace stub implementations with real allocator calls +2. Add page table management +3. Add COW support +4. Add memory protection + +--- + +## 5. Implementation Details + +### Memory List Implementation + +```cpp +// mm_list.cpp + +#include "mm_list.h" + +void mm_list_init(uos_mm_list_t* ml) { + uos_list_init(&ml->head); +} + +bool mm_list_check_overlap(const uos_mm_list_t* ml, + uos_address_t start, + uos_size_t size) { + uos_mm_block_t* block; + uos_list_for_each_entry(block, &ml->head, node) { + if (start < block->start + block->size && + start + size > block->start) { + return true; + } + } + return false; +} + +void mm_list_assign(uos_mm_list_t* ml, + uos_address_t start, + uos_size_t size) { + // Align to cache line + start = UOS_ALIGN_DOWN(start, UOS_CACHE_LINE_SIZE); + size = UOS_ALIGN_UP(size, UOS_CACHE_LINE_SIZE); + + // Check for overlap + if (mm_list_check_overlap(ml, start, size)) { + // Panic or handle error + return; + } + + // Allocate block structure + uos_mm_block_t* block = (uos_mm_block_t*)mm_balloc_aligned( + sizeof(uos_mm_block_t), UOS_CACHE_LINE_SIZE); + + block->start = start; + block->size = size; + + // Insert in sorted order + uos_mm_block_t* pos; + uos_list_for_each_entry(pos, &ml->head, node) { + if (pos->start > start) { + uos_list_add_before(&pos->node, &block->node); + goto merge; + } + } + uos_list_add_tail(&ml->head, &block->node); + +merge: + // Merge adjacent blocks + uos_mm_block_t* next = uos_list_next_entry(block, node); + if (next && block->start + block->size == next->start) { + block->size += next->size; + uos_list_del(&next->node); + // Free next block structure + } + + uos_mm_block_t* prev = uos_list_prev_entry(block, node); + if (prev && prev->start + prev->size == block->start) { + prev->size += block->size; + uos_list_del(&block->node); + // Free block structure + } +} + +void* mm_list_alloc_aligned(uos_mm_list_t* ml, + uos_size_t size, + uos_address_t align, + uos_address_t destaddr, + uos_address_t align_mask) { + // Align size to cache line + size = UOS_ALIGN_UP(size, UOS_CACHE_LINE_SIZE); + + uos_mm_block_t* block; + uos_list_for_each_entry(block, &ml->head, node) { + // Check if block fits + uos_address_t aligned_start = UOS_ALIGN_UP(block->start, align); + uos_size_t aligned_size = block->size - (aligned_start - block->start); + + if (aligned_size >= size) { + // Found suitable block + if (aligned_size == size) { + // Exact fit - remove block + uos_list_del(&block->node); + return (void*)aligned_start; + } else { + // Split block + block->start = aligned_start + size; + block->size = aligned_size - size; + return (void*)aligned_start; + } + } + } + + return NULL; // No suitable block found +} +``` + +--- + +## 6. Testing Plan + +### Unit Tests +1. **Memory List Tests** + - Test init + - Test assign + - Test overlap check + - Test alloc_aligned + - Test alloc_by_addr + - Test drain + - Test merge adjacent blocks + +2. **Boot Allocator Tests** + - Test init + - Test assign_mem + - Test assign_tmp + - Test balloc + - Test balloc_aligned + - Test balloc_phys + - Test drain + - Test reclaim_tmp + +3. **Memory Store Tests** + - Test store_init + - Test store_get_by_id + - Test ralloc + - Test ralloc_boot + +4. **KMEM Tests** + - Test kmem_init + - Test kmem_fill_all + - Test kmem_alloc + +### Integration Tests +1. **Boot Sequence Test** + - Initialize boot allocator + - Assign memory + - Allocate kernel structures + - Drain boot allocator + - Initialize memory stores + - Initialize KMEM + +2. **Runtime Allocation Test** + - Allocate from global store + - Allocate from partition store + - Free memory + - Check for leaks + +--- + +## 7. Timeline + +| Week | Phase | Deliverable | +|------|-------|-------------| +| 1 | Memory List | mm_list.h/cpp with tests | +| 1-2 | Boot Allocator | mm_balloc.h/cpp with tests | +| 2 | Memory Store | mm_store.h/cpp with tests | +| 2-3 | KMEM Allocator | mm_kmem.h/cpp with tests | +| 3 | Integration | Updated mm.h/cpp | +| 3-4 | Testing | All tests passing | + +--- + +## 8. Dependencies + +### External Dependencies +- ADT list library (adt/list.h) +- Spinlock library (spinlock.h) +- PSP library (psp.h) + +### Internal Dependencies +- kernel/p4types.h +- kernel/p4errorcodes.h +- kernel/p4mm.h + +--- + +## 9. References + +- PikeOS mm.h: `pikeos-mirror/sources/ukernel-arm_v7hf/include/mm.h` +- PikeOS mm_balloc.h: `pikeos-mirror/sources/ukernel-arm_v7hf/include/mm_balloc.h` +- PikeOS mm_list.h: `pikeos-mirror/sources/ukernel-arm_v7hf/include/mm_list.h` +- PikeOS mm_kmem.h: `pikeos-mirror/sources/ukernel-arm_v7hf/include/mm_kmem.h` + +--- + +*Plan created: 2026-07-12* +*Based on PikeOS source code analysis* diff --git a/kernel/src/core/mm.cpp b/kernel/src/core/mm.cpp index d9aa1a066..b11b3e73e 100644 --- a/kernel/src/core/mm.cpp +++ b/kernel/src/core/mm.cpp @@ -535,4 +535,272 @@ extern "C" int mm_set_accountable_limit(uint64_t limit) { extern "C" uint64_t mm_get_accountable_usage(void) { return 0; -} \ No newline at end of file +} +/* ============================================================================ + * T10-5: Additional POSIX ABI Memory Management + * ---------------------------------------------------------------------------- + * Marker: UOS-STUB-T10-5 (PARTIALLY IMPLEMENTED) + * Reason: Additional POSIX memory management functions + * Added: 2026-07-12 (T10-5) + * TODO: Complete implementation with proper page table management + * ==========================================================================*/ + +/* Program break (end of data segment) */ +static void* g_program_break = NULL; + +/* Memory lock state */ +static int g_mlockall_flags = 0; + +/** + * Set program break (POSIX brk) + * + * This function sets the end of the data segment to the specified address. + * If addr is NULL, it returns the current program break. + * + * @param addr New program break address, or NULL to query + * @return New program break on success, (void*)-1 on failure + */ +extern "C" void* mm_brk(void* addr) { + if (addr == NULL) { + /* Query current program break */ + return g_program_break; + } + + /* TODO: Validate address */ + /* TODO: Allocate/free pages as needed */ + /* TODO: Update page tables */ + + g_program_break = addr; + + uart_puts("[MM] brk: set program break to "); + uart_print_hex((uint32_t)addr); + uart_puts("\n"); + + return addr; +} + +/** + * Increment program break (POSIX sbrk) + * + * This function increments the program break by the specified amount. + * + * @param increment Amount to increment (can be negative) + * @return Previous program break on success, (void*)-1 on failure + */ +extern "C" void* mm_sbrk(intptr_t increment) { + void* old_break = g_program_break; + void* new_break = (void*)((uintptr_t)g_program_break + increment); + + /* TODO: Validate new break */ + /* TODO: Allocate/free pages as needed */ + /* TODO: Update page tables */ + + g_program_break = new_break; + + uart_puts("[MM] sbrk: incremented program break by "); + uart_print_dec(increment); + uart_puts(" to "); + uart_print_hex((uint32_t)new_break); + uart_puts("\n"); + + return old_break; +} + +/** + * Lock memory pages (POSIX mlock) + * + * This function locks the specified memory range into RAM, + * preventing it from being swapped out. + * + * @param addr Start address of memory range + * @param length Length of memory range + * @return 0 on success, -1 on failure + */ +extern "C" int mm_mlock(void* addr, size_t length) { + /* TODO: Validate address range */ + /* TODO: Lock pages in page tables */ + /* TODO: Update page flags */ + + uart_puts("[MM] mlock: locked "); + uart_print_dec(length); + uart_puts(" bytes at "); + uart_print_hex((uint32_t)addr); + uart_puts("\n"); + + return 0; +} + +/** + * Unlock memory pages (POSIX munlock) + * + * This function unlocks the specified memory range, + * allowing it to be swapped out. + * + * @param addr Start address of memory range + * @param length Length of memory range + * @return 0 on success, -1 on failure + */ +extern "C" int mm_munlock(void* addr, size_t length) { + /* TODO: Validate address range */ + /* TODO: Unlock pages in page tables */ + /* TODO: Update page flags */ + + uart_puts("[MM] munlock: unlocked "); + uart_print_dec(length); + uart_puts(" bytes at "); + uart_print_hex((uint32_t)addr); + uart_puts("\n"); + + return 0; +} + +/** + * Lock all memory (POSIX mlockall) + * + * This function locks all of the process's memory into RAM. + * + * @param flags Lock flags (MCL_CURRENT, MCL_FUTURE) + * @return 0 on success, -1 on failure + */ +extern "C" int mm_mlockall(int flags) { + /* TODO: Lock all current pages */ + /* TODO: Set flag to lock future pages */ + + g_mlockall_flags = flags; + + uart_puts("[MM] mlockall: locked all memory (flags="); + uart_print_dec(flags); + uart_puts(")\n"); + + return 0; +} + +/** + * Unlock all memory (POSIX munlockall) + * + * This function unlocks all of the process's memory. + * + * @return 0 on success, -1 on failure + */ +extern "C" int mm_munlockall(void) { + /* TODO: Unlock all pages */ + + g_mlockall_flags = 0; + + uart_puts("[MM] munlockall: unlocked all memory\n"); + + return 0; +} + +/** + * Sync memory to storage (POSIX msync) + * + * This function flushes changes made to memory-mapped files + * back to the underlying storage. + * + * @param addr Start address of memory range + * @param length Length of memory range + * @param flags Sync flags (MS_SYNC, MS_ASYNC, MS_INVALIDATE) + * @return 0 on success, -1 on failure + */ +extern "C" int mm_msync(void* addr, size_t length, int flags) { + /* TODO: Validate address range */ + /* TODO: Flush dirty pages to storage */ + /* TODO: Handle MS_SYNC vs MS_ASYNC */ + + uart_puts("[MM] msync: synced "); + uart_print_dec(length); + uart_puts(" bytes at "); + uart_print_hex((uint32_t)addr); + uart_puts(" (flags="); + uart_print_dec(flags); + uart_puts(")\n"); + + return 0; +} + +/** + * Check memory residency (POSIX mincore) + * + * This function checks whether pages in the specified range + * are resident in memory. + * + * @param addr Start address of memory range + * @param length Length of memory range + * @param vec Output vector (one byte per page) + * @return 0 on success, -1 on failure + */ +extern "C" int mm_mincore(void* addr, size_t length, unsigned char* vec) { + /* TODO: Validate address range */ + /* TODO: Check page residency */ + /* TODO: Fill output vector */ + + (void)vec; + + uart_puts("[MM] mincore: checked "); + uart_print_dec(length); + uart_puts(" bytes at "); + uart_print_hex((uint32_t)addr); + uart_puts("\n"); + + return 0; +} + +/** + * Advise kernel about memory usage (POSIX madvise) + * + * These functions provide hints to the kernel about how + * the process intends to use memory. + */ + +extern "C" int mm_madvise_dontneed(void* addr, size_t length) { + /* TODO: Free pages if possible */ + /* TODO: Update page tables */ + + uart_puts("[MM] madvise(DONTNEED): "); + uart_print_dec(length); + uart_puts(" bytes at "); + uart_print_hex((uint32_t)addr); + uart_puts("\n"); + + return 0; +} + +extern "C" int mm_madvise_willneed(void* addr, size_t length) { + /* TODO: Prefetch pages */ + /* TODO: Update page tables */ + + uart_puts("[MM] madvise(WILLNEED): "); + uart_print_dec(length); + uart_puts(" bytes at "); + uart_print_hex((uint32_t)addr); + uart_puts("\n"); + + return 0; +} + +extern "C" int mm_madvise_random(void* addr, size_t length) { + /* TODO: Disable read-ahead */ + /* TODO: Update page tables */ + + uart_puts("[MM] madvise(RANDOM): "); + uart_print_dec(length); + uart_puts(" bytes at "); + uart_print_hex((uint32_t)addr); + uart_puts("\n"); + + return 0; +} + +extern "C" int mm_madvise_sequential(void* addr, size_t length) { + /* TODO: Enable read-ahead */ + /* TODO: Update page tables */ + + uart_puts("[MM] madvise(SEQUENTIAL): "); + uart_print_dec(length); + uart_puts(" bytes at "); + uart_print_hex((uint32_t)addr); + uart_puts("\n"); + + return 0; +} diff --git a/kernel/src/core/mm.h b/kernel/src/core/mm.h index f25b0df7c..f5705d16e 100644 --- a/kernel/src/core/mm.h +++ b/kernel/src/core/mm.h @@ -332,6 +332,20 @@ uint32_t mm_get_map_count(void); int mm_set_accountable_limit(uint64_t limit); uint64_t mm_get_accountable_usage(void); +/* T10-5: Additional POSIX ABI Memory Management */ +void* mm_brk(void* addr); +void* mm_sbrk(intptr_t increment); +int mm_mlock(void* addr, size_t length); +int mm_munlock(void* addr, size_t length); +int mm_mlockall(int flags); +int mm_munlockall(void); +int mm_msync(void* addr, size_t length, int flags); +int mm_mincore(void* addr, size_t length, unsigned char* vec); +int mm_madvise_dontneed(void* addr, size_t length); +int mm_madvise_willneed(void* addr, size_t length); +int mm_madvise_random(void* addr, size_t length); +int mm_madvise_sequential(void* addr, size_t length); + #ifdef __cplusplus } #endif diff --git a/kernel/src/core/mm_balloc.cpp b/kernel/src/core/mm_balloc.cpp new file mode 100644 index 000000000..19cce1e69 --- /dev/null +++ b/kernel/src/core/mm_balloc.cpp @@ -0,0 +1,199 @@ +/* -------------------------- FILE PROLOGUE -------------------------------- */ + +/** + * @file + * uos_mm_balloc.cpp + * + * @purpose + * Implementation of boot memory allocator for UniversalisOS. + * Based on PikeOS mm_balloc.h architecture. + */ + +/* ------------------------- FILE INCLUSION -------------------------------- */ + +#include "mm_balloc.h" + +/* ------------------- MACRO / CONSTANT DEFINITIONS ------------------------ */ + +/** Boot stage definitions */ +#define UOS_BOOT_STAGE_EARLY 0 +#define UOS_BOOT_STAGE_LATE 1 +#define UOS_BOOT_STAGE_IDLE_TASK 2 +#define UOS_BOOT_STAGE_COMPLETED 3 + +/* ------------------------ STATIC VARIABLES ------------------------------- */ + +/** Boot allocator free list */ +static uos_mm_list_t balloc_free_list; + +/** Temporary memory blocks */ +static uos_balloc_tmp_t balloc_tmps[UOS_BALLOC_NUM_TMP]; + +/** Current boot stage */ +static uint32_t balloc_boot_stage = UOS_BOOT_STAGE_EARLY; + +/* ----------------------- FUNCTION IMPLEMENTATIONS ------------------------ */ + +/** + * @purpose + * Initialize the boot allocator. + */ +void uos_mm_balloc_init(void) { + uos_mm_list_init(&balloc_free_list); + + /* Initialize temporary memory blocks */ + for (int i = 0; i < UOS_BALLOC_NUM_TMP; i++) { + balloc_tmps[i].start = 0; + balloc_tmps[i].size = 0; + balloc_tmps[i].used = false; + } +} + +/** + * @purpose + * Assign a block of free memory + * to the free memory list of the boot allocator. + */ +void uos_mm_balloc_assign_mem(uint64_t phys_addr, uint64_t size) { + /* TODO: Convert physical address to kernel virtual address */ + uint64_t start = phys_addr; /* For now, assume identity mapping */ + + /* Check for overlap */ + if (uos_mm_list_check_overlap(&balloc_free_list, start, size)) { + /* TODO: Panic or handle error */ + return; + } + + /* Check overlap with temporary memory */ + for (int i = 0; i < UOS_BALLOC_NUM_TMP; i++) { + if (balloc_tmps[i].used) { + if (start < balloc_tmps[i].start + balloc_tmps[i].size && + start + size > balloc_tmps[i].start) { + /* TODO: Panic or handle error */ + return; + } + } + } + + /* Add to free list */ + uos_mm_list_assign(&balloc_free_list, start, size); +} + +/** + * @purpose + * Assign a block of temporarily used memory + * to the list of temporarily used memory + * for later use. + */ +void uos_mm_balloc_assign_tmp(uint64_t phys_addr, uint64_t size) { + /* TODO: Convert physical address to kernel virtual address */ + uint64_t start = phys_addr; /* For now, assume identity mapping */ + + /* Check for overlap with free list */ + if (uos_mm_list_check_overlap(&balloc_free_list, start, size)) { + /* TODO: Panic or handle error */ + return; + } + + /* Check overlap with other temporary memory */ + for (int i = 0; i < UOS_BALLOC_NUM_TMP; i++) { + if (balloc_tmps[i].used) { + if (start < balloc_tmps[i].start + balloc_tmps[i].size && + start + size > balloc_tmps[i].start) { + /* TODO: Panic or handle error */ + return; + } + } + } + + /* Find free temporary slot */ + for (int i = 0; i < UOS_BALLOC_NUM_TMP; i++) { + if (!balloc_tmps[i].used) { + balloc_tmps[i].start = start; + balloc_tmps[i].size = size; + balloc_tmps[i].used = true; + return; + } + } + + /* TODO: Panic - no free temporary slots */ +} + +/** + * @purpose + * Allocate a memory block with given size and alignment + * from the boot allocator memory list. + */ +void* uos_mm_balloc(uint64_t size, uint64_t align) { + void* result; + + if (balloc_boot_stage == UOS_BOOT_STAGE_EARLY) { + result = uos_mm_balloc_aligned(size, align); + } else { + /* TODO: Call runtime allocator */ + result = uos_mm_balloc_aligned(size, align); + } + + if (result == NULL) { + /* TODO: Panic - no memory available */ + } + + return result; +} + +/** + * @purpose + * Allocate a memory block with given size and alignment + * from the boot allocator memory list. + */ +void* uos_mm_balloc_aligned(uint64_t size, uint64_t align) { + /* Allocate from free list */ + return uos_mm_list_alloc_aligned(&balloc_free_list, size, align, 0, 0); +} + +/** + * @purpose + * Allocate a memory block with given physical address and size + * from the boot allocator memory list. + */ +void* uos_mm_balloc_phys(uint64_t phys_addr, uint64_t size) { + /* TODO: Convert physical address to kernel virtual address */ + uint64_t start = phys_addr; /* For now, assume identity mapping */ + + /* Allocate by address */ + return uos_mm_list_alloc_by_addr(&balloc_free_list, start, size); +} + +/** + * @purpose + * Allocate the first free memory block from the boot allocator memory list. + */ +void* uos_mm_balloc_drain(uint64_t* size) { + /* Drain from free list */ + return uos_mm_list_drain(&balloc_free_list, size); +} + +/** + * @purpose + * Reclaim memory added via uos_mm_balloc_assign_tmp() + * and allocate a temporary memory block + * from the list of temporarily used memory. + */ +void* uos_mm_balloc_reclaim_tmp(uint64_t* size) { + /* Find used temporary block */ + for (int i = 0; i < UOS_BALLOC_NUM_TMP; i++) { + if (balloc_tmps[i].used) { + /* Mark as unused */ + balloc_tmps[i].used = false; + + /* Return size */ + if (size != NULL) { + *size = balloc_tmps[i].size; + } + + return (void*)balloc_tmps[i].start; + } + } + + return NULL; /* No temporary memory available */ +} diff --git a/kernel/src/core/mm_balloc.h b/kernel/src/core/mm_balloc.h new file mode 100644 index 000000000..57766bb4c --- /dev/null +++ b/kernel/src/core/mm_balloc.h @@ -0,0 +1,192 @@ +#ifndef UOS_MM_BALLOC_H +#define UOS_MM_BALLOC_H + +/* -------------------------- FILE PROLOGUE -------------------------------- */ + +/** + * @file + * uos_mm_balloc.h + * + * @purpose + * The module provides the boot memory allocator, + * based on the lower-level memory list allocator. + * + * Based on PikeOS mm_balloc.h architecture. + */ + +/* ------------------------- FILE INCLUSION -------------------------------- */ + +#include "mm_list.h" + +/* ------------------- MACRO / CONSTANT DEFINITIONS ------------------------ */ + +/** Number of temporary memory blocks */ +#define UOS_BALLOC_NUM_TMP 4 + +/* ------------------------ TYPE DECLARATIONS ------------------------------ */ + +/** + * @brief Temporary memory block structure + */ +typedef struct uos_balloc_tmp_str { + uint64_t start; /**< Start address */ + uint64_t size; /**< Block size */ + bool used; /**< Used flag */ +} uos_balloc_tmp_t; + +/* ----------------------- FUNCTION DECLARATIONS --------------------------- */ + +/** + * @purpose + * Initialize the boot allocator. + * + * @note + * This function must be called before using any other functions. + */ +void uos_mm_balloc_init(void); + +/** + * @purpose + * Assign a block of free memory + * to the free memory list of the boot allocator. + * + * The memory must be fully mapped in kernel space, + * and the ASP must have all necessary information to convert + * physical to virtual addresses. + * + * This memory is immediately available for allocations at boot time. + * + * The block of free memory must not be added twice, + * or the boot allocator panics. + * + * @param phys_addr + * IN: Physical start address of memory block. + * @param size + * IN: Size of memory block. + */ +void uos_mm_balloc_assign_mem(uint64_t phys_addr, uint64_t size); + +/** + * @purpose + * Assign a block of temporarily used memory + * to the list of temporarily used memory + * for later use. + * + * The memory must be fully mapped in kernel space, + * and the ASP must have all necessary information to convert + * physical to virtual addresses. + * + * This memory becomes available for allocation in later phases, + * after a call to uos_mm_balloc_reclaim_tmp(). + * + * The block of free memory must not be added twice, + * or the boot allocator panics. + * + * The number of temporary entries in the boot allocator is limited + * to UOS_BALLOC_NUM_TMP == 4. If more entries are added, the kernel panics. + * + * @param phys_addr + * IN: Physical start address of memory block. + * @param size + * IN: Size of memory block. + */ +void uos_mm_balloc_assign_tmp(uint64_t phys_addr, uint64_t size); + +/** + * @purpose + * Allocate a memory block with given size and alignment + * from the boot allocator memory list. + * The allocation strategy here is "first fit". + * The allocated memory block is always aligned to a cache line. + * Also, the size of the allocated memory block is extended + * to the next multiple of a cache line. + * The size must be non-zero. + * The alignment must be a power of two. + * + * @param size + * IN: Requested size, must be >0 + * @param align + * IN: Requested alignment, must be a power of two + * + * @returns + * A pointer to the allocated memory block (virtual address), + * or the function panics if no suitable memory was found. + * Use uos_mm_balloc_aligned() if a not panicking behaviour is desired. + */ +void* uos_mm_balloc(uint64_t size, uint64_t align); + +/** + * @purpose + * Allocate a memory block with given size and alignment + * from the boot allocator memory list. + * The allocation strategy here is "first fit". + * The allocated memory block is always aligned to a cache line. + * Also, the size of the allocated memory block is extended + * to the next multiple of a cache line. + * The size must be non-zero. + * The alignment must be a power of two. + * + * @param size + * IN: Requested size, must be >0 + * @param align + * IN: Requested alignment, must be a power of two + * + * @returns + * A pointer to the allocated memory block (virtual address), + * or NULL if no suitable memory was found. + */ +void* uos_mm_balloc_aligned(uint64_t size, uint64_t align); + +/** + * @purpose + * Allocate a memory block with given physical address and size + * from the boot allocator memory list. + * The allocation strategy here is "exact fit". + * Also, the size of the allocated memory block is extended + * to the next multiple of a cache line. + * The size must be non-zero. + * + * @param phys_addr + * IN: Requested start address + * @param size + * IN: Requested size, must be >0 + * + * @returns + * A pointer to the allocated memory block (virtual address), + * or NULL if no suitable memory was found. + */ +void* uos_mm_balloc_phys(uint64_t phys_addr, uint64_t size); + +/** + * @purpose + * Allocate the first free memory block from the boot allocator memory list. + * This function is used to drain (empty) the boot allocator memory list + * by the runtime allocator. + * + * @param size + * OUT: Size of allocated memory block, must not be NULL + * + * @returns + * A pointer to the allocated memory block and its size in size, + * or NULL if no suitable memory was found (the memory list is empty). + */ +void* uos_mm_balloc_drain(uint64_t* size); + +/** + * @purpose + * Reclaim memory added via uos_mm_balloc_assign_tmp() + * and allocate a temporary memory block + * from the list of temporarily used memory. + * This function is used to drain (empty) the list of temporary memory + * by the runtime allocator. + * + * @param size + * OUT: Size of allocated memory block, must not be NULL + * + * @returns + * A pointer to the allocated memory block and its size in size, + * or NULL if no suitable memory was found (the memory list is empty). + */ +void* uos_mm_balloc_reclaim_tmp(uint64_t* size); + +#endif /* UOS_MM_BALLOC_H */ diff --git a/kernel/src/core/mm_kmem.cpp b/kernel/src/core/mm_kmem.cpp new file mode 100644 index 000000000..a8db64196 --- /dev/null +++ b/kernel/src/core/mm_kmem.cpp @@ -0,0 +1,80 @@ +/* -------------------------- FILE PROLOGUE -------------------------------- */ + +/** + * @file + * uos_mm_kmem.cpp + * + * @purpose + * Implementation of KMEM allocator for UniversalisOS. + * Based on PikeOS mm_kmem.h architecture. + */ + +/* ------------------------- FILE INCLUSION -------------------------------- */ + +#include "mm_kmem.h" + +/* ------------------------ STATIC VARIABLES ------------------------------- */ + +/** KMEM free lists for each partition */ +static uos_mm_list_t* mm_kmem_free_list[UOS_MAX_PARTITIONS]; + +/* ----------------------- FUNCTION IMPLEMENTATIONS ------------------------ */ + +/** + * @purpose + * For each partition, create KMEM data structures (still empty). + */ +void uos_mm_kmem_init(void) { + /* Allocate memory for KMEM free lists */ + for (int i = 0; i < UOS_MAX_PARTITIONS; i++) { + /* TODO: Allocate from boot allocator */ + mm_kmem_free_list[i] = (uos_mm_list_t*)uos_mm_balloc_aligned( + sizeof(uos_mm_list_t), UOS_CACHE_LINE_SIZE); + + if (mm_kmem_free_list[i] != NULL) { + uos_mm_list_init(mm_kmem_free_list[i]); + } + } +} + +/** + * @purpose + * Allocate KMEM from memory stores. + * For each partition, this function iterates the VMIT and allocates + * all KMEM memory requirements from the memory stores. + */ +void uos_mm_kmem_fill_all(void) { + /* TODO: Iterate VMIT and allocate KMEM for each partition */ + + /* For now, allocate some default KMEM for partition 0 */ + uint64_t default_kmem_size = 1024 * 1024; /* 1 MB */ + + /* Allocate from global store */ + void* kmem = uos_mm_ralloc_boot(default_kmem_size, UOS_PAGE_SIZE); + + if (kmem != NULL && mm_kmem_free_list[0] != NULL) { + uos_mm_list_assign(mm_kmem_free_list[0], (uint64_t)kmem, default_kmem_size); + } + + /* TODO: Handle respart0_pages configuration */ +} + +/** + * @purpose + * Allocates KMEM memory for the caller by a given size and alignment + * from the internal KMEM of the given partition. + */ +void* uos_mm_kmem_alloc(uint32_t rp_id, uint64_t size, uint64_t align) { + /* Validate partition ID */ + if (rp_id >= UOS_MAX_PARTITIONS) { + return NULL; + } + + /* Check if KMEM free list exists */ + if (mm_kmem_free_list[rp_id] == NULL) { + return NULL; + } + + /* Allocate from KMEM free list */ + return uos_mm_list_alloc_aligned(mm_kmem_free_list[rp_id], size, align, 0, 0); +} diff --git a/kernel/src/core/mm_kmem.h b/kernel/src/core/mm_kmem.h new file mode 100644 index 000000000..30721a96c --- /dev/null +++ b/kernel/src/core/mm_kmem.h @@ -0,0 +1,57 @@ +#ifndef UOS_MM_KMEM_H +#define UOS_MM_KMEM_H + +/* -------------------------- FILE PROLOGUE -------------------------------- */ + +/** + * @file + * uos_mm_kmem.h + * + * @purpose + * Early KMEM allocations at boot time. + * + * Based on PikeOS mm_kmem.h architecture. + */ + +/* ------------------------- FILE INCLUSION -------------------------------- */ + +#include "mm_list.h" +#include "mm_store.h" + +/* ----------------------- FUNCTION DECLARATIONS --------------------------- */ + +/** + * @purpose + * For each partition, create KMEM data structures (still empty). + */ +void uos_mm_kmem_init(void); + +/** + * @purpose + * Allocate KMEM from memory stores. + * For each partition, this function iterates the VMIT and allocates + * all KMEM memory requirements from the memory stores. + */ +void uos_mm_kmem_fill_all(void); + +/** + * @purpose + * Allocates KMEM memory for the caller by a given size and alignment + * from the internal KMEM of the given partition. + * The stores must have been initialized by + * uos_mm_store_init() before this function can be called. + * + * @param rp_id + * IN: Requested store. + * @param size + * IN: Requested memory size. + * @param align + * IN: Alignment request. + * + * @returns + * Pointer to the allocated block (virtual address), + * or NULL if the allocation fails. + */ +void* uos_mm_kmem_alloc(uint32_t rp_id, uint64_t size, uint64_t align); + +#endif /* UOS_MM_KMEM_H */ diff --git a/kernel/src/core/mm_list.cpp b/kernel/src/core/mm_list.cpp new file mode 100644 index 000000000..70f23baef --- /dev/null +++ b/kernel/src/core/mm_list.cpp @@ -0,0 +1,266 @@ +/* -------------------------- FILE PROLOGUE -------------------------------- */ + +/** + * @file + * uos_mm_list.cpp + * + * @purpose + * Implementation of memory list management for UniversalisOS. + * Based on PikeOS mm_list.h architecture. + */ + +/* ------------------------- FILE INCLUSION -------------------------------- */ + +#include "mm_list.h" + +/* ----------------------- FUNCTION IMPLEMENTATIONS ------------------------ */ + +/** + * @purpose + * Check if a virtual memory region overlaps with memory on the memory list. + */ +bool uos_mm_list_check_overlap(const uos_mm_list_t* ml, + uint64_t start, + uint64_t size) { + uos_mm_block_t* block; + uos_list_node_t* node; + + /* Iterate through all blocks in the list */ + for (node = ml->head.head.next; node != &ml->head.head; node = node->next) { + block = (uos_mm_block_t*)node; + + /* Check for overlap: [start, start+size) vs [block->start, block->start+block->size) */ + if (start < block->start + block->size && start + size > block->start) { + return true; + } + } + + return false; +} + +/** + * @purpose + * Add a free virtual memory block to the memory list. + * The free virtual memory block must not overlap with existing memory. + * Before adding, the memory block is aligned to multiple of cache line + * sizes. + */ +void uos_mm_list_assign(uos_mm_list_t* ml, + uint64_t start, + uint64_t size) { + uos_mm_block_t* block; + uos_mm_block_t* pos; + uos_list_node_t* node; + + /* Align to cache line */ + start = UOS_ALIGN_DOWN(start, UOS_CACHE_LINE_SIZE); + size = UOS_ALIGN_UP(size, UOS_CACHE_LINE_SIZE); + + /* Check for overlap */ + if (uos_mm_list_check_overlap(ml, start, size)) { + /* TODO: Handle overlap error - panic or log */ + return; + } + + /* Allocate block structure from static pool (TODO: use proper allocator) */ + static uos_mm_block_t block_pool[256]; + static uint32_t block_pool_index = 0; + + if (block_pool_index >= 256) { + /* TODO: Handle pool exhaustion */ + return; + } + + block = &block_pool[block_pool_index++]; + block->start = start; + block->size = size; + + /* Insert in sorted order by start address */ + for (node = ml->head.head.next; node != &ml->head.head; node = node->next) { + pos = (uos_mm_block_t*)node; + if (pos->start > start) { + /* Insert before pos */ + block->node.next = node; + block->node.prev = node->prev; + node->prev->next = &block->node; + node->prev = &block->node; + goto merge; + } + } + + /* Add to tail */ + block->node.next = &ml->head.head; + block->node.prev = ml->head.head.prev; + ml->head.head.prev->next = &block->node; + ml->head.head.prev = &block->node; + +merge: + /* Merge with next block if adjacent */ + if (block->node.next != &ml->head.head) { + uos_mm_block_t* next = (uos_mm_block_t*)block->node.next; + if (block->start + block->size == next->start) { + block->size += next->size; + next->node.prev->next = next->node.next; + next->node.next->prev = next->node.prev; + /* TODO: Free next block structure */ + } + } + + /* Merge with previous block if adjacent */ + if (block->node.prev != &ml->head.head) { + uos_mm_block_t* prev = (uos_mm_block_t*)block->node.prev; + if (prev->start + prev->size == block->start) { + prev->size += block->size; + block->node.prev->next = block->node.next; + block->node.next->prev = block->node.prev; + /* TODO: Free block structure */ + } + } +} + +/** + * @purpose + * Allocate a memory block with given size and alignment + * from the memory list. + * The allocation strategy here is "first fit". + */ +void* uos_mm_list_alloc_aligned(uos_mm_list_t* ml, + uint64_t size, + uint64_t align, + uint64_t destaddr, + uint64_t align_mask) { + uos_mm_block_t* block; + uos_list_node_t* node; + + /* Align size to cache line */ + size = UOS_ALIGN_UP(size, UOS_CACHE_LINE_SIZE); + + /* First fit allocation */ + for (node = ml->head.head.next; node != &ml->head.head; node = node->next) { + block = (uos_mm_block_t*)node; + + /* Calculate aligned start address */ + uint64_t aligned_start = UOS_ALIGN_UP(block->start, align); + + /* Apply cache aliasing mask if needed */ + if (align_mask != 0 && destaddr != 0) { + uint64_t mask_offset = (destaddr & align_mask) - (aligned_start & align_mask); + aligned_start += mask_offset; + } + + /* Check if block fits after alignment */ + if (aligned_start >= block->start && + aligned_start + size <= block->start + block->size) { + + /* Found suitable block */ + if (aligned_start == block->start && + aligned_start + size == block->start + block->size) { + /* Exact fit - remove block from list */ + block->node.prev->next = block->node.next; + block->node.next->prev = block->node.prev; + /* TODO: Free block structure */ + } else if (aligned_start == block->start) { + /* Allocate from beginning - shrink block */ + block->start = aligned_start + size; + block->size -= size; + } else if (aligned_start + size == block->start + block->size) { + /* Allocate from end - shrink block */ + block->size = aligned_start - block->start; + } else { + /* Allocate from middle - split block */ + /* TODO: Implement block splitting */ + /* For now, allocate from beginning */ + block->start = aligned_start + size; + block->size -= size; + } + + return (void*)aligned_start; + } + } + + return NULL; /* No suitable block found */ +} + +/** + * @purpose + * Allocate a memory block with given virtual address and size + * from the memory list. + * The allocation strategy here is "exact fit". + */ +void* uos_mm_list_alloc_by_addr(uos_mm_list_t* ml, + uint64_t start, + uint64_t size) { + uos_mm_block_t* block; + uos_list_node_t* node; + + /* Align size to cache line */ + size = UOS_ALIGN_UP(size, UOS_CACHE_LINE_SIZE); + + /* Find block containing the requested address */ + for (node = ml->head.head.next; node != &ml->head.head; node = node->next) { + block = (uos_mm_block_t*)node; + + /* Check if requested range is within this block */ + if (start >= block->start && + start + size <= block->start + block->size) { + + /* Found suitable block */ + if (start == block->start && size == block->size) { + /* Exact fit - remove block from list */ + block->node.prev->next = block->node.next; + block->node.next->prev = block->node.prev; + /* TODO: Free block structure */ + } else if (start == block->start) { + /* Allocate from beginning - shrink block */ + block->start = start + size; + block->size -= size; + } else if (start + size == block->start + block->size) { + /* Allocate from end - shrink block */ + block->size = start - block->start; + } else { + /* Allocate from middle - split block */ + /* TODO: Implement block splitting */ + /* For now, allocate from beginning */ + block->start = start + size; + block->size -= size; + } + + return (void*)start; + } + } + + return NULL; /* No suitable block found */ +} + +/** + * @purpose + * Allocate the first free memory block from the memory list. + * This function is used to drain (empty) a memory list + * by higher level allocators. + */ +void* uos_mm_list_drain(uos_mm_list_t* ml, uint64_t* size) { + uos_mm_block_t* block; + uos_list_node_t* node; + + /* Check if list is empty */ + if (ml->head.head.next == &ml->head.head) { + return NULL; + } + + /* Get first block */ + node = ml->head.head.next; + block = (uos_mm_block_t*)node; + + /* Remove from list */ + block->node.prev->next = block->node.next; + block->node.next->prev = block->node.prev; + + /* Return size */ + if (size != NULL) { + *size = block->size; + } + + /* TODO: Free block structure */ + + return (void*)block->start; +} diff --git a/kernel/src/core/mm_list.h b/kernel/src/core/mm_list.h new file mode 100644 index 000000000..95990fe81 --- /dev/null +++ b/kernel/src/core/mm_list.h @@ -0,0 +1,201 @@ +#ifndef UOS_MM_LIST_H +#define UOS_MM_LIST_H + +/* -------------------------- FILE PROLOGUE -------------------------------- */ + +/** + * @file + * uos_mm_list.h + * + * @purpose + * The module manages free virtual memory blocks in the kernel address space + * and provides low-level allocation functions. + * + * Based on PikeOS mm_list.h architecture. + */ + +/* ------------------------- FILE INCLUSION -------------------------------- */ + +#include +#include +#include + +/* ------------------- MACRO / CONSTANT DEFINITIONS ------------------------ */ + +/** Cache line size for alignment */ +#define UOS_CACHE_LINE_SIZE 64 + +/** Page size */ +#define UOS_PAGE_SIZE 4096 + +/** Alignment macros */ +#define UOS_ALIGN_DOWN(addr, align) ((addr) & ~((align) - 1)) +#define UOS_ALIGN_UP(addr, align) (((addr) + (align) - 1) & ~((align) - 1)) + +/* ------------------------ TYPE DECLARATIONS ------------------------------ */ + +/** + * @brief List node structure for linked list + */ +typedef struct uos_list_node_str { + struct uos_list_node_str* next; + struct uos_list_node_str* prev; +} uos_list_node_t; + +/** + * @brief List head structure + */ +typedef struct uos_list_str { + uos_list_node_t head; +} uos_list_t; + +/** + * @brief Memory block structure + * + * This structure represents a free memory block in the kernel address space. + */ +typedef struct uos_mm_block_str { + uos_list_node_t node; /**< List node */ + uint64_t start; /**< Start address of block */ + uint64_t size; /**< Size of block in bytes */ +} uos_mm_block_t; + +/** + * @brief Memory list root structure + * + * This data structure maintains a linked list of free virtual memory blocks + * in the kernel address space. + * + * The caller is responsible for locking. + */ +typedef struct uos_mm_list_str { + uos_list_t head; /**< Linked list of free blocks */ +} uos_mm_list_t; + +/* ----------------------- FUNCTION DECLARATIONS --------------------------- */ + +/** + * @purpose + * Initialize a memory list. + * + * @param ml + * INOUT: Memory list root. + */ +static inline void uos_mm_list_init(uos_mm_list_t* ml) { + ml->head.head.next = &ml->head.head; + ml->head.head.prev = &ml->head.head; +} + +/** + * @purpose + * Check if a virtual memory region overlaps with memory on the memory list. + * + * @param ml + * IN: Memory list root. + * @param start + * IN: Start address of memory block, must be >0 + * @param size + * IN: Size of memory block, must be >0 + * + * @returns + * true if the memory region given by start and size partly or fully + * overlaps with other memory regions on the memory list ml, + * false otherwise. + */ +bool uos_mm_list_check_overlap(const uos_mm_list_t* ml, + uint64_t start, + uint64_t size); + +/** + * @purpose + * Add a free virtual memory block to the memory list. + * The free virtual memory block must not overlap with existing memory. + * Before adding, the memory block is aligned to multiple of cache line + * sizes. + * + * @param ml + * INOUT: Memory list root. + * @param start + * IN: Start address of memory block, must be >0 + * @param size + * IN: Size of memory block to add, must be >0 + */ +void uos_mm_list_assign(uos_mm_list_t* ml, + uint64_t start, + uint64_t size); + +/** + * @purpose + * Allocate a memory block with given size and alignment + * from the memory list. + * The allocation strategy here is "first fit". + * The allocated memory block is always aligned to a cache line. + * Also, the size of the allocated memory block is extended + * to the next multiple of a cache line. + * The size must be non-zero. + * The alignment must be a power of two. + * + * @param ml + * INOUT: Memory list root. + * @param size + * IN: Requested size, must be >0 + * @param align + * IN: Requested alignment, must be a power of two + * @param destaddr + * IN: destination address in user space, used to prevent cache + * aliasing side effects; however, the alignment takes precedence + * @param align_mask + * IN: alignment mask for cache aliasing effects + * + * @returns + * A pointer to the allocated memory block (virtual address), + * or NULL if no suitable memory was found. + */ +void* uos_mm_list_alloc_aligned(uos_mm_list_t* ml, + uint64_t size, + uint64_t align, + uint64_t destaddr, + uint64_t align_mask); + +/** + * @purpose + * Allocate a memory block with given virtual address and size + * from the memory list. + * The allocation strategy here is "exact fit". + * Also, the size of the allocated memory block is extended + * to the next multiple of a cache line. + * The size must be non-zero. + * + * @param ml + * INOUT: Memory list root. + * @param start + * IN: Requested start address, must be >0 + * @param size + * IN: Requested size, must be >0 + * + * @returns + * A pointer to the allocated memory block (virtual address), + * or NULL if no suitable memory was found. + */ +void* uos_mm_list_alloc_by_addr(uos_mm_list_t* ml, + uint64_t start, + uint64_t size); + +/** + * @purpose + * Allocate the first free memory block from the memory list. + * This function is used to drain (empty) a memory list + * by higher level allocators. + * + * @param ml + * INOUT: Memory list root. + * @param size + * OUT: Size of allocated memory block, must not be NULL + * + * @returns + * A pointer to the allocated memory block and its size in size, + * or NULL if no suitable memory was found (the memory list is empty). + */ +void* uos_mm_list_drain(uos_mm_list_t* ml, uint64_t* size); + +#endif /* UOS_MM_LIST_H */