2023-01-13 18:05:20 +00:00
|
|
|
#include "memory/UserVM.h"
|
|
|
|
#include "Log.h"
|
|
|
|
#include "arch/MMU.h"
|
|
|
|
#include "memory/Heap.h"
|
2023-03-18 22:45:48 +00:00
|
|
|
#include <luna/CString.h>
|
2023-01-13 18:05:20 +00:00
|
|
|
#include <luna/ScopeGuard.h>
|
|
|
|
|
2023-06-17 23:48:36 +00:00
|
|
|
static constexpr u64 VM_START = ARCH_PAGE_SIZE;
|
|
|
|
static constexpr u64 VM_END = 0x0000800000000000;
|
2023-01-13 18:05:20 +00:00
|
|
|
|
|
|
|
Result<OwnedPtr<UserVM>> UserVM::try_create()
|
|
|
|
{
|
2023-06-17 23:48:36 +00:00
|
|
|
OwnedPtr<UserVM> ptr = TRY(make_owned<UserVM>());
|
2023-01-13 18:05:20 +00:00
|
|
|
|
2023-06-17 23:48:36 +00:00
|
|
|
TRY(ptr->create_null_region());
|
|
|
|
TRY(ptr->create_default_region());
|
2023-01-13 18:05:20 +00:00
|
|
|
|
|
|
|
return move(ptr);
|
|
|
|
}
|
|
|
|
|
2023-06-17 23:48:36 +00:00
|
|
|
Result<void> UserVM::create_null_region()
|
2023-03-18 22:45:48 +00:00
|
|
|
{
|
2023-06-17 23:48:36 +00:00
|
|
|
// Create a small region at the start of the address space to prevent anyone from mapping page 0.
|
|
|
|
auto* region = TRY(make<VMRegion>());
|
|
|
|
region->start = 0;
|
|
|
|
region->end = VM_START;
|
|
|
|
region->count = 1;
|
|
|
|
region->used = true;
|
|
|
|
region->persistent = true;
|
|
|
|
m_regions.append(region);
|
|
|
|
return {};
|
2023-03-18 22:45:48 +00:00
|
|
|
}
|
|
|
|
|
2023-06-17 23:48:36 +00:00
|
|
|
Result<void> UserVM::create_default_region()
|
2023-01-13 18:05:20 +00:00
|
|
|
{
|
2023-06-17 23:48:36 +00:00
|
|
|
// Create a free region covering the rest of the address space.
|
|
|
|
auto* region = TRY(make<VMRegion>());
|
|
|
|
region->start = VM_START;
|
|
|
|
region->end = VM_END;
|
|
|
|
region->count = (VM_END / ARCH_PAGE_SIZE) - 1;
|
|
|
|
region->used = false;
|
|
|
|
m_regions.append(region);
|
|
|
|
return {};
|
2023-01-13 18:05:20 +00:00
|
|
|
}
|
|
|
|
|
2023-06-17 23:48:36 +00:00
|
|
|
Result<OwnedPtr<UserVM>> UserVM::clone()
|
2023-01-13 18:05:20 +00:00
|
|
|
{
|
2023-06-17 23:48:36 +00:00
|
|
|
OwnedPtr<UserVM> ptr = TRY(make_owned<UserVM>());
|
2023-01-13 18:05:20 +00:00
|
|
|
|
2023-06-17 23:48:36 +00:00
|
|
|
for (const auto* region : m_regions)
|
|
|
|
{
|
|
|
|
auto* copied_region = TRY(make<VMRegion>());
|
|
|
|
copied_region->start = region->start;
|
|
|
|
copied_region->end = region->end;
|
|
|
|
copied_region->count = region->count;
|
|
|
|
copied_region->used = region->used;
|
|
|
|
copied_region->persistent = region->persistent;
|
|
|
|
ptr->m_regions.append(copied_region);
|
|
|
|
}
|
2023-01-13 18:05:20 +00:00
|
|
|
|
2023-06-17 23:48:36 +00:00
|
|
|
return move(ptr);
|
|
|
|
}
|
2023-01-13 18:05:20 +00:00
|
|
|
|
2023-06-17 23:48:36 +00:00
|
|
|
UserVM::UserVM()
|
|
|
|
{
|
2023-01-13 18:05:20 +00:00
|
|
|
}
|
|
|
|
|
2023-06-17 23:48:36 +00:00
|
|
|
Result<u64> UserVM::alloc_region(usize count, bool persistent)
|
2023-01-13 18:05:20 +00:00
|
|
|
{
|
2023-06-17 23:48:36 +00:00
|
|
|
for (auto* region = m_regions.expect_last(); region; region = m_regions.previous(region).value_or(nullptr))
|
2023-01-13 18:05:20 +00:00
|
|
|
{
|
2023-06-17 23:48:36 +00:00
|
|
|
if (!region->used)
|
|
|
|
{
|
|
|
|
if (region->count < count) continue;
|
|
|
|
if (region->count == count)
|
|
|
|
{
|
|
|
|
region->used = true;
|
|
|
|
region->persistent = persistent;
|
|
|
|
u64 address = region->start;
|
|
|
|
try_merge_region_with_neighbors(region);
|
|
|
|
return address;
|
|
|
|
}
|
|
|
|
|
|
|
|
u64 boundary = region->end - (count * ARCH_PAGE_SIZE);
|
|
|
|
|
|
|
|
auto* new_region = TRY(split_region(region, boundary));
|
|
|
|
new_region->used = true;
|
|
|
|
new_region->persistent = persistent;
|
|
|
|
try_merge_region_with_neighbors(new_region);
|
|
|
|
|
|
|
|
return boundary;
|
|
|
|
}
|
2023-01-13 18:05:20 +00:00
|
|
|
}
|
|
|
|
|
2023-06-17 23:48:36 +00:00
|
|
|
return err(ENOMEM);
|
2023-01-13 18:05:20 +00:00
|
|
|
}
|
|
|
|
|
2023-06-17 23:48:36 +00:00
|
|
|
Result<bool> UserVM::set_region(u64 address, usize count, bool used)
|
2023-01-13 18:05:20 +00:00
|
|
|
{
|
2023-06-17 23:48:36 +00:00
|
|
|
if (address >= VM_END) return err(EINVAL);
|
|
|
|
|
|
|
|
u64 end = address + (count * ARCH_PAGE_SIZE);
|
|
|
|
|
|
|
|
for (auto* region : m_regions)
|
2023-01-13 18:05:20 +00:00
|
|
|
{
|
2023-06-17 23:48:36 +00:00
|
|
|
if (region->end < address) continue;
|
|
|
|
if (region->start > end) return false;
|
|
|
|
|
|
|
|
if (region->persistent) return false;
|
|
|
|
if (region->used == used)
|
|
|
|
{
|
|
|
|
if (used) return false;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (region->start >= address && region->end <= end)
|
|
|
|
{
|
|
|
|
region->used = used;
|
|
|
|
if (region->start == address && region->end == end)
|
|
|
|
{
|
|
|
|
try_merge_region_with_neighbors(region);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (region->end > end && region->start < address)
|
|
|
|
{
|
|
|
|
auto* middle_region = TRY(split_region(region, address));
|
|
|
|
TRY(split_region(middle_region, end));
|
|
|
|
middle_region->used = used;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (region->start < address)
|
|
|
|
{
|
|
|
|
bool finished = region->end == end;
|
|
|
|
auto* split = TRY(split_region(region, address));
|
|
|
|
split->used = used;
|
|
|
|
try_merge_region_with_neighbors(split);
|
|
|
|
if (!finished) continue;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (region->end > end)
|
|
|
|
{
|
|
|
|
TRY(split_region(region, end));
|
|
|
|
region->used = used;
|
|
|
|
try_merge_region_with_neighbors(region);
|
|
|
|
return true;
|
|
|
|
}
|
2023-01-13 18:05:20 +00:00
|
|
|
}
|
|
|
|
|
2023-06-17 23:48:36 +00:00
|
|
|
return true;
|
2023-01-13 18:05:20 +00:00
|
|
|
}
|
|
|
|
|
2023-06-17 23:48:36 +00:00
|
|
|
void UserVM::merge_contiguous_regions(VMRegion* a, VMRegion* b)
|
2023-01-13 18:05:20 +00:00
|
|
|
{
|
2023-06-17 23:48:36 +00:00
|
|
|
a->end = b->end;
|
|
|
|
a->count += b->count;
|
|
|
|
m_regions.remove(b);
|
|
|
|
delete b;
|
|
|
|
}
|
2023-01-13 18:05:20 +00:00
|
|
|
|
2023-06-17 23:48:36 +00:00
|
|
|
void UserVM::try_merge_region_with_neighbors(VMRegion* region)
|
|
|
|
{
|
|
|
|
auto prev = m_regions.previous(region);
|
|
|
|
if (prev.has_value() && (*prev)->used == region->used && (*prev)->persistent == region->persistent)
|
|
|
|
{
|
|
|
|
merge_contiguous_regions(*prev, region);
|
|
|
|
region = *prev;
|
|
|
|
}
|
2023-01-13 18:05:20 +00:00
|
|
|
|
2023-06-17 23:48:36 +00:00
|
|
|
auto next = m_regions.next(region);
|
|
|
|
if (next.has_value() && (*next)->used == region->used && (*next)->persistent == region->persistent)
|
|
|
|
{
|
|
|
|
merge_contiguous_regions(region, *next);
|
|
|
|
}
|
2023-01-13 18:05:20 +00:00
|
|
|
}
|
|
|
|
|
2023-06-17 23:48:36 +00:00
|
|
|
Result<VMRegion*> UserVM::split_region(VMRegion* parent, u64 boundary)
|
2023-01-13 18:05:20 +00:00
|
|
|
{
|
2023-06-17 23:48:36 +00:00
|
|
|
auto* region = TRY(make<VMRegion>());
|
2023-01-13 18:05:20 +00:00
|
|
|
|
2023-06-17 23:48:36 +00:00
|
|
|
region->start = boundary;
|
|
|
|
region->end = parent->end;
|
|
|
|
region->count = (region->end - region->start) / ARCH_PAGE_SIZE;
|
|
|
|
region->used = parent->used;
|
|
|
|
region->persistent = parent->persistent;
|
|
|
|
m_regions.add_after(parent, region);
|
2023-01-13 18:05:20 +00:00
|
|
|
|
2023-06-17 23:48:36 +00:00
|
|
|
parent->end = boundary;
|
|
|
|
parent->count -= region->count;
|
2023-01-13 18:05:20 +00:00
|
|
|
|
2023-06-17 23:48:36 +00:00
|
|
|
return region;
|
2023-01-13 18:05:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
UserVM::~UserVM()
|
|
|
|
{
|
2023-06-17 23:48:36 +00:00
|
|
|
m_regions.consume([](VMRegion* region) { delete region; });
|
2023-01-13 18:05:20 +00:00
|
|
|
}
|