diff options
| author | Richard Qin <richard.qin.x@gmail.com> | 2026-04-21 23:22:52 +0800 |
|---|---|---|
| committer | Richard Qin <richard.qin.x@gmail.com> | 2026-04-21 23:22:52 +0800 |
| commit | ab2c0a8ea472679c846e74ca40823091becc5bf0 (patch) | |
| tree | eeb05d4f93dc6ab901a3a5a1562604f02ed9c885 /na/mm/vma.cc | |
| parent | 0a657596e19281f57ae74fd00e38cf25fee4d8cd (diff) | |
| download | lume-ab2c0a8ea472679c846e74ca40823091becc5bf0.tar.gz lume-ab2c0a8ea472679c846e74ca40823091becc5bf0.tar.xz | |
feat: add Virtual Memory Manager and Virtual Memory Area management
Diffstat (limited to 'na/mm/vma.cc')
| -rw-r--r-- | na/mm/vma.cc | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/na/mm/vma.cc b/na/mm/vma.cc new file mode 100644 index 0000000..d05a662 --- /dev/null +++ b/na/mm/vma.cc @@ -0,0 +1,181 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (C) 2026 Richard Qin */ + +/* + * vma.cc — Virtual Memory Area management (MI layer) + * + * Manages user address spaces using a red-black tree of VMA regions. + * Implements demand paging: vmm_map_user records intent only; + * physical pages are allocated on page fault via vmm_handle_page_fault. + * + * MI/MD separation: this file NEVER references pmap::PTE_* constants. + * Permission translation is delegated to pmap::vm_perm_to_pte(). + * + * Reference: docs/specs/vmm.md + */ + +#include <lume/vmm.h> +#include <lume/addr.h> +#include <lume/config.h> +#include <lume/errno.h> +#include <lume/pmm.h> +#include <lume/new.h> +#include <lume/panic.h> +#include <arch/pmap.h> + +/* ------------------------------------------------------------------ */ +/* Rbtree callbacks for VMA interval tree */ +/* ------------------------------------------------------------------ */ + +static bool vma_less(struct rb_node* node, const struct rb_node* parent) { + VmAreaStruct* n = reinterpret_cast<VmAreaStruct*>( + reinterpret_cast<char*>(node) - __builtin_offsetof(VmAreaStruct, rb)); + const VmAreaStruct* p = reinterpret_cast<const VmAreaStruct*>( + reinterpret_cast<const char*>(parent) - __builtin_offsetof(VmAreaStruct, rb)); + return n->start < p->start; +} + +static int vma_cmp(const void* key, const struct rb_node* node) { + uint64 addr = *static_cast<const uint64*>(key); + const VmAreaStruct* n = reinterpret_cast<const VmAreaStruct*>( + reinterpret_cast<const char*>(node) - __builtin_offsetof(VmAreaStruct, rb)); + if (addr < n->start) return -1; + if (addr >= n->end) return 1; + return 0; +} + +/* ------------------------------------------------------------------ */ +/* VmSpace lifecycle */ +/* ------------------------------------------------------------------ */ + +VmSpace* VmSpace::create() { + VmSpace* space = new VmSpace; + if (!space) return nullptr; + + space->root_pa = pmap::create(); + if (!space->root_pa) { + delete space; + return nullptr; + } + + space->vma_tree = RB_ROOT; + space->lock.init("VmSpace"); + return space; +} + +void VmSpace::destroy() { + /* Drain VMA tree under lock, then release BEFORE delete this */ + lock.acquire(); + struct rb_node* node = rb_first(&vma_tree); + while (node) { + VmAreaStruct* vma = reinterpret_cast<VmAreaStruct*>( + reinterpret_cast<char*>(node) - __builtin_offsetof(VmAreaStruct, rb)); + node = rb_next(node); + rb_erase(&vma->rb, &vma_tree); + delete vma; + } + uint64 root = root_pa; + lock.release(); + + /* Destroy hardware page tables outside the lock */ + pmap::destroy(root); + + /* Finally free VmSpace itself */ + delete this; +} + +/* ------------------------------------------------------------------ */ +/* Public API: VMA manipulation */ +/* ------------------------------------------------------------------ */ + +int vmm_map_user(VmSpace* space, uint64 va, uint64 len, uint64 perm) { + if (!space) return -EINVAL; + + va = page_align_down(va); + len = page_align_up(len); + if (len == 0) return 0; + + VmAreaStruct* vma = new VmAreaStruct; + if (!vma) return -ENOMEM; + vma->start = va; + vma->end = va + len; + vma->perm = perm | VM_USER; // Enforce user-mode access (MI flag) + vma->mm = space; + + LockGuard guard(space->lock); + rb_add(&vma->rb, &space->vma_tree, vma_less); + return 0; +} + +int vmm_unmap_user(VmSpace* space, uint64 va, uint64 len) { + if (!space) return -EINVAL; + va = page_align_down(va); + len = page_align_up(len); + uint64 end = va + len; + + LockGuard guard(space->lock); + struct rb_node* node = rb_first(&space->vma_tree); + while (node) { + VmAreaStruct* vma = reinterpret_cast<VmAreaStruct*>( + reinterpret_cast<char*>(node) - __builtin_offsetof(VmAreaStruct, rb)); + struct rb_node* nxt = rb_next(node); + if (vma->start >= va && vma->end <= end) { + rb_erase(node, &space->vma_tree); + delete vma; + } + node = nxt; + } + return 0; +} + +/* ------------------------------------------------------------------ */ +/* Page fault handler (demand paging) */ +/* ------------------------------------------------------------------ */ + +int vmm_handle_page_fault(VmSpace* space, uint64 fault_addr, uint64 cause) { + if (!space) return -EINVAL; + + uint64 aligned_addr = page_align_down(fault_addr); + + LockGuard guard(space->lock); + struct rb_node* node = rb_find(&fault_addr, &space->vma_tree, vma_cmp); + if (!node) { + return -EFAULT; // No VMA covers this address → segfault + } + + VmAreaStruct* vma = reinterpret_cast<VmAreaStruct*>( + reinterpret_cast<char*>(node) - __builtin_offsetof(VmAreaStruct, rb)); + + (void)cause; // TODO: differentiate read/write/exec faults + + /* Check if page is already mapped */ + uint64 paddr; + if (pmap::lookup(space->root_pa, aligned_addr, &paddr)) { + return 0; // Already mapped + } + + /* Allocate physical frame */ + Frame* f = pmm_alloc_frame(); + if (!f) return -ENOMEM; + + uint64 pa = frame_to_pa(f); + + /* Translate MI permission flags to hardware PTE bits (MD layer) */ + uint64 pte_perm = pmap::vm_perm_to_pte(vma->perm); + + if (pmap::map(space->root_pa, aligned_addr, pa, pte_perm) != 0) { + pmm_free_frame(f); + return -ENOMEM; + } + + return 0; +} + +/* ------------------------------------------------------------------ */ +/* Fork / Clone (stub — requires COW infrastructure) */ +/* ------------------------------------------------------------------ */ + +VmSpace* vmm_clone(VmSpace* parent) { + (void)parent; + return nullptr; +} |
