From aec99b87294cb5287e4b2fbee12233e97a6d5141 Mon Sep 17 00:00:00 2001 From: GPUCode Date: Tue, 31 Oct 2023 22:52:30 +0200 Subject: [PATCH] core: Implement interprocess memory SVCs --- src/core/hle/kernel/process.cpp | 37 +++- src/core/hle/kernel/process.h | 5 +- src/core/hle/kernel/svc.cpp | 291 +++++++++++++++++++++++++++- src/core/hle/kernel/vm_manager.cpp | 105 ++++++++-- src/core/hle/kernel/vm_manager.h | 8 + src/core/hle/service/fs/fs_user.cpp | 23 ++- src/core/hle/service/fs/fs_user.h | 32 ++- src/core/hle/service/ps/ps_ps.cpp | 49 ++++- src/core/hle/service/ps/ps_ps.h | 7 +- src/core/hle/service/service.cpp | 4 +- src/core/memory.cpp | 10 +- 11 files changed, 527 insertions(+), 44 deletions(-) diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 1027c0e12..8802ef07d 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -1,7 +1,7 @@ // Copyright 2015 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. - +#include "core/hle/result.h" #include #include #include @@ -459,8 +459,7 @@ ResultVal Process::AllocateThreadLocalStorage() { return tls_address; } -ResultCode Process::Map(VAddr target, VAddr source, u32 size, VMAPermission perms, - bool privileged) { +ResultCode Process::Map(VAddr target, VAddr source, u32 size, VMAPermission perms, bool privileged) { LOG_DEBUG(Kernel, "Map memory target={:08X}, source={:08X}, size={:08X}, perms={:08X}", target, source, size, perms); if (!privileged && (source < Memory::HEAP_VADDR || source + size > Memory::HEAP_VADDR_END || @@ -502,8 +501,10 @@ ResultCode Process::Map(VAddr target, VAddr source, u32 size, VMAPermission perm CASCADE_CODE(vm_manager.ChangeMemoryState(source, size, MemoryState::Private, VMAPermission::ReadWrite, source_state, source_perm)); + // Retrieve backing blocks from the source address space CASCADE_RESULT(auto backing_blocks, vm_manager.GetBackingBlocksForRange(source, size)); VAddr interval_target = target; + for (const auto& backing_block : backing_blocks) { auto backing_memory = kernel.memory.GetFCRAMRef(backing_block.lower()); auto block_size = backing_block.upper() - backing_block.lower(); @@ -516,6 +517,36 @@ ResultCode Process::Map(VAddr target, VAddr source, u32 size, VMAPermission perm return RESULT_SUCCESS; } + +ResultCode Process::MapInterprocess(Process& src, VAddr target, VAddr source, u32 size, VMAPermission perms) { + u32 cursor = 0; + while (true) { + const auto source_iter = src.vm_manager.FindVMA(source + cursor); + auto& source_vma = source_iter->second; + + // Skip non-allocated blocks + if (source_vma.type == VMAType::Free) { + cursor += source_vma.size; + continue; + } + + ASSERT(source_vma.type == VMAType::BackingMemory); + ASSERT(source_vma.backing_memory); + ASSERT(source_vma.base == source + cursor); + + if (source_vma.size + cursor > size) { + break; + } + + const auto target_vma = vm_manager.MapBackingMemory(target + cursor, source_vma.backing_memory, + source_vma.size, MemoryState::Shared); + ASSERT(target_vma.Succeeded()); + cursor += source_vma.size; + } + + return RESULT_SUCCESS; +} + ResultCode Process::Unmap(VAddr target, VAddr source, u32 size, VMAPermission perms, bool privileged) { LOG_DEBUG(Kernel, "Unmap memory target={:08X}, source={:08X}, size={:08X}, perms={:08X}", diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 1c55cce82..09edb04ec 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -209,9 +209,6 @@ public: */ void Exit(); - /////////////////////////////////////////////////////////////////////////////////////////////// - // Memory Management - VMManager vm_manager; u32 memory_used = 0; @@ -243,6 +240,8 @@ public: ResultCode Map(VAddr target, VAddr source, u32 size, VMAPermission perms, bool privileged = false); + ResultCode MapInterprocess(Process& src, VAddr target, VAddr source, + u32 size, VMAPermission perms); ResultCode Unmap(VAddr target, VAddr source, u32 size, VMAPermission perms, bool privileged = false); diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index c66e5cb03..16908c283 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -1,14 +1,16 @@ // Copyright 2014 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. - +#pragma optimize("", off) #include #include #include +#include "common/alignment.h" #include "common/archives.h" #include "common/logging/log.h" #include "common/microprofile.h" #include "common/scm_rev.h" +#include "common/scope_exit.h" #include "core/arm/arm_interface.h" #include "core/core.h" #include "core/core_timing.h" @@ -438,6 +440,10 @@ private: ResultCode UnmapProcessMemoryEx(Handle process, u32 dst_address, u32 size); ResultCode ControlProcess(Handle process_handle, u32 process_OP, u32 varg2, u32 varg3); + ResultCode ControlProcessMemory(Handle process, u32 addr0, u32 addr1, u32 size, u32 type, u32 perm); + ResultCode MapProcessMemory(Handle process, u32 dest_address, u32 size); + ResultCode UnmapProcessMemory(Handle process, u32 start_address, u32 size); + struct FunctionDef { using Func = void (SVC::*)(); @@ -2103,6 +2109,283 @@ ResultCode SVC::ControlProcess(Handle process_handle, u32 process_OP, u32 varg2, } } +ResultCode SVC::ControlProcessMemory(Handle process, u32 addr0, u32 addr1, u32 size, u32 operation, u32 permissions) { + LOG_DEBUG(Kernel_SVC, + "called operation=0x{:08X}, addr0=0x{:08X}, addr1=0x{:08X}, " + "size=0x{:X}, permissions=0x{:08X}", + operation, addr0, addr1, size, permissions); + + if (!addr0) { + return ResultCode{0xD8E007F6}; + } + + if ((addr0 & Memory::CITRA_PAGE_MASK) != 0 || (addr1 & Memory::CITRA_PAGE_MASK) != 0) { + return ERR_MISALIGNED_ADDRESS; + } + if ((size & Memory::CITRA_PAGE_MASK) != 0) { + return ERR_MISALIGNED_SIZE; + } + + u32 region = operation & MEMOP_REGION_MASK; + operation &= ~MEMOP_REGION_MASK; + + if (region != 0) { + LOG_WARNING(Kernel_SVC, "ControlMemory with specified region not supported, region={:X}", + region); + } + + VMAPermission vma_permissions = (VMAPermission)permissions; + + const auto dst_process = kernel.GetCurrentProcess()->handle_table.Get(process); + + SCOPE_EXIT({ system.GetRunningCore().ClearInstructionCache(); }); + + switch (operation & MEMOP_OPERATION_MASK) { + case MEMOP_MAP: { + CASCADE_CODE(dst_process->Map(addr0, addr1, size, vma_permissions, true)); + break; + } + case MEMOP_UNMAP: { + CASCADE_CODE(dst_process->Unmap(addr0, addr1, size, vma_permissions, true)); + break; + } + case MEMOP_PROTECT: { + CASCADE_CODE(dst_process->vm_manager.ReprotectRange(addr0, size, vma_permissions)); + break; + } + default: + LOG_ERROR(Kernel_SVC, "unknown operation=0x%08X", operation); + return ResultCode(ErrorDescription::NotImplemented, ErrorModule::Kernel, + ErrorSummary::NotSupported, ErrorLevel::Fatal); + } + + return RESULT_SUCCESS; +} + +ResultCode SVC::MapProcessMemory(Handle process, u32 start_addr, u32 size) { + const auto dst_process = kernel.GetCurrentProcess(); + const auto src_process = kernel.GetCurrentProcess()->handle_table.Get(process); + if (!dst_process) { + return ERR_INVALID_HANDLE; + } + + // Map up to 0x3F00000 bytes starting from 0x100000 from the source process into the current + // process' address space at the specified address. Note: Non-page aligned sizes do not return + // an error, the lower bits are just discarded. + const u32 code_size = + Common::AlignDown(std::min(size, Memory::PROCESS_IMAGE_MAX_SIZE), Memory::CITRA_PAGE_SIZE); + ResultCode result = dst_process->MapInterprocess(*src_process, start_addr, Memory::PROCESS_IMAGE_VADDR, + code_size, VMAPermission::ReadWrite); + ASSERT(result.IsSuccess()); + + static constexpr u32 MaxHeapSize = 0x6000000; + static constexpr u32 HeapOffset = 0x7F00000; + + // Now map up to 0x6000000 bytes starting from 0x8000000 from the source process into the + // current process' address space at the specified address + 0x7F00000. + const u32 heap_size = + Common::AlignDown(std::min(size - HeapOffset, MaxHeapSize), Memory::CITRA_PAGE_SIZE); + if (heap_size > 0) { + result = dst_process->MapInterprocess(*src_process, start_addr + HeapOffset, Memory::HEAP_VADDR, + heap_size, VMAPermission::ReadWrite); + ASSERT(result.IsSuccess()); + } + + return RESULT_SUCCESS; +} + +ResultCode SVC::UnmapProcessMemory(Handle process, VAddr start_addr, u32 size) { + const u32 code_size = + Common::AlignDown(std::min(size, Memory::PROCESS_IMAGE_MAX_SIZE), Memory::CITRA_PAGE_SIZE); + + ResultCode result = kernel.GetCurrentProcess()->vm_manager.UnmapRange(start_addr, code_size); + ASSERT(result.IsSuccess()); + + static constexpr u32 MaxHeapSize = 0x6000000; + static constexpr u32 HeapOffset = 0x7F00000; + + const u32 heap_size = + Common::AlignDown(std::min(size - HeapOffset, MaxHeapSize), Memory::CITRA_PAGE_SIZE); + result = kernel.GetCurrentProcess()->vm_manager.UnmapRange(start_addr + HeapOffset, heap_size); + ASSERT(result.IsSuccess()); + + system.GetRunningCore().ClearInstructionCache(); + return RESULT_SUCCESS; +} + +/*ResultCode SVC::ControlProcessMemory(Handle process, u32 addr0, u32 addr1, u32 size, u32 operation, u32 permissions) { + LOG_DEBUG(Kernel_SVC, + "called operation=0x{:08X}, addr0=0x{:08X}, addr1=0x{:08X}, " + "size=0x{:X}, permissions=0x{:08X}", + operation, addr0, addr1, size, permissions); + + if (!addr0) { + return ResultCode{0xD8E007F6}; + } + + if ((addr0 & Memory::CITRA_PAGE_MASK) != 0 || (addr1 & Memory::CITRA_PAGE_MASK) != 0) { + return ERR_MISALIGNED_ADDRESS; + } + if ((size & Memory::CITRA_PAGE_MASK) != 0) { + return ERR_MISALIGNED_SIZE; + } + + u32 region = operation & MEMOP_REGION_MASK; + operation &= ~MEMOP_REGION_MASK; + + if (region != 0) { + LOG_WARNING(Kernel_SVC, "ControlMemory with specified region not supported, region={:X}", + region); + } + + VMAPermission vma_permissions = (VMAPermission)permissions; + + const auto dst_process = kernel.GetCurrentProcess()->handle_table.Get(process); + + SCOPE_EXIT({ system.GetRunningCore().ClearInstructionCache(); }); + + switch (operation) { + case MEMOP_MAP: { + + if (addr0 != addr1) { + // TODO(Subv): Check if the addresses are valid. + auto addr1_vma = dst_process->vm_manager.FindVMA(addr1); + ASSERT(addr1_vma->second.meminfo_state == MemoryState::Private); + ASSERT(addr1 + size <= addr1_vma->second.base + addr1_vma->second.size); + ASSERT((static_cast(addr1_vma->second.permissions) & + static_cast(VMAPermission::ReadWrite)) == + static_cast(VMAPermission::ReadWrite)); + + auto addr0_vma = dst_process->vm_manager.FindVMA(addr0); + ASSERT(addr0_vma->second.meminfo_state == MemoryState::Free); + ASSERT(addr0 + size <= addr0_vma->second.base + addr0_vma->second.size); + + // Note: The real kernel will crash if this fails and the addresses are valid. + auto result = dst_process->vm_manager.AliasMemory( + addr0, addr1, size, MemoryState::AliasCode, MemoryState::Locked); + ASSERT(result.Succeeded()); + ASSERT(dst_process->vm_manager.ReprotectRange(addr0, size, vma_permissions).IsSuccess()); + ASSERT( + dst_process->vm_manager.ReprotectRange(addr1, size, VMAPermission::None).IsSuccess()); + } else { + // This code path acts like PROTECT but also changes the memory state. + CASCADE_CODE(dst_process->vm_manager.ChangeMemoryState( + addr0, size, MemoryState::Private, VMAPermission::ReadWrite, MemoryState::AliasCode, + vma_permissions)); + } + break; + } + + case MEMOP_UNMAP: { + if (addr0 != addr1) { + // TODO(Subv): Check if the addresses are valid. + auto addr1_vma = dst_process->vm_manager.FindVMA(addr1); + if (addr1_vma->second.meminfo_state != MemoryState::Locked) { + return ResultCode(0xE0A01BF5); + } + ASSERT(addr1 + size <= addr1_vma->second.base + addr1_vma->second.size); + + auto addr0_vma = dst_process->vm_manager.FindVMA(addr0); + ASSERT(addr0_vma->second.meminfo_state == MemoryState::AliasCode); + ASSERT(addr0 + size <= addr0_vma->second.base + addr0_vma->second.size); + + ResultCode result = dst_process->vm_manager.UnmapRange(addr0, size); + ASSERT(result.IsSuccess()); + + result = dst_process->vm_manager.ChangeMemoryState(addr1, size, MemoryState::Locked, + VMAPermission::None, + MemoryState::Private, vma_permissions); + ASSERT(result.IsSuccess()); + return result; + } else { + // This code path acts like PROTECT but also changes the memory state. + CASCADE_CODE(dst_process->vm_manager.ChangeMemoryState( + addr0, size, MemoryState::AliasCode, VMAPermission::None, MemoryState::Private, + vma_permissions)); + } + break; + } + + case MEMOP_PROTECT: { + ResultCode result = dst_process->vm_manager.ReprotectRange(addr0, size, vma_permissions); + if (result.IsError()) + return result; + break; + } + + default: + LOG_ERROR(Kernel_SVC, "unknown operation=0x%08X", operation); + return ResultCode(ErrorDescription::NotImplemented, ErrorModule::Kernel, + ErrorSummary::NotSupported, ErrorLevel::Fatal); + } + + return RESULT_SUCCESS; +} + +ResultCode SVC::MapProcessMemory(Handle process, u32 start_addr, u32 size) { + const auto dst_process = kernel.GetCurrentProcess(); + const auto src_process = kernel.GetCurrentProcess()->handle_table.Get(process); + if (!dst_process) { + return ERR_INVALID_HANDLE; + } + + SCOPE_EXIT({ system.GetRunningCore().ClearInstructionCache(); }); + + // Map up to 0x3F00000 bytes starting from 0x100000 from the source process into the current + // process' address space at the specified address. Note: Non-page aligned sizes do not return + // an error, the lower bits are just discarded. + u32 code_size = + Common::AlignDown(std::min(size, Memory::PROCESS_IMAGE_MAX_SIZE), Memory::CITRA_PAGE_SIZE); + + auto vma_handle = dst_process->vm_manager.FindVMA(start_addr); + if (vma_handle->second.type != Kernel::VMAType::Free) { + return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, + ErrorSummary::InvalidState, ErrorLevel::Usage); + } + + auto result = dst_process->vm_manager.AliasMemory( + start_addr, Memory::PROCESS_IMAGE_VADDR, code_size, Kernel::MemoryState::Shared, + Kernel::VMAPermission::ReadWrite, src_process->vm_manager); + // TODO(Subv): Handle error cases. + ASSERT(result.IsSuccess()); + + // Now map up to 0x6000000 bytes starting from 0x8000000 from the source process into the + // current process' address space at the specified address + 0x7F00000. + static constexpr u32 MaxHeapSize = 0x6000000; + static constexpr u32 HeapOffset = 0x7F00000; + u32 heap_size = + Common::AlignDown(std::min(size - HeapOffset, MaxHeapSize), Memory::CITRA_PAGE_SIZE); + if (heap_size > 0) { + result = dst_process->vm_manager.AliasMemory( + start_addr + HeapOffset, Memory::HEAP_VADDR, heap_size, Kernel::MemoryState::Shared, + Kernel::VMAPermission::ReadWrite, src_process->vm_manager); + // TODO(Subv): Handle error cases. + ASSERT(result.IsSuccess()); + } + return RESULT_SUCCESS; +} + +ResultCode SVC::UnmapProcessMemory(Handle process, VAddr start_addr, u32 size) { + const u32 code_size = + Common::AlignDown(std::min(size, Memory::PROCESS_IMAGE_MAX_SIZE), Memory::CITRA_PAGE_SIZE); + + SCOPE_EXIT({ system.GetRunningCore().ClearInstructionCache(); }); + + ResultCode result = kernel.GetCurrentProcess()->vm_manager.UnmapRange(start_addr, code_size); + ASSERT(result.IsSuccess()); + + static constexpr u32 MaxHeapSize = 0x6000000; + static constexpr u32 HeapOffset = 0x7F00000; + + const u32 heap_size = + Common::AlignDown(std::min(size - HeapOffset, MaxHeapSize), Memory::CITRA_PAGE_SIZE); + result = kernel.GetCurrentProcess()->vm_manager.UnmapRange(start_addr + HeapOffset, heap_size); + ASSERT(result.IsSuccess()); + + system.GetRunningCore().ClearInstructionCache(); + return RESULT_SUCCESS; +}*/ + const std::array SVC::SVC_Table{{ {0x00, nullptr, "Unknown"}, {0x01, &SVC::Wrap<&SVC::ControlMemory>, "ControlMemory"}, @@ -2216,9 +2499,9 @@ const std::array SVC::SVC_Table{{ {0x6D, nullptr, "GetDebugThreadParam"}, {0x6E, nullptr, "Unknown"}, {0x6F, nullptr, "Unknown"}, - {0x70, nullptr, "ControlProcessMemory"}, - {0x71, nullptr, "MapProcessMemory"}, - {0x72, nullptr, "UnmapProcessMemory"}, + {0x70, &SVC::Wrap<&SVC::ControlProcessMemory>, "ControlProcessMemory"}, + {0x71, &SVC::Wrap<&SVC::MapProcessMemory>, "MapProcessMemory"}, + {0x72, &SVC::Wrap<&SVC::UnmapProcessMemory>, "UnmapProcessMemory"}, {0x73, nullptr, "CreateCodeSet"}, {0x74, nullptr, "RandomStub"}, {0x75, nullptr, "CreateProcess"}, diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index f187b92bf..b636b14fd 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -1,7 +1,6 @@ // Copyright 2015 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. - #include #include #include "common/assert.h" @@ -307,11 +306,11 @@ ResultVal VMManager::CarveVMARange(VAddr target, u32 size) { ASSERT(size > 0); VMAIter begin_vma = StripIterConstness(FindVMA(target)); - const VMAIter i_end = vma_map.lower_bound(target_end); - if (std::any_of(begin_vma, i_end, - [](const auto& entry) { return entry.second.type == VMAType::Free; })) { - return ERR_INVALID_ADDRESS_STATE; - } + //const VMAIter i_end = vma_map.lower_bound(target_end); + //if (std::any_of(begin_vma, i_end, + // [](const auto& entry) { return entry.second.type == VMAType::Free; })) { + // return ERR_INVALID_ADDRESS_STATE; + //} if (target != begin_vma->second.base) { begin_vma = SplitVMA(begin_vma, target - begin_vma->second.base); @@ -342,6 +341,7 @@ VMManager::VMAIter VMManager::SplitVMA(VMAIter vma_handle, u32 offset_in_vma) { case VMAType::Free: break; case VMAType::BackingMemory: + case VMAType::MemoryAlias: new_vma.backing_memory += offset_in_vma; break; case VMAType::MMIO: @@ -379,6 +379,7 @@ void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { memory.UnmapRegion(*page_table, vma.base, vma.size); break; case VMAType::BackingMemory: + case VMAType::MemoryAlias: memory.MapMemoryRegion(*page_table, vma.base, vma.size, vma.backing_memory); break; case VMAType::MMIO: @@ -396,21 +397,97 @@ ResultVal VMManager::GetBackingBlocksForRange(VAd MemoryRegionInfo::IntervalSet backing_blocks; VAddr interval_target = address; while (interval_target != address + size) { - auto vma = FindVMA(interval_target); - if (vma->second.type != VMAType::BackingMemory) { + const auto it = FindVMA(interval_target); + if (it == vma_map.end() || (it->second.type != VMAType::BackingMemory && it->second.type != VMAType::Free)) { LOG_ERROR(Kernel, "Trying to use already freed memory"); return ERR_INVALID_ADDRESS_STATE; } - VAddr interval_end = std::min(address + size, vma->second.base + vma->second.size); - u32 interval_size = interval_end - interval_target; - auto backing_memory = memory.GetFCRAMOffset(vma->second.backing_memory + - (interval_target - vma->second.base)); - backing_blocks += - MemoryRegionInfo::Interval(backing_memory, backing_memory + interval_size); + const auto vma = it->second; + const VAddr interval_end = std::min(address + size, vma.base + vma.size); + const u32 interval_size = interval_end - interval_target; + if (vma.type != VMAType::Free) { + const auto backing_memory = memory.GetFCRAMOffset(vma.backing_memory + (interval_target - vma.base)); + backing_blocks += + MemoryRegionInfo::Interval(backing_memory, backing_memory + interval_size); + } interval_target += interval_size; } return backing_blocks; } + +ResultVal VMManager::AliasMemory(VAddr target, VAddr source, u32 size, + MemoryState alias_state, + MemoryState aliased_state) { + VMAIter source_iter = StripIterConstness(FindVMA(source)); + ASSERT(source_iter != vma_map.end()); + + auto& source_vma = source_iter->second; + ASSERT(source_vma.type == VMAType::BackingMemory); + ASSERT(source_vma.backing_memory != nullptr); + ASSERT(source + size <= source_vma.base + source_vma.size); + + CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target, size)); + VirtualMemoryArea& final_vma = vma_handle->second; + ASSERT(final_vma.size == size); + + final_vma.type = VMAType::MemoryAlias; + final_vma.permissions = VMAPermission::None; + final_vma.meminfo_state = alias_state; + final_vma.backing_memory = source_vma.backing_memory + (source - source_vma.base); + UpdatePageTableForVMA(final_vma); + + // Update the source VMA state with the new value + CASCADE_RESULT(auto new_vma, CarveVMARange(source, size)); + new_vma->second.meminfo_state = aliased_state; + UpdatePageTableForVMA(new_vma->second); + + return MergeAdjacent(vma_handle); +} + +ResultCode VMManager::AliasMemory(VAddr target, VAddr source, u32 size, MemoryState alias_state, + VMAPermission alias_permissions, + const VMManager& source_address_space) { + + u32 cursor = 0; + + while (true) { + VMAHandle source_iter = source_address_space.FindVMA(source + cursor); + ASSERT(source_iter != vma_map.end()); + + auto& source_vma = source_iter->second; + // Skip non-allocated blocks + if (source_vma.type == VMAType::Free) { + cursor += source_vma.size; + continue; + } + + ASSERT(source_vma.type == VMAType::BackingMemory || + source_vma.type == VMAType::MemoryAlias); + ASSERT(source_vma.backing_memory != nullptr); + ASSERT(source_vma.base == source + cursor); + + if (source_vma.size + cursor > size) { + break; + } + + CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target + cursor, source_vma.size)); + VirtualMemoryArea& final_vma = vma_handle->second; + ASSERT(final_vma.size == source_vma.size); + + final_vma.type = VMAType::MemoryAlias; + final_vma.meminfo_state = alias_state; + final_vma.permissions = alias_permissions; + final_vma.backing_memory = source_vma.backing_memory; + UpdatePageTableForVMA(final_vma); + + MergeAdjacent(vma_handle); + + cursor += source_vma.size; + } + + return RESULT_SUCCESS; +} + } // namespace Kernel diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index d520eeccb..2587d7fd4 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -27,6 +27,8 @@ enum class VMAType : u8 { BackingMemory, /// VMA is mapped to MMIO registers at a fixed PAddr. MMIO, + /// VMA is an alias of another VMA. + MemoryAlias, }; /// Permissions for mapped memory blocks @@ -178,6 +180,12 @@ public: ResultVal MapMMIO(VAddr target, PAddr paddr, u32 size, MemoryState state, Memory::MMIORegionPointer mmio_handler); + ResultVal AliasMemory(VAddr target, VAddr source, u32 size, MemoryState alias_state, + MemoryState aliased_state); + + ResultCode AliasMemory(VAddr target, VAddr source, u32 size, MemoryState alias_state, + VMAPermission alias_permissions, const VMManager& source_address_space); + /** * Updates the memory state and permissions of the specified range. The range's original memory * state and permissions must match the `expected` parameters. diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index cf01f78a2..612eead8b 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -1,7 +1,8 @@ // Copyright 2014 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. - +#pragma optimize("", off) +#include #include "common/archives.h" #include "common/assert.h" #include "common/common_types.h" @@ -878,6 +879,24 @@ ResultVal FS_USER::GetSpecialContentIndexFromTMD(MediaType media_type, u64 return ResultCode(-1); } +void FS_USER::UpdateSha256Context(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + rp.Skip(8, false); + [[maybe_unused]] const u32 size = rp.Pop(); + rp.Skip(4, false); + auto input = rp.PopMappedBuffer(); + + std::array hash; + std::vector data(size); + input.Read(data.data(), 0, data.size()); + + CryptoPP::SHA256().CalculateDigest(hash.data(), data.data(), size); + + IPC::RequestBuilder rb = rp.MakeBuilder(9, 0); + rb.Push(RESULT_SUCCESS); + rb.PushRaw(hash); +} + FS_USER::FS_USER(Core::System& system) : ServiceFramework("fs:USER", 30), system(system), archives(system.ArchiveManager()) { static const FunctionInfo functions[] = { @@ -961,7 +980,7 @@ FS_USER::FS_USER(Core::System& system) {0x084B, nullptr, "ImportIntegrityVerificationSeed"}, {0x084C, &FS_USER::FormatSaveData, "FormatSaveData"}, {0x084D, nullptr, "GetLegacySubBannerData"}, - {0x084E, nullptr, "UpdateSha256Context"}, + {0x084E, &FS_USER::UpdateSha256Context, "UpdateSha256Context"}, {0x084F, nullptr, "ReadSpecialFile"}, {0x0850, nullptr, "GetSpecialFileSize"}, {0x0851, &FS_USER::CreateExtSaveData, "CreateExtSaveData"}, diff --git a/src/core/hle/service/fs/fs_user.h b/src/core/hle/service/fs/fs_user.h index 37c7ba880..9494f7f88 100644 --- a/src/core/hle/service/fs/fs_user.h +++ b/src/core/hle/service/fs/fs_user.h @@ -8,6 +8,7 @@ #include #include "common/common_types.h" #include "core/file_sys/errors.h" +#include "core/hle/service/fs/archive.h" #include "core/hle/service/service.h" namespace Core { @@ -106,7 +107,7 @@ private: */ void OpenFileDirectly(Kernel::HLERequestContext& ctx); - /* + /** * FS_User::DeleteFile service function * Inputs: * 1 : Transaction @@ -120,7 +121,7 @@ private: */ void DeleteFile(Kernel::HLERequestContext& ctx); - /* + /** * FS_User::RenameFile service function * Inputs: * 1 : Transaction @@ -139,7 +140,7 @@ private: */ void RenameFile(Kernel::HLERequestContext& ctx); - /* + /** * FS_User::DeleteDirectory service function * Inputs: * 1 : Transaction @@ -153,7 +154,7 @@ private: */ void DeleteDirectory(Kernel::HLERequestContext& ctx); - /* + /** * FS_User::DeleteDirectoryRecursively service function * Inputs: * 0 : Command header 0x08070142 @@ -168,7 +169,7 @@ private: */ void DeleteDirectoryRecursively(Kernel::HLERequestContext& ctx); - /* + /** * FS_User::CreateFile service function * Inputs: * 0 : Command header 0x08080202 @@ -185,7 +186,7 @@ private: */ void CreateFile(Kernel::HLERequestContext& ctx); - /* + /** * FS_User::CreateDirectory service function * Inputs: * 1 : Transaction @@ -200,7 +201,7 @@ private: */ void CreateDirectory(Kernel::HLERequestContext& ctx); - /* + /** * FS_User::RenameDirectory service function * Inputs: * 1 : Transaction @@ -617,6 +618,23 @@ private: */ void GetSaveDataSecureValue(Kernel::HLERequestContext& ctx); + /** + * FS_User::UpdateSha256Context service function. + * Inputs: + * 0 : 0x084E0342 + * 1-8 : Input Hash + * 9 : Input Data Size + * 10-12 : Flag, must be zero + * 13 : Flag, must be non-zero + * 14 : (Size << 4) | 0xA + * 15 : Input Data Pointer + * Outputs: + * 0 : 0x084E0342 + * 1 : Result of function, 0 on success, otherwise error code + * 2-9 : Output SHA-256 Hash + */ + void UpdateSha256Context(Kernel::HLERequestContext& ctx); + static ResultVal GetSpecialContentIndexFromGameCard(u64 title_id, SpecialContentType type); static ResultVal GetSpecialContentIndexFromTMD(MediaType media_type, u64 title_id, SpecialContentType type); diff --git a/src/core/hle/service/ps/ps_ps.cpp b/src/core/hle/service/ps/ps_ps.cpp index 32026fa0c..58b36e9f6 100644 --- a/src/core/hle/service/ps/ps_ps.cpp +++ b/src/core/hle/service/ps/ps_ps.cpp @@ -1,9 +1,10 @@ // Copyright 2018 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. - +#pragma optimize("", off) #include #include +#include #include "common/archives.h" #include "common/logging/log.h" #include "core/core.h" @@ -13,6 +14,7 @@ #include "core/hw/aes/key.h" SERIALIZE_EXPORT_IMPL(Service::PS::PS_PS) +SERVICE_CONSTRUCT_IMPL(Service::PS::PS_PS) namespace Service::PS { @@ -146,11 +148,50 @@ void PS_PS::EncryptDecryptAes(Kernel::HLERequestContext& ctx) { rb.PushMappedBuffer(destination); } -PS_PS::PS_PS() : ServiceFramework("ps:ps", DefaultMaxSessions) { +void PS_PS::VerifyRsaSha256(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + std::array sha; + rp.PopRaw(sha); + rp.Pop(); + const auto rsa_ctx_buf = rp.PopStaticBuffer(); + auto sig_buf = rp.PopMappedBuffer(); + + struct RsaContext { + std::array modulo; + std::array exponent; + u32 rsa_bit_size; + bool is_long_exponent; + }; + static_assert(sizeof(RsaContext) == 520); + + RsaContext rsa_ctx; + std::memcpy(&rsa_ctx, rsa_ctx_buf.data(), rsa_ctx_buf.size()); + + std::vector signature_buffer(sig_buf.GetSize()); + sig_buf.Read(signature_buffer.data(), 0, sig_buf.GetSize()); + + ASSERT(!rsa_ctx.is_long_exponent); + + u32 exponent; + std::memcpy(&exponent, rsa_ctx.exponent.data(), sizeof(exponent)); + + using Verifier = CryptoPP::RSASS::Verifier; + + const auto modulus = CryptoPP::Integer(rsa_ctx.modulo.data(), rsa_ctx.modulo.size()); + const auto verifier = Verifier(modulus, CryptoPP::Integer(exponent)); + const bool valid = verifier.VerifyMessage(sha.data(), sha.size(), signature_buffer.data(), signature_buffer.size()); + ASSERT(!valid); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(RESULT_SUCCESS); + rb.PushMappedBuffer(sig_buf); +} + +PS_PS::PS_PS(Core::System& system) : ServiceFramework("ps:ps", DefaultMaxSessions), memory{system.Memory()} { static const FunctionInfo functions[] = { // clang-format off {0x0001, nullptr, "SignRsaSha256"}, - {0x0002, nullptr, "VerifyRsaSha256"}, + {0x0002, &PS_PS::VerifyRsaSha256, "VerifyRsaSha256"}, {0x0004, &PS_PS::EncryptDecryptAes, "EncryptDecryptAes"}, {0x0005, nullptr, "EncryptSignDecryptVerifyAesCcm"}, {0x0006, nullptr, "GetRomId"}, @@ -173,7 +214,7 @@ PS_PS::PS_PS() : ServiceFramework("ps:ps", DefaultMaxSessions) { void InstallInterfaces(Core::System& system) { auto& service_manager = system.ServiceManager(); - std::make_shared()->InstallAsService(service_manager); + std::make_shared(system)->InstallAsService(service_manager); } } // namespace Service::PS diff --git a/src/core/hle/service/ps/ps_ps.h b/src/core/hle/service/ps/ps_ps.h index f5005d444..ddefdad97 100644 --- a/src/core/hle/service/ps/ps_ps.h +++ b/src/core/hle/service/ps/ps_ps.h @@ -14,11 +14,10 @@ namespace Service::PS { class PS_PS final : public ServiceFramework { public: - PS_PS(); + PS_PS(Core::System& system); ~PS_PS() = default; private: - SERVICE_SERIALIZATION_SIMPLE /** * PS_PS::SignRsaSha256 service function @@ -227,6 +226,9 @@ private: * 2 : */ void InterfaceForPXI_0x04040044(Kernel::HLERequestContext& ctx); + +private: + Memory::MemorySystem& memory; }; /// Initializes the PS_PS Service @@ -235,3 +237,4 @@ void InstallInterfaces(Core::System& system); } // namespace Service::PS BOOST_CLASS_EXPORT_KEY(Service::PS::PS_PS) +SERVICE_CONSTRUCT(Service::PS::PS_PS) diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 1e4c071f2..1393270bd 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -58,7 +58,8 @@ namespace Service { const std::array service_module_map{ - {{"FS", 0x00040130'00001102, FS::InstallInterfaces}, + {{"CFG", 0x00040130'00001702, CFG::InstallInterfaces}, + {"FS", 0x00040130'00001102, FS::InstallInterfaces}, {"PM", 0x00040130'00001202, PM::InstallInterfaces}, {"LDR", 0x00040130'00003702, LDR::InstallInterfaces}, {"PXI", 0x00040130'00001402, PXI::InstallInterfaces}, @@ -74,7 +75,6 @@ const std::array service_module_map{ Y2R::InstallInterfaces(system); }}, {"CECD", 0x00040130'00002602, CECD::InstallInterfaces}, - {"CFG", 0x00040130'00001702, CFG::InstallInterfaces}, {"DLP", 0x00040130'00002802, DLP::InstallInterfaces}, {"DSP", 0x00040130'00001A02, DSP::InstallInterfaces}, {"FRD", 0x00040130'00003202, FRD::InstallInterfaces}, diff --git a/src/core/memory.cpp b/src/core/memory.cpp index dd71cb7f7..812c43225 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -1,7 +1,7 @@ // Copyright 2015 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. - +#pragma optimize("", off) #include #include #include @@ -156,6 +156,10 @@ public: return system.GetRunningCore().GetPC(); } + u32 GetLR() const noexcept { + return system.GetRunningCore().GetReg(14); + } + /** * This function should only be called for virtual addreses with attribute `PageType::Special`. */ @@ -469,8 +473,8 @@ T MemorySystem::Read(const VAddr vaddr) { PageType type = impl->current_page_table->attributes[vaddr >> CITRA_PAGE_BITS]; switch (type) { case PageType::Unmapped: - LOG_ERROR(HW_Memory, "unmapped Read{} @ 0x{:08X} at PC 0x{:08X}", sizeof(T) * 8, vaddr, - impl->GetPC()); + LOG_ERROR(HW_Memory, "unmapped Read{} @ 0x{:08X} at PC 0x{:08X} with LR {:#08X}", sizeof(T) * 8, vaddr, + impl->GetPC(), impl->GetLR()); return 0; case PageType::Memory: ASSERT_MSG(false, "Mapped memory page without a pointer @ {:08X}", vaddr);