From 7e1443d0324104f616a473ad6e0c4a2ff54d5e1e Mon Sep 17 00:00:00 2001 From: Matheus Date: Wed, 2 Aug 2023 01:18:35 -0300 Subject: [PATCH] vmm: copy on write and changes to how page faults are handled --- kernel-src/arch/x86-64/mmu.c | 11 ++++++ kernel-src/include/x86-64/arch/mmu.h | 2 ++ kernel-src/mm/pmm.c | 2 +- kernel-src/mm/vmm.c | 52 +++++++++++++++++++++++----- 4 files changed, 57 insertions(+), 10 deletions(-) diff --git a/kernel-src/arch/x86-64/mmu.c b/kernel-src/arch/x86-64/mmu.c index 008ebad4..004cebb8 100644 --- a/kernel-src/arch/x86-64/mmu.c +++ b/kernel-src/arch/x86-64/mmu.c @@ -5,6 +5,7 @@ #include #include #include +#include #define ADDRMASK (uint64_t)0x7ffffffffffff000 #define PTMASK (uint64_t)0b111111111000000000000 @@ -162,6 +163,16 @@ void *arch_mmu_getphysical(pagetableptr_t table, void *vaddr) { return (void *)(*entry & ADDRMASK); } +bool arch_mmu_ispresent(pagetableptr_t table, void *vaddr) { + uint64_t *entry = get_page(table, vaddr); + return entry == NULL ? false : *entry & ARCH_MMU_FLAGS_READ; +} + +bool arch_mmu_iswritable(pagetableptr_t table, void *vaddr) { + uint64_t *entry = get_page(table, vaddr); + return entry == NULL ? false : *entry & ARCH_MMU_FLAGS_WRITE; +} + void arch_mmu_switch(pagetableptr_t table) { asm volatile("mov %%rax, %%cr3" : : "a"(table)); } diff --git a/kernel-src/include/x86-64/arch/mmu.h b/kernel-src/include/x86-64/arch/mmu.h index de0bddf5..730416a0 100644 --- a/kernel-src/include/x86-64/arch/mmu.h +++ b/kernel-src/include/x86-64/arch/mmu.h @@ -30,6 +30,8 @@ void arch_mmu_unmap(pagetableptr_t table, void *vaddr); void arch_mmu_remap(pagetableptr_t table, void *paddr, void *vaddr, mmuflags_t flags); void arch_mmu_switch(pagetableptr_t table); void *arch_mmu_getphysical(pagetableptr_t table, void *vaddr); +bool arch_mmu_ispresent(pagetableptr_t table, void *vaddr); +bool arch_mmu_iswritable(pagetableptr_t table, void *vaddr); pagetableptr_t arch_mmu_newtable(); void arch_mmu_init(); diff --git a/kernel-src/mm/pmm.c b/kernel-src/mm/pmm.c index 737b73c9..61c58db0 100644 --- a/kernel-src/mm/pmm.c +++ b/kernel-src/mm/pmm.c @@ -184,7 +184,7 @@ void pmm_init() { pages = MAKE_HHDM((page_t *)biggest->base); pagecount = ROUND_UP(top, PAGE_SIZE) / PAGE_SIZE; - memset(pages, 0, pagecount / sizeof(page_t)); + memset(pages, 0, pagecount * sizeof(page_t)); printf("pmm: %d pages used for page list\n", ROUND_UP(pagecount * sizeof(page_t), PAGE_SIZE) / PAGE_SIZE); // place pages in free lists diff --git a/kernel-src/mm/vmm.c b/kernel-src/mm/vmm.c index 71cd1d23..5553a8dd 100644 --- a/kernel-src/mm/vmm.c +++ b/kernel-src/mm/vmm.c @@ -285,6 +285,8 @@ static void unmap(vmmspace_t *space, void *address, size_t size) { } +static void *zeropage; + bool vmm_pagefault(void *addr, bool user, int actions) { if (user == false && addr > USERSPACE_END) return false; @@ -320,20 +322,45 @@ bool vmm_pagefault(void *addr, bool user, int actions) { if (invalidactions & actions) goto cleanup; - // TODO CoW thread_t *thread = _cpu()->thread; proc_t *proc = thread ? thread->proc : NULL; cred_t *cred = proc ? &proc->cred : NULL; - if (range->flags & VMM_FLAGS_FILE) { - uintmax_t mapoffset = (uintptr_t)addr - (uintptr_t)range->start; - __assert(VOP_MMAP(range->vnode, addr, range->offset + mapoffset, mmuflagstovnodeflags(range->mmuflags) | (range->flags & VMM_FLAGS_SHARED ? V_FFLAGS_SHARED : 0), cred) == 0); - status = true; + if (arch_mmu_ispresent(_cpu()->vmmctx->pagetable, addr) == false) { + // page not present in the page tables + if (range->flags & VMM_FLAGS_FILE) { + uintmax_t mapoffset = (uintptr_t)addr - (uintptr_t)range->start; + __assert(VOP_MMAP(range->vnode, addr, range->offset + mapoffset, mmuflagstovnodeflags(range->mmuflags) | (range->flags & VMM_FLAGS_SHARED ? V_FFLAGS_SHARED : 0), cred) == 0); + status = true; + } else { + // anonymous memory. map the zero'd page + status = arch_mmu_map(_cpu()->vmmctx->pagetable, zeropage, addr, range->mmuflags & ~ARCH_MMU_FLAGS_WRITE); + if (!status) + printf("vmm: out of memory to map zero page into address space\n"); + else + pmm_hold(zeropage); + } + } else if (arch_mmu_iswritable(_cpu()->vmmctx->pagetable, addr) == false){ + // page present but not writeable in the page tables + if ((range->flags & VMM_FLAGS_FILE) && (range->flags & VMM_FLAGS_SHARED)) { + // TODO shared file mappings (set as dirty and set as writable on PTE) + } else { + void *oldphys = arch_mmu_getphysical(_cpu()->vmmctx->pagetable, addr); + // do copy on write + void *newphys = pmm_allocpage(PMM_SECTION_DEFAULT); + if (newphys == NULL) { + printf("vmm: out of memory to do copy on write on address space\n"); + status = false; + } else { + // this assert should always succeed, here just to make sure + memcpy(MAKE_HHDM(newphys), MAKE_HHDM(oldphys), PAGE_SIZE); + __assert(arch_mmu_map(_cpu()->vmmctx->pagetable, newphys, addr, range->mmuflags)); + pmm_release(oldphys); + status = true; + } + } } else { - void *paddr = pmm_allocpage(PMM_SECTION_DEFAULT); - __assert(paddr); // XXX handle these better than an assert once there are signals and such - __assert(arch_mmu_map(_cpu()->vmmctx->pagetable, paddr, addr, range->mmuflags)); - memset(MAKE_HHDM(paddr), 0, PAGE_SIZE); + // another thread already did the work, so just return success status = true; } @@ -576,6 +603,7 @@ extern void *_rodata_end; extern volatile struct limine_memmap_request pmm_liminemap; void vmm_init() { + // set up initial state __assert(sizeof(vmmcache_t) <= PAGE_SIZE); MUTEX_INIT(&kernelspace.lock); MUTEX_INIT(&kernelspace.pflock); @@ -589,12 +617,14 @@ void vmm_init() { vmm_switchcontext(&vmm_kernelctx); + // map HHDM for (uint64_t i = 0; i < pmm_liminemap.response->entry_count; ++i) { struct limine_memmap_entry *e = pmm_liminemap.response->entries[i]; mmuflags_t mmuflags = ARCH_MMU_FLAGS_READ | ARCH_MMU_FLAGS_WRITE | ARCH_MMU_FLAGS_NOEXEC; __assert(vmm_map(MAKE_HHDM(e->base), e->length, VMM_FLAGS_EXACT, mmuflags, NULL)); } + // map kernel __assert(vmm_map(&_text_start, (uintptr_t)&_text_end - (uintptr_t)&_text_start, VMM_FLAGS_EXACT, ARCH_MMU_FLAGS_READ, NULL)); __assert(vmm_map(&_rodata_start, (uintptr_t)&_rodata_end - (uintptr_t)&_rodata_start, VMM_FLAGS_EXACT, ARCH_MMU_FLAGS_READ | ARCH_MMU_FLAGS_NOEXEC, NULL)); __assert(vmm_map(&_data_start, (uintptr_t)&_data_end - (uintptr_t)&_data_start, VMM_FLAGS_EXACT, ARCH_MMU_FLAGS_READ | ARCH_MMU_FLAGS_WRITE | ARCH_MMU_FLAGS_NOEXEC, NULL)); @@ -602,5 +632,9 @@ void vmm_init() { // null page vmm_map(MAKE_HHDM(NULL), PAGE_SIZE, VMM_FLAGS_EXACT, ARCH_MMU_FLAGS_NOEXEC, NULL); + // zero page + zeropage = pmm_allocpage(PMM_SECTION_DEFAULT); + memset(MAKE_HHDM(zeropage), 0, PAGE_SIZE); + printspace(&kernelspace); }