Move to mapping guest AS as shared memory

We want to create arbitrary mirrors in the guest address space and to make this possible, we map the entire address space as a shared memory file. A mirror is mapped by using `mmap` with the offset into the guest address space.
This commit is contained in:
PixelyIon 2022-02-27 04:35:15 +05:30
parent a5dd961f01
commit e35ab6d1e0
4 changed files with 44 additions and 10 deletions

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <android/sharedmem.h>
#include "memory.h"
#include "types/KProcess.h"
@ -61,7 +62,11 @@ namespace skyline::kernel {
if (!base.address)
throw exception("Cannot find a suitable carveout for the guest address space");
auto result{mmap(reinterpret_cast<void *>(base.address), base.size, PROT_NONE, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)};
memoryFd = ASharedMemory_create("HOS-AS", base.size);
if (memoryFd < 0)
throw exception("Failed to create shared memory for guest address space: {}", strerror(errno));
auto result{mmap(reinterpret_cast<void *>(base.address), base.size, PROT_NONE, MAP_FIXED | MAP_SHARED, memoryFd, 0)};
if (result == MAP_FAILED)
throw exception("Failed to mmap guest address space: {}", strerror(errno));
@ -135,6 +140,22 @@ namespace skyline::kernel {
.address + heap.size, heap.size, stack.address, stack.address + stack.size, stack.size, tlsIo.address, tlsIo.address + tlsIo.size, tlsIo.size);
}
span<u8> MemoryManager::CreateMirror(u8 *pointer, size_t size) {
auto address{reinterpret_cast<u64>(pointer)};
if (address < base.address || address + size > base.address + base.size)
throw exception("Mapping is outside of VMM base: 0x{:X} - 0x{:X}", address, address + size);
size_t offset{address - base.address};
if (!util::IsPageAligned(offset) || !util::IsPageAligned(size))
throw exception("Mapping is not aligned to a page: 0x{:X}-0x{:X} (0x{:X})", address, address + size, offset);
auto mirror{mmap(nullptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, memoryFd, static_cast<off_t>(offset))};
if (mirror == MAP_FAILED)
throw exception("Failed to create mirror mapping at 0x{:X}-0x{:X} (0x{:X}): {}", address, address + size, offset, strerror(errno));
return span<u8>{reinterpret_cast<u8 *>(mirror), size};
}
void MemoryManager::InsertChunk(const ChunkDescriptor &chunk) {
std::unique_lock lock(mutex);

View File

@ -210,7 +210,7 @@ namespace skyline {
};
/**
* @brief MemoryManager keeps track of guest virtual memory and its related attributes
* @brief MemoryManager allocates and keeps track of guest virtual memory and its related attributes
*/
class MemoryManager {
private:
@ -226,6 +226,8 @@ namespace skyline {
memory::Region stack{};
memory::Region tlsIo{}; //!< TLS/IO
int memoryFd{}; //!< The file descriptor of the memory backing for the entire guest address space
std::shared_mutex mutex; //!< Synchronizes any operations done on the VMM, it's locked in shared mode by readers and exclusive mode by writers
MemoryManager(const DeviceState &state);
@ -239,6 +241,13 @@ namespace skyline {
void InitializeRegions(u8 *codeStart, u64 size);
/**
* @brief Mirrors a page-aligned mapping in the guest address space to the host address space
* @return A span to the host address space mirror mapped as RWX, unmapping it is the responsibility of the caller
* @note The supplied mapping **must** be page-aligned and inside the guest address space
*/
span<u8> CreateMirror(u8* pointer, size_t size);
void InsertChunk(const ChunkDescriptor &chunk);
std::optional<ChunkDescriptor> Get(void *ptr);

View File

@ -11,7 +11,7 @@ namespace skyline::kernel::type {
KSharedMemory::KSharedMemory(const DeviceState &state, size_t size, memory::MemoryState memState, KType type)
: memoryState(memState),
KMemory(state, type) {
fd = ASharedMemory_create("KSharedMemory", size);
fd = ASharedMemory_create(type == KType::KSharedMemory ? "HOS-KSharedMemory" : "HOS-KTransferMemory", size);
if (fd < 0)
throw exception("An error occurred while creating shared memory: {}", fd);
@ -49,18 +49,19 @@ namespace skyline::kernel::type {
}
void KSharedMemory::Unmap(u8 *ptr, u64 size) {
if (!state.process->memory.base.IsInside(ptr) || !state.process->memory.base.IsInside(ptr + size))
auto &memoryManager{state.process->memory};
if (!memoryManager.base.IsInside(ptr) || !memoryManager.base.IsInside(ptr + size))
throw exception("KPrivateMemory allocation isn't inside guest address space: 0x{:X} - 0x{:X}", ptr, ptr + size);
if (!util::IsPageAligned(ptr) || !util::IsPageAligned(size))
throw exception("KSharedMemory mapping isn't page-aligned: 0x{:X} - 0x{:X} (0x{:X})", ptr, ptr + size, size);
if (guest.ptr != ptr && guest.size != size)
throw exception("Unmapping KSharedMemory partially is not supported: Requested Unmap: 0x{:X} - 0x{:X} (0x{:X}), Current Mapping: 0x{:X} - 0x{:X} (0x{:X})", ptr, ptr + size, size, guest.ptr, guest.ptr + guest.size, guest.size);
if (mmap(ptr, size, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, 0, 0) == MAP_FAILED)
if (mmap(ptr, size, PROT_NONE, MAP_SHARED | MAP_FIXED, memoryManager.memoryFd, reinterpret_cast<off_t>(ptr - memoryManager.base.address)) == MAP_FAILED)
throw exception("An error occurred while unmapping shared memory in guest: {}", strerror(errno));
guest = {};
state.process->memory.InsertChunk(ChunkDescriptor{
memoryManager.InsertChunk(ChunkDescriptor{
.ptr = ptr,
.size = size,
.state = memory::states::Unmapped,
@ -90,8 +91,11 @@ namespace skyline::kernel::type {
KSharedMemory::~KSharedMemory() {
if (state.process && guest.Valid()) {
auto &memoryManager{state.process->memory};
if (objectType != KType::KTransferMemory) {
mmap(guest.ptr, guest.size, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); // It doesn't particularly matter if this fails as it shouldn't really affect anything
if (mmap(guest.ptr, guest.size, PROT_NONE, MAP_SHARED | MAP_FIXED, memoryManager.memoryFd, reinterpret_cast<off_t>(guest.ptr - memoryManager.base.address)) == MAP_FAILED)
Logger::Warn("An error occurred while unmapping shared memory: {}", strerror(errno));
state.process->memory.InsertChunk(ChunkDescriptor{
.ptr = guest.ptr,
.size = guest.size,
@ -101,8 +105,8 @@ namespace skyline::kernel::type {
// KTransferMemory remaps the region with R/W permissions during destruction
constexpr memory::Permission UnborrowPermission{true, true, false};
if (mmap(guest.ptr, guest.size, UnborrowPermission.Get(), MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0) == MAP_FAILED)
Logger::Warn("An error occurred while remapping transfer memory as anonymous memory in guest: {}", strerror(errno));
if (mmap(guest.ptr, guest.size, UnborrowPermission.Get(), MAP_SHARED | MAP_FIXED, memoryManager.memoryFd, reinterpret_cast<off_t>(guest.ptr - memoryManager.base.address)) == MAP_FAILED)
Logger::Warn("An error occurred while remapping transfer memory: {}", strerror(errno));
else if (!host.Valid())
Logger::Warn("Expected host mapping of transfer memory to be valid during KTransferMemory destruction");

View File

@ -22,7 +22,7 @@ namespace skyline::kernel::type {
constexpr bool Valid() {
return ptr && size;
}
} host, guest{}; //!< We keep two mirrors of the underlying shared memory for guest access and host access, the host mirror is persistently mapped and should be used by anything accessing the memory on the host
} host{}, guest{}; //!< We keep two mirrors of the underlying shared memory for guest access and host access, the host mirror is persistently mapped and should be used by anything accessing the memory on the host
KSharedMemory(const DeviceState &state, size_t size, memory::MemoryState memState = memory::states::SharedMemory, KType type = KType::KSharedMemory);