From 1513e5f6dfd35b321ab7414173cfcb4c219800b9 Mon Sep 17 00:00:00 2001 From: TheASVigilante <65920585+TheASVigilante@users.noreply.github.com> Date: Wed, 19 Apr 2023 17:57:10 +0200 Subject: [PATCH] Implement svcSetMemoryPermission Used by Minecraft legends, this should allow it to proceed further. --- app/src/main/cpp/skyline/kernel/memory.cpp | 10 ++- app/src/main/cpp/skyline/kernel/svc.cpp | 61 ++++++++++++++++--- app/src/main/cpp/skyline/kernel/svc.h | 12 +++- .../main/cpp/skyline/kernel/types/KMemory.cpp | 2 +- .../skyline/kernel/types/KTransferMemory.cpp | 2 +- 5 files changed, 69 insertions(+), 18 deletions(-) diff --git a/app/src/main/cpp/skyline/kernel/memory.cpp b/app/src/main/cpp/skyline/kernel/memory.cpp index 1c297832..a0e13bad 100644 --- a/app/src/main/cpp/skyline/kernel/memory.cpp +++ b/app/src/main/cpp/skyline/kernel/memory.cpp @@ -551,9 +551,13 @@ namespace skyline::kernel { std::shared_lock lock(mutex); size_t size{}; - for (auto &chunk : chunks) { - if (chunk.second.state == memory::states::Heap) - size += chunk.second.size; + auto currChunk = upper_bound(heap.data()); + if (heap.data() < currChunk->first) [[likely]] + --currChunk; + while (currChunk->first < heap.end().base()) { + if (currChunk->second.state == memory::states::Heap) + size += currChunk->second.size; + ++currChunk; } return size + code.size() + state.process->mainThreadStack.size(); diff --git a/app/src/main/cpp/skyline/kernel/svc.cpp b/app/src/main/cpp/skyline/kernel/svc.cpp index 69646055..84c10cb2 100644 --- a/app/src/main/cpp/skyline/kernel/svc.cpp +++ b/app/src/main/cpp/skyline/kernel/svc.cpp @@ -43,6 +43,47 @@ namespace skyline::kernel::svc { Logger::Debug("Heap size changed to 0x{:X} bytes (0x{:X} - 0x{:X})", size, heapBaseAddr, heapBaseAddr + size); } + void SetMemoryPermission(const DeviceState &state) { + u8 *address{reinterpret_cast(state.ctx->gpr.x0)}; + if (!util::IsPageAligned(address)) [[unlikely]] { + state.ctx->gpr.w0 = result::InvalidAddress; + Logger::Warn("'address' not page aligned: 0x{:X}", address); + return; + } + + u64 size{state.ctx->gpr.x1}; + 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 (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 newPermission(static_cast(state.ctx->gpr.w2)); + if ((!newPermission.r && newPermission.w) || newPermission.x) [[unlikely]] { + state.ctx->gpr.w0 = result::InvalidNewMemoryPermission; + Logger::Warn("'permission' invalid: {}{}{}", newPermission.r ? 'R' : '-', newPermission.w ? 'W' : '-', newPermission.x ? 'X' : '-'); + return; + } + + auto chunk{state.process->memory.GetChunk(address).value()}; + if (!chunk.second.state.permissionChangeAllowed) [[unlikely]] { + state.ctx->gpr.w0 = result::InvalidState; + Logger::Warn("Permission change not allowed for chunk at: 0x{:X}, state: 0x{:X}", chunk.first, chunk.second.state.value); + return; + } + + state.process->memory.SetChunkPermission(span(address, size), newPermission); + + Logger::Debug("Set permission to {}{}{} at 0x{:X} - 0x{:X} (0x{:X} bytes)", newPermission.r ? 'R' : '-', newPermission.w ? 'W' : '-', newPermission.x ? 'X' : '-', address, address + size, size); + state.ctx->gpr.w0 = Result{}; + } + void SetMemoryAttribute(const DeviceState &state) { u8 *address{reinterpret_cast(state.ctx->gpr.x0)}; if (!util::IsPageAligned(address)) [[unlikely]] { @@ -74,12 +115,12 @@ namespace skyline::kernel::svc { return; } - auto chunk{state.process->memory.GetChunk(address)}; + auto chunk{state.process->memory.GetChunk(address).value()}; // We only check the first found chunk for whatever reason. - if (!chunk->second.state.attributeChangeAllowed) [[unlikely]] { + if (!chunk.second.state.attributeChangeAllowed) [[unlikely]] { state.ctx->gpr.w0 = result::InvalidState; - Logger::Warn("Attribute change not allowed for chunk: 0x{:X}", chunk->first); + Logger::Warn("Attribute change not allowed for chunk: 0x{:X}", chunk.first); return; } @@ -164,15 +205,15 @@ namespace skyline::kernel::svc { 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); + auto dstChunk{state.process->memory.GetChunk(destination).value()}; + while (dstChunk.second.state.value == memory::states::Unmapped) + dstChunk = state.process->memory.GetChunk(dstChunk.first + dstChunk.second.size).value(); - 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); + 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); + std::memcpy(source + (dstChunk.first - destination), dstChunk.first, dstChunk.second.size); state.process->memory.UnmapMemory(span{destination, size}); } diff --git a/app/src/main/cpp/skyline/kernel/svc.h b/app/src/main/cpp/skyline/kernel/svc.h index e8acfc21..7d3e9df0 100644 --- a/app/src/main/cpp/skyline/kernel/svc.h +++ b/app/src/main/cpp/skyline/kernel/svc.h @@ -12,6 +12,12 @@ namespace skyline::kernel::svc { */ void SetHeapSize(const DeviceState &state); + /** + * @brief Reprotects a page-aligned memory region. + * @url https://switchbrew.org/wiki/SVC#SetMemoryPermission + */ + void SetMemoryPermission(const DeviceState &state); + /** * @brief Change attribute of page-aligned memory region, this is used to turn on/off caching for a given memory area * @url https://switchbrew.org/wiki/SVC#SetMemoryAttribute @@ -20,7 +26,7 @@ namespace skyline::kernel::svc { /** * @brief Maps a memory range into a different range, mainly used for adding guard pages around stack - * @url https://switchbrew.org/wiki/SVC#SetMemoryAttribute + * @url https://switchbrew.org/wiki/SVC#MapMemory */ void MapMemory(const DeviceState &state); @@ -115,7 +121,7 @@ namespace skyline::kernel::svc { void UnmapSharedMemory(const DeviceState &state); /** - * @brief Returns a handle to a KSharedMemory object + * @brief Returns a handle to a KTransferMemory object * @url https://switchbrew.org/wiki/SVC#CreateTransferMemory */ void CreateTransferMemory(const DeviceState &state); @@ -269,7 +275,7 @@ namespace skyline::kernel::svc { static constexpr std::array SvcTable{ SVC_NONE, // 0x00 (Does not exist) SVC_ENTRY(SetHeapSize), // 0x01 - SVC_NONE, // 0x02 + SVC_ENTRY(SetMemoryPermission), // 0x02 SVC_ENTRY(SetMemoryAttribute), // 0x03 SVC_ENTRY(MapMemory), // 0x04 SVC_ENTRY(UnmapMemory), // 0x05 diff --git a/app/src/main/cpp/skyline/kernel/types/KMemory.cpp b/app/src/main/cpp/skyline/kernel/types/KMemory.cpp index a4184899..90ac788c 100644 --- a/app/src/main/cpp/skyline/kernel/types/KMemory.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KMemory.cpp @@ -28,7 +28,7 @@ namespace skyline::kernel::type { if (guest.valid()) [[unlikely]] throw exception("Mapping KMemory 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()); - if (mmap(map.data(), map.size(), permission.Get() ? PROT_READ | PROT_WRITE | PROT_EXEC : PROT_NONE, MAP_SHARED | (map.data() ? MAP_FIXED : 0), fd, 0) == MAP_FAILED) [[unlikely]] + if (mmap(map.data(), map.size(), permission.Get() ? PROT_READ | PROT_WRITE : PROT_NONE, MAP_SHARED | (map.data() ? MAP_FIXED : 0), fd, 0) == MAP_FAILED) [[unlikely]] throw exception("An error occurred while mapping shared memory in guest: {}", strerror(errno)); guest = map; diff --git a/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp b/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp index f37fcc53..fecd138b 100644 --- a/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp @@ -45,7 +45,7 @@ namespace skyline::kernel::type { KTransferMemory::~KTransferMemory() { if (state.process && guest.valid()) { - if (mmap(guest.data(), guest.size(), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, -1, 0) == MAP_FAILED) [[unlikely]] + if (mmap(guest.data(), guest.size(), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS | MAP_POPULATE, -1, 0) == MAP_FAILED) [[unlikely]] Logger::Warn("An error occurred while unmapping transfer memory in guest: {}", strerror(errno)); switch (originalMapping.state.type) {