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:
parent
7462b764d8
commit
139c0b5eb1
@ -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
|
||||
|
115
kernel/src/memory/UserVM.cpp
Normal file
115
kernel/src/memory/UserVM.cpp
Normal 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());
|
||||
}
|
23
kernel/src/memory/UserVM.h
Normal file
23
kernel/src/memory/UserVM.h
Normal 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;
|
||||
};
|
@ -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 };
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user