From 22990b77b84cf7c66c1ccb6ce86ef1898baeb71f Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Wed, 21 Mar 2012 17:19:26 +0100 Subject: [PATCH] Refactored the internal kernel memory management API. It is now permission-oriented, not just user/kernel oriented. Added with nice PROT_{READ,WRITE,EXEC,FORK} constants. --- libmaxsi/heap.cpp | 9 +- libmaxsi/include/sys/mman.h | 36 +++ sortix/Makefile | 1 + sortix/elf.cpp | 17 +- .../include/sortix/kernel/memorymanagement.h | 34 +- sortix/include/sortix/mman.h | 47 +++ sortix/initrd.cpp | 3 +- sortix/memorymanagement.cpp | 53 ++++ sortix/process.cpp | 8 +- sortix/scheduler.cpp | 4 +- sortix/thread.cpp | 8 +- sortix/x86-family/memorymanagement.cpp | 299 ++++++++++-------- sortix/x86-family/memorymanagement.h | 7 +- 13 files changed, 369 insertions(+), 157 deletions(-) create mode 100644 libmaxsi/include/sys/mman.h create mode 100644 sortix/include/sortix/mman.h create mode 100644 sortix/memorymanagement.cpp diff --git a/libmaxsi/heap.cpp b/libmaxsi/heap.cpp index 54ec7cb8..355702db 100644 --- a/libmaxsi/heap.cpp +++ b/libmaxsi/heap.cpp @@ -22,6 +22,7 @@ ******************************************************************************/ +#include #include #include #include @@ -78,11 +79,11 @@ namespace Maxsi void FreeMemory(addr_t where, size_t bytes) { - ASSERT( (bytes & (PAGESIZE-1UL)) == 0 ); + ASSERT(Sortix::Page::IsAligned(where + bytes)); while ( bytes ) { - addr_t page = Sortix::Memory::UnmapKernel(where); + addr_t page = Sortix::Memory::Unmap(where); Sortix::Page::Put(page); bytes -= PAGESIZE; @@ -92,7 +93,7 @@ namespace Maxsi bool AllocateMemory(addr_t where, size_t bytes) { - ASSERT( (bytes & (PAGESIZE-1UL)) == 0 ); + ASSERT(Sortix::Page::IsAligned(where + bytes)); addr_t pos = where; @@ -105,7 +106,7 @@ namespace Maxsi return false; } - if ( !Sortix::Memory::MapKernel(page, pos) ) + if ( !Sortix::Memory::Map(page, pos, PROT_KREAD | PROT_KWRITE) ) { Sortix::Page::Put(page); FreeMemory(where, pos-where); diff --git a/libmaxsi/include/sys/mman.h b/libmaxsi/include/sys/mman.h new file mode 100644 index 00000000..0966f0ea --- /dev/null +++ b/libmaxsi/include/sys/mman.h @@ -0,0 +1,36 @@ +/******************************************************************************* + + COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2012. + + This file is part of LibMaxsi. + + LibMaxsi is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + LibMaxsi is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. + + You should have received a copy of the GNU Lesser General Public License + along with LibMaxsi. If not, see . + + sys/mman.h + Memory management declarations. + +*******************************************************************************/ + +#ifndef _SYS_MMAN_H +#define _SYS_MMAN_H 1 + +#include +#include + +__BEGIN_DECLS + +__END_DECLS + +#endif + diff --git a/sortix/Makefile b/sortix/Makefile index 45405c6a..a324485f 100644 --- a/sortix/Makefile +++ b/sortix/Makefile @@ -93,6 +93,7 @@ interrupt.o \ time.o \ log.o \ utf8.o \ +memorymanagement.o \ calltrace.o \ $(CPU)/calltrace.o \ kthread.o \ diff --git a/sortix/elf.cpp b/sortix/elf.cpp index 988d40e1..4c8295af 100644 --- a/sortix/elf.cpp +++ b/sortix/elf.cpp @@ -23,6 +23,7 @@ ******************************************************************************/ #include +#include #include #include #include "elf.h" @@ -104,14 +105,20 @@ namespace Sortix segment->size = Page::AlignUp(mapbytes); segment->type = ToProgramSectionType(pht->flags); + int prot = PROT_FORK | PROT_KREAD | PROT_KWRITE; + if ( pht->flags & PF_X ) { prot |= PROT_EXEC; } + if ( pht->flags & PF_R ) { prot |= PROT_READ; } + if ( pht->flags & PF_W ) { prot |= PROT_WRITE; } + if ( segment->Intersects(process->segments) ) { delete segment; return 0; } - if ( !Memory::MapRangeUser(mapto, mapbytes)) + if ( !Memory::MapRange(mapto, mapbytes, prot)) { + // TODO: Memory leak of segment? return 0; } @@ -184,14 +191,20 @@ namespace Sortix segment->size = Page::AlignUp(mapbytes); segment->type = ToProgramSectionType(pht->flags); + int prot = PROT_FORK | PROT_KREAD | PROT_KWRITE; + if ( pht->flags & PF_X ) { prot |= PROT_EXEC; } + if ( pht->flags & PF_R ) { prot |= PROT_READ; } + if ( pht->flags & PF_W ) { prot |= PROT_WRITE; } + if ( segment->Intersects(process->segments) ) { delete segment; return 0; } - if ( !Memory::MapRangeUser(mapto, mapbytes)) + if ( !Memory::MapRange(mapto, mapbytes, prot)) { + // TODO: Memory leak of segment? return 0; } diff --git a/sortix/include/sortix/kernel/memorymanagement.h b/sortix/include/sortix/kernel/memorymanagement.h index 446fb962..0e02a1c0 100644 --- a/sortix/include/sortix/kernel/memorymanagement.h +++ b/sortix/include/sortix/kernel/memorymanagement.h @@ -1,6 +1,6 @@ -/****************************************************************************** +/******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011, 2012. This file is part of Sortix. @@ -14,13 +14,13 @@ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along - with Sortix. If not, see . + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . memorymanagement.h Functions that allow modification of virtual memory. -******************************************************************************/ +*******************************************************************************/ #ifndef SORTIX_MEMORYMANAGEMENT_H #define SORTIX_MEMORYMANAGEMENT_H @@ -32,9 +32,14 @@ namespace Sortix { namespace Page { + bool Reserve(size_t* counter, size_t amount); + bool Reserve(size_t* counter, size_t least, size_t ideal); + addr_t GetReserved(size_t* counter); addr_t Get(); void Put(addr_t page); + inline size_t Size() { return 4096UL; } + // Rounds a memory address down to nearest page. inline addr_t AlignDown(addr_t page) { return page & ~(0xFFFUL); } @@ -53,14 +58,17 @@ namespace Sortix addr_t Fork(); addr_t SwitchAddressSpace(addr_t addrspace); void DestroyAddressSpace(); - bool MapRangeKernel(addr_t where, size_t bytes); - void UnmapRangeKernel(addr_t where, size_t bytes); - bool MapRangeUser(addr_t where, size_t bytes); - void UnmapRangeUser(addr_t where, size_t bytes); - bool MapKernel(addr_t physical, addr_t mapto); - bool MapUser(addr_t physical, addr_t mapto); - addr_t UnmapKernel(addr_t mapto); - addr_t UnmapUser(addr_t mapto); + bool Map(addr_t physical, addr_t mapto, int prot); + addr_t Unmap(addr_t mapto); + addr_t Physical(addr_t mapto); + int PageProtection(addr_t mapto); + bool LookUp(addr_t mapto, addr_t* physical, int* prot); + int ProvidedProtection(int prot); + void PageProtect(addr_t mapto, int protection); + void PageProtectAdd(addr_t mapto, int protection); + void PageProtectSub(addr_t mapto, int protection); + bool MapRange(addr_t where, size_t bytes, int protection); + bool UnmapRange(addr_t where, size_t bytes); void Statistics(size_t* amountused, size_t* totalmem); addr_t GetKernelStack(); size_t GetKernelStackSize(); diff --git a/sortix/include/sortix/mman.h b/sortix/include/sortix/mman.h new file mode 100644 index 00000000..2281abe6 --- /dev/null +++ b/sortix/include/sortix/mman.h @@ -0,0 +1,47 @@ +/******************************************************************************* + + COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2012. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + sortix/mman.h + Memory management declarations. + +*******************************************************************************/ + +#ifndef SORTIX_MMAN_H +#define SORTIX_MMAN_H + +/* Note that not all combinations of the following may be possible on all + architectures. However, you do get at least as much access as you request. */ + +#define PROT_NONE (0) + +/* Flags that control user-space access to memory. */ +#define PROT_EXEC (1<<0) +#define PROT_WRITE (1<<1) +#define PROT_READ (1<<2) +#define PROT_USER (PROT_EXEC | PROT_WRITE | PROT_READ) + +/* Flags that control kernel access to memory. */ +#define PROT_KEXEC (1<<3) +#define PROT_KWRITE (1<<4) +#define PROT_KREAD (1<<5) +#define PROT_KERNEL (PROT_KEXEC | PROT_KWRITE | PROT_KREAD) + +#define PROT_FORK (1<<6) + +#endif diff --git a/sortix/initrd.cpp b/sortix/initrd.cpp index e24f67f5..bb52699d 100644 --- a/sortix/initrd.cpp +++ b/sortix/initrd.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -188,7 +189,7 @@ void Init(addr_t phys, size_t size) size_t amount = 0; while ( amount < size ) { - if ( !Memory::MapKernel(phys + amount, virt + amount) ) + if ( !Memory::Map(phys + amount, virt + amount, PROT_KREAD) ) { Panic("Unable to map the init ramdisk into virtual memory"); } diff --git a/sortix/memorymanagement.cpp b/sortix/memorymanagement.cpp new file mode 100644 index 00000000..29b4a61e --- /dev/null +++ b/sortix/memorymanagement.cpp @@ -0,0 +1,53 @@ +/******************************************************************************* + + COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011, 2012. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + memorymanagement.cpp + Functions that allow modification of virtual memory. + +*******************************************************************************/ + +#include +#include +#include "syscall.h" + +namespace Sortix { +namespace Memory { + +int SysMemStat(size_t* memused, size_t* memtotal) +{ + size_t used; + size_t total; + Statistics(&used, &total); + // TODO: Check if legal user-space buffers! + *memused = used; + *memtotal = total; + return 0; +} + +void InitCPU(multiboot_info_t* bootinfo); + +void Init(multiboot_info_t* bootinfo) +{ + InitCPU(bootinfo); + + Syscall::Register(SYSCALL_MEMSTAT, (void*) SysMemStat); +} + +} // namespace Memory +} // namespace Sortix diff --git a/sortix/process.cpp b/sortix/process.cpp index cc285e33..8e984b86 100644 --- a/sortix/process.cpp +++ b/sortix/process.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -129,7 +130,7 @@ namespace Sortix ProcessSegment* tmp = segments; while ( tmp != NULL ) { - Memory::UnmapRangeUser(tmp->position, tmp->size); + Memory::UnmapRange(tmp->position, tmp->size); ProcessSegment* todelete = tmp; tmp = tmp->next; delete todelete; @@ -807,7 +808,7 @@ namespace Sortix if ( unmapfrom < currentend ) { size_t unmapbytes = Page::AlignUp(currentend - unmapfrom); - Memory::UnmapRangeUser(unmapfrom, unmapbytes); + Memory::UnmapRange(unmapfrom, unmapbytes); } } else if ( currentend < newend ) @@ -818,7 +819,8 @@ namespace Sortix if ( mapfrom < newend ) { size_t mapbytes = Page::AlignUp(newend - mapfrom); - if ( !Memory::MapRangeUser(mapfrom, mapbytes) ) + int prot = PROT_FORK | PROT_READ | PROT_WRITE | PROT_KREAD | PROT_KWRITE; + if ( !Memory::MapRange(mapfrom, mapbytes, prot) ) { return (void*) -1UL; } diff --git a/sortix/scheduler.cpp b/sortix/scheduler.cpp index 2672a497..6bc109ea 100644 --- a/sortix/scheduler.cpp +++ b/sortix/scheduler.cpp @@ -23,6 +23,7 @@ ******************************************************************************/ #include +#include #include #include #include "thread.h" @@ -82,7 +83,8 @@ namespace Sortix addr_t stackstart = Memory::GetKernelStack(); size_t stacksize = Memory::GetKernelStackSize(); addr_t stackend = stackstart - stacksize; - if ( !Memory::MapRangeKernel(stackend, stacksize) ) + int prot = PROT_KREAD | PROT_KWRITE; + if ( !Memory::MapRange(stackend, stacksize, prot) ) { PanicF("could not create kernel stack (%zx to %zx)", stackend, stackstart); diff --git a/sortix/thread.cpp b/sortix/thread.cpp index 6ff772a5..83ef1901 100644 --- a/sortix/thread.cpp +++ b/sortix/thread.cpp @@ -23,6 +23,7 @@ ******************************************************************************/ #include +#include #include #include #include "event.h" @@ -106,7 +107,7 @@ namespace Sortix delete todelete; } - Memory::UnmapRangeUser(stackpos, stacksize); + Memory::UnmapRange(stackpos, stacksize); terminated = true; } @@ -152,7 +153,8 @@ namespace Sortix addr_t stackpos = process->AllocVirtualAddr(stacksize); if ( !stackpos ) { return NULL; } - if ( !Memory::MapRangeUser(stackpos, stacksize) ) + int prot = PROT_FORK | PROT_READ | PROT_WRITE | PROT_KREAD | PROT_KWRITE; + if ( !Memory::MapRange(stackpos, stacksize, prot) ) { // TODO: Free the reserved virtual memory area. return NULL; @@ -161,7 +163,7 @@ namespace Sortix Thread* thread = new Thread(); if ( !thread ) { - Memory::UnmapRangeUser(stackpos, stacksize); + Memory::UnmapRange(stackpos, stacksize); // TODO: Free the reserved virtual memory area. return NULL; } diff --git a/sortix/x86-family/memorymanagement.cpp b/sortix/x86-family/memorymanagement.cpp index 7fde19d1..e8a6ef1e 100644 --- a/sortix/x86-family/memorymanagement.cpp +++ b/sortix/x86-family/memorymanagement.cpp @@ -1,6 +1,6 @@ -/****************************************************************************** +/******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of Sortix. @@ -14,19 +14,20 @@ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along - with Sortix. If not, see . + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . memorymanagement.cpp Handles memory for the x86 family of architectures. -******************************************************************************/ +*******************************************************************************/ #include #include #include #include "multiboot.h" #include +#include #include #include "memorymanagement.h" #include "syscall.h" @@ -43,6 +44,7 @@ namespace Sortix void InitPushRegion(addr_t position, size_t length); size_t pagesnotonstack; size_t stackused; + size_t stackreserved; size_t stacklength; size_t totalmem; } @@ -56,7 +58,7 @@ namespace Sortix int SysMemStat(size_t* memused, size_t* memtotal); addr_t PAT2PMLFlags[PAT_NUM]; - void Init(multiboot_info_t* bootinfo) + void InitCPU(multiboot_info_t* bootinfo) { const size_t MAXKERNELEND = 0x400000UL; /* 4 MiB */ addr_t kernelend = Page::AlignUp((addr_t) &end); @@ -68,6 +70,7 @@ namespace Sortix MAXKERNELEND); } + Page::stackreserved = 0; Page::pagesnotonstack = 0; Page::totalmem = 0; @@ -108,7 +111,7 @@ namespace Sortix // Loop over every detected memory region. for ( - mmap_t mmap = (mmap_t) bootinfo->mmap_addr; + mmap_t mmap = (mmap_t) (addr_t) bootinfo->mmap_addr; (addr_t) mmap < bootinfo->mmap_addr + bootinfo->mmap_length; mmap = (mmap_t) ((addr_t) mmap + mmap->size + sizeof(mmap->size)) ) @@ -145,7 +148,7 @@ namespace Sortix // Don't give any of our modules to the physical page // allocator, we'll need them. bool continuing = false; - uint32_t* modules = (uint32_t*) bootinfo->mods_addr; + uint32_t* modules = (uint32_t*) (addr_t) bootinfo->mods_addr; for ( uint32_t i = 0; i < bootinfo->mods_count; i++ ) { size_t modsize = (size_t) (modules[2*i+1] - modules[2*i+0]); @@ -188,29 +191,16 @@ namespace Sortix // Finish allocating the top level PMLs for the kernels use. AllocateKernelPMLs(); - - Syscall::Register(SYSCALL_MEMSTAT, (void*) SysMemStat); } void Statistics(size_t* amountused, size_t* totalmem) { - size_t memfree = Page::stackused << 12UL; + size_t memfree = (Page::stackused - Page::stackreserved) << 12UL; size_t memused = Page::totalmem - memfree; if ( amountused ) { *amountused = memused; } if ( totalmem ) { *totalmem = Page::totalmem; } } - int SysMemStat(size_t* memused, size_t* memtotal) - { - size_t used; - size_t total; - Statistics(&used, &total); - // TODO: Check if legal user-space buffers! - *memused = used; - *memtotal = total; - return 0; - } - // Prepare the non-forkable kernel PMLs such that forking the kernel // address space will always keep the kernel mapped. void AllocateKernelPMLs() @@ -249,7 +239,8 @@ namespace Sortix // This call will also succeed, since there are plenty of physical // pages available and it might need some. - if ( !Memory::MapKernel(page, (addr_t) (STACK + stacklength)) ) + addr_t virt = (addr_t) (STACK + stacklength); + if ( !Memory::Map(page, virt, PROT_KREAD | PROT_KWRITE) ) { Panic("Unable to extend page stack, which should have worked"); } @@ -280,16 +271,49 @@ namespace Sortix ExtendStack(); } - STACK[stackused++] = position; + addr_t* stackentry = &(STACK[stackused++]); + *stackentry = position; length -= 4096UL; position += 4096UL; } } + bool Reserve(size_t* counter, size_t least, size_t ideal) + { + ASSERT(least < ideal); + size_t available = stackused - stackreserved; + if ( least < available ) { Error::Set(ENOMEM); return false; } + if ( available < ideal ) { ideal = available; } + stackreserved += ideal; + *counter += ideal; + return true; + } + + bool Reserve(size_t* counter, size_t amount) + { + return Reserve(counter, amount, amount); + } + + addr_t GetReserved(size_t* counter) + { + if ( !*counter ) { return false; } + ASSERT(stackused); // After all, we did _reserve_ the memory. + addr_t result = STACK[--stackused]; + ASSERT(result == AlignDown(result)); + stackreserved--; + (*counter)--; + return result; + } + addr_t Get() { - if ( unlikely(stackused == 0) ) { Error::Set(ENOMEM); return 0; } + ASSERT(stackreserved <= stackused); + if ( unlikely(stackreserved == stackused) ) + { + Error::Set(ENOMEM); + return 0; + } addr_t result = STACK[--stackused]; ASSERT(result == AlignDown(result)); return result; @@ -305,6 +329,76 @@ namespace Sortix namespace Memory { + addr_t ProtectionToPMLFlags(int prot) + { + addr_t result = 0; + if ( prot & PROT_EXEC ) { result |= PML_USERSPACE; } + if ( prot & PROT_READ ) { result |= PML_USERSPACE; } + if ( prot & PROT_WRITE ) { result |= PML_USERSPACE | PML_WRITABLE; } + if ( prot & PROT_KEXEC ) { result |= 0; } + if ( prot & PROT_KREAD ) { result |= 0; } + if ( prot & PROT_KWRITE ) { result |= PML_WRITABLE; } + if ( prot & PROT_FORK ) { result |= PML_FORK; } + return result; + } + + int PMLFlagsToProtection(addr_t flags) + { + int prot = PROT_KREAD | PROT_KEXEC; + bool user = flags & PML_USERSPACE; + bool write = flags & PML_WRITABLE; + if ( user ) { prot |= PROT_EXEC | PROT_READ; } + if ( user && write ) { prot |= PROT_WRITE; } + return prot; + } + + int ProvidedProtection(int prot) + { + addr_t flags = ProtectionToPMLFlags(prot); + return PMLFlagsToProtection(flags); + } + + bool LookUp(addr_t mapto, addr_t* physical, int* protection) + { + // Translate the virtual address into PML indexes. + const size_t MASK = (1<> (12+(i-1)*TRANSBITS)) & MASK; + } + + int prot = PROT_USER | PROT_KERNEL | PROT_FORK; + + // For each PML level, make sure it exists. + size_t offset = 0; + for ( size_t i = TOPPMLLEVEL; i > 1; i-- ) + { + size_t childid = pmlchildid[i]; + PML* pml = PMLS[i] + offset; + + addr_t entry = pml->entry[childid]; + if ( !(entry & PML_PRESENT) ) { return false; } + int entryflags = entry & PML_ADDRESS; + int entryprot = PMLFlagsToProtection(entryflags); + prot &= entryprot; + + // Find the index of the next PML in the fractal mapped memory. + offset = offset * ENTRIES + childid; + } + + addr_t entry = (PMLS[1] + offset)->entry[pmlchildid[1]]; + int entryflags = entry & PML_ADDRESS; + int entryprot = PMLFlagsToProtection(entryflags); + prot &= entryprot; + addr_t phys = entry & PML_ADDRESS; + + if ( physical ) { *physical = phys; } + if ( protection ) { *protection = prot; } + + return true; + } + void InvalidatePage(addr_t /*addr*/) { // TODO: Actually just call the instruction. @@ -342,7 +436,7 @@ namespace Sortix return previous; } - bool MapRangeKernel(addr_t where, size_t bytes) + bool MapRange(addr_t where, size_t bytes, int protection) { for ( addr_t page = where; page < where + bytes; page += 4096UL ) { @@ -352,61 +446,31 @@ namespace Sortix while ( where < page ) { page -= 4096UL; - physicalpage = UnmapKernel(page); + physicalpage = Unmap(page); Page::Put(physicalpage); } return false; } - MapKernel(physicalpage, page); + Map(physicalpage, page, protection); } return true; } - void UnmapRangeKernel(addr_t where, size_t bytes) + bool UnmapRange(addr_t where, size_t bytes) { for ( addr_t page = where; page < where + bytes; page += 4096UL ) { - addr_t physicalpage = UnmapKernel(page); + addr_t physicalpage = Unmap(page); Page::Put(physicalpage); } - } - - bool MapRangeUser(addr_t where, size_t bytes) - { - for ( addr_t page = where; page < where + bytes; page += 4096UL ) - { - addr_t physicalpage = Page::Get(); - if ( physicalpage == 0 || !MapUser(physicalpage, page) ) - { - while ( where < page ) - { - page -= 4096UL; - physicalpage = UnmapUser(page); - Page::Put(physicalpage); - } - return false; - } - } - return true; } - void UnmapRangeUser(addr_t where, size_t bytes) + static bool MapInternal(addr_t physical, addr_t mapto, int prot, addr_t extraflags = 0) { - for ( addr_t page = where; page < where + bytes; page += 4096UL ) - { - addr_t physicalpage = UnmapUser(page); - Page::Put(physicalpage); - } - } - - template - bool Map(addr_t physical, addr_t mapto, addr_t extraflags = 0) - { - const addr_t userflags = userspace ? (PML_USERSPACE | PML_FORK) : 0; - const addr_t flags = userflags | PML_PRESENT | PML_WRITABLE; + addr_t flags = ProtectionToPMLFlags(prot) | PML_PRESENT; // Translate the virtual address into PML indexes. const size_t MASK = (1<> (12+(i-1)*TRANSBITS)) & MASK; } - // For each PML level, make sure it exists, and that we may use it. + // For each PML level, make sure it exists. size_t offset = 0; for ( size_t i = TOPPMLLEVEL; i > 1; i-- ) { @@ -432,21 +496,17 @@ namespace Sortix { // TODO: Possible memory leak when page allocation fails. addr_t page = Page::Get(); - if ( page == 0 ) { return false; } - entry = page | flags | extraflags; + + if ( !page ) { return false; } + addr_t pmlflags = PML_PRESENT | PML_WRITABLE | PML_USERSPACE + | PML_FORK; + entry = page | pmlflags; // Invalidate the new PML and reset it to zeroes. addr_t pmladdr = (addr_t) (PMLS[i-1] + childoffset); InvalidatePage(pmladdr); Maxsi::Memory::Set((void*) pmladdr, 0, sizeof(PML)); } - else if ( userspace && !(entry & PML_USERSPACE) ) - { - PanicF("attempted to map physical page %p to virtual page " - "%p with userspace permissions, but the virtual page " - "wasn't in an userspace PML[%zu]. This is a bug in the " - "code calling this function", physical, mapto, i-1); - } offset = childoffset; } @@ -454,16 +514,39 @@ namespace Sortix // Actually map the physical page to the virtual page. const addr_t entry = physical | flags | extraflags; (PMLS[1] + offset)->entry[pmlchildid[1]] = entry; - - if ( invalidate ) - { - InvalidatePage(mapto); - } - return true; } - template + bool Map(addr_t physical, addr_t mapto, int prot) + { + return MapInternal(physical, mapto, prot); + } + + void PageProtect(addr_t mapto, int protection) + { + addr_t phys; + LookUp(mapto, &phys, NULL); + Map(phys, mapto, protection); + } + + void PageProtectAdd(addr_t mapto, int protection) + { + addr_t phys; + int prot; + LookUp(mapto, &phys, &prot); + prot |= protection; + Map(phys, mapto, prot); + } + + void PageProtectSub(addr_t mapto, int protection) + { + addr_t phys; + int prot; + LookUp(mapto, &phys, &prot); + prot &= ~protection; + Map(phys, mapto, prot); + } + addr_t Unmap(addr_t mapto) { // Translate the virtual address into PML indexes. @@ -474,8 +557,7 @@ namespace Sortix pmlchildid[i] = (mapto >> (12+(i-1)*TRANSBITS)) & MASK; } - // For each PML level, make sure it exists, and that it belongs to - // user-space. + // For each PML level, make sure it exists. size_t offset = 0; for ( size_t i = TOPPMLLEVEL; i > 1; i-- ) { @@ -486,15 +568,9 @@ namespace Sortix if ( !(entry & PML_PRESENT) ) { - PanicF("attempted to unmap virtual page %p with userspace, " - " but the virtual page wasn't mapped. This is a bug " - "in the code calling this function", mapto); - } - else if ( userspace && !(entry & PML_USERSPACE) ) - { - PanicF("attempted to unmap virtual page %p it wasn't in an " - "userspace PML[%zu]. This is a bug in the code " - "calling this function", mapto, i-1); + PanicF("Attempted to unmap virtual page %p, but the virtual" + " page was wasn't mapped. This is a bug in the code " + "code calling this function", mapto); } // Find the index of the next PML in the fractal mapped memory. @@ -508,44 +584,13 @@ namespace Sortix // TODO: If all the entries in PML[N] are not-present, then who // unmaps its entry from PML[N-1]? - if ( invalidate ) - { - Flush(); - } - return result; } - bool MapKernelPAT(addr_t physical, addr_t mapto, addr_t mtype) + bool MapPAT(addr_t physical, addr_t mapto, int prot, addr_t mtype) { addr_t extraflags = PAT2PMLFlags[mtype]; - return Map(physical, mapto, extraflags); - } - - bool MapKernel(addr_t physical, addr_t mapto) - { - return MapKernelPAT(physical, mapto, PAT_WB); - } - - bool MapUserPAT(addr_t physical, addr_t mapto, addr_t mtype) - { - addr_t extraflags = PAT2PMLFlags[mtype]; - return Map(physical, mapto, extraflags); - } - - bool MapUser(addr_t physical, addr_t mapto) - { - return MapUserPAT(physical, mapto, PAT_WB); - } - - addr_t UnmapKernel(addr_t mapto) - { - return Unmap(mapto); - } - - addr_t UnmapUser(addr_t mapto) - { - return Unmap(mapto); + return MapInternal(physical, mapto, prot, extraflags); } void ForkCleanup(size_t i, size_t level) @@ -560,7 +605,7 @@ namespace Sortix if ( 1 < level ) { addr_t destaddr = (addr_t) (FORKPML + level-1); - MapKernel(phys, destaddr); + Map(phys, destaddr, PROT_KREAD | PROT_KWRITE); InvalidatePage(destaddr); ForkCleanup(ENTRIES+1UL, level-1); } @@ -592,7 +637,7 @@ namespace Sortix // Map the destination page. addr_t destaddr = (addr_t) (FORKPML + level-1); - MapKernel(phys, destaddr); + Map(phys, destaddr, PROT_KREAD | PROT_KWRITE); InvalidatePage(destaddr); size_t offset = pmloffset * ENTRIES + i; @@ -625,7 +670,7 @@ namespace Sortix PML* destpml = FORKPML + level; // This call always succeeds. - MapKernel(dir, (addr_t) destpml); + Map(dir, (addr_t) destpml, PROT_KREAD | PROT_KWRITE); InvalidatePage((addr_t) destpml); return Fork(level, pmloffset); @@ -649,7 +694,7 @@ namespace Sortix for ( size_t i = TOPPMLLEVEL-1; i > 0; i-- ) { mapto = (addr_t) (FORKPML + i); - MapKernel(childaddr, mapto); + Map(childaddr, mapto, PROT_KREAD | PROT_KWRITE); InvalidatePage(mapto); (FORKPML + i)->entry[ENTRIES-1] = dir | flags; childaddr = (FORKPML + i)->entry[ENTRIES-2] & PML_ADDRESS; diff --git a/sortix/x86-family/memorymanagement.h b/sortix/x86-family/memorymanagement.h index d6c1752f..e35ca1c7 100644 --- a/sortix/x86-family/memorymanagement.h +++ b/sortix/x86-family/memorymanagement.h @@ -1,6 +1,6 @@ /****************************************************************************** - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of Sortix. @@ -86,8 +86,9 @@ namespace Sortix if ( pat & 0x4 ) { result |= PML_PAT; } return result; } - bool MapKernelPAT(addr_t physical, addr_t mapto, addr_t mtype); - bool MapUserPAT(addr_t physical, addr_t mapto, addr_t mtype); + bool Map(addr_t physical, addr_t mapto, int prot, addr_t mtype); + addr_t ProtectionToPMLFlags(int prot); + int PMLFlagsToProtection(addr_t flags); } }