1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 16:37:35 +00:00

UserspaceEmulator: Implement a proper VM allocator

This patch brings Kernel::RangeAllocator to UserspaceEmulator in a
slightly simplified form.

It supports the basic three allocation types needed by virt$mmap():
allocate_anywhere, allocate_specific, and allocate_randomized.

Porting virt$mmap() and virt$munmap() to use the allocator makes
UE work correctly once again. :^)
This commit is contained in:
Andreas Kling 2021-02-06 23:08:51 +01:00
parent 9dacd7c0ec
commit 89483a9408
10 changed files with 438 additions and 36 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -32,6 +32,7 @@
#include <AK/Format.h>
#include <AK/LexicalPath.h>
#include <AK/MappedFile.h>
#include <AK/Random.h>
#include <LibELF/AuxiliaryVector.h>
#include <LibELF/Image.h>
#include <LibELF/Validation.h>
@ -81,6 +82,19 @@ Emulator::Emulator(const String& executable_path, const Vector<String>& argument
, m_cpu(*this)
{
m_malloc_tracer = make<MallocTracer>(*this);
static constexpr FlatPtr userspace_range_base = 0x00800000;
static constexpr FlatPtr userspace_range_ceiling = 0xbe000000;
#ifdef UE_ASLR
static constexpr FlatPtr page_mask = 0xfffff000u;
size_t random_offset = (AK::get_random<u8>() % 32 * MiB) & page_mask;
FlatPtr base = userspace_range_base + random_offset;
#else
FlatPtr base = userspace_range_base;
#endif
m_range_allocator.initialize_with_range(VirtualAddress(base), userspace_range_ceiling - base);
ASSERT(!s_the);
s_the = this;
// setup_stack(arguments, environment);
@ -972,41 +986,31 @@ u32 Emulator::virt$munmap(FlatPtr address, u32 size)
ASSERT(region);
if (region->size() != round_up_to_power_of_two(size, PAGE_SIZE))
TODO();
m_range_allocator.deallocate(region->range());
mmu().remove_region(*region);
return 0;
}
FlatPtr Emulator::allocate_vm(size_t size, size_t alignment)
{
// FIXME: Write a proper VM allocator
static FlatPtr next_address = 0x30000000;
FlatPtr final_address;
if (!alignment)
alignment = PAGE_SIZE;
// FIXME: What if alignment is not a power of 2?
final_address = round_up_to_power_of_two(next_address, alignment);
next_address = round_up_to_power_of_two(final_address + size, PAGE_SIZE);
return final_address;
}
u32 Emulator::virt$mmap(u32 params_addr)
{
Syscall::SC_mmap_params params;
mmu().copy_from_vm(&params, params_addr, sizeof(params));
u32 final_size = round_up_to_power_of_two(params.size, PAGE_SIZE);
u32 final_address = allocate_vm(final_size, params.alignment);
if (params.addr != 0) {
// NOTE: We currently do not support allocating VM at a requeted address in the emulator.
// The loader needs this functionality to load .data just after .text.
// Luckily, since the loader calls mmap for .data right after it calls mmap for .text,
// the emulator will allocate a chunk of memory that is just after what we allocated for .text
// because of the way we currently allocate VM.
ASSERT(params.addr == final_address);
u32 requested_size = round_up_to_power_of_two(params.size, PAGE_SIZE);
FlatPtr final_address;
Optional<Range> result;
if (params.flags & MAP_RANDOMIZED) {
result = m_range_allocator.allocate_randomized(requested_size, params.alignment);
} else if (params.flags & MAP_FIXED) {
result = m_range_allocator.allocate_specific(VirtualAddress { params.addr }, requested_size);
} else {
result = m_range_allocator.allocate_anywhere(requested_size, params.alignment);
}
if (!result.has_value())
return -ENOMEM;
final_address = result.value().base().get();
auto final_size = result.value().size();
if (params.flags & MAP_ANONYMOUS)
mmu().add_region(MmapRegion::create_anonymous(final_address, final_size, params.prot));