From 577a67babd2ec3d1bfb90856ea61dd0fee79e041 Mon Sep 17 00:00:00 2001 From: PixelyIon Date: Sun, 6 Mar 2022 04:22:53 +0530 Subject: [PATCH] Support mirrors of multiple non-contiguous memory regions `CreateMirror` is limited to creating a mirror of a single contiguous region which does not work when creating a contiguous mirror of multiple non-contiguous regions. To support this functionality, `CreateMirrors` which expects a list of page-aligned regions and maps them into a contiguous mirror. --- app/src/main/cpp/skyline/kernel/memory.cpp | 32 ++++++++++++++++++++++ app/src/main/cpp/skyline/kernel/memory.h | 9 ++++++ 2 files changed, 41 insertions(+) diff --git a/app/src/main/cpp/skyline/kernel/memory.cpp b/app/src/main/cpp/skyline/kernel/memory.cpp index be882bfb..8d2e5bc6 100644 --- a/app/src/main/cpp/skyline/kernel/memory.cpp +++ b/app/src/main/cpp/skyline/kernel/memory.cpp @@ -156,6 +156,38 @@ namespace skyline::kernel { return span{reinterpret_cast(mirror), size}; } + span MemoryManager::CreateMirrors(const std::vector> ®ions) { + size_t totalSize{}; + for (const auto ®ion : regions) + totalSize += region.size(); + + auto mirrorBase{mmap(nullptr, totalSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)}; // Reserve address space for all mirrors + if (mirrorBase == MAP_FAILED) + throw exception("Failed to create mirror base: {} (0x{:X} bytes)", strerror(errno), totalSize); + + size_t mirrorOffset{}; + for (const auto ®ion : regions) { + auto address{reinterpret_cast(region.data())}; + if (address < base.address || address + region.size() > base.address + base.size) + throw exception("Mapping is outside of VMM base: 0x{:X} - 0x{:X}", address, address + region.size()); + + size_t offset{address - base.address}; + if (!util::IsPageAligned(offset) || !util::IsPageAligned(region.size())) + throw exception("Mapping is not aligned to a page: 0x{:X}-0x{:X} (0x{:X})", address, address + region.size(), offset); + + auto mirror{mmap(reinterpret_cast(mirrorBase) + mirrorOffset, region.size(), PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | MAP_FIXED, memoryFd, static_cast(offset))}; + if (mirror == MAP_FAILED) + throw exception("Failed to create mirror mapping at 0x{:X}-0x{:X} (0x{:X}): {}", address, address + region.size(), offset, strerror(errno)); + + mirrorOffset += region.size(); + } + + if (mirrorOffset != totalSize) + throw exception("Mirror size mismatch: 0x{:X} != 0x{:X}", mirrorOffset, totalSize); + + return span{reinterpret_cast(mirrorBase), totalSize}; + } + void MemoryManager::InsertChunk(const ChunkDescriptor &chunk) { std::unique_lock lock(mutex); diff --git a/app/src/main/cpp/skyline/kernel/memory.h b/app/src/main/cpp/skyline/kernel/memory.h index 7c717164..5231ad58 100644 --- a/app/src/main/cpp/skyline/kernel/memory.h +++ b/app/src/main/cpp/skyline/kernel/memory.h @@ -248,6 +248,15 @@ namespace skyline { */ span CreateMirror(u8* pointer, size_t size); + /** + * @brief Mirrors multiple page-aligned mapping in the guest address space to the host address space + * @param totalSize The total size of all the regions to be mirrored combined + * @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 + * @note If a single mapping is mirrored, it is recommended to use CreateMirror instead + */ + span CreateMirrors(const std::vector>& regions); + void InsertChunk(const ChunkDescriptor &chunk); std::optional Get(void *ptr);