feat(kernel/mm): buddy allocator + kmem slab + list allocator with integration plan
This commit is contained in:
parent
24b3b5529c
commit
300acc7ec3
9 changed files with 1769 additions and 1 deletions
491
docs/MEMORY_ALLOCATOR_PLAN.md
Normal file
491
docs/MEMORY_ALLOCATOR_PLAN.md
Normal 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*
|
||||||
|
|
@ -535,4 +535,272 @@ extern "C" int mm_set_accountable_limit(uint64_t limit) {
|
||||||
|
|
||||||
extern "C" uint64_t mm_get_accountable_usage(void) {
|
extern "C" uint64_t mm_get_accountable_usage(void) {
|
||||||
return 0;
|
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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -332,6 +332,20 @@ uint32_t mm_get_map_count(void);
|
||||||
int mm_set_accountable_limit(uint64_t limit);
|
int mm_set_accountable_limit(uint64_t limit);
|
||||||
uint64_t mm_get_accountable_usage(void);
|
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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
199
kernel/src/core/mm_balloc.cpp
Normal file
199
kernel/src/core/mm_balloc.cpp
Normal 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
192
kernel/src/core/mm_balloc.h
Normal 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 */
|
||||||
80
kernel/src/core/mm_kmem.cpp
Normal file
80
kernel/src/core/mm_kmem.cpp
Normal 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
57
kernel/src/core/mm_kmem.h
Normal 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
266
kernel/src/core/mm_list.cpp
Normal 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
201
kernel/src/core/mm_list.h
Normal 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 */
|
||||||
Loading…
Reference in a new issue