From dd75dc2d87329ad78e79f387e9ff66b9e957997e Mon Sep 17 00:00:00 2001 From: Matheus Date: Wed, 21 Aug 2024 21:33:29 -0300 Subject: [PATCH] vmm: vmm_changemmuflags --- kernel-src/arch/x86-64/mmu.c | 14 +- kernel-src/include/kernel/vmm.h | 1 + kernel-src/include/x86-64/arch/mmu.h | 1 + kernel-src/mm/vmm.c | 187 ++++++++++++++++++++++----- 4 files changed, 167 insertions(+), 36 deletions(-) diff --git a/kernel-src/arch/x86-64/mmu.c b/kernel-src/arch/x86-64/mmu.c index c6febec4..305327bb 100644 --- a/kernel-src/arch/x86-64/mmu.c +++ b/kernel-src/arch/x86-64/mmu.c @@ -167,7 +167,7 @@ void *arch_mmu_getphysical(pagetableptr_t table, void *vaddr) { 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; + return entry == NULL ? false : *entry; } bool arch_mmu_iswritable(pagetableptr_t table, void *vaddr) { @@ -182,6 +182,18 @@ bool arch_mmu_isdirty(pagetableptr_t table, void *vaddr) { return entry == NULL ? false : *entry & ARCH_MMU_FLAGS_DIRTY; } +#define FLAGS_MASK (ARCH_MMU_FLAGS_WRITE | ARCH_MMU_FLAGS_READ | ARCH_MMU_FLAGS_NOEXEC | ARCH_MMU_FLAGS_USER) + +bool arch_mmu_getflags(pagetableptr_t table, void *vaddr, mmuflags_t *mmuflagsp) { + uint64_t *entry = get_page(table, vaddr); + if (entry == NULL) + return false; + + *mmuflagsp = *entry & FLAGS_MASK; + + return true; +} + void arch_mmu_switch(pagetableptr_t table) { asm volatile("mov %%rax, %%cr3" : : "a"(table)); } diff --git a/kernel-src/include/kernel/vmm.h b/kernel-src/include/kernel/vmm.h index 86872ca9..0dfa5886 100644 --- a/kernel-src/include/kernel/vmm.h +++ b/kernel-src/include/kernel/vmm.h @@ -92,6 +92,7 @@ static inline int mmuflagstovnodeflags(mmuflags_t mmuflags) { return flags; } +bool vmm_changemmuflags(void *base, size_t size, mmuflags_t mmuflags, int flags); void vmm_destroycontext(vmmcontext_t *context); vmmcontext_t *vmm_fork(vmmcontext_t *oldcontext); void *vmm_map(void *addr, size_t size, int flags, mmuflags_t mmuflags, void *private); diff --git a/kernel-src/include/x86-64/arch/mmu.h b/kernel-src/include/x86-64/arch/mmu.h index 6e89bdef..6c343d95 100644 --- a/kernel-src/include/x86-64/arch/mmu.h +++ b/kernel-src/include/x86-64/arch/mmu.h @@ -39,5 +39,6 @@ pagetableptr_t arch_mmu_newtable(); void arch_mmu_init(); void arch_mmu_apswitch(); void arch_mmu_tlbshootdown(void *page); +bool arch_mmu_getflags(pagetableptr_t table, void *vaddr, mmuflags_t *mmuflagsp); #endif diff --git a/kernel-src/mm/vmm.c b/kernel-src/mm/vmm.c index 69712d91..be78bea3 100644 --- a/kernel-src/mm/vmm.c +++ b/kernel-src/mm/vmm.c @@ -254,30 +254,68 @@ static void destroyrange(vmmrange_t *range, uintmax_t _offset, size_t size, int VOP_RELEASE(range->vnode); } -static void unmap(vmmspace_t *space, void *address, size_t size) { +static void changemmurange(void *base, size_t size, mmuflags_t newflags) { + for (uintmax_t offset = 0; offset < size; offset += PAGE_SIZE) { + void *address = (void *)((uintptr_t)base + offset); + + mmuflags_t currentflags; + // if page is not mapped, do nothing + if (arch_mmu_getflags(_cpu()->vmmctx->pagetable, address, ¤tflags) == false) + continue; + + void *physical = arch_mmu_getphysical(_cpu()->vmmctx->pagetable, address); + + // we will only change the mapping if the permissions decreased + if ( ((newflags & ARCH_MMU_FLAGS_READ) == 0 && (currentflags & ARCH_MMU_FLAGS_READ)) || + ((newflags & ARCH_MMU_FLAGS_WRITE) == 0 && (currentflags & ARCH_MMU_FLAGS_WRITE)) || + ((newflags & ARCH_MMU_FLAGS_USER) == 0 && (currentflags & ARCH_MMU_FLAGS_USER)) || + ((newflags & ARCH_MMU_FLAGS_NOEXEC) && (currentflags & ARCH_MMU_FLAGS_NOEXEC))) { + arch_mmu_remap(_cpu()->vmmctx->pagetable, physical, address, newflags); + arch_mmu_invalidate(address); + } + } +} + +static void changemap(vmmspace_t *space, void *address, size_t size, bool free, mmuflags_t newmmuflags) { void *top = (void *)((uintptr_t)address + size); vmmrange_t *range = space->ranges; + vmmrange_t *newrange = NULL; + // allocated here and as soon as its used to make sure that + // even in an allocation failure (TODO) there will always be a valid mapping + if (free == false) { + newrange = allocrange(); + __assert(newrange); + } + while (range) { __assert(range != space->ranges || range->prev == NULL); void *rangetop = RANGE_TOP(range); - // completely unmapped if (range->start >= address && rangetop <= top) { - if (range->prev) - range->prev->next = range->next; - else - space->ranges = range->next; + // completely changed + if (free) { + if (range->prev) + range->prev->next = range->next; + else + space->ranges = range->next; - if (range->next) - range->next->prev = range->prev; + if (range->next) + range->next->prev = range->prev; - destroyrange(range, 0, range->size, 0); - freerange(range); - } else if (address > range->start && top < rangetop) { // split + destroyrange(range, 0, range->size, 0); + freerange(range); + } else { + changemmurange(range->start, range->size, newmmuflags); + range->mmuflags = newmmuflags; + } + } else if (address > range->start && top < rangetop) { + // split (entire change was within a single range) vmmrange_t *new = allocrange(); __assert(new); // XXX deal with this better *new = *range; - destroyrange(range, (uintptr_t)address - (uintptr_t)range->start, size, 0); + if (free) { + destroyrange(range, (uintptr_t)address - (uintptr_t)range->start, size, 0); + } new->start = top; new->size = (uintptr_t)rangetop - (uintptr_t)new->start; @@ -293,25 +331,108 @@ static void unmap(vmmspace_t *space, void *address, size_t size) { if (range->flags & VMM_FLAGS_FILE) { VOP_HOLD(range->vnode); new->offset += range->size + size; - } - } else if (top > range->start && range->start >= address) { // partially unmap from start + + if (free == false) { + newrange->start = address; + newrange->size = size; + newrange->flags = range->flags; + newrange->mmuflags = newmmuflags; + + changemmurange(newrange->start, newrange->size, newrange->mmuflags); + + if (range->flags & VMM_FLAGS_FILE) { + newrange->vnode = range->vnode; + newrange->offset = range->offset; + VOP_HOLD(range->vnode); + } + + insertrange(space, newrange); + newrange = allocrange(); + __assert(newrange); + } + } else if (top > range->start && range->start >= address) { + // partially change from start (end of change was within this range) size_t difference = (uintptr_t)top - (uintptr_t)range->start; - destroyrange(range, 0, difference, 0); + if (free) { + destroyrange(range, 0, difference, 0); + } range->start = (void *)((uintptr_t)range->start + difference); range->size -= difference; if (range->flags & VMM_FLAGS_FILE) range->offset += difference; - } else if (address < rangetop && rangetop <= top){ // partially unmap from end + + if (free == false) { + newrange->start = (void *)((uintptr_t)range->start - difference); + newrange->size = difference; + newrange->flags = range->flags; + newrange->mmuflags = newmmuflags; + + changemmurange(newrange->start, newrange->size, newrange->mmuflags); + + if (range->flags & VMM_FLAGS_FILE) { + newrange->vnode = range->vnode; + newrange->offset = range->offset - difference; + VOP_HOLD(range->vnode); + } + + insertrange(space, newrange); + newrange = allocrange(); + __assert(newrange); + } + } else if (address < rangetop && rangetop <= top) { + // partially change from end (start of change was within this range) size_t difference = (uintptr_t)rangetop - (uintptr_t)address; range->size -= difference; - destroyrange(range, range->size, difference, 0); + if (free) { + destroyrange(range, range->size, difference, 0); + } else { + newrange->start = (void *)((uintptr_t)range->start + difference); + newrange->size = difference; + newrange->flags = range->flags; + newrange->mmuflags = newmmuflags; + + changemmurange(newrange->start, newrange->size, newrange->mmuflags); + + if (range->flags & VMM_FLAGS_FILE) { + newrange->vnode = range->vnode; + newrange->offset = range->offset + difference; + VOP_HOLD(range->vnode); + } + + insertrange(space, newrange); + newrange = allocrange(); + __assert(newrange); + } } range = range->next; } + if (free == false) + freerange(newrange); +} + +bool vmm_changemmuflags(void *base, size_t size, mmuflags_t mmuflags, int flags) { + base = (void *)ROUND_DOWN((uintptr_t)base, PAGE_SIZE); + + if (flags & VMM_FLAGS_PAGESIZE) + size *= PAGE_SIZE; + else + size = ROUND_UP(size, PAGE_SIZE); + + if (size == 0) + return true; + + vmmspace_t *space = getspace(base); + if (space == NULL) + return false; + + MUTEX_ACQUIRE(&space->lock, false); + changemap(space, base, size, false, mmuflags); + MUTEX_RELEASE(&space->lock); + return true; } static void printspace(vmmspace_t *space) { @@ -326,24 +447,29 @@ static void printspace(vmmspace_t *space) { static void *zeropage; bool vmm_pagefault(void *addr, bool user, int actions) { - if (user == false && addr > USERSPACE_END) + if (user == false && addr > USERSPACE_END) { + printf("vmm: kernel access\n"); return false; + } addr = (void *)ROUND_DOWN((uintptr_t)addr, PAGE_SIZE); vmmspace_t *space = getspace(addr); - if (space == NULL || (space == &kernelspace && user)) + if (space == NULL || (space == &kernelspace && user)) { + printf("vmm: no such space or space accessed is kerneƧ\n"); return false; + } - MUTEX_ACQUIRE(&space->pflock, false); MUTEX_ACQUIRE(&space->lock, false); vmmrange_t *range = getrange(space, addr); bool status = false; - if (range == NULL) + if (range == NULL) { + printf("vmm: no range\n"); goto cleanup; + } // check if valid @@ -358,8 +484,10 @@ bool vmm_pagefault(void *addr, bool user, int actions) { if ((range->mmuflags & ARCH_MMU_FLAGS_NOEXEC)) invalidactions |= VMM_ACTION_EXEC; - if (invalidactions & actions) + if (invalidactions & actions) { + printf("vmm: bad action\n"); goto cleanup; + } thread_t *thread = _cpu()->thread; proc_t *proc = thread ? thread->proc : NULL; @@ -432,7 +560,6 @@ bool vmm_pagefault(void *addr, bool user, int actions) { cleanup: MUTEX_RELEASE(&space->lock); - MUTEX_RELEASE(&space->pflock); return status; } @@ -490,7 +617,7 @@ void *vmm_map(void *addr, volatile size_t size, int flags, mmuflags_t mmuflags, range->flags = VMM_PERMANENT_FLAGS_MASK & flags; range->mmuflags = mmuflags; __assert((flags & (VMM_FLAGS_ALLOCATE | VMM_FLAGS_PHYSICAL)) == 0); - unmap(space, addr, size); + changemap(space, addr, size, true, 0); } else { retaddr = start; range->start = start; @@ -556,7 +683,6 @@ void *vmm_map(void *addr, volatile size_t size, int flags, mmuflags_t mmuflags, } void vmm_unmap(void *addr, size_t size, int flags) { - // XXX maps where addr is not page aligned can break addr = (void *)ROUND_DOWN((uintptr_t)addr, PAGE_SIZE); if (flags & VMM_FLAGS_PAGESIZE) @@ -571,16 +697,9 @@ void vmm_unmap(void *addr, size_t size, int flags) { if (space == NULL) return; - // the pagefault handler lock is acquired - // in order to prevent some consistency issues - // when other threads are running on the same address space. - // the lock ordering is still pflock -> lock on both - // vmm_pagefault and vmm_unmap. - MUTEX_ACQUIRE(&space->pflock, false); MUTEX_ACQUIRE(&space->lock, false); - unmap(space, addr, size); + changemap(space, addr, size, true, 0); MUTEX_RELEASE(&space->lock); - MUTEX_RELEASE(&space->pflock); } static scache_t *ctxcache; @@ -590,7 +709,6 @@ static void ctxctor(scache_t *cache, void *obj) { ctx->space.start = USERSPACE_START; ctx->space.end = USERSPACE_END; MUTEX_INIT(&ctx->space.lock); - MUTEX_INIT(&ctx->space.pflock); ctx->space.ranges = NULL; } @@ -688,7 +806,6 @@ void vmm_init() { // set up initial state __assert(sizeof(vmmcache_t) <= PAGE_SIZE); MUTEX_INIT(&kernelspace.lock); - MUTEX_INIT(&kernelspace.pflock); cachelist = newcache(); vmm_kernelctx.pagetable = arch_mmu_newtable();