Memory Management

Physical & virtual allocators, address space management, heap providers, and error handling.

Profile Reference

Memory Management

The helix-memory crate provides the entire memory stack — physical frame allocation, virtual address mapping, heap allocators, and address space management. Every layer is trait-based, so you can swap out any allocator without touching the rest of the kernel.

Memory is the first thing initialized after boot (stages 3-6), and every other subsystem depends on it. Understanding this crate is fundamental to working with Helix.

Memory Stack5N · 5E
frames → pagesmapped → usablealloc → trackzone classifyzone-aware mapPhysical Frame AllocatorManages physical RAM…2Virtual Memory MapperPage tables & addres…3Kernel HeapDynamic allocation (…2Address Space ManagerPer-process virtual …1Memory ZonesDMA, DMA32, Normal, …2
100%
☝ Drag to pan·🤏 Pinch to zoom·Tap a node

Physical Allocator Trait

The physical allocator manages raw hardware memory frames. Helix provides four implementations with different tradeoffs — you pick the right one in your profile's helix.toml.

subsystems/memory/src/physical/mod.rs
rust
pub trait PhysicalAllocator: Send + Sync {
fn name(&self) -> &'static str;
fn init(&mut self, regions: &[PhysicalRegion]) -> MemResult<()>;
fn allocate(&self, size: PageSize) -> MemResult<Frame>;
fn allocate_contiguous(&self, count: usize, size: PageSize) -> MemResult<Frame>;
fn allocate_zone(&self, size: PageSize, zone: MemoryZone) -> MemResult<Frame>;
fn deallocate(&self, frame: Frame) -> MemResult<()>;
fn free_frames(&self) -> usize;
fn total_frames(&self) -> usize;
fn stats(&self) -> AllocatorStats;
11
}
12
5 refs
pub struct Frame {
14
address: PhysAddr, // Private fields, accessed via methods
15
size: PageSize,
16
}
17
5 refs
pub enum PageSize { Size4KiB, Size2MiB, Size1GiB }
2 refs
pub enum MemoryZone { Dma, Dma32, Normal, High, Device }
6 refs
pub type MemResult<T> = Result<T, MemError>;
Index

Provided Allocators

Each allocator is designed for a specific phase of kernel operation. The profile config controls which one is active.

AllocatorTime ComplexitySupports FreeBest For
BumpAllocatorO(1) alloc❌ NoEarly boot (stages 1-3) — fast, no-free temporary heap
BitmapAllocatorO(n) worst case✅ YesSimple general-purpose — one bit per frame
BuddyAllocatorO(log n)✅ YesProduction — efficient power-of-two splitting
SlabAllocatorO(1) amortized✅ YesFixed-size kernel objects (TCBs, inodes, buffers)

Virtual Memory Mapper

The virtual mapper creates the mapping between virtual addresses (what the CPU sees) and physical frames (actual RAM). It works hand-in-hand with the HAL's page table trait.

subsystems/memory/src/virtual_memory/mod.rs
rust
1
bitflags! {
4 refs
pub struct PageFlags: u64 {
3
const PRESENT = 1 << 0;
4
const WRITABLE = 1 << 1;
5
const USER = 1 << 2;
6
const WRITE_THROUGH = 1 << 3;
7
const NO_CACHE = 1 << 4;
8
const ACCESSED = 1 << 5;
9
const DIRTY = 1 << 6;
10
const HUGE = 1 << 7;
11
const GLOBAL = 1 << 8;
12
const NO_EXECUTE = 1 << 63;
13
}
14
}
15
4 refs
impl PageFlags {
17
pub const KERNEL_READ: Self; // PRESENT
18
pub const KERNEL_WRITE: Self; // PRESENT | WRITABLE
19
pub const KERNEL_CODE: Self; // PRESENT
20
pub const USER_READ: Self; // PRESENT | USER
21
pub const USER_WRITE: Self; // PRESENT | USER | WRITABLE
22
pub const USER_CODE: Self; // PRESENT | USER
23
}
24
pub trait VirtualMapper: Send + Sync {
fn map(&self, page: Page, frame: Frame, flags: PageFlags) -> MemResult<()>;
fn unmap(&self, page: Page) -> MemResult<Frame>;
fn update_flags(&self, page: Page, flags: PageFlags) -> MemResult<()>;
fn translate(&self, virt: VirtAddr) -> Option<PhysAddr>;
2 refs
fn flush(&self, page: Page); // Invalidate single TLB entry
fn flush_all(&self); // Full TLB flush (expensive)
32
}
Index

Address Space Management

Each process gets its own AddressSpace that tracks all virtual memory regions. The address space provides POSIX-compatible memory operations — mmap, munmap, mprotect, and brk.

subsystems/memory/src/virtual_memory/address_space.rs
rust
2 refs
pub struct AddressSpace {
2
id: AddressSpaceId,
3
regions: RwLock<BTreeMap<u64, VmRegion>>,
4
mapper: Arc<dyn VirtualMapper>,
5
user_start: VirtAddr,
6
user_end: VirtAddr,
7
brk: RwLock<VirtAddr>,
8
}
9
2 refs
impl AddressSpace {
pub fn new(id: AddressSpaceId, mapper: Arc<dyn VirtualMapper>,
12
user_start: VirtAddr, user_end: VirtAddr) -> Self;
pub fn add_region(&self, region: VmRegion) -> MemResult<()>;
pub fn remove_region(&self, start: VirtAddr) -> MemResult<VmRegion>;
pub fn find_region(&self, addr: VirtAddr) -> Option<VmRegion>;
16
17
// POSIX-compatible memory operations
pub fn mmap_anonymous(&self, hint: Option<VirtAddr>,
19
size: u64, flags: PageFlags) -> MemResult<VirtAddr>;
pub fn munmap(&self, addr: VirtAddr, size: u64) -> MemResult<()>;
pub fn mprotect(&self, addr: VirtAddr, size: u64,
22
flags: PageFlags) -> MemResult<()>;
2 refs
pub fn brk(&self, new_brk: Option<VirtAddr>) -> VirtAddr;
24
}
25
5 refs
pub struct VmRegion {
27
pub start: VirtAddr,
28
pub size: u64,
29
pub flags: PageFlags,
30
pub region_type: VmRegionType,
31
}
32
2 refs
pub enum VmRegionType {
34
Anonymous, File, Device, Kernel, Guard, Reserved,
35
}
Index

Heap Allocator Trait

The kernel heap is what alloc::vec!, Box::new(), and all dynamic allocations use. The GlobalHeap struct wraps a dyn HeapAllocator and registers itself as Rust's #[global_allocator].

subsystems/memory/src/allocator/mod.rs
rust
2 refs
pub trait HeapAllocator: Send + Sync {
fn allocate(&self, layout: Layout) -> *mut u8;
unsafe fn deallocate(&self, ptr: *mut u8, layout: Layout);
unsafe fn reallocate(&self, ptr: *mut u8, old_layout: Layout,
5
new_size: usize) -> *mut u8;
fn name(&self) -> &'static str;
fn stats(&self) -> HeapStats;
8
}
9
2 refs
pub struct HeapStats {
11
pub total_size: usize,
12
pub used_size: usize,
13
pub free_size: usize,
14
pub allocations: u64,
15
pub deallocations: u64,
16
}
17
18
// Early boot bump heap — no free, just for initialization
pub struct BumpHeap {
20
start: usize,
21
end: usize,
22
next: Mutex<usize>, // spin::Mutex, not AtomicUsize
23
}
24
25
// Production: the GlobalHeap wraps any HeapAllocator
26
// and implements core::alloc::GlobalAlloc
27
#[global_allocator]
static HEAP: GlobalHeap = GlobalHeap::new();
Index

Memory Errors

Every memory operation returns MemResult<T>. These errors are descriptive enough for the self-healing system to decide on recovery strategies.

ErrorDescriptionRecovery
OutOfMemoryNo frames or pages availableTrigger memory pressure event
InvalidAddressAddress outside valid rangeReturn error to caller
NotMappedVirtual address has no mappingPage fault handler
AlreadyMappedPage already has a mappingUnmap first, then remap
InvalidAlignmentAddress not aligned to page boundaryCaller bug — align up
PermissionDeniedInsufficient capability rightsCheck capability token

During early boot (stages 1-3), the BumpAllocator provides a 4MB static heap. It's blazing fast (O(1) allocation) but never frees — perfect for the temporary allocations needed to get the full memory subsystem running. Once the buddy allocator is ready, the bump allocator is retired.