feat(kernel/mm): buddy allocator + kmem slab + list allocator with integration plan

This commit is contained in:
Fábio Coutada 2026-07-12 21:06:27 +01:00
parent 24b3b5529c
commit 300acc7ec3
9 changed files with 1769 additions and 1 deletions

View file

@ -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*

View file

@ -536,3 +536,271 @@ extern "C" int mm_set_accountable_limit(uint64_t limit) {
extern "C" uint64_t mm_get_accountable_usage(void) {
return 0;
}
/* ============================================================================
* 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;
}

View file

@ -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

View file

@ -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 */
}

192
kernel/src/core/mm_balloc.h Normal file
View file

@ -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 */

View file

@ -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);
}

57
kernel/src/core/mm_kmem.h Normal file
View file

@ -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 */

266
kernel/src/core/mm_list.cpp Normal file
View file

@ -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;
}

201
kernel/src/core/mm_list.h Normal file
View file

@ -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 <stdint.h>
#include <stdbool.h>
#include <stddef.h>
/* ------------------- 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 */