From 617b38835456f5d311fdfed5f3f00eeee20a76ba Mon Sep 17 00:00:00 2001 From: Weiyi Wang Date: Wed, 7 Nov 2018 12:13:00 -0500 Subject: [PATCH] Kernel/Process: implement prvileged Map/Unmap This is used by svcControlProcessMemory and maps memory as Locked/AliasCode pair. Also fixed a bug where map didn't apply specified permissions to the alias memory --- src/core/hle/kernel/process.cpp | 46 +++++++++++++++++++++++++++------ src/core/hle/kernel/process.h | 6 +++-- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 5e699937b..97ff283b8 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -303,7 +303,8 @@ ResultCode Process::LinearFree(VAddr target, u32 size) { return RESULT_SUCCESS; } -ResultCode Process::Map(VAddr target, VAddr source, u32 size, VMAPermission perms) { +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, static_cast(perms)); if (source < Memory::HEAP_VADDR || source + size > Memory::HEAP_VADDR_END || @@ -320,22 +321,39 @@ ResultCode Process::Map(VAddr target, VAddr source, u32 size, VMAPermission perm return ERR_INVALID_ADDRESS_STATE; } + if (source == target) { + if (privileged) { + // privileged Map allows identical source and target address, which simply changes the + // state and the permission of the memory + return vm_manager.ChangeMemoryState(source, size, MemoryState::Private, + VMAPermission::ReadWrite, MemoryState::AliasCode, + perms); + } + return ERR_INVALID_ADDRESS_STATE; + } + + MemoryState source_state = privileged ? MemoryState::Locked : MemoryState::Aliased; + MemoryState target_state = privileged ? MemoryState::AliasCode : MemoryState::Alias; + VMAPermission source_perm = privileged ? VMAPermission::None : VMAPermission::ReadWrite; + // Mark source region as Aliased CASCADE_CODE(vm_manager.ChangeMemoryState(source, size, MemoryState::Private, - VMAPermission::ReadWrite, MemoryState::Aliased, - VMAPermission::ReadWrite)); + VMAPermission::ReadWrite, source_state, source_perm)); CASCADE_RESULT(auto backing_blocks, vm_manager.GetBackingBlocksForRange(source, size)); VAddr interval_target = target; for (const auto [backing_memory, block_size] : backing_blocks) { - auto target_vma = vm_manager.MapBackingMemory(interval_target, backing_memory, block_size, - MemoryState::Alias); + auto target_vma = + vm_manager.MapBackingMemory(interval_target, backing_memory, block_size, target_state); + ASSERT(target_vma.Succeeded()); + vm_manager.Reprotect(target_vma.Unwrap(), perms); interval_target += block_size; } return RESULT_SUCCESS; } -ResultCode Process::Unmap(VAddr target, VAddr source, u32 size, VMAPermission perms) { +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}", target, source, size, static_cast(perms)); if (source < Memory::HEAP_VADDR || source + size > Memory::HEAP_VADDR_END || @@ -349,11 +367,23 @@ ResultCode Process::Unmap(VAddr target, VAddr source, u32 size, VMAPermission pe // TODO(wwylele): check that the source and the target are actually a pair created by Map // Should return error 0xD8E007F5 in this case + if (source == target) { + if (privileged) { + // privileged Unmap allows identical source and target address, which simply changes + // the state and the permission of the memory + return vm_manager.ChangeMemoryState(source, size, MemoryState::AliasCode, + VMAPermission::None, MemoryState::Private, perms); + } + return ERR_INVALID_ADDRESS_STATE; + } + + MemoryState source_state = privileged ? MemoryState::Locked : MemoryState::Aliased; + CASCADE_CODE(vm_manager.UnmapRange(target, size)); // Change back source region state. Note that the permission is reprotected according to param - CASCADE_CODE(vm_manager.ChangeMemoryState(source, size, MemoryState::Aliased, - VMAPermission::None, MemoryState::Private, perms)); + CASCADE_CODE(vm_manager.ChangeMemoryState(source, size, source_state, VMAPermission::None, + MemoryState::Private, perms)); return RESULT_SUCCESS; } diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 6924afe36..0ef9e4278 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -188,8 +188,10 @@ public: ResultVal LinearAllocate(VAddr target, u32 size, VMAPermission perms); ResultCode LinearFree(VAddr target, u32 size); - ResultCode Map(VAddr target, VAddr source, u32 size, VMAPermission perms); - ResultCode Unmap(VAddr target, VAddr source, u32 size, VMAPermission perms); + ResultCode Map(VAddr target, VAddr source, u32 size, VMAPermission perms, + bool privileged = false); + ResultCode Unmap(VAddr target, VAddr source, u32 size, VMAPermission perms, + bool privileged = false); private: explicit Process(Kernel::KernelSystem& kernel);