From: Tulio A M Mendes Date: Sat, 7 Feb 2026 21:50:10 +0000 (-0300) Subject: mm: add per-process address spaces (x86) X-Git-Url: https://projects.tadryanom.me/sitemap.xml?a=commitdiff_plain;h=07914d214cc9e2ef5ca2ffc9e063d0208f8cd645;p=AdrOS.git mm: add per-process address spaces (x86) --- diff --git a/include/elf.h b/include/elf.h index 55d998a..b811b08 100644 --- a/include/elf.h +++ b/include/elf.h @@ -51,6 +51,6 @@ typedef struct { #define PF_W 0x2 #define PF_R 0x4 -int elf32_load_user_from_initrd(const char* filename, uintptr_t* entry_out, uintptr_t* user_stack_top_out); +int elf32_load_user_from_initrd(const char* filename, uintptr_t* entry_out, uintptr_t* user_stack_top_out, uintptr_t* addr_space_out); #endif diff --git a/include/hal/cpu.h b/include/hal/cpu.h index 776ac6a..c7812b0 100644 --- a/include/hal/cpu.h +++ b/include/hal/cpu.h @@ -5,6 +5,7 @@ uintptr_t hal_cpu_get_stack_pointer(void); uintptr_t hal_cpu_get_address_space(void); +void hal_cpu_set_address_space(uintptr_t as); void hal_cpu_set_kernel_stack(uintptr_t sp_top); diff --git a/include/vmm.h b/include/vmm.h index 2a3ad84..852169a 100644 --- a/include/vmm.h +++ b/include/vmm.h @@ -22,6 +22,11 @@ void vmm_init(void); */ void vmm_map_page(uint64_t phys, uint64_t virt, uint32_t flags); +uintptr_t vmm_as_create_kernel_clone(void); +void vmm_as_destroy(uintptr_t as); +void vmm_as_activate(uintptr_t as); +void vmm_as_map_page(uintptr_t as, uint64_t phys, uint64_t virt, uint32_t flags); + /* * Update flags for an already-mapped virtual page. * Keeps the physical frame, only changes PRESENT/RW/USER bits. diff --git a/src/arch/x86/arch_platform.c b/src/arch/x86/arch_platform.c index 757356e..98fb466 100644 --- a/src/arch/x86/arch_platform.c +++ b/src/arch/x86/arch_platform.c @@ -10,6 +10,8 @@ #include "vga_console.h" #include "vmm.h" +#include "process.h" + #include "hal/cpu.h" #include "hal/usermode.h" @@ -21,6 +23,48 @@ extern void x86_usermode_test_start(void); static uint8_t ring0_trap_stack[16384] __attribute__((aligned(16))); #endif +#if defined(__i386__) +static void userspace_init_thread(void) { + if (!fs_root) { + uart_print("[ELF] fs_root missing\n"); + process_exit_notify(1); + schedule(); + for (;;) hal_cpu_idle(); + } + + uintptr_t entry = 0; + uintptr_t user_sp = 0; + uintptr_t user_as = 0; + if (elf32_load_user_from_initrd("/bin/init.elf", &entry, &user_sp, &user_as) != 0) { + process_exit_notify(1); + schedule(); + for (;;) hal_cpu_idle(); + } + + current_process->addr_space = user_as; + vmm_as_activate(user_as); + + uart_print("[ELF] starting /bin/init.elf\n"); + + uart_print("[ELF] user_range_ok(entry)="); + uart_put_char(user_range_ok((const void*)entry, 1) ? '1' : '0'); + uart_print(" user_range_ok(stack)="); + uart_put_char(user_range_ok((const void*)(user_sp - 16), 16) ? '1' : '0'); + uart_print("\n"); + + hal_cpu_set_kernel_stack((uintptr_t)&ring0_trap_stack[sizeof(ring0_trap_stack)]); + + if (hal_usermode_enter(entry, user_sp) < 0) { + uart_print("[USER] usermode enter not supported on this architecture.\n"); + process_exit_notify(1); + schedule(); + for (;;) hal_cpu_idle(); + } + + for (;;) hal_cpu_idle(); +} +#endif + int arch_platform_setup(const struct boot_info* bi) { (void)bi; #if defined(__i386__) @@ -42,29 +86,8 @@ int arch_platform_setup(const struct boot_info* bi) { int arch_platform_start_userspace(const struct boot_info* bi) { (void)bi; #if defined(__i386__) - if (!fs_root) return -1; - - uintptr_t entry = 0; - uintptr_t user_sp = 0; - if (elf32_load_user_from_initrd("/bin/init.elf", &entry, &user_sp) != 0) { - return -1; - } - - uart_print("[ELF] starting /bin/init.elf\n"); - - uart_print("[ELF] user_range_ok(entry)="); - uart_put_char(user_range_ok((const void*)entry, 1) ? '1' : '0'); - uart_print(" user_range_ok(stack)="); - uart_put_char(user_range_ok((const void*)(user_sp - 16), 16) ? '1' : '0'); - uart_print("\n"); - - hal_cpu_set_kernel_stack((uintptr_t)&ring0_trap_stack[sizeof(ring0_trap_stack)]); - - if (hal_usermode_enter(entry, user_sp) < 0) { - uart_print("[USER] usermode enter not supported on this architecture.\n"); - return -1; - } - + struct process* p = process_create_kernel(userspace_init_thread); + if (!p) return -1; return 0; #else return -1; diff --git a/src/arch/x86/vmm.c b/src/arch/x86/vmm.c index b0b85b1..f3aa87e 100644 --- a/src/arch/x86/vmm.c +++ b/src/arch/x86/vmm.c @@ -1,6 +1,7 @@ #include "vmm.h" #include "pmm.h" #include "uart_console.h" +#include "hal/cpu.h" #include /* Constants */ @@ -21,6 +22,25 @@ So accessing boot_pd directly works fine! */ extern uint32_t boot_pd[1024]; +static uintptr_t g_kernel_as = 0; + +static inline void invlpg(uintptr_t vaddr) { + __asm__ volatile("invlpg (%0)" : : "r" (vaddr) : "memory"); +} + +static uint32_t vmm_flags_to_x86(uint32_t flags) { + uint32_t x86_flags = 0; + if (flags & VMM_FLAG_PRESENT) x86_flags |= X86_PTE_PRESENT; + if (flags & VMM_FLAG_RW) x86_flags |= X86_PTE_RW; + if (flags & VMM_FLAG_USER) x86_flags |= X86_PTE_USER; + return x86_flags; +} + +static uint32_t* vmm_active_pd_virt(void) { + uintptr_t as = hal_cpu_get_address_space(); + return (uint32_t*)P2V((uint32_t)as); +} + static void* pmm_alloc_page_low(void) { // Bring-up safety: allocate only from identity-mapped area (0-4MB) // until we have a general phys->virt mapping. @@ -35,66 +55,105 @@ static void* pmm_alloc_page_low(void) { return 0; } -static inline void invlpg(uintptr_t vaddr) { - __asm__ volatile("invlpg (%0)" : : "r" (vaddr) : "memory"); -} - -static uint32_t vmm_flags_to_x86(uint32_t flags) { - uint32_t x86_flags = 0; - if (flags & VMM_FLAG_PRESENT) x86_flags |= X86_PTE_PRESENT; - if (flags & VMM_FLAG_RW) x86_flags |= X86_PTE_RW; - if (flags & VMM_FLAG_USER) x86_flags |= X86_PTE_USER; - return x86_flags; -} - -void vmm_map_page(uint64_t phys, uint64_t virt, uint32_t flags) { +static void vmm_map_page_in_pd(uint32_t* pd_virt, uint64_t phys, uint64_t virt, uint32_t flags) { uint32_t pd_index = virt >> 22; uint32_t pt_index = (virt >> 12) & 0x03FF; - // Check if Page Table exists - if (!(boot_pd[pd_index] & X86_PTE_PRESENT)) { - // Allocate a new PT + if (!(pd_virt[pd_index] & X86_PTE_PRESENT)) { uint32_t pt_phys = (uint32_t)pmm_alloc_page_low(); if (!pt_phys) { uart_print("[VMM] OOM allocating page table.\n"); return; } - // ACCESS SAFETY: Convert Physical to Virtual to write to it uint32_t* pt_virt = (uint32_t*)P2V(pt_phys); - - // Clear table - for(int i=0; i<1024; i++) pt_virt[i] = 0; + for (int i = 0; i < 1024; i++) pt_virt[i] = 0; - // Add to Directory uint32_t pde_flags = X86_PTE_PRESENT | X86_PTE_RW; if (flags & VMM_FLAG_USER) pde_flags |= X86_PTE_USER; - boot_pd[pd_index] = pt_phys | pde_flags; + pd_virt[pd_index] = pt_phys | pde_flags; } - if ((flags & VMM_FLAG_USER) && !(boot_pd[pd_index] & X86_PTE_USER)) { - boot_pd[pd_index] |= X86_PTE_USER; + if ((flags & VMM_FLAG_USER) && !(pd_virt[pd_index] & X86_PTE_USER)) { + pd_virt[pd_index] |= X86_PTE_USER; } - // Get table address from Directory - uint32_t pt_phys = boot_pd[pd_index] & 0xFFFFF000; - - // ACCESS SAFETY: Convert to Virtual + uint32_t pt_phys = pd_virt[pd_index] & 0xFFFFF000; uint32_t* pt = (uint32_t*)P2V(pt_phys); - pt[pt_index] = ((uint32_t)phys) | vmm_flags_to_x86(flags); - invlpg(virt); + invlpg((uintptr_t)virt); +} + +void vmm_map_page(uint64_t phys, uint64_t virt, uint32_t flags) { + vmm_map_page_in_pd(vmm_active_pd_virt(), phys, virt, flags); +} + +uintptr_t vmm_as_create_kernel_clone(void) { + uint32_t pd_phys = (uint32_t)pmm_alloc_page_low(); + if (!pd_phys) return 0; + + uint32_t* pd_virt = (uint32_t*)P2V(pd_phys); + for (int i = 0; i < 1024; i++) pd_virt[i] = 0; + + // Copy kernel mappings (higher-half PDEs) + for (int i = 768; i < 1024; i++) { + pd_virt[i] = boot_pd[i]; + } + + return (uintptr_t)pd_phys; +} + +void vmm_as_activate(uintptr_t as) { + if (!as) return; + hal_cpu_set_address_space(as); +} + +void vmm_as_map_page(uintptr_t as, uint64_t phys, uint64_t virt, uint32_t flags) { + if (!as) return; + uint32_t* pd_virt = (uint32_t*)P2V((uint32_t)as); + vmm_map_page_in_pd(pd_virt, phys, virt, flags); +} + +void vmm_as_destroy(uintptr_t as) { + if (!as) return; + if (as == g_kernel_as) return; + + uint32_t* pd = (uint32_t*)P2V((uint32_t)as); + + // Free user page tables + frames for user space. + for (int pdi = 0; pdi < 768; pdi++) { + uint32_t pde = pd[pdi]; + if (!(pde & X86_PTE_PRESENT)) continue; + + uint32_t pt_phys = pde & 0xFFFFF000; + uint32_t* pt = (uint32_t*)P2V(pt_phys); + + for (int pti = 0; pti < 1024; pti++) { + uint32_t pte = pt[pti]; + if (!(pte & X86_PTE_PRESENT)) continue; + uint32_t frame = pte & 0xFFFFF000; + pmm_free_page((void*)(uintptr_t)frame); + pt[pti] = 0; + } + + pmm_free_page((void*)(uintptr_t)pt_phys); + pd[pdi] = 0; + } + + pmm_free_page((void*)(uintptr_t)as); } void vmm_set_page_flags(uint64_t virt, uint32_t flags) { uint32_t pd_index = virt >> 22; uint32_t pt_index = (virt >> 12) & 0x03FF; - if (!(boot_pd[pd_index] & X86_PTE_PRESENT)) { + uint32_t* pd = vmm_active_pd_virt(); + + if (!(pd[pd_index] & X86_PTE_PRESENT)) { return; } - uint32_t pt_phys = boot_pd[pd_index] & 0xFFFFF000; + uint32_t pt_phys = pd[pd_index] & 0xFFFFF000; uint32_t* pt = (uint32_t*)P2V(pt_phys); uint32_t pte = pt[pt_index]; @@ -121,9 +180,10 @@ void vmm_protect_range(uint64_t vaddr, uint64_t len, uint32_t flags) { void vmm_unmap_page(uint64_t virt) { uint32_t pd_index = virt >> 22; uint32_t pt_index = (virt >> 12) & 0x03FF; - - if (boot_pd[pd_index] & X86_PTE_PRESENT) { - uint32_t pt_phys = boot_pd[pd_index] & 0xFFFFF000; + + uint32_t* pd = vmm_active_pd_virt(); + if (pd[pd_index] & X86_PTE_PRESENT) { + uint32_t pt_phys = pd[pd_index] & 0xFFFFF000; uint32_t* pt = (uint32_t*)P2V(pt_phys); pt[pt_index] = 0; @@ -133,6 +193,8 @@ void vmm_unmap_page(uint64_t virt) { void vmm_init(void) { uart_print("[VMM] Higher Half Kernel Active.\n"); + + g_kernel_as = hal_cpu_get_address_space(); // Test mapping vmm_map_page(0xB8000, 0xC00B8000, VMM_FLAG_PRESENT | VMM_FLAG_RW); diff --git a/src/hal/x86/cpu.c b/src/hal/x86/cpu.c index 4c46c9c..1ebd6ef 100644 --- a/src/hal/x86/cpu.c +++ b/src/hal/x86/cpu.c @@ -24,6 +24,14 @@ uintptr_t hal_cpu_get_address_space(void) { return as; } +void hal_cpu_set_address_space(uintptr_t as) { +#if defined(__x86_64__) + __asm__ volatile("mov %0, %%cr3" : : "r"(as) : "memory"); +#else + __asm__ volatile("mov %0, %%cr3" : : "r"(as) : "memory"); +#endif +} + void hal_cpu_set_kernel_stack(uintptr_t sp_top) { tss_set_kernel_stack(sp_top); } @@ -46,6 +54,10 @@ uintptr_t hal_cpu_get_address_space(void) { return 0; } +void hal_cpu_set_address_space(uintptr_t as) { + (void)as; +} + void hal_cpu_set_kernel_stack(uintptr_t sp_top) { (void)sp_top; } diff --git a/src/kernel/elf.c b/src/kernel/elf.c index 087756c..e13cabd 100644 --- a/src/kernel/elf.c +++ b/src/kernel/elf.c @@ -7,6 +7,8 @@ #include "utils.h" #include "vmm.h" +#include "hal/cpu.h" + #include #if defined(__i386__) @@ -53,7 +55,7 @@ static int elf32_validate(const elf32_ehdr_t* eh, size_t file_len) { return 0; } -static int elf32_map_user_range(uintptr_t vaddr, size_t len, uint32_t flags) { +static int elf32_map_user_range(uintptr_t as, uintptr_t vaddr, size_t len, uint32_t flags) { if (len == 0) return 0; if (vaddr == 0) return -1; if (vaddr >= X86_KERNEL_VIRT_BASE) return -1; @@ -69,7 +71,7 @@ static int elf32_map_user_range(uintptr_t vaddr, size_t len, uint32_t flags) { void* phys = pmm_alloc_page_low_16mb(); if (!phys) return -1; - vmm_map_page((uint64_t)(uintptr_t)phys, (uint64_t)va, flags | VMM_FLAG_PRESENT | VMM_FLAG_USER); + vmm_as_map_page(as, (uint64_t)(uintptr_t)phys, (uint64_t)va, flags | VMM_FLAG_PRESENT | VMM_FLAG_USER); if (va == end_page) break; } @@ -77,10 +79,16 @@ static int elf32_map_user_range(uintptr_t vaddr, size_t len, uint32_t flags) { return 0; } -int elf32_load_user_from_initrd(const char* filename, uintptr_t* entry_out, uintptr_t* user_stack_top_out) { - if (!filename || !entry_out || !user_stack_top_out) return -1; +int elf32_load_user_from_initrd(const char* filename, uintptr_t* entry_out, uintptr_t* user_stack_top_out, uintptr_t* addr_space_out) { + if (!filename || !entry_out || !user_stack_top_out || !addr_space_out) return -1; if (!fs_root) return -1; + uintptr_t new_as = vmm_as_create_kernel_clone(); + if (!new_as) return -1; + + uintptr_t old_as = hal_cpu_get_address_space(); + vmm_as_activate(new_as); + fs_node_t* node = vfs_lookup(filename); if (!node) { uart_print("[ELF] file not found: "); @@ -98,6 +106,8 @@ int elf32_load_user_from_initrd(const char* filename, uintptr_t* entry_out, uint uint32_t rd = vfs_read(node, 0, file_len, file); if (rd != file_len) { kfree(file); + vmm_as_activate(old_as); + vmm_as_destroy(new_as); return -1; } @@ -105,6 +115,8 @@ int elf32_load_user_from_initrd(const char* filename, uintptr_t* entry_out, uint if (elf32_validate(eh, file_len) < 0) { uart_print("[ELF] invalid ELF header\n"); kfree(file); + vmm_as_activate(old_as); + vmm_as_destroy(new_as); return -1; } @@ -117,11 +129,15 @@ int elf32_load_user_from_initrd(const char* filename, uintptr_t* entry_out, uint if (ph[i].p_vaddr == 0) { uart_print("[ELF] PT_LOAD with vaddr=0 rejected\n"); kfree(file); + vmm_as_activate(old_as); + vmm_as_destroy(new_as); return -1; } if (ph[i].p_vaddr >= X86_KERNEL_VIRT_BASE) { uart_print("[ELF] PT_LOAD in kernel range rejected\n"); kfree(file); + vmm_as_activate(old_as); + vmm_as_destroy(new_as); return -1; } @@ -143,9 +159,11 @@ int elf32_load_user_from_initrd(const char* filename, uintptr_t* entry_out, uint const uint32_t map_flags = VMM_FLAG_RW; - if (elf32_map_user_range((uintptr_t)ph[i].p_vaddr, (size_t)ph[i].p_memsz, map_flags) < 0) { + if (elf32_map_user_range(new_as, (uintptr_t)ph[i].p_vaddr, (size_t)ph[i].p_memsz, map_flags) < 0) { uart_print("[ELF] OOM mapping user segment\n"); kfree(file); + vmm_as_activate(old_as); + vmm_as_destroy(new_as); return -1; } @@ -166,23 +184,28 @@ int elf32_load_user_from_initrd(const char* filename, uintptr_t* entry_out, uint const uintptr_t user_stack_base = 0x00800000U; const size_t user_stack_size = 0x1000; - if (elf32_map_user_range(user_stack_base, user_stack_size, VMM_FLAG_RW) < 0) { + if (elf32_map_user_range(new_as, user_stack_base, user_stack_size, VMM_FLAG_RW) < 0) { uart_print("[ELF] OOM mapping user stack\n"); kfree(file); + vmm_as_activate(old_as); + vmm_as_destroy(new_as); return -1; } *entry_out = (uintptr_t)eh->e_entry; *user_stack_top_out = user_stack_base + user_stack_size; + *addr_space_out = new_as; kfree(file); + vmm_as_activate(old_as); return 0; } #else -int elf32_load_user_from_initrd(const char* filename, uintptr_t* entry_out, uintptr_t* user_stack_top_out) { +int elf32_load_user_from_initrd(const char* filename, uintptr_t* entry_out, uintptr_t* user_stack_top_out, uintptr_t* addr_space_out) { (void)filename; (void)entry_out; (void)user_stack_top_out; + (void)addr_space_out; return -1; } #endif diff --git a/src/kernel/scheduler.c b/src/kernel/scheduler.c index 6615a81..83aab6e 100644 --- a/src/kernel/scheduler.c +++ b/src/kernel/scheduler.c @@ -15,6 +15,7 @@ struct process* ready_queue_tail = NULL; static uint32_t next_pid = 1; static spinlock_t sched_lock = {0}; +static uintptr_t kernel_as = 0; static struct process* process_find_locked(uint32_t pid) { if (!ready_queue_head) return NULL; @@ -56,6 +57,11 @@ static void process_reap_locked(struct process* p) { p->kernel_stack = NULL; } + if (p->addr_space && p->addr_space != kernel_as) { + vmm_as_destroy(p->addr_space); + p->addr_space = 0; + } + kfree(p); } @@ -179,6 +185,7 @@ void process_init(void) { kernel_proc->state = PROCESS_RUNNING; kernel_proc->wake_at_tick = 0; kernel_proc->addr_space = hal_cpu_get_address_space(); + kernel_as = kernel_proc->addr_space; kernel_proc->exit_status = 0; kernel_proc->waiting = 0; kernel_proc->wait_pid = -1; @@ -221,7 +228,7 @@ struct process* process_create_kernel(void (*entry_point)(void)) { proc->pid = next_pid++; proc->parent_pid = current_process ? current_process->pid : 0; proc->state = PROCESS_READY; - proc->addr_space = current_process->addr_space; + proc->addr_space = kernel_as ? kernel_as : (current_process ? current_process->addr_space : 0); proc->wake_at_tick = 0; proc->exit_status = 0; proc->waiting = 0; @@ -314,6 +321,10 @@ void schedule(void) { current_process = next; current_process->state = PROCESS_RUNNING; + if (current_process->addr_space && current_process->addr_space != prev->addr_space) { + hal_cpu_set_address_space(current_process->addr_space); + } + // For ring3->ring0 transitions, esp0 must point to the top of the kernel stack. if (current_process->kernel_stack) { hal_cpu_set_kernel_stack((uintptr_t)current_process->kernel_stack + 4096);