diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 740fbc1a..bccf58c5 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -24,7 +24,7 @@ set(SOURCES src/sys/exit.cpp src/sys/console_write.cpp src/sys/clock_gettime.cpp - src/sys/allocate_memory.cpp + src/sys/mmap.cpp src/sys/usleep.cpp src/sys/open.cpp src/sys/file.cpp diff --git a/kernel/src/memory/UserVM.cpp b/kernel/src/memory/UserVM.cpp index 93dd04a0..e4d5172b 100644 --- a/kernel/src/memory/UserVM.cpp +++ b/kernel/src/memory/UserVM.cpp @@ -78,30 +78,33 @@ Result UserVM::alloc_several_pages(usize count) return VM_BASE + index * ARCH_PAGE_SIZE; } -Result UserVM::free_one_page(u64 address) +Result UserVM::free_one_page(u64 address) { if (address < VM_BASE) return err(EINVAL); const u64 index = (address - VM_BASE) / ARCH_PAGE_SIZE; if (index > (MAX_VM_SIZE * 8)) return err(EINVAL); - if (!m_bitmap.get(index)) return err(EFAULT); + // NOTE: POSIX says munmap() should silently do nothing if the address is not mapped, instead of throwing an error + // like EFAULT. + if (!m_bitmap.get(index)) return false; m_bitmap.set(index, false); - return {}; + return true; } -Result UserVM::free_several_pages(u64 address, usize count) +Result UserVM::free_several_pages(u64 address, usize count) { if (address < VM_BASE) return err(EINVAL); const u64 index = (address - VM_BASE) / ARCH_PAGE_SIZE; if ((index + count) > (MAX_VM_SIZE * 8)) return err(EINVAL); - if (!TRY(m_bitmap.try_match_region(index, count, true))) return err(EFAULT); + // NOTE: Same as above. + if (!TRY(m_bitmap.try_match_region(index, count, true))) return false; m_bitmap.clear_region(index, count, false); - return {}; + return true; } UserVM::~UserVM() diff --git a/kernel/src/memory/UserVM.h b/kernel/src/memory/UserVM.h index 0c95e3e1..0b57537d 100644 --- a/kernel/src/memory/UserVM.h +++ b/kernel/src/memory/UserVM.h @@ -12,8 +12,8 @@ class UserVM Result alloc_one_page(); Result alloc_several_pages(usize count); - Result free_one_page(u64 address); - Result free_several_pages(u64 address, usize count); + Result free_one_page(u64 address); + Result free_several_pages(u64 address, usize count); static Result> try_create(); diff --git a/kernel/src/sys/allocate_memory.cpp b/kernel/src/sys/allocate_memory.cpp deleted file mode 100644 index 91362e00..00000000 --- a/kernel/src/sys/allocate_memory.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "Log.h" -#include "arch/MMU.h" -#include "memory/MemoryManager.h" -#include "sys/Syscall.h" -#include "thread/Scheduler.h" -#include -#include - -constexpr uintptr_t USERSPACE_HEAP_BASE = 0x3000000; - -Result sys_allocate_memory(Registers*, SyscallArgs args) -{ - usize size = (usize)args[0]; - int flags = (int)args[1]; - - if (flags < 0) return err(EINVAL); - if (size == 0) return 0; - - size = align_up(size); - - Thread* current = Scheduler::current(); - - u64 address = TRY(current->vm_allocator->alloc_several_pages(size / ARCH_PAGE_SIZE)); - - int mmu_flags = MMU::User | MMU::NoExecute; - if (flags & PROT_WRITE) mmu_flags |= MMU::ReadWrite; - if (flags & PROT_EXEC) mmu_flags &= ~MMU::NoExecute; - if (flags == PROT_NONE) mmu_flags = MMU::NoExecute; - - kdbgln("allocate_memory: allocating memory at %#lx, size=%zu", address, size); - - return MemoryManager::alloc_at(address, size / ARCH_PAGE_SIZE, mmu_flags); -} - -Result sys_deallocate_memory(Registers*, SyscallArgs args) -{ - u64 address = (u64)args[0]; - usize size = (usize)args[1]; - - if (size == 0) return 0; - - size = align_up(size); - - Thread* current = Scheduler::current(); - - TRY(current->vm_allocator->free_several_pages(address, size / ARCH_PAGE_SIZE)); - - kdbgln("deallocate_memory: deallocating memory at %#lx, size=%zu", address, size); - - TRY(MemoryManager::unmap_owned(address, size / ARCH_PAGE_SIZE)); - - return { 0 }; -} diff --git a/kernel/src/sys/mmap.cpp b/kernel/src/sys/mmap.cpp new file mode 100644 index 00000000..7a111ecf --- /dev/null +++ b/kernel/src/sys/mmap.cpp @@ -0,0 +1,77 @@ +#include "Log.h" +#include "arch/MMU.h" +#include "memory/MemoryManager.h" +#include "sys/Syscall.h" +#include "thread/Scheduler.h" +#include +#include + +constexpr uintptr_t USERSPACE_HEAP_BASE = 0x3000000; + +Result sys_mmap(Registers*, SyscallArgs args) +{ + void* addr = (void*)args[0]; + usize len = (usize)args[1]; + int prot = (int)args[2]; + int flags = (int)args[3]; + + if (len == 0) return err(EINVAL); + + if (flags < 0) return err(EINVAL); + + // We support only anonymous mappings for now. + if ((flags & MAP_ANONYMOUS) != MAP_ANONYMOUS) + { + kwarnln("mmap: FIXME: attempt to mmap file instead of anonymous memory"); + return err(ENOTSUP); + } + + if (flags & MAP_SHARED) + { + kwarnln("mmap: FIXME: attempt to mmap shared memory"); + return err(ENOTSUP); + } + + len = align_up(len); + + Thread* current = Scheduler::current(); + + u64 address; + if (!addr) address = TRY(current->vm_allocator->alloc_several_pages(len / ARCH_PAGE_SIZE)); + else + { + kwarnln("mmap: FIXME: tried to mmap at a given address, instead of letting us choose"); + return err(ENOTSUP); + } + + int mmu_flags = MMU::User | MMU::NoExecute; + if (prot & PROT_WRITE) mmu_flags |= MMU::ReadWrite; + if (prot & PROT_EXEC) mmu_flags &= ~MMU::NoExecute; + if (prot == PROT_NONE) mmu_flags = MMU::NoExecute; + + kdbgln("mmap: mapping memory at %#lx, size=%zu", address, len); + + return MemoryManager::alloc_at(address, len / ARCH_PAGE_SIZE, mmu_flags); +} + +Result sys_munmap(Registers*, SyscallArgs args) +{ + u64 address = (u64)args[0]; + usize size = (usize)args[1]; + + if (size == 0) return err(EINVAL); + if (!is_aligned(size)) return err(EINVAL); + + Thread* current = Scheduler::current(); + + bool ok = TRY(current->vm_allocator->free_several_pages(address, size / ARCH_PAGE_SIZE)); + + // POSIX says munmap should silently do nothing if the memory was not already mapped. + if (!ok) return 0; + + kdbgln("munmap: unmapping memory at %#lx, size=%zu", address, size); + + TRY(MemoryManager::unmap_owned(address, size / ARCH_PAGE_SIZE)); + + return { 0 }; +} diff --git a/libc/include/bits/mmap-flags.h b/libc/include/bits/mmap-flags.h index 2a89537d..7be5e70c 100644 --- a/libc/include/bits/mmap-flags.h +++ b/libc/include/bits/mmap-flags.h @@ -8,4 +8,8 @@ #define PROT_WRITE 2 #define PROT_EXEC 4 +#define MAP_PRIVATE 0 +#define MAP_SHARED 1 +#define MAP_ANONYMOUS 2 + #endif diff --git a/libc/include/sys/mman.h b/libc/include/sys/mman.h index 967147da..97c7022c 100644 --- a/libc/include/sys/mman.h +++ b/libc/include/sys/mman.h @@ -15,8 +15,11 @@ extern "C" { #endif - void* allocate_memory(size_t size, int flags); - int deallocate_memory(void* address, size_t size); + /* Map a file into memory. */ + void* mmap(void* addr, size_t len, int prot, int flags, int fd, off_t offset); + + /* Remove a memory mapping. */ + int munmap(void* addr, size_t len); #ifdef __cplusplus } diff --git a/libc/src/sys/mman.cpp b/libc/src/sys/mman.cpp index 8f1f1fae..df3f2a31 100644 --- a/libc/src/sys/mman.cpp +++ b/libc/src/sys/mman.cpp @@ -7,29 +7,29 @@ extern "C" { - void* allocate_memory(size_t size, int flags) + void* mmap(void* addr, size_t len, int prot, int flags, int, off_t) { - long rc = syscall(SYS_allocate_memory, size, flags); + long rc = syscall(SYS_mmap, addr, len, prot, flags); __errno_return(rc, void*); } - int deallocate_memory(void* address, size_t size) + int munmap(void* addr, size_t len) { - long rc = syscall(SYS_deallocate_memory, address, size); + long rc = syscall(SYS_munmap, addr, len); __errno_return(rc, int); } } Result allocate_pages_impl(usize count) { - long rc = syscall(SYS_allocate_memory, count * PAGE_SIZE, PROT_READ | PROT_WRITE); + long rc = syscall(SYS_mmap, nullptr, count * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS); if (rc < 0) { return err((int)-rc); } return (void*)rc; } Result release_pages_impl(void* address, usize count) { - long rc = syscall(SYS_deallocate_memory, address, count * PAGE_SIZE); + long rc = syscall(SYS_munmap, address, count * PAGE_SIZE); if (rc < 0) { return err((int)-rc); } return {}; } diff --git a/libluna/include/luna/Syscall.h b/libluna/include/luna/Syscall.h index 85349469..0e65143e 100644 --- a/libluna/include/luna/Syscall.h +++ b/libluna/include/luna/Syscall.h @@ -1,8 +1,8 @@ #pragma once #define enumerate_syscalls(_e) \ - _e(exit) _e(console_write) _e(clock_gettime) _e(allocate_memory) _e(deallocate_memory) _e(usleep) _e(open) \ - _e(close) _e(read) _e(getpid) _e(write) _e(lseek) _e(mkdir) + _e(exit) _e(console_write) _e(clock_gettime) _e(mmap) _e(munmap) _e(usleep) _e(open) _e(close) _e(read) _e(getpid) \ + _e(write) _e(lseek) _e(mkdir) enum Syscalls {