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.
This commit is contained in:
PixelyIon 2022-03-06 04:22:53 +05:30
parent e35ab6d1e0
commit 577a67babd
2 changed files with 41 additions and 0 deletions

View File

@ -156,6 +156,38 @@ namespace skyline::kernel {
return span<u8>{reinterpret_cast<u8 *>(mirror), size}; return span<u8>{reinterpret_cast<u8 *>(mirror), size};
} }
span<u8> MemoryManager::CreateMirrors(const std::vector<span<u8>> &regions) {
size_t totalSize{};
for (const auto &region : 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 &region : regions) {
auto address{reinterpret_cast<u64>(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<u8 *>(mirrorBase) + mirrorOffset, region.size(), PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | MAP_FIXED, 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 + region.size(), offset, strerror(errno));
mirrorOffset += region.size();
}
if (mirrorOffset != totalSize)
throw exception("Mirror size mismatch: 0x{:X} != 0x{:X}", mirrorOffset, totalSize);
return span<u8>{reinterpret_cast<u8 *>(mirrorBase), totalSize};
}
void MemoryManager::InsertChunk(const ChunkDescriptor &chunk) { void MemoryManager::InsertChunk(const ChunkDescriptor &chunk) {
std::unique_lock lock(mutex); std::unique_lock lock(mutex);

View File

@ -248,6 +248,15 @@ namespace skyline {
*/ */
span<u8> CreateMirror(u8* pointer, size_t size); span<u8> 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<u8> CreateMirrors(const std::vector<span<u8>>& regions);
void InsertChunk(const ChunkDescriptor &chunk); void InsertChunk(const ChunkDescriptor &chunk);
std::optional<ChunkDescriptor> Get(void *ptr); std::optional<ChunkDescriptor> Get(void *ptr);