/* * ELSA OS - Virtual Memory Manager * Temporal Epistemic Separation Kernel v0.4 * Phase 1: Paging + Ring3 + APIC + TSC/HPET * 2026-05-16 */ #include #include #include #include #include "vmm.h" #include "../arch/x86_64/paging.h" /* ============================================================ * FORWARD DECLARATIONS (internal helpers) * ============================================================ */ static kernel_pt_t *vmm_alloc_page_table(void); static void vmm_free_page_table_recursive(void *table, int level); static uintptr_t vmm_translate_virtual(address_space_t *as, uintptr_t virt); static pid_t vmm_allocate_pid(void); static uintptr_t vmm_alloc_physical_page(void); static void vmm_free_physical_page(uintptr_t phys); static void vmm_oom_kill(void); static void vmm_fatal_fault(uintptr_t addr, uint64_t error_code); /* ============================================================ * GLOBAL STATE * ============================================================ */ static kernel_page_table_t *kernel_pml4 = NULL; static bitmap_t *kernel_vmm_bitmap = NULL; static uintptr_t kernel_heap_start = 0; static uintptr_t kernel_heap_end = 0; static size_t kernel_heap_used = 0; /* ============================================================ * PML4 INITIALIZATION * ============================================================ */ int vmm_init(uintptr_t phys_mem_start, size_t phys_mem_size, uintptr_t kernel_virt_base) { /* Allocate PML4 (4KB aligned) */ kernel_pml4 = (kernel_page_table_t *)phys_mem_start; memset(kernel_pml4, 0, sizeof(kernel_page_table_t)); /* Identity map kernel region */ uintptr_t kernel_phys_start = phys_mem_start + 0x1000; /* after PML4 */ size_t kernel_size = 0x200000; /* 2MB initial kernel mapping */ for (size_t i = 0; i < kernel_size; i += PAGE_SIZE_2MB) { vmm_map_page(kernel_pml4, kernel_virt_base + i, kernel_phys_start + i, PAGE_FLAG_PRESENT | PAGE_FLAG_WRITE | PAGE_FLAG_HUGE); } /* Map higher-half kernel space */ kernel_heap_start = HIGHER_HALF_BASE; kernel_heap_end = HIGHER_HALF_BASE + (phys_mem_size / 2); kernel_heap_used = 0; /* Initialize VMM bitmap for virtual address tracking */ size_t bitmap_size = (phys_mem_size / PAGE_SIZE) / 8; kernel_vmm_bitmap = (bitmap_t *)(kernel_phys_start + kernel_size); memset(kernel_vmm_bitmap, 0, bitmap_size); /* Load CR3 with PML4 physical address */ paging_load_cr3((uintptr_t)kernel_pml4); /* Enable paging: set PG and WP bits in CR0 */ paging_enable_paging(); return 0; } /* ============================================================ * PAGE MAPPING * ============================================================ */ int vmm_map_page(kernel_page_table_t *pml4, uintptr_t virt, uintptr_t phys, uint64_t flags) { if (!pml4) return -1; uint64_t pml4_idx = (virt >> 39) & 0x1FF; uint64_t pdp_idx = (virt >> 30) & 0x1FF; uint64_t pd_idx = (virt >> 21) & 0x1FF; uint64_t pt_idx = (virt >> 12) & 0x1FF; /* PML4 entry */ kernel_pdp_t *pdp = NULL; if (!(pml4->entries[pml4_idx] & PAGE_FLAG_PRESENT)) { pdp = vmm_alloc_page_table(); if (!pdp) return -1; memset(pdp, 0, sizeof(kernel_pdp_t)); pml4->entries[pml4_idx] = (uintptr_t)pdp | PAGE_FLAG_PRESENT | PAGE_FLAG_WRITE | PAGE_FLAG_USER; } else { pdp = (kernel_pdp_t *)(pml4->entries[pml4_idx] & PAGE_ADDR_MASK); } /* PDP entry */ kernel_pd_t *pd = NULL; if (!(pdp->entries[pdp_idx] & PAGE_FLAG_PRESENT)) { pd = vmm_alloc_page_table(); if (!pd) return -1; memset(pd, 0, sizeof(kernel_pd_t)); pdp->entries[pdp_idx] = (uintptr_t)pd | PAGE_FLAG_PRESENT | PAGE_FLAG_WRITE | PAGE_FLAG_USER; } else { pd = (kernel_pd_t *)(pdp->entries[pdp_idx] & PAGE_ADDR_MASK); } /* PD entry - check for 2MB huge page */ if (flags & PAGE_FLAG_HUGE) { pd->entries[pd_idx] = (phys & PAGE_ADDR_MASK_2MB) | flags; paging_invalidate_tlb(virt); return 0; } /* PT entry */ kernel_pt_t *pt = NULL; if (!(pd->entries[pd_idx] & PAGE_FLAG_PRESENT)) { pt = vmm_alloc_page_table(); if (!pt) return -1; memset(pt, 0, sizeof(kernel_pt_t)); pd->entries[pd_idx] = (uintptr_t)pt | PAGE_FLAG_PRESENT | PAGE_FLAG_WRITE | PAGE_FLAG_USER; } else { pt = (kernel_pt_t *)(pd->entries[pd_idx] & PAGE_ADDR_MASK); } pt->entries[pt_idx] = (phys & PAGE_ADDR_MASK) | flags; paging_invalidate_tlb(virt); return 0; } int vmm_unmap_page(kernel_page_table_t *pml4, uintptr_t virt) { if (!pml4) return -1; uint64_t pml4_idx = (virt >> 39) & 0x1FF; uint64_t pdp_idx = (virt >> 30) & 0x1FF; uint64_t pd_idx = (virt >> 21) & 0x1FF; uint64_t pt_idx = (virt >> 12) & 0x1FF; kernel_pdp_t *pdp = (kernel_pdp_t *)(pml4->entries[pml4_idx] & PAGE_ADDR_MASK); if (!pdp || !(pml4->entries[pml4_idx] & PAGE_FLAG_PRESENT)) return -1; kernel_pd_t *pd = (kernel_pd_t *)(pdp->entries[pdp_idx] & PAGE_ADDR_MASK); if (!pd || !(pdp->entries[pdp_idx] & PAGE_FLAG_PRESENT)) return -1; kernel_pt_t *pt = (kernel_pt_t *)(pd->entries[pd_idx] & PAGE_ADDR_MASK); if (!pt || !(pd->entries[pd_idx] & PAGE_FLAG_PRESENT)) return -1; pt->entries[pt_idx] = 0; paging_invalidate_tlb(virt); return 0; } /* ============================================================ * ADDRESS SPACE MANAGEMENT * ============================================================ */ address_space_t *vmm_create_address_space(void) { address_space_t *as = (address_space_t *)vmm_kernel_alloc(sizeof(address_space_t)); if (!as) return NULL; as->pml4 = (kernel_page_table_t *)vmm_alloc_page_table(); if (!as->pml4) { vmm_kernel_free(as, sizeof(address_space_t)); return NULL; } memset(as->pml4, 0, sizeof(kernel_page_table_t)); as->base_virt = USER_SPACE_BASE; as->size = USER_SPACE_SIZE; as->pid = vmm_allocate_pid(); as->refcount = 1; return as; } int vmm_switch_address_space(address_space_t *as) { if (!as || !as->pml4) return -1; paging_load_cr3((uintptr_t)as->pml4); return 0; } void vmm_destroy_address_space(address_space_t *as) { if (!as) return; as->refcount--; if (as->refcount > 0) return; /* Free all page tables recursively */ vmm_free_page_table_recursive(as->pml4, 4); vmm_kernel_free(as, sizeof(address_space_t)); } /* ============================================================ * COPY-ON-WRITE * ============================================================ */ int vmm_copy_on_write(address_space_t *src, address_space_t *dst, uintptr_t virt, size_t size) { for (uintptr_t v = virt; v < virt + size; v += PAGE_SIZE) { uint64_t phys = vmm_translate_virtual(src, v); if (phys == 0) continue; /* Map as read-only in both spaces */ vmm_map_page(dst->pml4, v, phys, PAGE_FLAG_PRESENT | PAGE_FLAG_USER); vmm_map_page(src->pml4, v, phys, PAGE_FLAG_PRESENT | PAGE_FLAG_USER); } return 0; } /* ============================================================ * PAGE FAULT HANDLER * ============================================================ */ void vmm_page_fault_handler(uintptr_t fault_addr, uint64_t error_code) { bool present = (error_code & 0x1) != 0; bool write = (error_code & 0x2) != 0; bool user = (error_code & 0x4) != 0; bool reserved = (error_code & 0x8) != 0; bool exec = (error_code & 0x10) != 0; /* Copy-on-write fault */ if (present && write) { /* Allocate new page, copy data, update mapping */ uintptr_t new_phys = vmm_alloc_physical_page(); if (new_phys == 0) { vmm_oom_kill(); return; } /* TODO: copy old page content to new page */ /* TODO: update PTE to point to new_phys with write flag */ return; } /* Demand paging */ if (!present && !reserved) { /* TODO: load page from swap/disk */ return; } /* Fatal fault */ vmm_fatal_fault(fault_addr, error_code); } /* ============================================================ * KERNEL ALLOCATOR * ============================================================ */ void *vmm_kernel_alloc(size_t size) { size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); if (kernel_heap_used + size > (kernel_heap_end - kernel_heap_start)) { return NULL; /* OOM */ } void *ptr = (void *)(kernel_heap_start + kernel_heap_used); kernel_heap_used += size; /* Zero the allocation */ memset(ptr, 0, size); return ptr; } void vmm_kernel_free(void *ptr, size_t size) { /* Simple bump allocator - no individual free */ (void)ptr; (void)size; } /* ============================================================ * INTERNAL HELPERS * ============================================================ */ static kernel_pt_t *vmm_alloc_page_table(void) { uintptr_t phys = vmm_alloc_physical_page(); if (phys == 0) return NULL; return (kernel_pt_t *)phys; } static void vmm_free_page_table_recursive(void *table, int level) { if (!table || level <= 0) return; uint64_t *entries = (uint64_t *)table; for (int i = 0; i < 512; i++) { if (entries[i] & PAGE_FLAG_PRESENT) { void *child = (void *)(entries[i] & PAGE_ADDR_MASK); if (level > 1) { vmm_free_page_table_recursive(child, level - 1); } } } vmm_free_physical_page((uintptr_t)table); } static uintptr_t vmm_translate_virtual(address_space_t *as, uintptr_t virt) { if (!as || !as->pml4) return 0; uint64_t pml4_idx = (virt >> 39) & 0x1FF; uint64_t pdp_idx = (virt >> 30) & 0x1FF; uint64_t pd_idx = (virt >> 21) & 0x1FF; uint64_t pt_idx = (virt >> 12) & 0x1FF; kernel_pdp_t *pdp = (kernel_pdp_t *)(as->pml4->entries[pml4_idx] & PAGE_ADDR_MASK); if (!pdp) return 0; kernel_pd_t *pd = (kernel_pd_t *)(pdp->entries[pdp_idx] & PAGE_ADDR_MASK); if (!pd) return 0; /* Check for huge page */ if (pd->entries[pd_idx] & PAGE_FLAG_HUGE) { return (pd->entries[pd_idx] & PAGE_ADDR_MASK_2MB) | (virt & 0x1FFFFF); } kernel_pt_t *pt = (kernel_pt_t *)(pd->entries[pd_idx] & PAGE_ADDR_MASK); if (!pt) return 0; return (pt->entries[pt_idx] & PAGE_ADDR_MASK) | (virt & 0xFFF); } static pid_t vmm_allocate_pid(void) { static pid_t next_pid = 1; return next_pid++; } static uintptr_t vmm_alloc_physical_page(void) { /* TODO: integrate with physical memory manager */ static uintptr_t next_phys = 0x100000; /* 1MB */ uintptr_t page = next_phys; next_phys += PAGE_SIZE; return page; } static void vmm_free_physical_page(uintptr_t phys) { /* TODO: return page to physical memory manager */ (void)phys; } static void vmm_oom_kill(void) { /* ELSA OS: ABSTAIN - no execution under OOM */ /* TODO: invoke temporal decision kernel for OOM handling */ while (1) { __asm__ volatile("hlt"); } } static void vmm_fatal_fault(uintptr_t addr, uint64_t error_code) { /* ELSA OS: ABSTAIN - no execution under fatal fault */ /* TODO: log to AuditChain, halt core */ (void)addr; (void)error_code; while (1) { __asm__ volatile("hlt"); } }