From 139c0b5eb1f2f8b4e2a1596a818e10084a8d6477 Mon Sep 17 00:00:00 2001 From: apio Date: Fri, 13 Jan 2023 19:05:20 +0100 Subject: [PATCH] Kernel: Make a UserVM wrapper around Bitmap and use that to allocate user VM This lets us allocate more than one page of memory from the user side. --- kernel/CMakeLists.txt | 1 + kernel/src/memory/UserVM.cpp | 115 +++++++++++++++++++++++++++++ kernel/src/memory/UserVM.h | 23 ++++++ kernel/src/sys/allocate_memory.cpp | 34 ++++----- kernel/src/thread/Scheduler.cpp | 7 +- kernel/src/thread/Thread.h | 5 +- 6 files changed, 157 insertions(+), 28 deletions(-) create mode 100644 kernel/src/memory/UserVM.cpp create mode 100644 kernel/src/memory/UserVM.h diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 6c259b6a..7e67fbbd 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -9,6 +9,7 @@ set(SOURCES src/memory/MemoryManager.cpp src/memory/Heap.cpp src/memory/KernelVM.cpp + src/memory/UserVM.cpp src/memory/MemoryMap.cpp src/boot/Init.cpp src/arch/Serial.cpp diff --git a/kernel/src/memory/UserVM.cpp b/kernel/src/memory/UserVM.cpp new file mode 100644 index 00000000..56c19e63 --- /dev/null +++ b/kernel/src/memory/UserVM.cpp @@ -0,0 +1,115 @@ +#include "memory/UserVM.h" +#include "Log.h" +#include "arch/MMU.h" +#include "memory/Heap.h" +#include + +static constexpr u64 VM_BASE = 0x10000000; + +static constexpr usize INITIAL_VM_SIZE = 80; +static constexpr usize MAX_VM_SIZE = 1024 * 1024 * 16; + +Result> UserVM::try_create() +{ + void* const base = TRY(kmalloc(INITIAL_VM_SIZE)); + + auto guard = make_scope_guard([&] { kfree(base); }); + + OwnedPtr ptr = TRY(make_owned(base, INITIAL_VM_SIZE)); + + guard.deactivate(); + + return move(ptr); +} + +UserVM::UserVM(void* base, usize size) +{ + kdbgln("user vm created with base=%p, size=%zu", base, size); + m_bitmap.initialize(base, size); + m_bitmap.clear(false); +} + +Result UserVM::try_expand(usize size) +{ + if (m_bitmap.size_in_bytes() == MAX_VM_SIZE) { return false; } + + usize new_size = m_bitmap.size_in_bytes() + size; + + if (new_size > MAX_VM_SIZE) new_size = MAX_VM_SIZE; + + usize old_size = m_bitmap.size_in_bytes(); + + void* const base = TRY(krealloc(m_bitmap.location(), new_size)); + + m_bitmap.initialize(base, new_size); + m_bitmap.clear_region(old_size * 8, (new_size - old_size) * 8, false); + + kdbgln("user vm expanded to base=%p, size=%zu", base, new_size); + + return true; +} + +Result UserVM::alloc_one_page() +{ + u64 index; + const auto maybe_index = m_bitmap.find_and_toggle(false); + if (!maybe_index.has_value()) + { + bool success = TRY(try_expand()); + if (!success) return err(ENOMEM); + index = TRY(Result::from_option(m_bitmap.find_and_toggle(false), ENOMEM)); + } + else + index = maybe_index.value(); + + return VM_BASE + index * ARCH_PAGE_SIZE; +} + +Result UserVM::alloc_several_pages(usize count) +{ + u64 index; + const auto maybe_index = m_bitmap.find_and_toggle_region(false, count); + if (!maybe_index.has_value()) + { + bool success = TRY(try_expand((count / 8) + INITIAL_VM_SIZE)); + if (!success) return err(ENOMEM); + index = TRY(Result::from_option(m_bitmap.find_and_toggle_region(false, count), ENOMEM)); + } + else + index = maybe_index.value(); + + return VM_BASE + index * ARCH_PAGE_SIZE; +} + +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); + + m_bitmap.set(index, false); + + return {}; +} + +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); + + // FIXME: Is it necessary to check all pages? + if (!m_bitmap.get(index)) return err(EFAULT); + + m_bitmap.clear_region(index, count, false); + + return {}; +} + +UserVM::~UserVM() +{ + kdbgln("user vm destroyed: base=%p, size=%zu", m_bitmap.location(), m_bitmap.size_in_bytes()); + kfree(m_bitmap.location()); +} diff --git a/kernel/src/memory/UserVM.h b/kernel/src/memory/UserVM.h new file mode 100644 index 00000000..0c95e3e1 --- /dev/null +++ b/kernel/src/memory/UserVM.h @@ -0,0 +1,23 @@ +#pragma once +#include +#include +#include + +class UserVM +{ + public: + UserVM(void* base, usize size); + ~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); + + static Result> try_create(); + + private: + Result try_expand(usize size = 160); + Bitmap m_bitmap; +}; diff --git a/kernel/src/sys/allocate_memory.cpp b/kernel/src/sys/allocate_memory.cpp index 5203d551..91362e00 100644 --- a/kernel/src/sys/allocate_memory.cpp +++ b/kernel/src/sys/allocate_memory.cpp @@ -13,49 +13,41 @@ Result sys_allocate_memory(Registers*, SyscallArgs args) usize size = (usize)args[0]; int flags = (int)args[1]; - if (size != ARCH_PAGE_SIZE) return err(EINVAL); if (flags < 0) return err(EINVAL); - if (size == 0) return 0; + size = align_up(size); + Thread* current = Scheduler::current(); - if (!current->heap_bitmap.initialized()) - { - void* bitmap_location = (void*)TRY(MemoryManager::alloc_for_kernel(1, MMU::ReadWrite)); - current->heap_bitmap.initialize(bitmap_location, ARCH_PAGE_SIZE); - current->heap_bitmap.clear(false); - } - u64 index = TRY(Result::from_option(current->heap_bitmap.find_and_toggle(false), ENOMEM)); - - u64 address = USERSPACE_HEAP_BASE + (index * ARCH_PAGE_SIZE); + 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", address); + kdbgln("allocate_memory: allocating memory at %#lx, size=%zu", address, size); - return MemoryManager::alloc_at(address, 1, mmu_flags); + 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(); - if (!current->heap_bitmap.initialized()) return err(EFAULT); - u64 index = (address - USERSPACE_HEAP_BASE) / ARCH_PAGE_SIZE; + TRY(current->vm_allocator->free_several_pages(address, size / ARCH_PAGE_SIZE)); - if (!current->heap_bitmap.get(index)) return err(EFAULT); + kdbgln("deallocate_memory: deallocating memory at %#lx, size=%zu", address, size); - current->heap_bitmap.set(index, false); - - kdbgln("deallocate_memory: deallocating memory at %#lx", address); - - TRY(MemoryManager::unmap_owned(address, 1)); + TRY(MemoryManager::unmap_owned(address, size / ARCH_PAGE_SIZE)); return { 0 }; } diff --git a/kernel/src/thread/Scheduler.cpp b/kernel/src/thread/Scheduler.cpp index 79e2b56e..44b981fd 100644 --- a/kernel/src/thread/Scheduler.cpp +++ b/kernel/src/thread/Scheduler.cpp @@ -126,6 +126,8 @@ namespace Scheduler auto guard = make_scope_guard([&] { delete thread; }); + thread->vm_allocator = TRY(UserVM::try_create()); + PageDirectory* const directory = TRY(MMU::create_page_directory_for_userspace()); auto directory_guard = make_scope_guard([&] { @@ -178,11 +180,6 @@ namespace Scheduler if (!thread->is_kernel) MMU::delete_userspace_page_directory(thread->directory); - if (thread->heap_bitmap.initialized()) - MemoryManager::unmap_owned_and_free_vm( - (u64)thread->heap_bitmap.location(), - get_blocks_from_size(thread->heap_bitmap.size_in_bytes(), ARCH_PAGE_SIZE)); - delete thread; } diff --git a/kernel/src/thread/Thread.h b/kernel/src/thread/Thread.h index fbcae6db..72207b05 100644 --- a/kernel/src/thread/Thread.h +++ b/kernel/src/thread/Thread.h @@ -1,8 +1,9 @@ #pragma once #include "arch/MMU.h" -#include +#include "memory/UserVM.h" #include +#include #include #include @@ -36,7 +37,7 @@ struct Thread : public LinkedListNode Stack stack; Stack kernel_stack; - Bitmap heap_bitmap; + OwnedPtr vm_allocator; ThreadState state = ThreadState::Runnable;