Allocation Backend
Relevant source files
This document covers the dynamic allocation backend implementation for guest memory management in axaddrspace. The allocation backend provides flexible memory allocation strategies with support for both eager and lazy allocation patterns. For information about the alternative linear mapping strategy, see Linear Backend. For broader context on how backends fit into the address space management system, see Memory Mapping Backends and Address Space Management.
Overview
The allocation backend implements dynamic memory management for guest physical memory regions, where physical frames are allocated from the host system either eagerly at mapping time or lazily on first access. This backend is implemented as part of the Backend<H> enum and provides three core operations: mapping, unmapping, and page fault handling.
Allocation Backend Architecture
flowchart TD
subgraph subGraph3["Page Table Operations"]
PT["PageTable<H>"]
PT_MAP["pt.map()"]
PT_UNMAP["pt.unmap()"]
PT_REMAP["pt.remap()"]
PT_MAP_REGION["pt.map_region()"]
end
subgraph subGraph2["Physical Memory Management"]
HAL_ALLOC["H::alloc_frame()"]
HAL_DEALLOC["H::dealloc_frame()"]
FRAME["HostPhysAddr"]
end
subgraph subGraph1["Allocation Operations"]
NEW["new_alloc(populate: bool)"]
MAP["map_alloc()"]
UNMAP["unmap_alloc()"]
PF["handle_page_fault_alloc()"]
end
subgraph subGraph0["Backend Selection"]
BE["Backend<H>"]
ALLOC_VAR["Backend::Alloc { populate, _phantom }"]
end
ALLOC_VAR --> MAP
ALLOC_VAR --> NEW
ALLOC_VAR --> PF
ALLOC_VAR --> UNMAP
BE --> ALLOC_VAR
HAL_ALLOC --> FRAME
HAL_DEALLOC --> FRAME
MAP --> HAL_ALLOC
MAP --> PT_MAP
MAP --> PT_MAP_REGION
PF --> HAL_ALLOC
PF --> PT_REMAP
UNMAP --> HAL_DEALLOC
UNMAP --> PT_UNMAP
Sources: src/address_space/backend/alloc.rs(L7 - L14)
Allocation Strategies
The allocation backend supports two distinct allocation strategies controlled by the populate boolean parameter:
| Strategy | Description | Physical Frame Allocation | Page Fault Behavior |
|---|---|---|---|
| Eager (populate=true) | Physical frames allocated at mapping time | Immediate allocation duringmap_alloc() | Should not occur |
| Lazy (populate=false) | Physical frames allocated on first access | Deferred until page fault | Allocates frame on demand |
Eager Allocation Strategy
When populate is true, the backend performs immediate physical frame allocation for all pages in the requested range:
Eager Allocation Flow
Sources: src/address_space/backend/alloc.rs(L31 - L41)
Lazy Allocation Strategy
When populate is false, the backend creates empty page table entries that will trigger page faults on first access:
Lazy Allocation Flow
Sources: src/address_space/backend/alloc.rs(L42 - L54) src/address_space/backend/alloc.rs(L79 - L96)
Implementation Details
Backend Creation
The allocation backend is created through the new_alloc() constructor method:
pub const fn new_alloc(populate: bool) -> Self {
Self::Alloc {
populate,
_phantom: core::marker::PhantomData,
}
}
The populate parameter determines the allocation strategy for the lifetime of this backend instance.
Sources: src/address_space/backend/alloc.rs(L8 - L14)
Memory Mapping Process
The map_alloc() method handles the mapping process differently based on the populate flag:
Mapping Implementation Structure
flowchart TD
subgraph subGraph1["Lazy Branch"]
MAP_REGION["pt.map_region(start, |_va| PhysAddr::from(0), size, empty_flags)"]
SUCCESS_LAZY["Return pt.map_region().is_ok()"]
end
subgraph subGraph0["Populate Branch"]
POPULATE_CHECK["populate == true?"]
PAGE_ITER["PageIter4K::new(start, start + size)"]
ALLOC_FRAME["H::alloc_frame()"]
PT_MAP["pt.map(addr, frame, Size4K, flags)"]
SUCCESS_EAGER["Return true"]
FAIL_EAGER["Return false"]
end
MAP_ALLOC["map_alloc(start, size, flags, pt, populate)"]
ALLOC_FRAME --> PT_MAP
MAP_ALLOC --> POPULATE_CHECK
MAP_REGION --> SUCCESS_LAZY
PAGE_ITER --> ALLOC_FRAME
POPULATE_CHECK --> MAP_REGION
POPULATE_CHECK --> PAGE_ITER
PT_MAP --> FAIL_EAGER
PT_MAP --> SUCCESS_EAGER
Sources: src/address_space/backend/alloc.rs(L16 - L54)
Memory Unmapping Process
The unmap_alloc() method handles cleanup by iterating through all pages and deallocating physical frames:
- Uses
PageIter4Kto iterate through all 4KB pages in the range - Calls
pt.unmap()to remove page table entries - Deallocates physical frames via
H::dealloc_frame()when mappings exist - Gracefully handles pages that are not mapped
- Rejects huge page mappings for safety
Sources: src/address_space/backend/alloc.rs(L56 - L77)
Page Fault Handling
The allocation backend implements lazy allocation through page fault handling in handle_page_fault_alloc():
Page Fault Resolution Logic
flowchart TD
subgraph subGraph1["Lazy Allocation"]
ALLOC_FRAME["H::alloc_frame()"]
PT_REMAP["pt.remap(vaddr, frame, orig_flags)"]
SUCCESS_LAZY["Return success"]
end
subgraph subGraph0["Eager Allocation Error"]
ERROR_POPULATED["Return false"]
ERROR_NOTE["Populated mappings should not fault"]
end
PF_START["handle_page_fault_alloc(vaddr, orig_flags, pt, populate)"]
POPULATE_CHECK["populate == true?"]
ALLOC_FRAME --> PT_REMAP
ERROR_POPULATED --> ERROR_NOTE
PF_START --> POPULATE_CHECK
POPULATE_CHECK --> ALLOC_FRAME
POPULATE_CHECK --> ERROR_POPULATED
PT_REMAP --> SUCCESS_LAZY
Key characteristics:
- Eager allocation faults: Return false since populated mappings should never fault
- Lazy allocation faults: Allocate physical frame and remap the faulting virtual address
- Address alignment: The
pt.remap()method automatically handles address alignment
Sources: src/address_space/backend/alloc.rs(L79 - L96)
Memory Management Lifecycle
The allocation backend integrates with the hardware abstraction layer for physical memory management:
- Frame Allocation: Uses
H::alloc_frame()to obtainHostPhysAddrvalues - Page Table Integration: Works with
PageTable<H>for virtual-to-physical mappings - Frame Deallocation: Uses
H::dealloc_frame()during unmapping operations - Size Constraints: Currently supports only 4KB pages, rejecting huge page operations
The backend ensures proper resource management through RAII principles and explicit deallocation during unmapping operations.