From dac180d7c1db3d7ccc3c3c5483a2bef8bf5bdcae Mon Sep 17 00:00:00 2001 From: TheASVigilante <65920585+TheASVigilante@users.noreply.github.com> Date: Sat, 15 Apr 2023 15:36:22 +0200 Subject: [PATCH] Restructure the memory manager Changes and improves the accuracy of various features of the memory manager. Reduces stuttering in certain games. --- app/CMakeLists.txt | 1 - app/src/main/cpp/skyline/kernel/memory.cpp | 529 +++++++++++++----- app/src/main/cpp/skyline/kernel/memory.h | 138 +++-- app/src/main/cpp/skyline/kernel/svc.cpp | 353 ++++++------ .../main/cpp/skyline/kernel/types/KMemory.h | 10 +- .../main/cpp/skyline/kernel/types/KObject.h | 1 - .../skyline/kernel/types/KPrivateMemory.cpp | 96 ---- .../cpp/skyline/kernel/types/KPrivateMemory.h | 43 -- .../cpp/skyline/kernel/types/KProcess.cpp | 78 +-- .../main/cpp/skyline/kernel/types/KProcess.h | 19 +- .../skyline/kernel/types/KSharedMemory.cpp | 108 +--- .../cpp/skyline/kernel/types/KSharedMemory.h | 5 +- .../main/cpp/skyline/kernel/types/KThread.h | 1 - .../skyline/kernel/types/KTransferMemory.h | 4 +- app/src/main/cpp/skyline/loader/loader.cpp | 14 +- .../cpp/skyline/services/ro/IRoInterface.cpp | 9 +- 16 files changed, 751 insertions(+), 658 deletions(-) delete mode 100644 app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp delete mode 100644 app/src/main/cpp/skyline/kernel/types/KPrivateMemory.h diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 03a0209d..9de5b996 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -175,7 +175,6 @@ add_library(skyline SHARED ${source_DIR}/skyline/kernel/types/KProcess.cpp ${source_DIR}/skyline/kernel/types/KThread.cpp ${source_DIR}/skyline/kernel/types/KSharedMemory.cpp - ${source_DIR}/skyline/kernel/types/KPrivateMemory.cpp ${source_DIR}/skyline/kernel/types/KSyncObject.cpp ${source_DIR}/skyline/audio.cpp ${source_DIR}/skyline/gpu.cpp diff --git a/app/src/main/cpp/skyline/kernel/memory.cpp b/app/src/main/cpp/skyline/kernel/memory.cpp index 919e93ae..1c297832 100644 --- a/app/src/main/cpp/skyline/kernel/memory.cpp +++ b/app/src/main/cpp/skyline/kernel/memory.cpp @@ -7,13 +7,165 @@ #include "types/KProcess.h" namespace skyline::kernel { - MemoryManager::MemoryManager(const DeviceState &state) : state(state) {} + MemoryManager::MemoryManager(const DeviceState &state) noexcept : state(state), setHeapSize(), chunks() {} - MemoryManager::~MemoryManager() { + MemoryManager::~MemoryManager() noexcept { if (base.valid() && !base.empty()) munmap(reinterpret_cast(base.data()), base.size()); } + std::map::iterator MemoryManager::upper_bound(u8 *address) { + std::map::iterator result{chunks.begin()}; + + if (chunks.size() != 1) [[likely]] + while (result->first <= address) { + ++result; + if (result->first + result->second.size == addressSpace.end().base()) + break; + } + + return result; + } + + void MemoryManager::MapInternal(std::pair *newDesc) { + // The chunk that contains / precedes the new chunk base address + auto firstChunkBase{upper_bound(newDesc->first)}; + while (newDesc->first <= firstChunkBase->first) + --firstChunkBase; + + // The chunk that contains / follows the end address of the new chunk + auto lastChunkBase{upper_bound(newDesc->first + newDesc->second.size)}; + while ((newDesc->first + newDesc->second.size) < lastChunkBase->first) + --lastChunkBase; + + ChunkDescriptor firstChunk{firstChunkBase->second}; + ChunkDescriptor lastChunk{lastChunkBase->second}; + + bool needsReprotection{false}; + bool isUnmapping{newDesc->second.state == memory::states::Unmapped}; + + // We cut a hole in a single chunk + if (firstChunkBase->first == lastChunkBase->first) { + if (firstChunk.IsCompatible(newDesc->second)) [[unlikely]] + // No editing necessary + return; + + if ((firstChunk.state == memory::states::Unmapped) != isUnmapping) + needsReprotection = true; + + // We edit the chunk's first half + firstChunk.size = static_cast(newDesc->first - firstChunkBase->first); + chunks[firstChunkBase->first] = firstChunk; + + // We create the chunk's second half + lastChunk.size = static_cast((lastChunkBase->first + lastChunk.size) - (newDesc->first + newDesc->second.size)); + chunks[newDesc->first + newDesc->second.size] = lastChunk; + + // Insert new chunk in between + chunks[newDesc->first] = newDesc->second; + } else { + // If there are descriptors between first and last chunk, delete them + if ((firstChunkBase->first + firstChunk.size) != lastChunkBase->first) { + auto tempChunkBase{firstChunkBase}; + + ++tempChunkBase; + while (tempChunkBase->first != lastChunkBase->first) { + auto tmp{tempChunkBase++}; + if ((tmp->second.state == memory::states::Unmapped) != isUnmapping) + needsReprotection = true; + chunks.erase(tmp); + } + } + + bool shouldInsert{true}; + + if (firstChunk.IsCompatible(newDesc->second)) { + shouldInsert = false; + + firstChunk.size = static_cast((newDesc->first + newDesc->second.size) - firstChunkBase->first); + chunks[firstChunkBase->first] = firstChunk; + } else if ((firstChunkBase->first + firstChunk.size) != newDesc->first) { + firstChunk.size = static_cast(newDesc->first - firstChunkBase->first); + + chunks[firstChunkBase->first] = firstChunk; + + if ((firstChunk.state == memory::states::Unmapped) != isUnmapping) + needsReprotection = true; + } + + if (lastChunk.IsCompatible(newDesc->second)) { + u8 *oldBase{lastChunkBase->first}; + chunks.erase(lastChunkBase); + + if (shouldInsert) { + shouldInsert = false; + + lastChunk.size = static_cast((lastChunk.size + oldBase) - (newDesc->first)); + + chunks[newDesc->first] = lastChunk; + } else { + firstChunk.size = static_cast((lastChunk.size + oldBase) - firstChunkBase->first); + chunks[firstChunkBase->first] = firstChunk; + } + } else if ((newDesc->first + newDesc->second.size) != lastChunkBase->first) { + lastChunk.size = static_cast((lastChunk.size + lastChunkBase->first) - (newDesc->first + newDesc->second.size)); + + chunks.erase(lastChunkBase); + chunks[newDesc->first + newDesc->second.size] = lastChunk; + + if ((lastChunk.state == memory::states::Unmapped) != isUnmapping) + needsReprotection = true; + } + + // Insert if not merged + if (shouldInsert) + chunks[newDesc->first] = newDesc->second; + } + + if (needsReprotection) + if (mprotect(newDesc->first, newDesc->second.size, !isUnmapping ? PROT_READ | PROT_WRITE | PROT_EXEC : PROT_NONE)) [[unlikely]] + Logger::Warn("Reprotection failed: {}", strerror(errno)); + } + + void MemoryManager::ForeachChunkinRange(span memory, auto editCallback) { + auto chunkBase{upper_bound(memory.data())}; + if (memory.data() < chunkBase->first) + --chunkBase; + + ChunkDescriptor resultChunk{chunkBase->second}; + + size_t sizeLeft{memory.size()}; + + if (chunkBase->first < memory.data()) { + size_t copySize{std::min(resultChunk.size - (static_cast(memory.data() - chunkBase->first)), memory.size())}; + + std::pair temp(memory.data(), resultChunk); + temp.second.size = copySize; + editCallback(temp); + + ++chunkBase; + resultChunk = chunkBase->second; + sizeLeft -= copySize; + } + + while (sizeLeft) { + if (sizeLeft < resultChunk.size) { + std::pair temp(chunkBase->first, resultChunk); + temp.second.size = sizeLeft; + editCallback(temp); + break; + } else [[likely]] { + std::pair temp(chunkBase->first, resultChunk); + + editCallback(temp); + + sizeLeft = sizeLeft - resultChunk.size; + ++chunkBase; + resultChunk = chunkBase->second; + } + } + } + constexpr size_t RegionAlignment{1ULL << 21}; //!< The minimum alignment of a HOS memory region constexpr size_t CodeRegionSize{4ULL * 1024 * 1024 * 1024}; //!< The assumed maximum size of the code region (4GiB) @@ -44,11 +196,11 @@ namespace skyline::kernel { break; } while ((line = maps.find_first_of('\n', line)) != std::string::npos && line++); - if (!region.valid()) + if (!region.valid()) [[unlikely]] throw exception("Allocation failed"); auto result{mmap(reinterpret_cast(region.data()), size, PROT_WRITE, MAP_FIXED | MAP_ANONYMOUS | MAP_SHARED, -1, 0)}; - if (result == MAP_FAILED) + if (result == MAP_FAILED) [[unlikely]] throw exception("Failed to mmap guest address space: {}", strerror(errno)); return region; @@ -64,8 +216,9 @@ namespace skyline::kernel { throw exception("32-bit address spaces are not supported"); case memory::AddressSpaceType::AddressSpace36Bit: { - addressSpace = span{reinterpret_cast(0x8000000), (1ULL << 39) - 0x8000000}; - baseSize = 0x180000000 + 0x78000000 + 0x180000000; + addressSpace = span{reinterpret_cast(0), (1ULL << 36)}; + baseSize = 0x180000000 + 0x180000000; + break; } case memory::AddressSpaceType::AddressSpace39Bit: { @@ -83,68 +236,50 @@ namespace skyline::kernel { if (type != memory::AddressSpaceType::AddressSpace36Bit) { base = AllocateMappedRange(baseSize, RegionAlignment, KgslReservedRegionSize, addressSpace.size(), false); - chunks = { - ChunkDescriptor{ - .ptr = addressSpace.data(), - .size = static_cast(base.data() - addressSpace.data()), - .state = memory::states::Reserved, - }, - ChunkDescriptor{ - .ptr = base.data(), - .size = base.size(), - .state = memory::states::Unmapped, - }, - ChunkDescriptor{ - .ptr = base.end().base(), - .size = addressSpace.size() - reinterpret_cast(base.end().base()), - .state = memory::states::Reserved, - }}; + chunks[addressSpace.data()] = ChunkDescriptor{ + .size = addressSpace.size(), + .state = memory::states::Unmapped, + }; code = base; } else { - base = AllocateMappedRange(baseSize, 1ULL << 36, KgslReservedRegionSize, addressSpace.size(), false); - codeBase36Bit = AllocateMappedRange(0x32000000, RegionAlignment, 0xC000000, 0x78000000ULL + reinterpret_cast(addressSpace.data()), true); + codeBase36Bit = AllocateMappedRange(0x78000000, RegionAlignment, 0x8000000, KgslReservedRegionSize, false); + base = AllocateMappedRange(baseSize, RegionAlignment, KgslReservedRegionSize, addressSpace.size(), false); + + if ((reinterpret_cast(base.data()) + baseSize) > (1ULL << 36)) { + Logger::Warn("Couldn't fit regions into AS! Resizing AS instead!"); + addressSpace = span{reinterpret_cast(0), 1ULL << 39}; + } + + chunks[addressSpace.data()] = ChunkDescriptor{ + .size = addressSpace.size(), + .state = memory::states::Unmapped, + }; - chunks = { - ChunkDescriptor{ - .ptr = addressSpace.data(), - .size = static_cast(codeBase36Bit.data() - addressSpace.data()), - .state = memory::states::Heap, // We can't use reserved here as rtld uses it to know when to halt memory walking - }, - ChunkDescriptor{ - .ptr = codeBase36Bit.data(), - .size = codeBase36Bit.size(), - .state = memory::states::Unmapped, - }, - ChunkDescriptor{ - .ptr = codeBase36Bit.end().base(), - .size = static_cast(base.data() - codeBase36Bit.end().base()), - .state = memory::states::Heap, - }, - ChunkDescriptor{ - .ptr = base.data(), - .size = base.size(), - .state = memory::states::Unmapped, - }, - ChunkDescriptor{ - .ptr = base.end().base(), - .size = addressSpace.size() - reinterpret_cast(base.end().base()), - .state = memory::states::Reserved, - }}; code = codeBase36Bit; } } void MemoryManager::InitializeRegions(span codeRegion) { - if (!util::IsAligned(codeRegion.data(), RegionAlignment)) + if (!util::IsAligned(codeRegion.data(), RegionAlignment)) [[unlikely]] throw exception("Non-aligned code region was used to initialize regions: 0x{:X} - 0x{:X}", codeRegion.data(), codeRegion.end().base()); switch (addressSpaceType) { case memory::AddressSpaceType::AddressSpace36Bit: { - // Place code, stack and TLS/IO in the lower 36-bits of the host AS and heap past that - code = span{codeBase36Bit.data(), util::AlignUp(codeRegion.size(), RegionAlignment)}; - stack = span{code.end().base(), codeBase36Bit.size() - code.size()}; + + // As a workaround if we can't place the code region at the base of the AS we mark it as inaccessible heap so rtld doesn't crash + if (codeBase36Bit.data() != reinterpret_cast(0x8000000)) { + std::pair tmp(reinterpret_cast(0x8000000), ChunkDescriptor{ + .size = reinterpret_cast(codeBase36Bit.data() - 0x8000000), + .state = memory::states::Heap, + }); + MapInternal(&tmp); + } + + // Place code, stack and TLS/IO in the lower 36-bits of the host AS and heap and alias past that + code = span{codeBase36Bit.data(), codeBase36Bit.data() + 0x70000000}; + stack = span{codeBase36Bit.data(), codeBase36Bit.data() + 0x78000000}; tlsIo = stack; //!< TLS/IO is shared with Stack on 36-bit alias = span{base.data(), 0x180000000}; heap = span{alias.end().base(), 0x180000000}; @@ -157,6 +292,15 @@ namespace skyline::kernel { heap = span{alias.end().base(), 0x180000000}; stack = span{heap.end().base(), 0x80000000}; tlsIo = span{stack.end().base(), 0x1000000000}; + + u64 newSize{code.size() + alias.size() + stack.size() + heap.size() + tlsIo.size()}; + + if (newSize > base.size()) [[unlikely]] + throw exception("Guest VMM size has exceeded host carveout size: 0x{:X}/0x{:X} (Code: 0x{:X}/0x{:X})", newSize, base.size(), code.size(), CodeRegionSize); + + if (newSize != base.size()) + munmap(base.end().base(), newSize - base.size()); + break; } @@ -164,31 +308,25 @@ namespace skyline::kernel { throw exception("Regions initialized without VMM initialization"); } - auto newSize{code.size() + alias.size() + stack.size() + heap.size() + ((addressSpaceType == memory::AddressSpaceType::AddressSpace39Bit) ? tlsIo.size() : 0)}; - if (newSize > base.size()) - throw exception("Guest VMM size has exceeded host carveout size: 0x{:X}/0x{:X} (Code: 0x{:X}/0x{:X})", newSize, base.size(), code.size(), CodeRegionSize); - if (newSize != base.size()) - munmap(base.end().base(), newSize - base.size()); - - if (codeRegion.size() > code.size()) + if (codeRegion.size() > code.size()) [[unlikely]] throw exception("Code region ({}) is smaller than mapped code size ({})", code.size(), codeRegion.size()); - Logger::Debug("Region Map:\nVMM Base: 0x{:X}\nCode Region: 0x{:X} - 0x{:X} (Size: 0x{:X})\nAlias Region: 0x{:X} - 0x{:X} (Size: 0x{:X})\nHeap Region: 0x{:X} - 0x{:X} (Size: 0x{:X})\nStack Region: 0x{:X} - 0x{:X} (Size: 0x{:X})\nTLS/IO Region: 0x{:X} - 0x{:X} (Size: 0x{:X})", base.data(), code.data(), code.end().base(), code.size(), alias.data(), alias.end().base(), alias.size(), heap.data(), heap.end().base(), heap.size(), stack.data(), stack.end().base(), stack.size(), tlsIo.data(), tlsIo.end().base(), tlsIo.size()); + Logger::Debug("Region Map:\nVMM Base: 0x{:X}\nCode Region: 0x{:X} - 0x{:X} (Size: 0x{:X})\nAlias Region: 0x{:X} - 0x{:X} (Size: 0x{:X})\nHeap Region: 0x{:X} - 0x{:X} (Size: 0x{:X})\nStack Region: 0x{:X} - 0x{:X} (Size: 0x{:X})\nTLS/IO Region: 0x{:X} - 0x{:X} (Size: 0x{:X})", code.data(), code.data(), code.end().base(), code.size(), alias.data(), alias.end().base(), alias.size(), heap.data(), heap.end().base(), heap.size(), stack.data(), stack.end().base(), stack.size(), tlsIo.data(), tlsIo.end().base(), tlsIo.size()); } span MemoryManager::CreateMirror(span mapping) { - if (!base.contains(mapping)) + if (!base.contains(mapping)) [[unlikely]] throw exception("Mapping is outside of VMM base: 0x{:X} - 0x{:X}", mapping.data(), mapping.end().base()); auto offset{static_cast(mapping.data() - base.data())}; - if (!util::IsPageAligned(offset) || !util::IsPageAligned(mapping.size())) + if (!util::IsPageAligned(offset) || !util::IsPageAligned(mapping.size())) [[unlikely]] throw exception("Mapping is not aligned to a page: 0x{:X}-0x{:X} (0x{:X})", mapping.data(), mapping.end().base(), offset); auto mirror{mremap(mapping.data(), 0, mapping.size(), MREMAP_MAYMOVE)}; - if (mirror == MAP_FAILED) + if (mirror == MAP_FAILED) [[unlikely]] throw exception("Failed to create mirror mapping at 0x{:X}-0x{:X} (0x{:X}): {}", mapping.data(), mapping.end().base(), offset, strerror(errno)); - mprotect(mirror, mapping.size(), PROT_READ | PROT_WRITE | PROT_EXEC); + mprotect(mirror, mapping.size(), PROT_READ | PROT_WRITE); return span{reinterpret_cast(mirror), mapping.size()}; } @@ -199,113 +337,226 @@ namespace skyline::kernel { 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) + if (mirrorBase == MAP_FAILED) [[unlikely]] throw exception("Failed to create mirror base: {} (0x{:X} bytes)", strerror(errno), totalSize); size_t mirrorOffset{}; for (const auto ®ion : regions) { - if (!base.contains(region)) + if (!base.contains(region)) [[unlikely]] throw exception("Mapping is outside of VMM base: 0x{:X} - 0x{:X}", region.data(), region.end().base()); auto offset{static_cast(region.data() - base.data())}; - if (!util::IsPageAligned(offset) || !util::IsPageAligned(region.size())) + if (!util::IsPageAligned(offset) || !util::IsPageAligned(region.size())) [[unlikely]] throw exception("Mapping is not aligned to a page: 0x{:X}-0x{:X} (0x{:X})", region.data(), region.end().base(), offset); auto mirror{mremap(region.data(), 0, region.size(), MREMAP_FIXED | MREMAP_MAYMOVE, reinterpret_cast(mirrorBase) + mirrorOffset)}; - if (mirror == MAP_FAILED) + if (mirror == MAP_FAILED) [[unlikely]] throw exception("Failed to create mirror mapping at 0x{:X}-0x{:X} (0x{:X}): {}", region.data(), region.end().base(), offset, strerror(errno)); - mprotect(mirror, region.size(), PROT_READ | PROT_WRITE | PROT_EXEC); + mprotect(mirror, region.size(), PROT_READ | PROT_WRITE); mirrorOffset += region.size(); } - if (mirrorOffset != totalSize) + if (mirrorOffset != totalSize) [[unlikely]] throw exception("Mirror size mismatch: 0x{:X} != 0x{:X}", mirrorOffset, totalSize); return span{reinterpret_cast(mirrorBase), totalSize}; } - void MemoryManager::FreeMemory(span memory) { + void MemoryManager::SetLockOnChunks(span memory, bool value) { + std::unique_lock lock(mutex); + + ForeachChunkinRange(memory, [&](std::pair &desc) __attribute__((always_inline)) { + desc.second.attributes.isBorrowed = value; + MapInternal(&desc); + }); + } + + void MemoryManager::SetCPUCachingOnChunks(span memory, bool value) { + std::unique_lock lock(mutex); + + ForeachChunkinRange(memory, [&](std::pair &desc) __attribute__((always_inline)) { + desc.second.attributes.isUncached = value; + MapInternal(&desc); + }); + } + + void MemoryManager::SetChunkPermission(span memory, memory::Permission permission) { + std::unique_lock lock(mutex); + + ForeachChunkinRange(memory, [&](std::pair &desc) __attribute__((always_inline)) { + desc.second.permission = permission; + MapInternal(&desc); + }); + } + + std::optional> MemoryManager::GetChunk(u8 *addr) { + std::shared_lock lock(mutex); + + if (!addressSpace.contains(addr)) [[unlikely]] + return std::nullopt; + + auto chunkBase = upper_bound(addr); + if (addr < chunkBase->first) [[likely]] + --chunkBase; + + return std::make_optional(*chunkBase); + } + + __attribute__((always_inline)) void MemoryManager::MapCodeMemory(span memory, memory::Permission permission) { + std::unique_lock lock(mutex); + + std::pair temp( + memory.data(), + ChunkDescriptor{ + .size = memory.size(), + .permission = permission, + .state = memory::states::Code}); + + MapInternal(&temp); + } + + __attribute__((always_inline)) void MemoryManager::MapMutableCodeMemory(span memory) { + std::unique_lock lock(mutex); + + std::pair temp( + memory.data(), + ChunkDescriptor{ + .size = memory.size(), + .permission = {true, true, false}, + .state = memory::states::CodeMutable}); + + MapInternal(&temp); + } + + __attribute__((always_inline)) void MemoryManager::MapStackMemory(span memory) { + std::unique_lock lock(mutex); + + std::pair temp( + memory.data(), + ChunkDescriptor{ + .size = memory.size(), + .permission = {true, true, false}, + .state = memory::states::Stack, + .isSrcMergeDisallowed = true}); + + MapInternal(&temp); + } + + __attribute__((always_inline)) void MemoryManager::MapHeapMemory(span memory) { + std::unique_lock lock(mutex); + + std::pair temp( + memory.data(), + ChunkDescriptor{ + .size = memory.size(), + .permission = {true, true, false}, + .state = memory::states::Heap}); + + MapInternal(&temp); + } + + __attribute__((always_inline)) void MemoryManager::MapSharedMemory(span memory, memory::Permission permission) { + std::unique_lock lock(mutex); + + std::pair temp( + memory.data(), + ChunkDescriptor{ + .size = memory.size(), + .permission = permission, + .state = memory::states::SharedMemory, + .isSrcMergeDisallowed = true}); + + MapInternal(&temp); + } + + __attribute__((always_inline)) void MemoryManager::MapTransferMemory(span memory, memory::Permission permission) { + std::unique_lock lock(mutex); + + std::pair temp( + memory.data(), + ChunkDescriptor{ + .size = memory.size(), + .permission = permission, + .state = permission.raw ? memory::states::TransferMemory : memory::states::TransferMemoryIsolated, + .isSrcMergeDisallowed = true}); + + MapInternal(&temp); + } + + __attribute__((always_inline)) void MemoryManager::MapThreadLocalMemory(span memory) { + std::unique_lock lock(mutex); + + std::pair temp( + memory.data(), + ChunkDescriptor{ + .size = memory.size(), + .permission = {true, true, false}, + .state = memory::states::ThreadLocal}); + MapInternal(&temp); + } + + __attribute__((always_inline)) void MemoryManager::Reserve(span memory) { + std::unique_lock lock(mutex); + + std::pair temp( + memory.data(), + ChunkDescriptor{ + .size = memory.size(), + .permission = {false, false, false}, + .state = memory::states::Reserved}); + MapInternal(&temp); + } + + __attribute__((always_inline)) void MemoryManager::UnmapMemory(span memory) { + std::unique_lock lock(mutex); + + ForeachChunkinRange(memory, [&](std::pair &desc) { + if (desc.second.state != memory::states::Unmapped) + FreeMemory(span((u8 *)desc.first, desc.second.size)); + }); + + std::pair temp( + memory.data(), + ChunkDescriptor{ + .size = memory.size(), + .permission = {false, false, false}, + .state = memory::states::Unmapped}); + MapInternal(&temp); + } + + __attribute__((always_inline)) void MemoryManager::FreeMemory(span memory) { u8 *alignedStart{util::AlignUp(memory.data(), constant::PageSize)}; u8 *alignedEnd{util::AlignDown(memory.end().base(), constant::PageSize)}; - if (alignedStart < alignedEnd) - if (madvise(alignedStart, static_cast(alignedEnd - alignedStart), MADV_REMOVE) == -1) - throw exception("Failed to free memory: {}", strerror(errno)) ; + if (alignedStart < alignedEnd) [[likely]] + if (madvise(alignedStart, static_cast(alignedEnd - alignedStart), MADV_REMOVE) == -1) [[unlikely]] + Logger::Error("Failed to free memory: {}", strerror(errno)); } - void MemoryManager::InsertChunk(const ChunkDescriptor &chunk) { - std::unique_lock lock(mutex); - - auto upper{std::upper_bound(chunks.begin(), chunks.end(), chunk.ptr, [](const u8 *ptr, const ChunkDescriptor &chunk) -> bool { return ptr < chunk.ptr; })}; - if (upper == chunks.begin()) - throw exception("InsertChunk: Chunk inserted outside address space: 0x{:X} - 0x{:X} and 0x{:X} - 0x{:X}", upper->ptr, upper->ptr + upper->size, chunk.ptr, chunk.ptr + chunk.size); - - upper = chunks.erase(upper, std::upper_bound(upper, chunks.end(), chunk.ptr + chunk.size, [](const u8 *ptr, const ChunkDescriptor &chunk) -> bool { return ptr < chunk.ptr + chunk.size; })); - if (upper != chunks.end() && upper->ptr < chunk.ptr + chunk.size) { - auto end{upper->ptr + upper->size}; - upper->ptr = chunk.ptr + chunk.size; - upper->size = static_cast(end - upper->ptr); - } - - auto lower{std::prev(upper)}; - if (lower->ptr == chunk.ptr && lower->size == chunk.size) { - lower->state = chunk.state; - lower->permission = chunk.permission; - lower->attributes = chunk.attributes; - lower->memory = chunk.memory; - } else if (lower->ptr + lower->size > chunk.ptr + chunk.size) { - auto lowerExtension{*lower}; - lowerExtension.ptr = chunk.ptr + chunk.size; - lowerExtension.size = static_cast((lower->ptr + lower->size) - lowerExtension.ptr); - - lower->size = static_cast(chunk.ptr - lower->ptr); - if (lower->size) { - upper = chunks.insert(upper, lowerExtension); - chunks.insert(upper, chunk); - } else { - auto lower2{std::prev(lower)}; - if (chunk.IsCompatible(*lower2) && lower2->ptr + lower2->size >= chunk.ptr) { - lower2->size = static_cast(chunk.ptr + chunk.size - lower2->ptr); - upper = chunks.erase(lower); - } else { - *lower = chunk; - } - upper = chunks.insert(upper, lowerExtension); - } - } else if (chunk.IsCompatible(*lower) && lower->ptr + lower->size >= chunk.ptr) { - lower->size = static_cast(chunk.ptr + chunk.size - lower->ptr); - } else { - if (lower->ptr + lower->size > chunk.ptr) - lower->size = static_cast(chunk.ptr - lower->ptr); - if (upper != chunks.end() && chunk.IsCompatible(*upper) && chunk.ptr + chunk.size >= upper->ptr) { - upper->ptr = chunk.ptr; - upper->size = chunk.size + upper->size; - } else { - chunks.insert(upper, chunk); - } - } + void MemoryManager::AddRef(const std::shared_ptr &ptr) { + memRefs.push_back(ptr); } - std::optional MemoryManager::Get(void *ptr) { - std::shared_lock lock(mutex); + void MemoryManager::RemoveRef(const std::shared_ptr &ptr) { + std::vector>::iterator i{std::find(memRefs.begin(), memRefs.end(), ptr)}; - auto chunk{std::upper_bound(chunks.begin(), chunks.end(), reinterpret_cast(ptr), [](const u8 *ptr, const ChunkDescriptor &chunk) -> bool { return ptr < chunk.ptr; })}; - if (chunk-- != chunks.begin()) - if ((chunk->ptr + chunk->size) > ptr) - return std::make_optional(*chunk); - - return std::nullopt; + if (*i == ptr) { + memRefs.erase(i); + } } size_t MemoryManager::GetUserMemoryUsage() { std::shared_lock lock(mutex); size_t size{}; - for (const auto &chunk : chunks) - if (chunk.state == memory::states::Heap) - size += chunk.size; - return size + code.size() + state.process->mainThreadStack->guest.size(); + + for (auto &chunk : chunks) { + if (chunk.second.state == memory::states::Heap) + size += chunk.second.size; + } + + return size + code.size() + state.process->mainThreadStack.size(); } size_t MemoryManager::GetSystemResourceUsage() { diff --git a/app/src/main/cpp/skyline/kernel/memory.h b/app/src/main/cpp/skyline/kernel/memory.h index 641ecd78..e2d1d51b 100644 --- a/app/src/main/cpp/skyline/kernel/memory.h +++ b/app/src/main/cpp/skyline/kernel/memory.h @@ -6,6 +6,7 @@ #include #include #include +#include namespace skyline { namespace kernel::type { @@ -17,7 +18,7 @@ namespace skyline { /** * @brief Initializes all permissions to false */ - constexpr Permission() : r(), w(), x() {} + constexpr Permission() : raw() {} /** * @brief Initializes permissions where the first three bits correspond to RWX @@ -93,26 +94,28 @@ namespace skyline { enum class MemoryType : u8 { Unmapped = 0x0, Io = 0x1, - Normal = 0x2, - CodeStatic = 0x3, + Static = 0x2, + Code = 0x3, CodeMutable = 0x4, Heap = 0x5, SharedMemory = 0x6, - Alias = 0x7, - ModuleCodeStatic = 0x8, - ModuleCodeMutable = 0x9, + + AliasCode = 0x8, + AliasCodeData = 0x9, Ipc = 0xA, Stack = 0xB, ThreadLocal = 0xC, TransferMemoryIsolated = 0xD, TransferMemory = 0xE, - ProcessMemory = 0xF, + SharedCode = 0xF, Reserved = 0x10, NonSecureIpc = 0x11, NonDeviceIpc = 0x12, KernelStack = 0x13, - CodeReadOnly = 0x14, - CodeWritable = 0x15, + CodeGenerated = 0x14, + CodeExternal = 0x15, + Coverage = 0x16, + InsecureMemory = 0x17 }; /** @@ -138,7 +141,7 @@ namespace skyline { bool ipcSendAllowed : 1; //!< If this block is allowed to be sent as an IPC buffer with flags=0 bool nonDeviceIpcSendAllowed : 1; //!< If this block is allowed to be sent as an IPC buffer with flags=3 bool nonSecureIpcSendAllowed : 1; //!< If this block is allowed to be sent as an IPC buffer with flags=1 - bool _pad0_ : 1; + bool isMappedInKernel : 1; //!< If this block is mapped in kernel bool processPermissionChangeAllowed : 1; //!< If the application can use svcSetProcessMemoryPermission on this block bool mapAllowed : 1; //!< If the application can use svcMapMemory on this block bool unmapProcessCodeMemoryAllowed : 1; //!< If the application can use svcUnmapProcessCodeMemory on this block @@ -151,6 +154,7 @@ namespace skyline { bool mapProcessAllowed : 1; //!< If the application can use svcMapProcessMemory on this block bool attributeChangeAllowed : 1; //!< If the application can use svcSetMemoryAttribute on this block bool codeMemoryAllowed : 1; //!< If the application can use svcCreateCodeMemory on this block + bool isLinearMapped : 1; //!< If this block is mapped linearly }; u32 value{}; }; @@ -162,26 +166,29 @@ namespace skyline { */ namespace states { constexpr MemoryState Unmapped{0x00000000}; - constexpr MemoryState Io{0x00002001}; - constexpr MemoryState CodeStatic{0x00DC7E03}; - constexpr MemoryState CodeMutable{0x03FEBD04}; - constexpr MemoryState Heap{0x037EBD05}; - constexpr MemoryState SharedMemory{0x00402006}; - constexpr MemoryState Alias{0x00482907}; - constexpr MemoryState AliasCode{0x00DD7E08}; - constexpr MemoryState AliasCodeData{0x03FFBD09}; - constexpr MemoryState Ipc{0x005C3C0A}; - constexpr MemoryState Stack{0x005C3C0B}; - constexpr MemoryState ThreadLocal{0x0040200C}; - constexpr MemoryState TransferMemoryIsolated{0x015C3C0D}; - constexpr MemoryState TransferMemory{0x005C380E}; - constexpr MemoryState SharedCode{0x0040380F}; + constexpr MemoryState Io{0x00182001}; + constexpr MemoryState Static{0x00042002}; + constexpr MemoryState Code{0x04DC7E03}; + constexpr MemoryState CodeMutable{0x07FEBD04}; + constexpr MemoryState Heap{0x077EBD05}; + constexpr MemoryState SharedMemory{0x04402006}; + + constexpr MemoryState AliasCode{0x04DD7E08}; + constexpr MemoryState AliasCodeData{0x07FFBD09}; + constexpr MemoryState Ipc{0x045C3C0A}; + constexpr MemoryState Stack{0x045C3C0B}; + constexpr MemoryState ThreadLocal{0x0400200C}; + constexpr MemoryState TransferMemoryIsolated{0x055C3C0D}; + constexpr MemoryState TransferMemory{0x045C380E}; + constexpr MemoryState SharedCode{0x0440380F}; constexpr MemoryState Reserved{0x00000010}; - constexpr MemoryState NonSecureIpc{0x005C3811}; - constexpr MemoryState NonDeviceIpc{0x004C2812}; + constexpr MemoryState NonSecureIpc{0x045C3811}; + constexpr MemoryState NonDeviceIpc{0x044C2812}; constexpr MemoryState KernelStack{0x00002013}; - constexpr MemoryState CodeReadOnly{0x00402214}; - constexpr MemoryState CodeWritable{0x00402015}; + constexpr MemoryState CodeGenerated{0x04402214}; + constexpr MemoryState CodeExternal{0x04402015}; + constexpr MemoryState Coverage{0x00002016}; + constexpr MemoryState InsecureMemory{0x05583817}; } enum class AddressSpaceType : u8 { @@ -194,15 +201,14 @@ namespace skyline { namespace kernel { struct ChunkDescriptor { - u8 *ptr; + bool isSrcMergeDisallowed; size_t size; memory::Permission permission; memory::MemoryState state; memory::MemoryAttribute attributes; - kernel::type::KMemory *memory{}; - constexpr bool IsCompatible(const ChunkDescriptor &chunk) const { - return chunk.permission == permission && chunk.state.value == state.value && chunk.attributes.value == attributes.value && chunk.memory == memory; + constexpr bool IsCompatible(const ChunkDescriptor &chunk) const noexcept { + return chunk.permission == permission && chunk.state.value == state.value && chunk.attributes.value == attributes.value && !isSrcMergeDisallowed; } }; @@ -212,7 +218,16 @@ namespace skyline { class MemoryManager { private: const DeviceState &state; - std::vector chunks; + std::map chunks; + + std::vector> memRefs; + + // Workaround for broken std implementation + std::map::iterator upper_bound(u8 *address); + + void MapInternal(std::pair *newDesc); + + void ForeachChunkinRange(span memory, auto editCallback); public: memory::AddressSpaceType addressSpaceType{}; @@ -225,11 +240,13 @@ namespace skyline { span stack{}; span tlsIo{}; //!< TLS/IO + size_t setHeapSize; //!< For use by svcSetHeapSize + 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); + MemoryManager(const DeviceState &state) noexcept; - ~MemoryManager(); + ~MemoryManager() noexcept; /** * @note This should be called before any mappings in the VMM or calls to InitalizeRegions are done @@ -255,14 +272,57 @@ namespace skyline { span CreateMirrors(const std::vector> ®ions); /** - * @brief Frees the underlying physical memory for all full pages in the contained mapping - * @note All subsequent accesses to freed memory will return 0s + * @brief Sets the attributes for chunks within a certain range + */ + void SetLockOnChunks(span memory, bool value); + + void SetCPUCachingOnChunks(span memory, bool value); + + /** + * @brief Sets the permission for chunks within a certain range + * @note The permissions set here are not accurate to the actual permissions set on the chunk and are only for the guest + */ + void SetChunkPermission(span memory, memory::Permission permission); + + /** + * @brief Gets the highest chunk's descriptor that contains this address + */ + std::optional> GetChunk(u8 *addr); + + /** + * Various mapping functions for use by the guest + * @note UnmapMemory frees the underlying memory as well + */ + void MapCodeMemory(span memory, memory::Permission permission); + + void MapMutableCodeMemory(span memory); + + void MapStackMemory(span memory); + + void MapHeapMemory(span memory); + + void MapSharedMemory(span memory, memory::Permission permission); + + void MapTransferMemory(span memory, memory::Permission permission); + + void MapThreadLocalMemory(span memory); + + void Reserve(span memory); + + void UnmapMemory(span memory); + + /** + * Frees the underlying memory + * @note Memory that's not aligned to page boundaries at the edges of the span will not be freed */ void FreeMemory(span memory); - void InsertChunk(const ChunkDescriptor &chunk); + /** + * Allows you to add/remove references to shared/transfer memory + */ + void AddRef(const std::shared_ptr &ptr); - std::optional Get(void *ptr); + void RemoveRef(const std::shared_ptr &ptr); /** * @return The cumulative size of all heap (Physical Memory + Process Heap) memory mappings, the code region and the main thread stack in bytes diff --git a/app/src/main/cpp/skyline/kernel/svc.cpp b/app/src/main/cpp/skyline/kernel/svc.cpp index ab8c16fd..d705b076 100644 --- a/app/src/main/cpp/skyline/kernel/svc.cpp +++ b/app/src/main/cpp/skyline/kernel/svc.cpp @@ -11,37 +11,56 @@ namespace skyline::kernel::svc { void SetHeapSize(const DeviceState &state) { - u32 size{state.ctx->gpr.w1}; + u64 size{state.ctx->gpr.w1}; - if (!util::IsAligned(size, 0x200000)) { + if (!util::IsAligned(size, 0x200000)) [[unlikely]] { state.ctx->gpr.w0 = result::InvalidSize; state.ctx->gpr.x1 = 0; - Logger::Warn("'size' not divisible by 2MB: {}", size); + Logger::Warn("'size' not divisible by 2MB: 0x{:X}", size); + return; + } else if (state.process->memory.heap.size() < size) [[unlikely]] { + state.ctx->gpr.w0 = result::InvalidSize; + state.ctx->gpr.x1 = 0; + + Logger::Warn("'size' exceeded size of heap region: 0x{:X}", size); return; } - auto &heap{state.process->heap}; - heap->Resize(size); + size_t heapCurrSize{state.process->memory.setHeapSize}; + u8 *heapBaseAddr{state.process->memory.heap.data()}; + + if (heapCurrSize < size) + state.process->memory.MapHeapMemory(span{heapBaseAddr + heapCurrSize, size - heapCurrSize}); + else if (size < heapCurrSize) + state.process->memory.UnmapMemory(span{heapBaseAddr + size, heapCurrSize - size}); + + state.process->memory.setHeapSize = size; state.ctx->gpr.w0 = Result{}; - state.ctx->gpr.x1 = reinterpret_cast(heap->guest.data()); + state.ctx->gpr.x1 = reinterpret_cast(heapBaseAddr); - Logger::Debug("Allocated at 0x{:X} - 0x{:X} (0x{:X} bytes)", heap->guest.data(), heap->guest.end().base(), heap->guest.size()); + Logger::Debug("Heap size changed to 0x{:X} bytes (0x{:X} - 0x{:X})", size, heapBaseAddr, heapBaseAddr + size); } void SetMemoryAttribute(const DeviceState &state) { - auto pointer{reinterpret_cast(state.ctx->gpr.x0)}; - if (!util::IsPageAligned(pointer)) { + u8 *address{reinterpret_cast(state.ctx->gpr.x0)}; + if (!util::IsPageAligned(address)) [[unlikely]] { state.ctx->gpr.w0 = result::InvalidAddress; - Logger::Warn("'pointer' not page aligned: 0x{:X}", pointer); + Logger::Warn("'address' not page aligned: 0x{:X}", address); return; } - size_t size{state.ctx->gpr.x1}; - if (!util::IsPageAligned(size)) { + u64 size{state.ctx->gpr.x1}; + if (!size || !util::IsPageAligned(size)) [[unlikely]] { state.ctx->gpr.w0 = result::InvalidSize; - Logger::Warn("'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size); + Logger::Warn("'size' {}: 0x{:X}", size ? "is not page aligned" : "is zero", size); + return; + } + + if (address >= (address + size) || !state.process->memory.AddressSpaceContains(span{address, size})) [[unlikely]] { + state.ctx->gpr.w0 = result::InvalidCurrentMemory; + Logger::Warn("Invalid address and size combination: 'address': 0x{:X}, 'size': 0x{:X} ", address, size); return; } @@ -49,171 +68,152 @@ namespace skyline::kernel::svc { memory::MemoryAttribute value{.value = state.ctx->gpr.w3}; auto maskedValue{mask.value | value.value}; - if (maskedValue != mask.value || !mask.isUncached || mask.isDeviceShared || mask.isBorrowed || mask.isIpcLocked) { + if (maskedValue != mask.value || !mask.isUncached || mask.isDeviceShared || mask.isBorrowed || mask.isIpcLocked) [[unlikely]] { state.ctx->gpr.w0 = result::InvalidCombination; Logger::Warn("'mask' invalid: 0x{:X}, 0x{:X}", mask.value, value.value); return; } - auto chunk{state.process->memory.Get(pointer)}; - if (!chunk) { - state.ctx->gpr.w0 = result::InvalidAddress; - Logger::Warn("Cannot find memory region: 0x{:X}", pointer); - return; - } + auto chunk{state.process->memory.GetChunk(address)}; - if (!chunk->state.attributeChangeAllowed) { + // We only check the first found chunk for whatever reason. + if (!chunk->second.state.attributeChangeAllowed) [[unlikely]] { state.ctx->gpr.w0 = result::InvalidState; - Logger::Warn("Attribute change not allowed for chunk: 0x{:X}", pointer); + Logger::Warn("Attribute change not allowed for chunk: 0x{:X}", chunk->first); return; } - auto newChunk{*chunk}; - newChunk.ptr = pointer; - newChunk.size = size; - newChunk.attributes.isUncached = value.isUncached; - state.process->memory.InsertChunk(newChunk); + state.process->memory.SetCPUCachingOnChunks(span{address, size}, value.isUncached); - Logger::Debug("Set CPU caching to {} at 0x{:X} - 0x{:X} (0x{:X} bytes)", !static_cast(value.isUncached), pointer, pointer + size, size); + Logger::Debug("Set CPU caching to {} at 0x{:X} - 0x{:X} (0x{:X} bytes)", static_cast(value.isUncached), address, address + size, size); state.ctx->gpr.w0 = Result{}; } void MapMemory(const DeviceState &state) { - auto destination{reinterpret_cast(state.ctx->gpr.x0)}; - auto source{reinterpret_cast(state.ctx->gpr.x1)}; + u8 *destination{reinterpret_cast(state.ctx->gpr.x0)}; + u8 *source{reinterpret_cast(state.ctx->gpr.x1)}; size_t size{state.ctx->gpr.x2}; - if (!util::IsPageAligned(destination) || !util::IsPageAligned(source)) { + if (!util::IsPageAligned(destination) || !util::IsPageAligned(source)) [[unlikely]] { state.ctx->gpr.w0 = result::InvalidAddress; - Logger::Warn("Addresses not page aligned: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size); + Logger::Warn("Addresses not page aligned: 'source': 0x{:X}, 'destination': 0x{:X}, 'size': 0x{:X} bytes", source, destination, size); return; } - if (!util::IsPageAligned(size)) { + if (!size || !util::IsPageAligned(size)) [[unlikely]] { state.ctx->gpr.w0 = result::InvalidSize; - Logger::Warn("'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size); + Logger::Warn("'size' {}: 0x{:X}", size ? "is not page aligned" : "is zero", size); return; } - auto stack{state.process->memory.stack}; - if (!stack.contains(span{destination, size})) { + if (destination >= (destination + size) || !state.process->memory.AddressSpaceContains(span{destination, size})) [[unlikely]] { + state.ctx->gpr.w0 = result::InvalidCurrentMemory; + Logger::Warn("Invalid address and size combination: 'destination': 0x{:X}, 'size': 0x{:X} bytes", destination, size); + return; + } + + if (source >= (source + size) || !state.process->memory.AddressSpaceContains(span{source, size})) [[unlikely]] { + state.ctx->gpr.w0 = result::InvalidCurrentMemory; + Logger::Warn("Invalid address and size combination: 'source': 0x{:X}, 'size': 0x{:X} bytes", source, size); + return; + } + + if (!state.process->memory.stack.contains(span{destination, size})) [[unlikely]] { state.ctx->gpr.w0 = result::InvalidMemoryRegion; - Logger::Warn("Destination not within stack region: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size); + Logger::Warn("Destination not within stack region: 'source': 0x{:X}, 'destination': 0x{:X}, 'size': 0x{:X} bytes", source, destination, size); return; } - auto chunk{state.process->memory.Get(source)}; - if (!chunk) { - state.ctx->gpr.w0 = result::InvalidAddress; - Logger::Warn("Source has no descriptor: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size); - return; - } - if (!chunk->state.mapAllowed) { + auto chunk{state.process->memory.GetChunk(source)}; + if (!chunk->second.state.mapAllowed) [[unlikely]] { state.ctx->gpr.w0 = result::InvalidState; - Logger::Warn("Source doesn't allow usage of svcMapMemory: Source: 0x{:X}, Destination: 0x{:X}, Size: 0x{:X}, MemoryState: 0x{:X}", source, destination, size, chunk->state.value); + Logger::Warn("Source doesn't allow usage of svcMapMemory: 'source': 0x{:X}, 'size': 0x{:X}, MemoryState: 0x{:X}", source, size, chunk->second.state.value); return; } - state.process->NewHandle(span{destination, size}, chunk->permission, memory::states::Stack); + state.process->memory.MapStackMemory(span{destination, size}); std::memcpy(destination, source, size); - auto object{state.process->GetMemoryObject(source)}; - if (!object) - throw exception("svcMapMemory: Cannot find memory object in handle table for address 0x{:X}", source); - object->item->UpdatePermission(span{source, size}, {false, false, false}); + state.process->memory.SetChunkPermission(span{source, size}, {false, false, false}); + state.process->memory.SetLockOnChunks(span{source, size}, true); Logger::Debug("Mapped range 0x{:X} - 0x{:X} to 0x{:X} - 0x{:X} (Size: 0x{:X} bytes)", source, source + size, destination, destination + size, size); state.ctx->gpr.w0 = Result{}; } void UnmapMemory(const DeviceState &state) { - auto source{reinterpret_cast(state.ctx->gpr.x0)}; - auto destination{reinterpret_cast(state.ctx->gpr.x1)}; + u8 *destination{reinterpret_cast(state.ctx->gpr.x0)}; + u8 *source{reinterpret_cast(state.ctx->gpr.x1)}; size_t size{state.ctx->gpr.x2}; - if (!util::IsPageAligned(destination) || !util::IsPageAligned(source)) { + if (!util::IsPageAligned(destination) || !util::IsPageAligned(source)) [[unlikely]] { state.ctx->gpr.w0 = result::InvalidAddress; - Logger::Warn("Addresses not page aligned: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size); + Logger::Warn("Addresses not page aligned: 'source': 0x{:X}, 'destination': 0x{:X}, 'size': {} bytes", source, destination, size); return; } - if (!util::IsPageAligned(size)) { + if (!size || !util::IsPageAligned(size)) [[unlikely]] { state.ctx->gpr.w0 = result::InvalidSize; - Logger::Warn("'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size); + Logger::Warn("'size' {}: 0x{:X}", size ? "is not page aligned" : "is zero", size); return; } - auto stack{state.process->memory.stack}; - if (!stack.contains(span{source, size})) { + if (!state.process->memory.stack.contains(span{destination, size})) [[unlikely]] { state.ctx->gpr.w0 = result::InvalidMemoryRegion; - Logger::Warn("Source not within stack region: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size); + Logger::Warn("Source not within stack region: 'source': 0x{:X}, 'destination': 0x{:X}, 'size': 0x{:X} bytes", source, destination, size); return; } - auto sourceChunk{state.process->memory.Get(source)}; - auto destChunk{state.process->memory.Get(destination)}; - if (!sourceChunk || !destChunk) { - state.ctx->gpr.w0 = result::InvalidAddress; - Logger::Warn("Addresses have no descriptor: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes)", source, destination, size); - return; + auto dstChunk{state.process->memory.GetChunk(destination)}; + while (dstChunk->second.state.value == memory::states::Unmapped) + dstChunk = state.process->memory.GetChunk(dstChunk->first + dstChunk->second.size); + + if ((destination + size) > dstChunk->first) [[likely]] { + state.process->memory.SetChunkPermission(span{source + (dstChunk->first - destination), dstChunk->second.size}, dstChunk->second.permission); + state.process->memory.SetLockOnChunks(span{source + (dstChunk->first - destination), dstChunk->second.size}, false); + + std::memcpy(source + (dstChunk->first - destination), dstChunk->first, dstChunk->second.size); + + state.process->memory.UnmapMemory(span{destination, size}); } - if (!destChunk->state.mapAllowed) { - state.ctx->gpr.w0 = result::InvalidState; - Logger::Warn("Destination doesn't allow usage of svcMapMemory: Source: 0x{:X}, Destination: 0x{:X} (Size: 0x{:X} bytes) 0x{:X}", source, destination, size, destChunk->state.value); - return; - } - - auto destObject{state.process->GetMemoryObject(destination)}; - if (!destObject) - throw exception("svcUnmapMemory: Cannot find destination memory object in handle table for address 0x{:X}", destination); - - destObject->item->UpdatePermission(span{destination, size}, sourceChunk->permission); - - std::memcpy(source, destination, size); - - auto sourceObject{state.process->GetMemoryObject(source)}; - if (!sourceObject) - throw exception("svcUnmapMemory: Cannot find source memory object in handle table for address 0x{:X}", source); - - state.process->memory.FreeMemory(std::span(source, size)); - state.process->CloseHandle(sourceObject->handle); - - Logger::Debug("Unmapped range 0x{:X} - 0x{:X} to 0x{:X} - 0x{:X} (Size: 0x{:X} bytes)", source, source + size, destination, destination + size, size); + Logger::Debug("Unmapped range 0x{:X} - 0x{:X} to 0x{:X} - 0x{:X} (Size: 0x{:X} bytes)", destination, destination + size, source, source + size, size); state.ctx->gpr.w0 = Result{}; } void QueryMemory(const DeviceState &state) { memory::MemoryInfo memInfo{}; - auto pointer{reinterpret_cast(state.ctx->gpr.x2)}; - auto chunk{state.process->memory.Get(pointer)}; + u8 *address{reinterpret_cast(state.ctx->gpr.x2)}; + auto chunk{state.process->memory.GetChunk(address)}; if (chunk) { memInfo = { - .address = reinterpret_cast(chunk->ptr), - .size = chunk->size, - .type = static_cast(chunk->state.type), - .attributes = chunk->attributes.value, - .permissions = static_cast(chunk->permission.Get()), + .address = reinterpret_cast(chunk->first), + .size = chunk->second.size, + .type = static_cast(chunk->second.state.type), + .attributes = chunk->second.attributes.value, + .permissions = static_cast(chunk->second.permission.Get()), .deviceRefCount = 0, .ipcRefCount = 0, }; - Logger::Debug("Address: 0x{:X}, Region Start: 0x{:X}, Size: 0x{:X}, Type: 0x{:X}, Is Uncached: {}, Permissions: {}{}{}", pointer, memInfo.address, memInfo.size, memInfo.type, static_cast(chunk->attributes.isUncached), chunk->permission.r ? 'R' : '-', chunk->permission.w ? 'W' : '-', chunk->permission.x ? 'X' : '-'); + Logger::Debug("Address: 0x{:X}, Region Start: 0x{:X}, Size: 0x{:X}, Type: 0x{:X}, Attributes: 0x{:X}, Permissions: {}{}{}", address, memInfo.address, memInfo.size, memInfo.type, memInfo.attributes, chunk->second.permission.r ? 'R' : '-', chunk->second.permission.w ? 'W' : '-', chunk->second.permission.x ? 'X' : '-'); } else { - auto addressSpaceEnd{reinterpret_cast(state.process->memory.addressSpace.end().base())}; + u64 addressSpaceEnd{reinterpret_cast(state.process->memory.addressSpace.end().base())}; memInfo = { .address = addressSpaceEnd, - .size = ~addressSpaceEnd + 1, + .size = 0 - addressSpaceEnd, .type = static_cast(memory::MemoryType::Reserved), }; - Logger::Debug("Trying to query memory outside of the application's address space: 0x{:X}", pointer); + Logger::Debug("Trying to query memory outside of the application's address space: 0x{:X}", address); } *reinterpret_cast(state.ctx->gpr.x0) = memInfo; + // The page info, which is always 0 + state.ctx->gpr.x1 = 0; state.ctx->gpr.w0 = Result{}; } @@ -247,10 +247,6 @@ namespace skyline::kernel::svc { return; } - auto stack{state.process->GetMemoryObject(stackTop)}; - if (!stack) - throw exception("svcCreateThread: Cannot find memory object in handle table for thread stack: 0x{:X}", stackTop); - auto thread{state.process->CreateThread(entry, entryArgument, stackTop, priority, static_cast(idealCore))}; if (thread) { Logger::Debug("Created thread #{} with handle 0x{:X} (Entry Point: 0x{:X}, Argument: 0x{:X}, Stack Pointer: 0x{:X}, Priority: {}, Ideal Core: {})", thread->id, thread->handle, entry, entryArgument, stackTop, priority, idealCore); @@ -476,31 +472,38 @@ namespace skyline::kernel::svc { try { KHandle handle{state.ctx->gpr.w0}; auto object{state.process->GetHandle(handle)}; - auto pointer{reinterpret_cast(state.ctx->gpr.x1)}; + u8 *address{reinterpret_cast(state.ctx->gpr.x1)}; - if (!util::IsPageAligned(pointer)) { + if (!util::IsPageAligned(address)) [[unlikely]] { state.ctx->gpr.w0 = result::InvalidAddress; - Logger::Warn("'pointer' not page aligned: 0x{:X}", pointer); + Logger::Warn("'address' not page aligned: 0x{:X}", address); return; } size_t size{state.ctx->gpr.x2}; - if (!util::IsPageAligned(size)) { + if (!size || !util::IsPageAligned(size)) [[unlikely]] { state.ctx->gpr.w0 = result::InvalidSize; - Logger::Warn("'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size); + Logger::Warn("'size' {}: 0x{:X}", size ? "is not page aligned" : "is zero", size); + return; + } + + if (address >= (address + size) || !state.process->memory.AddressSpaceContains(span{address, size})) [[unlikely]] { + state.ctx->gpr.w0 = result::InvalidCurrentMemory; + Logger::Warn("Invalid address and size combination: 'address': 0x{:X}, 'size': 0x{:X}", address, size); return; } memory::Permission permission(static_cast(state.ctx->gpr.w3)); - if ((permission.w && !permission.r) || (permission.x && !permission.r)) { - Logger::Warn("'permission' invalid: {}{}{}", permission.r ? 'R' : '-', permission.w ? 'W' : '-', permission.x ? 'X' : '-'); + if ((!permission.r && !permission.w && !permission.x) || (permission.w && !permission.r) || permission.x) [[unlikely]] { state.ctx->gpr.w0 = result::InvalidNewMemoryPermission; + Logger::Warn("'permission' invalid: {}{}{}", permission.r ? 'R' : '-', permission.w ? 'W' : '-', permission.x ? 'X' : '-'); return; } - Logger::Debug("Mapping shared memory (0x{:X}) at 0x{:X} - 0x{:X} (0x{:X} bytes) ({}{}{})", handle, pointer, pointer + size, size, permission.r ? 'R' : '-', permission.w ? 'W' : '-', permission.x ? 'X' : '-'); + Logger::Debug("Mapping shared memory (0x{:X}) at 0x{:X} - 0x{:X} (0x{:X} bytes), with permissions: ({}{}{})", handle, address, address + size, size, permission.r ? 'R' : '-', permission.w ? 'W' : '-', permission.x ? 'X' : '-'); - object->Map(span{pointer, size}, permission); + object->Map(span{address, size}, permission); + state.process->memory.AddRef(object); state.ctx->gpr.w0 = Result{}; } catch (const std::out_of_range &) { @@ -513,24 +516,31 @@ namespace skyline::kernel::svc { try { KHandle handle{state.ctx->gpr.w0}; auto object{state.process->GetHandle(handle)}; - auto pointer{reinterpret_cast(state.ctx->gpr.x1)}; + u8 *address{reinterpret_cast(state.ctx->gpr.x1)}; - if (!util::IsPageAligned(pointer)) { + if (!util::IsPageAligned(address)) [[unlikely]] { state.ctx->gpr.w0 = result::InvalidAddress; - Logger::Warn("'pointer' not page aligned: 0x{:X}", pointer); + Logger::Warn("'address' not page aligned: 0x{:X}", address); return; } size_t size{state.ctx->gpr.x2}; - if (!util::IsPageAligned(size)) { + if (!size || !util::IsPageAligned(size)) [[unlikely]] { state.ctx->gpr.w0 = result::InvalidSize; - Logger::Warn("'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size); + Logger::Warn("'size' {}: 0x{:X}", size ? "is not page aligned" : "is zero", size); return; } - Logger::Debug("Unmapping shared memory (0x{:X}) at 0x{:X} - 0x{:X} (0x{:X} bytes)", handle, pointer, pointer + size, size); + if (address >= (address + size) || !state.process->memory.AddressSpaceContains(span{address, size})) [[unlikely]] { + state.ctx->gpr.w0 = result::InvalidCurrentMemory; + Logger::Warn("Invalid address and size combination: 'address': 0x{:X}, 'size': 0x{:X}", address, size); + return; + } - object->Unmap(span{pointer, size}); + Logger::Debug("Unmapping shared memory (0x{:X}) at 0x{:X} - 0x{:X} (0x{:X} bytes)", handle, address, address + size, size); + + object->Unmap(span{address, size}); + state.process->memory.RemoveRef(object); state.ctx->gpr.w0 = Result{}; } catch (const std::out_of_range &) { @@ -540,29 +550,37 @@ namespace skyline::kernel::svc { } void CreateTransferMemory(const DeviceState &state) { - auto pointer{reinterpret_cast(state.ctx->gpr.x1)}; - if (!util::IsPageAligned(pointer)) { + u8 *address{reinterpret_cast(state.ctx->gpr.x1)}; + if (!util::IsPageAligned(address)) [[unlikely]] { state.ctx->gpr.w0 = result::InvalidAddress; - Logger::Warn("'pointer' not page aligned: 0x{:X}", pointer); + Logger::Warn("'address' not page aligned: 0x{:X}", address); return; } size_t size{state.ctx->gpr.x2}; - if (!util::IsPageAligned(size)) { + if (!size || !util::IsPageAligned(size)) [[unlikely]] { state.ctx->gpr.w0 = result::InvalidSize; - Logger::Warn("'size' {}: 0x{:X}", size ? "not page aligned" : "is zero", size); + Logger::Warn("'size' {}: 0x{:X}", size ? "is not page aligned" : "is zero", size); + return; + } + + if (address >= (address + size) || !state.process->memory.AddressSpaceContains(span{address, size})) [[unlikely]] { + state.ctx->gpr.w0 = result::InvalidCurrentMemory; + Logger::Warn("Invalid address and size combination: 'address': 0x{:X}, 'size': 0x{:X}", address, size); return; } memory::Permission permission(static_cast(state.ctx->gpr.w3)); - if ((permission.w && !permission.r) || (permission.x && !permission.r)) { + if ((permission.w && !permission.r) || permission.x) [[unlikely]] { Logger::Warn("'permission' invalid: {}{}{}", permission.r ? 'R' : '-', permission.w ? 'W' : '-', permission.x ? 'X' : '-'); state.ctx->gpr.w0 = result::InvalidNewMemoryPermission; return; } - auto tmem{state.process->NewHandle(pointer, size, permission, permission.raw ? memory::states::TransferMemory : memory::states::TransferMemoryIsolated)}; - Logger::Debug("Creating transfer memory (0x{:X}) at 0x{:X} - 0x{:X} (0x{:X} bytes) ({}{}{})", tmem.handle, pointer, pointer + size, size, permission.r ? 'R' : '-', permission.w ? 'W' : '-', permission.x ? 'X' : '-'); + auto tmem{state.process->NewHandle(address, size, permission)}; + state.process->memory.AddRef(tmem.item); + + Logger::Debug("Creating transfer memory (0x{:X}) at 0x{:X} - 0x{:X} (0x{:X} bytes) ({}{}{})", tmem.handle, address, address + size, size, permission.r ? 'R' : '-', permission.w ? 'W' : '-', permission.x ? 'X' : '-'); state.ctx->gpr.w0 = Result{}; state.ctx->gpr.w1 = tmem.handle; @@ -896,8 +914,8 @@ namespace skyline::kernel::svc { IdleTickCount = 10, RandomEntropy = 11, // 2.0.0+ - AddressSpaceBaseAddr = 12, - AddressSpaceSize = 13, + AslrRegionBaseAddr = 12, + AslrRegionSize = 13, StackRegionBaseAddr = 14, StackRegionSize = 15, // 3.0.0+ @@ -965,11 +983,11 @@ namespace skyline::kernel::svc { out = util::GetTimeTicks(); break; - case InfoState::AddressSpaceBaseAddr: + case InfoState::AslrRegionBaseAddr: out = reinterpret_cast(state.process->memory.base.data()); break; - case InfoState::AddressSpaceSize: + case InfoState::AslrRegionSize: out = state.process->memory.base.size(); break; @@ -1019,93 +1037,64 @@ namespace skyline::kernel::svc { } void MapPhysicalMemory(const DeviceState &state) { - auto pointer{reinterpret_cast(state.ctx->gpr.x0)}; + u8 *address{reinterpret_cast(state.ctx->gpr.x0)}; size_t size{state.ctx->gpr.x1}; - if (!util::IsPageAligned(pointer)) { - Logger::Warn("Pointer 0x{:X} is not page aligned", pointer); + if (!util::IsPageAligned(address)) [[unlikely]] { state.ctx->gpr.w0 = result::InvalidAddress; + Logger::Warn("'address' not page aligned: 0x{:X}", address); return; } - if (!size || !util::IsPageAligned(size)) { - Logger::Warn("Size 0x{:X} is not page aligned", size); + if (!size || !util::IsPageAligned(size)) [[unlikely]] { state.ctx->gpr.w0 = result::InvalidSize; + Logger::Warn("'size' {}: 0x{:X}", size ? "is not page aligned" : "is zero", size); return; } - if (!state.process->memory.alias.contains(span{pointer, size})) { - Logger::Warn("Memory region 0x{:X} - 0x{:X} (0x{:X}) is invalid", pointer, pointer + size, size); + if (address >= (address + size)) [[unlikely]] { state.ctx->gpr.w0 = result::InvalidMemoryRegion; + Logger::Warn("Invalid address and size combination: 'address': 0x{:X}, 'size': 0x{:X}", address, size); return; } - state.process->NewHandle(span{pointer, size}, memory::Permission{true, true, false}, memory::states::Heap); + if (!state.process->memory.alias.contains(span{address, size})) [[unlikely]] { + state.ctx->gpr.w0 = result::InvalidMemoryRegion; + Logger::Warn("Tried to map physical memory outside of alias region: 0x{:X} - 0x{:X} (0x{:X} bytes)", address, address + size, size); + return; + } - Logger::Debug("Mapped physical memory at 0x{:X} - 0x{:X} (0x{:X})", pointer, pointer + size, size); + state.process->memory.MapHeapMemory(span{address, size}); + Logger::Debug("Mapped physical memory at 0x{:X} - 0x{:X} (0x{:X} bytes)", address, address + size, size); state.ctx->gpr.w0 = Result{}; } void UnmapPhysicalMemory(const DeviceState &state) { - auto pointer{reinterpret_cast(state.ctx->gpr.x0)}; + u8 *address{reinterpret_cast(state.ctx->gpr.x0)}; size_t size{state.ctx->gpr.x1}; - if (!util::IsPageAligned(pointer)) { - Logger::Warn("Pointer 0x{:X} is not page aligned", pointer); + if (!util::IsPageAligned(address)) [[unlikely]] { state.ctx->gpr.w0 = result::InvalidAddress; + Logger::Warn("'address' not page aligned: 0x{:X}", address); return; } - if (!size || !util::IsPageAligned(size)) { - Logger::Warn("Size 0x{:X} is not page aligned", size); + if (!size || !util::IsPageAligned(size)) [[unlikely]] { state.ctx->gpr.w0 = result::InvalidSize; + Logger::Warn("'size' {}: 0x{:X}", size ? "is not page aligned" : "is zero", size); return; } - if (!state.process->memory.alias.contains(span{pointer, size})) { - Logger::Warn("Memory region 0x{:X} - 0x{:X} (0x{:X}) is invalid", pointer, pointer + size, size); + if (!state.process->memory.alias.contains(span{address, size})) [[unlikely]] { state.ctx->gpr.w0 = result::InvalidMemoryRegion; + Logger::Warn("Tried to unmap physical memory outside of alias region: 0x{:X} - 0x{:X} (0x{:X} bytes)", address, address + size, size); return; } - Logger::Debug("Unmapped physical memory at 0x{:X} - 0x{:X} (0x{:X})", pointer, pointer + size, size); - - auto end{pointer + size}; - while (pointer < end) { - auto chunk{state.process->memory.Get(pointer)}; - if (chunk && chunk->memory) { - if (chunk->memory->objectType != type::KType::KPrivateMemory) - throw exception("Trying to unmap non-private memory"); - - auto memory{static_cast(chunk->memory)}; - auto initialSize{memory->guest.size()}; - if (memory->memoryState == memory::states::Heap) { - if (memory->guest.data() >= pointer) { - if (memory->guest.size() <= size) { - memory->Resize(0); - state.process->CloseHandle(memory->handle); - } else { - memory->Remap(span{pointer + size, static_cast((pointer + memory->guest.size() - memory->guest.data())) - size}); - } - } else if (memory->guest.data() < pointer) { - memory->Resize(static_cast(pointer - memory->guest.data())); - - if (memory->guest.data() + initialSize > end) - state.process->NewHandle(span{end, static_cast(memory->guest.data() + initialSize - end)}, memory::Permission{true, true, false}, memory::states::Heap); - } - } - pointer += initialSize; - size -= initialSize; - } else { - auto block{*state.process->memory.Get(pointer)}; - pointer += block.size; - size -= block.size; - } - } - - state.process->memory.FreeMemory(std::span(pointer, size)); + state.process->memory.UnmapMemory(span{address, size}); + Logger::Debug("Unmapped physical memory at 0x{:X} - 0x{:X} (0x{:X} bytes)", address, address + size, size); state.ctx->gpr.w0 = Result{}; } diff --git a/app/src/main/cpp/skyline/kernel/types/KMemory.h b/app/src/main/cpp/skyline/kernel/types/KMemory.h index ea99387a..72513079 100644 --- a/app/src/main/cpp/skyline/kernel/types/KMemory.h +++ b/app/src/main/cpp/skyline/kernel/types/KMemory.h @@ -17,15 +17,7 @@ namespace skyline::kernel::type { /** * @return A span representing the memory object on the guest */ - span guest; - - /** - * @brief Updates the permissions of a block of mapped memory - * @param ptr The starting address to change the permissions at - * @param size The size of the partition to change the permissions of - * @param permission The new permissions to be set for the memory - */ - virtual void UpdatePermission(span map, memory::Permission permission) = 0; + span guest; virtual ~KMemory() = default; }; diff --git a/app/src/main/cpp/skyline/kernel/types/KObject.h b/app/src/main/cpp/skyline/kernel/types/KObject.h index 674579b9..fe6943f1 100644 --- a/app/src/main/cpp/skyline/kernel/types/KObject.h +++ b/app/src/main/cpp/skyline/kernel/types/KObject.h @@ -14,7 +14,6 @@ namespace skyline::kernel::type { KProcess, KSharedMemory, KTransferMemory, - KPrivateMemory, KSession, KEvent, }; diff --git a/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp b/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp deleted file mode 100644 index c4728a19..00000000 --- a/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.cpp +++ /dev/null @@ -1,96 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 -// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) - -#include -#include -#include -#include "KPrivateMemory.h" -#include "KProcess.h" - -namespace skyline::kernel::type { - KPrivateMemory::KPrivateMemory(const DeviceState &state, KHandle handle, span guest, memory::Permission permission, memory::MemoryState memState) - : permission(permission), - memoryState(memState), - handle(handle), - KMemory(state, KType::KPrivateMemory, guest) { - if (!state.process->memory.AddressSpaceContains(guest)) - throw exception("KPrivateMemory allocation isn't inside guest address space: 0x{:X} - 0x{:X}", guest.data(), guest.data() + guest.size()); - if (!util::IsPageAligned(guest.data()) || !util::IsPageAligned(guest.size())) - throw exception("KPrivateMemory mapping isn't page-aligned: 0x{:X} - 0x{:X} (0x{:X})", guest.data(), guest.data() + guest.size(), guest.size()); - - if (mprotect(guest.data(), guest.size(), PROT_READ | PROT_WRITE | PROT_EXEC) < 0) // We only need to reprotect as the allocation has already been reserved by the MemoryManager - throw exception("An occurred while mapping private memory: {} with 0x{:X} @ 0x{:X}", strerror(errno), guest.data(), guest.size()); - - state.process->memory.InsertChunk(ChunkDescriptor{ - .ptr = guest.data(), - .size = guest.size(), - .permission = permission, - .state = memState, - .memory = this, - }); - } - - void KPrivateMemory::Resize(size_t nSize) { - if (mprotect(guest.data(), nSize, PROT_READ | PROT_WRITE | PROT_EXEC) < 0) - throw exception("An occurred while resizing private memory: {}", strerror(errno)); - - if (nSize < guest.size()) { - state.process->memory.InsertChunk(ChunkDescriptor{ - .ptr = guest.data() + nSize, - .size = guest.size() - nSize, - .state = memory::states::Unmapped, - }); - } else if (guest.size() < nSize) { - state.process->memory.InsertChunk(ChunkDescriptor{ - .ptr = guest.data() + guest.size(), - .size = nSize - guest.size(), - .permission = permission, - .state = memoryState, - .memory = this, - }); - } - guest = span{guest.data(), nSize}; - } - - void KPrivateMemory::Remap(span map) { - if (!state.process->memory.AddressSpaceContains(map)) - throw exception("KPrivateMemory remapping isn't inside guest address space: 0x{:X} - 0x{:X}", map.data(), map.end().base()); - if (!util::IsPageAligned(map.data()) || !util::IsPageAligned(map.size())) - throw exception("KPrivateMemory remapping isn't page-aligned: 0x{:X} - 0x{:X} (0x{:X})", map.data(), map.end().base(), map.size()); - - if (mprotect(guest.data(), guest.size(), PROT_NONE) < 0) - throw exception("An occurred while remapping private memory: {}", strerror(errno)); - - if (mprotect(map.data(), map.size(), PROT_READ | PROT_WRITE | PROT_EXEC) < 0) - throw exception("An occurred while remapping private memory: {}", strerror(errno)); - } - - void KPrivateMemory::UpdatePermission(span map, memory::Permission pPermission) { - auto ptr{std::clamp(map.data(), guest.data(), guest.end().base())}; - auto size{std::min(map.size(), static_cast((guest.end().base()) - ptr))}; - - if (ptr && !util::IsPageAligned(ptr)) - throw exception("KPrivateMemory permission updated with a non-page-aligned address: 0x{:X}", ptr); - - // If a static code region has been mapped as writable it needs to be changed to mutable - if (memoryState == memory::states::CodeStatic && pPermission.w) - memoryState = memory::states::CodeMutable; - - state.process->memory.InsertChunk(ChunkDescriptor{ - .ptr = ptr, - .size = size, - .permission = pPermission, - .state = memoryState, - .memory = this, - }); - } - - KPrivateMemory::~KPrivateMemory() { - mprotect(guest.data(), guest.size(), PROT_NONE); - state.process->memory.InsertChunk(ChunkDescriptor{ - .ptr = guest.data(), - .size = guest.size(), - .state = memory::states::Unmapped, - }); - } -} diff --git a/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.h b/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.h deleted file mode 100644 index ac873c7d..00000000 --- a/app/src/main/cpp/skyline/kernel/types/KPrivateMemory.h +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 -// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) - -#pragma once - -#include "KMemory.h" - -namespace skyline::kernel::type { - /** - * @brief KPrivateMemory is used to map memory local to the guest process - * @note This does not reflect a kernel object in Horizon OS, it is an abstraction which makes things simpler to manage in Skyline instead - */ - class KPrivateMemory : public KMemory { - public: - memory::Permission permission; - memory::MemoryState memoryState; - KHandle handle; - - /** - * @param permission The permissions for the allocated memory (As reported to the application, host memory permissions aren't reflected by this) - * @note 'ptr' needs to be in guest-reserved address space - */ - KPrivateMemory(const DeviceState &state, KHandle handle, span guest, memory::Permission permission, memory::MemoryState memState); - - /** - * @note There is no check regarding if any expansions will cause the memory mapping to leak into other mappings - * @note Any extensions will have the same permissions and memory state as the initial mapping as opposed to extending the end - */ - void Resize(size_t size); - - /** - * @note This does not copy over anything, only contents of any overlapping regions will be retained - */ - void Remap(span map); - - void UpdatePermission(span map, memory::Permission pPermission) override; - - /** - * @brief The destructor of private memory, it deallocates the memory - */ - ~KPrivateMemory(); - }; -} diff --git a/app/src/main/cpp/skyline/kernel/types/KProcess.cpp b/app/src/main/cpp/skyline/kernel/types/KProcess.cpp index 19b2ee10..31ed9acc 100644 --- a/app/src/main/cpp/skyline/kernel/types/KProcess.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KProcess.cpp @@ -8,12 +8,12 @@ #include "KProcess.h" namespace skyline::kernel::type { - KProcess::TlsPage::TlsPage(std::shared_ptr memory) : memory(std::move(memory)) {} + KProcess::TlsPage::TlsPage(u8 *memory) : memory(memory) {} u8 *KProcess::TlsPage::ReserveSlot() { if (index == constant::TlsSlots) return nullptr; - return memory->guest.data() + (constant::TlsSlotSize * index++); + return memory + (constant::TlsSlotSize * index++); } KProcess::KProcess(const DeviceState &state) : memory(state), KSyncObject(state, KType::KProcess) {} @@ -26,7 +26,7 @@ namespace skyline::kernel::type { } void KProcess::Kill(bool join, bool all, bool disableCreation) { - Logger::Warn("Killing {}{}KProcess{}", join ? "and joining " : "", all ? "all threads in " : "HOS-0 in ", disableCreation ? " with new thread creation disabled" : ""); + Logger::Warn("Killing {}{}KProcess{}", join ? "and joining " : "", all ? "all threads in " : "HOS-1 in ", disableCreation ? " with new thread creation disabled" : ""); Logger::EmulationContext.Flush(); bool expected{false}; @@ -49,8 +49,8 @@ namespace skyline::kernel::type { void KProcess::InitializeHeapTls() { constexpr size_t DefaultHeapSize{0x200000}; - heap = std::make_shared(state, 0, span{state.process->memory.heap.data(), DefaultHeapSize}, memory::Permission{true, true, false}, memory::states::Heap); - InsertItem(heap); // Insert it into the handle table so GetMemoryObject will contain it + memory.MapHeapMemory(span{state.process->memory.heap.data(), DefaultHeapSize}); + memory.setHeapSize = DefaultHeapSize; tlsExceptionContext = AllocateTlsSlot(); } @@ -61,8 +61,26 @@ namespace skyline::kernel::type { if ((slot = tlsPage->ReserveSlot())) return slot; - slot = tlsPages.empty() ? reinterpret_cast(memory.tlsIo.data()) : ((*(tlsPages.end() - 1))->memory->guest.data() + constant::PageSize); - auto tlsPage{std::make_shared(std::make_shared(state, 0, span{slot, constant::PageSize}, memory::Permission(true, true, false), memory::states::ThreadLocal))}; + bool isAllocated{}; + + u8 *pageCandidate{state.process->memory.tlsIo.data()}; + std::pair chunk; + while (state.process->memory.tlsIo.contains(span(pageCandidate, constant::PageSize))) { + chunk = memory.GetChunk(pageCandidate).value(); + + if (chunk.second.state == memory::states::Unmapped) { + memory.MapThreadLocalMemory(span{pageCandidate, constant::PageSize}); + isAllocated = true; + break; + } else { + pageCandidate = chunk.first + chunk.second.size; + } + } + + if (!isAllocated) [[unlikely]] + throw exception("Failed to find free memory for a tls slot!"); + + auto tlsPage{std::make_shared(pageCandidate)}; tlsPages.push_back(tlsPage); return tlsPage->ReserveSlot(); } @@ -72,8 +90,27 @@ namespace skyline::kernel::type { if (disableThreadCreation) return nullptr; if (!stackTop && threads.empty()) { //!< Main thread stack is created by the kernel and owned by the process - mainThreadStack = std::make_shared(state, 0, span{state.process->memory.stack.data(), state.process->npdm.meta.mainThreadStackSize}, memory::Permission{true, true, false}, memory::states::Stack); - stackTop = mainThreadStack->guest.end().base(); + bool isAllocated{}; + + u8 *pageCandidate{memory.stack.data()}; + std::pair chunk; + while (state.process->memory.stack.contains(span(pageCandidate, state.process->npdm.meta.mainThreadStackSize))) { + chunk = memory.GetChunk(pageCandidate).value(); + + if (chunk.second.state == memory::states::Unmapped && chunk.second.size >= state.process->npdm.meta.mainThreadStackSize) { + memory.MapStackMemory(span{pageCandidate, state.process->npdm.meta.mainThreadStackSize}); + isAllocated = true; + break; + } else { + pageCandidate = chunk.first + chunk.second.size; + } + } + + if (!isAllocated) [[unlikely]] + throw exception("Failed to map main thread stack!"); + + stackTop = pageCandidate + state.process->npdm.meta.mainThreadStackSize; + mainThreadStack = span(pageCandidate, state.process->npdm.meta.mainThreadStackSize); } size_t tid{threads.size() + 1}; //!< The first thread is HOS-1 rather than HOS-0, this is to match the HOS kernel's behaviour auto thread{NewHandle(this, tid, entry, argument, stackTop, priority ? *priority : state.process->npdm.meta.mainThreadPriority, idealCore ? *idealCore : state.process->npdm.meta.idealCore).item}; @@ -81,29 +118,6 @@ namespace skyline::kernel::type { return thread; } - std::optional> KProcess::GetMemoryObject(u8 *ptr) { - std::shared_lock lock(handleMutex); - - for (KHandle index{}; index < handles.size(); index++) { - auto &object{handles[index]}; - if (object) { - switch (object->objectType) { - case type::KType::KPrivateMemory: - case type::KType::KSharedMemory: - case type::KType::KTransferMemory: { - auto mem{std::static_pointer_cast(object)}; - if (mem->guest.contains(ptr)) - return std::make_optional>({mem, constant::BaseHandleIndex + index}); - } - - default: - break; - } - } - } - return std::nullopt; - } - void KProcess::ClearHandleTable() { std::shared_lock lock(handleMutex); handles.clear(); diff --git a/app/src/main/cpp/skyline/kernel/types/KProcess.h b/app/src/main/cpp/skyline/kernel/types/KProcess.h index 2781ca76..f190a57e 100644 --- a/app/src/main/cpp/skyline/kernel/types/KProcess.h +++ b/app/src/main/cpp/skyline/kernel/types/KProcess.h @@ -42,9 +42,9 @@ namespace skyline { */ struct TlsPage { u8 index{}; //!< The slots are assigned sequentially, this holds the index of the last TLS slot reserved - std::shared_ptr memory; //!< A single page sized memory allocation for this TLS page + u8 *memory; //!< A single page sized memory allocation for this TLS page - TlsPage(std::shared_ptr memory); + TlsPage(u8 *memory); /** * @return A non-null pointer to a TLS page slot on success, a nullptr will be returned if this page is full @@ -57,10 +57,8 @@ namespace skyline { u8 *tlsExceptionContext{}; //!< A pointer to the TLS exception handling context slot std::mutex tlsMutex; //!< A mutex to synchronize allocation of TLS pages to prevent extra pages from being created std::vector> tlsPages; //!< All TLS pages allocated by this process - std::shared_ptr mainThreadStack; //!< The stack memory of the main thread stack is owned by the KProcess itself - std::shared_ptr heap; vfs::NPDM npdm; - + span mainThreadStack; private: std::shared_mutex handleMutex; std::vector> handles; @@ -117,7 +115,7 @@ namespace skyline { std::unique_lock lock(handleMutex); std::shared_ptr item; - if constexpr (std::is_same() || std::is_same()) + if constexpr (std::is_same()) item = std::make_shared(state, constant::BaseHandleIndex + handles.size(), args...); else item = std::make_shared(state, args...); @@ -156,8 +154,6 @@ namespace skyline { objectType = KType::KSharedMemory; } else if constexpr(std::is_same()) { objectType = KType::KTransferMemory; - } else if constexpr(std::is_same()) { - objectType = KType::KPrivateMemory; } else if constexpr(std::is_same()) { objectType = KType::KSession; } else if constexpr(std::is_same()) { @@ -188,13 +184,6 @@ namespace skyline { throw std::out_of_range(fmt::format("GetHandle was called with a deleted handle: 0x{:X}", handle)); } - /** - * @brief Retrieves a kernel memory object that owns the specified address - * @param address The address to look for - * @return A shared pointer to the corresponding KMemory object - */ - std::optional> GetMemoryObject(u8 *ptr); - /** * @brief Closes a handle in the handle table */ diff --git a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp index fe3d84c2..cdcafa4e 100644 --- a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp @@ -8,122 +8,66 @@ #include "KProcess.h" namespace skyline::kernel::type { - KSharedMemory::KSharedMemory(const DeviceState &state, size_t size, memory::MemoryState memState, KType type) - : memoryState(memState), - KMemory(state, type, span{}) { + KSharedMemory::KSharedMemory(const DeviceState &state, size_t size, KType type) + : KMemory(state, type, span{}) { fd = ASharedMemory_create(type == KType::KSharedMemory ? "HOS-KSharedMemory" : "HOS-KTransferMemory", size); - if (fd < 0) + if (fd < 0) [[unlikely]] throw exception("An error occurred while creating shared memory: {}", fd); - auto hostPtr{static_cast(mmap(nullptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, fd, 0))}; - if (hostPtr == MAP_FAILED) + u8 *hostPtr{static_cast(mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0))}; + if (hostPtr == MAP_FAILED) [[unlikely]] throw exception("An occurred while mapping shared memory: {}", strerror(errno)); host = span{hostPtr, size}; } u8 *KSharedMemory::Map(span map, memory::Permission permission) { - if (!state.process->memory.AddressSpaceContains(map)) - throw exception("KPrivateMemory allocation isn't inside guest address space: 0x{:X} - 0x{:X}", map.data(), map.end().base()); - if (!util::IsPageAligned(map.data()) || !util::IsPageAligned(map.size())) + if (!state.process->memory.AddressSpaceContains(map)) [[unlikely]] + throw exception("KSharedMemory allocation isn't inside guest address space: 0x{:X} - 0x{:X}", map.data(), map.end().base()); + if (!util::IsPageAligned(map.data()) || !util::IsPageAligned(map.size())) [[unlikely]] throw exception("KSharedMemory mapping isn't page-aligned: 0x{:X} - 0x{:X} (0x{:X})", map.data(), map.end().base(), map.size()); - if (guest.valid()) + if (guest.valid()) [[unlikely]] throw exception("Mapping KSharedMemory multiple times on guest is not supported: Requested Mapping: 0x{:X} - 0x{:X} (0x{:X}), Current Mapping: 0x{:X} - 0x{:X} (0x{:X})", map.data(), map.end().base(), map.size(), guest.data(), guest.end().base(), guest.size()); auto guestPtr{static_cast(mmap(map.data(), map.size(), permission.Get(), MAP_SHARED | (map.data() ? MAP_FIXED : 0), fd, 0))}; - if (guestPtr == MAP_FAILED) + if (guestPtr == MAP_FAILED) [[unlikely]] throw exception("An error occurred while mapping shared memory in guest: {}", strerror(errno)); guest = span{guestPtr, map.size()}; - state.process->memory.InsertChunk(ChunkDescriptor{ - .ptr = guest.data(), - .size = guest.size(), - .permission = permission, - .state = memoryState, - .attributes = memory::MemoryAttribute{ - .isBorrowed = objectType == KType::KTransferMemory, - }, - .memory = this - }); + if (objectType == KType::KTransferMemory) { + state.process->memory.MapTransferMemory(guest, permission); + state.process->memory.SetLockOnChunks(guest, true); + } else { + state.process->memory.MapSharedMemory(guest, permission); + } return guest.data(); } void KSharedMemory::Unmap(span map) { auto &memoryManager{state.process->memory}; - if (!memoryManager.AddressSpaceContains(map)) - throw exception("KPrivateMemory allocation isn't inside guest address space: 0x{:X} - 0x{:X}", map.data(), map.end().base()); - if (!util::IsPageAligned(map.data()) || !util::IsPageAligned(map.size())) - throw exception("KSharedMemory mapping isn't page-aligned: 0x{:X} - 0x{:X} (0x{:X})", map.data(), map.end().base(), map.size()); - if (guest.data() != map.data() && guest.size() != map.size()) + if (!memoryManager.AddressSpaceContains(map)) [[unlikely]] + throw exception("KSharedMemory allocation isn't inside guest address space: 0x{:X} - 0x{:X}", map.data(), map.end().base()); + if (!util::IsPageAligned(map.data()) || !util::IsPageAligned(map.size())) [[unlikely]] + throw exception("KSharedMemory mapping isn't page-aligned: 0x{:X} - 0x{:X} ({} bytes)", map.data(), map.end().base(), map.size()); + if (guest.data() != map.data() && guest.size() != map.size()) [[unlikely]] throw exception("Unmapping KSharedMemory partially is not supported: Requested Unmap: 0x{:X} - 0x{:X} (0x{:X}), Current Mapping: 0x{:X} - 0x{:X} (0x{:X})", map.data(), map.end().base(), map.size(), guest.data(), guest.end().base(), guest.size()); - if (mmap(map.data(), map.size(), PROT_NONE, MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, -1, 0) == MAP_FAILED) + if (mmap(map.data(), map.size(), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, -1, 0) == MAP_FAILED) [[unlikely]] throw exception("An error occurred while unmapping shared memory in guest: {}", strerror(errno)); guest = span{}; - memoryManager.InsertChunk(ChunkDescriptor{ - .ptr = map.data(), - .size = map.size(), - .state = memory::states::Unmapped, - }); - } - - void KSharedMemory::UpdatePermission(span map, memory::Permission permission) { - if (map.valid() && !util::IsPageAligned(map.data())) - throw exception("KSharedMemory permission updated with a non-page-aligned address: 0x{:X}", map.data()); - - if (guest.valid()) { - mprotect(map.data(), map.size(), permission.Get()); - if (guest.data() == MAP_FAILED) - throw exception("An error occurred while updating shared memory's permissions in guest: {}", strerror(errno)); - - state.process->memory.InsertChunk(ChunkDescriptor{ - .ptr = map.data(), - .size = map.size(), - .permission = permission, - .state = memoryState, - .attributes = memory::MemoryAttribute{ - .isBorrowed = objectType == KType::KTransferMemory, - }, - .memory = this - }); - } + memoryManager.UnmapMemory(map); } KSharedMemory::~KSharedMemory() { if (state.process && guest.valid()) { auto &memoryManager{state.process->memory}; - if (objectType != KType::KTransferMemory) { - if (mmap(guest.data(), guest.size(), PROT_NONE, MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, -1, 0) == MAP_FAILED) - Logger::Warn("An error occurred while unmapping shared memory: {}", strerror(errno)); - state.process->memory.InsertChunk(ChunkDescriptor{ - .ptr = guest.data(), - .size = guest.size(), - .state = memory::states::Unmapped, - }); - } else { - // KTransferMemory remaps the region with R/W permissions during destruction - constexpr memory::Permission UnborrowPermission{true, true, false}; + if (mmap(guest.data(), guest.size(), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, -1, 0) == MAP_FAILED) [[unlikely]] + Logger::Warn("An error occurred while unmapping shared memory: {}", strerror(errno)); - if (mmap(guest.data(), guest.size(), UnborrowPermission.Get(), MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, -1, 0) == 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"); - guest.copy_from(host); - - state.process->memory.InsertChunk(ChunkDescriptor{ - .ptr = guest.data(), - .size = guest.size(), - .permission = UnborrowPermission, - .state = memoryState, - .attributes = memory::MemoryAttribute{ - .isBorrowed = false, - }, - .memory = this - }); - } + state.process->memory.UnmapMemory(guest); } if (host.valid()) diff --git a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.h b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.h index b807c526..724329fd 100644 --- a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.h +++ b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.h @@ -12,12 +12,11 @@ namespace skyline::kernel::type { class KSharedMemory : public KMemory { private: int fd; //!< A file descriptor to the underlying shared memory - memory::MemoryState memoryState; //!< The state of the memory as supplied initially, this is retained for any mappings public: span host; //!< We also keep a host mirror of the underlying shared memory for host access, it 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); + KSharedMemory(const DeviceState &state, size_t size, KType type = KType::KSharedMemory); /** * @note 'ptr' needs to be in guest-reserved address space @@ -29,8 +28,6 @@ namespace skyline::kernel::type { */ void Unmap(span map); - void UpdatePermission(span map, memory::Permission permission) override; - /** * @brief The destructor of shared memory, it deallocates the memory from all processes */ diff --git a/app/src/main/cpp/skyline/kernel/types/KThread.h b/app/src/main/cpp/skyline/kernel/types/KThread.h index c5d014e4..bc0f2d76 100644 --- a/app/src/main/cpp/skyline/kernel/types/KThread.h +++ b/app/src/main/cpp/skyline/kernel/types/KThread.h @@ -9,7 +9,6 @@ #include #include #include "KSyncObject.h" -#include "KPrivateMemory.h" #include "KSharedMemory.h" namespace skyline { diff --git a/app/src/main/cpp/skyline/kernel/types/KTransferMemory.h b/app/src/main/cpp/skyline/kernel/types/KTransferMemory.h index 75085ace..63a9df55 100644 --- a/app/src/main/cpp/skyline/kernel/types/KTransferMemory.h +++ b/app/src/main/cpp/skyline/kernel/types/KTransferMemory.h @@ -15,8 +15,8 @@ namespace skyline::kernel::type { /** * @note 'ptr' needs to be in guest-reserved address space */ - KTransferMemory(const DeviceState &state, u8 *ptr, size_t size, memory::Permission permission, memory::MemoryState memState = memory::states::TransferMemory) - : KSharedMemory(state, size, memState, KType::KTransferMemory) { + KTransferMemory(const DeviceState &state, u8 *ptr, size_t size, memory::Permission permission) + : KSharedMemory(state, size, KType::KTransferMemory) { std::memcpy(host.data(), ptr, size); Map(span{ptr, size}, permission); } diff --git a/app/src/main/cpp/skyline/loader/loader.cpp b/app/src/main/cpp/skyline/loader/loader.cpp index beb1e3c8..8db204c1 100644 --- a/app/src/main/cpp/skyline/loader/loader.cpp +++ b/app/src/main/cpp/skyline/loader/loader.cpp @@ -89,20 +89,24 @@ namespace skyline::loader { hookSize = util::AlignUp(state.nce->GetHookSectionSize(executableSymbols), PAGE_SIZE); } - auto patchType{process->memory.addressSpaceType == memory::AddressSpaceType::AddressSpace36Bit ? memory::states::Heap : memory::states::Reserved}; - process->NewHandle(span{base, patch.size + hookSize}, memory::Permission{false, false, false}, patchType); // --- + if (process->memory.addressSpaceType == memory::AddressSpaceType::AddressSpace36Bit) { + process->memory.MapHeapMemory(span{base, patch.size + hookSize}); // --- + process->memory.SetChunkPermission(span{base, patch.size + hookSize}, memory::Permission{false, false, false}); + } else { + process->memory.Reserve(span{base, patch.size + hookSize}); // --- + } Logger::Debug("Successfully mapped section .patch @ 0x{:X}, Size = 0x{:X}", base, patch.size); if (hookSize > 0) Logger::Debug("Successfully mapped section .hook @ 0x{:X}, Size = 0x{:X}", base + patch.size, hookSize); u8 *executableBase{base + patch.size + hookSize}; - process->NewHandle(span{executableBase + executable.text.offset, textSize}, memory::Permission{true, false, true}, memory::states::CodeStatic); // R-X + process->memory.MapCodeMemory(span{executableBase + executable.text.offset, textSize}, memory::Permission{true, false, true}); // R-X Logger::Debug("Successfully mapped section .text @ 0x{:X}, Size = 0x{:X}", executableBase, textSize); - process->NewHandle(span{executableBase + executable.ro.offset, roSize}, memory::Permission{true, false, false}, memory::states::CodeStatic); // R-- + process->memory.MapCodeMemory(span{executableBase + executable.ro.offset, roSize}, memory::Permission{true, false, false}); // R-- Logger::Debug("Successfully mapped section .rodata @ 0x{:X}, Size = 0x{:X}", executableBase + executable.ro.offset, roSize); - process->NewHandle(span{executableBase + executable.data.offset, dataSize}, memory::Permission{true, true, false}, memory::states::CodeMutable); // RW- + process->memory.MapMutableCodeMemory(span{executableBase + executable.data.offset, dataSize}); // RW- Logger::Debug("Successfully mapped section .data + .bss @ 0x{:X}, Size = 0x{:X}", executableBase + executable.data.offset, dataSize); size_t size{patch.size + hookSize + textSize + roSize + dataSize}; diff --git a/app/src/main/cpp/skyline/services/ro/IRoInterface.cpp b/app/src/main/cpp/skyline/services/ro/IRoInterface.cpp index a981e0e8..3d5f886d 100644 --- a/app/src/main/cpp/skyline/services/ro/IRoInterface.cpp +++ b/app/src/main/cpp/skyline/services/ro/IRoInterface.cpp @@ -71,8 +71,8 @@ namespace skyline::service::ro { if (state.process->memory.heap.contains(ptr) || state.process->memory.alias.contains(ptr)) continue; - auto desc{state.process->memory.Get(ptr)}; - if (!desc || desc->state != memory::states::Unmapped || (static_cast(ptr - desc->ptr) + size) < desc->size) + auto desc{state.process->memory.GetChunk(ptr)}; + if (!desc || desc->second.state != memory::states::Unmapped || (static_cast(ptr - desc->first) + size) < desc->second.size) continue; } while (!ptr); @@ -85,26 +85,21 @@ namespace skyline::service::ro { Result IRoInterface::UnloadModule(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Logger::Error("Module unloading is unimplemented!"); return {}; - } Result IRoInterface::RegisterModuleInfo(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { return {}; - } Result IRoInterface::UnregisterModuleInfo(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { return {}; - } Result IRoInterface::RegisterProcessHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { return {}; - } Result IRoInterface::RegisterProcessModuleInfo(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { return {}; - } }