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.
This commit is contained in:
apio 2023-01-13 19:05:20 +01:00
parent 7462b764d8
commit 139c0b5eb1
Signed by: apio
GPG Key ID: B8A7D06E42258954
6 changed files with 157 additions and 28 deletions

View File

@ -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

View File

@ -0,0 +1,115 @@
#include "memory/UserVM.h"
#include "Log.h"
#include "arch/MMU.h"
#include "memory/Heap.h"
#include <luna/ScopeGuard.h>
static constexpr u64 VM_BASE = 0x10000000;
static constexpr usize INITIAL_VM_SIZE = 80;
static constexpr usize MAX_VM_SIZE = 1024 * 1024 * 16;
Result<OwnedPtr<UserVM>> UserVM::try_create()
{
void* const base = TRY(kmalloc(INITIAL_VM_SIZE));
auto guard = make_scope_guard([&] { kfree(base); });
OwnedPtr<UserVM> ptr = TRY(make_owned<UserVM>(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<bool> 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<u64> 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<u64>::from_option(m_bitmap.find_and_toggle(false), ENOMEM));
}
else
index = maybe_index.value();
return VM_BASE + index * ARCH_PAGE_SIZE;
}
Result<u64> 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<u64>::from_option(m_bitmap.find_and_toggle_region(false, count), ENOMEM));
}
else
index = maybe_index.value();
return VM_BASE + index * ARCH_PAGE_SIZE;
}
Result<void> 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<void> 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());
}

View File

@ -0,0 +1,23 @@
#pragma once
#include <luna/Bitmap.h>
#include <luna/OwnedPtr.h>
#include <luna/Result.h>
class UserVM
{
public:
UserVM(void* base, usize size);
~UserVM();
Result<u64> alloc_one_page();
Result<u64> alloc_several_pages(usize count);
Result<void> free_one_page(u64 address);
Result<void> free_several_pages(u64 address, usize count);
static Result<OwnedPtr<UserVM>> try_create();
private:
Result<bool> try_expand(usize size = 160);
Bitmap m_bitmap;
};

View File

@ -13,49 +13,41 @@ Result<u64> 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<ARCH_PAGE_SIZE>(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<u64>::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<u64> sys_deallocate_memory(Registers*, SyscallArgs args)
{
u64 address = (u64)args[0];
usize size = (usize)args[1];
if (size == 0) return 0;
size = align_up<ARCH_PAGE_SIZE>(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 };
}

View File

@ -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;
}

View File

@ -1,8 +1,9 @@
#pragma once
#include "arch/MMU.h"
#include <luna/Bitmap.h>
#include "memory/UserVM.h"
#include <luna/LinkedList.h>
#include <luna/OwnedPtr.h>
#include <luna/Result.h>
#include <luna/Stack.h>
@ -36,7 +37,7 @@ struct Thread : public LinkedListNode<Thread>
Stack stack;
Stack kernel_stack;
Bitmap heap_bitmap;
OwnedPtr<UserVM> vm_allocator;
ThreadState state = ThreadState::Runnable;