From 9749a43cc4f7b1374619035f057c383d292af5ed Mon Sep 17 00:00:00 2001 From: wwylele Date: Tue, 5 Dec 2017 21:05:40 +0200 Subject: [PATCH] ldr_ro: create per-client storage; pass process where needed --- src/core/hle/service/ldr_ro/ldr_ro.cpp | 114 ++++++++++-------- src/core/hle/service/ldr_ro/ldr_ro.h | 8 +- .../service/ldr_ro/memory_synchronizer.cpp | 5 +- .../hle/service/ldr_ro/memory_synchronizer.h | 6 +- 4 files changed, 77 insertions(+), 56 deletions(-) diff --git a/src/core/hle/service/ldr_ro/ldr_ro.cpp b/src/core/hle/service/ldr_ro/ldr_ro.cpp index bedbf2f54..5c8fedd1e 100644 --- a/src/core/hle/service/ldr_ro/ldr_ro.cpp +++ b/src/core/hle/service/ldr_ro/ldr_ro.cpp @@ -40,9 +40,9 @@ static const ResultCode ERROR_NOT_LOADED = // 0xD8A12C0D ResultCode(static_cast(13), ErrorModule::RO, ErrorSummary::InvalidState, ErrorLevel::Permanent); -static bool VerifyBufferState(VAddr buffer_ptr, u32 size) { - auto vma = Kernel::g_current_process->vm_manager.FindVMA(buffer_ptr); - return vma != Kernel::g_current_process->vm_manager.vma_map.end() && +static bool VerifyBufferState(Kernel::Process& process, VAddr buffer_ptr, u32 size) { + auto vma = process.vm_manager.FindVMA(buffer_ptr); + return vma != process.vm_manager.vma_map.end() && vma->second.base + vma->second.size >= buffer_ptr + size && vma->second.permissions == Kernel::VMAPermission::ReadWrite && vma->second.meminfo_state == Kernel::MemoryState::Private; @@ -63,12 +63,15 @@ void RO::Initialize(Kernel::HLERequestContext& ctx) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - if (loaded_crs != 0) { + auto insert_result = slots.insert({process->process_id, {}}); + if (!insert_result.second) { LOG_ERROR(Service_LDR, "Already initialized"); rb.Push(ERROR_ALREADY_INITIALIZED); return; } + auto& slot = insert_result.first->second; + if (crs_size < CRO_HEADER_SIZE) { LOG_ERROR(Service_LDR, "CRS is too small"); rb.Push(ERROR_BUFFER_TOO_SMALL); @@ -93,7 +96,7 @@ void RO::Initialize(Kernel::HLERequestContext& ctx) { return; } - if (!VerifyBufferState(crs_buffer_ptr, crs_size)) { + if (!VerifyBufferState(*process, crs_buffer_ptr, crs_size)) { LOG_ERROR(Service_LDR, "CRS original buffer is in invalid state"); rb.Push(ERROR_INVALID_MEMORY_STATE); return; @@ -112,7 +115,7 @@ void RO::Initialize(Kernel::HLERequestContext& ctx) { // TODO(wwylele): should be memory aliasing std::shared_ptr> crs_mem = std::make_shared>(crs_size); Memory::ReadBlock(crs_buffer_ptr, crs_mem->data(), crs_size); - result = Kernel::g_current_process->vm_manager + result = process->vm_manager .MapMemoryBlock(crs_address, crs_mem, 0, crs_size, Kernel::MemoryState::Code) .Code(); if (result.IsError()) { @@ -121,15 +124,15 @@ void RO::Initialize(Kernel::HLERequestContext& ctx) { return; } - result = Kernel::g_current_process->vm_manager.ReprotectRange(crs_address, crs_size, - Kernel::VMAPermission::Read); + result = + process->vm_manager.ReprotectRange(crs_address, crs_size, Kernel::VMAPermission::Read); if (result.IsError()) { LOG_ERROR(Service_LDR, "Error reprotecting memory block %08X", result.raw); rb.Push(result); return; } - memory_synchronizer.AddMemoryBlock(crs_address, crs_buffer_ptr, crs_size); + slot.memory_synchronizer.AddMemoryBlock(crs_address, crs_buffer_ptr, crs_size); } else { // Do nothing if buffer_ptr == address // TODO(wwylele): verify this behaviour. This is only seen in the web browser app, @@ -148,9 +151,9 @@ void RO::Initialize(Kernel::HLERequestContext& ctx) { return; } - memory_synchronizer.SynchronizeOriginalMemory(); + slot.memory_synchronizer.SynchronizeOriginalMemory(*process); - loaded_crs = crs_address; + slot.loaded_crs = crs_address; rb.Push(RESULT_SUCCESS); } @@ -204,12 +207,14 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) { IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); - if (loaded_crs == 0) { + auto find_result = slots.find(process->process_id); + if (find_result == slots.end()) { LOG_ERROR(Service_LDR, "Not initialized"); rb.Push(ERROR_NOT_INITIALIZED); rb.Push(0); return; } + auto& slot = find_result->second; if (cro_size < CRO_HEADER_SIZE) { LOG_ERROR(Service_LDR, "CRO too small"); @@ -239,7 +244,7 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) { return; } - if (!VerifyBufferState(cro_buffer_ptr, cro_size)) { + if (!VerifyBufferState(*process, cro_buffer_ptr, cro_size)) { LOG_ERROR(Service_LDR, "CRO original buffer is in invalid state"); rb.Push(ERROR_INVALID_MEMORY_STATE); rb.Push(0); @@ -268,7 +273,7 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) { // TODO(wwylele): should be memory aliasing std::shared_ptr> cro_mem = std::make_shared>(cro_size); Memory::ReadBlock(cro_buffer_ptr, cro_mem->data(), cro_size); - result = Kernel::g_current_process->vm_manager + result = process->vm_manager .MapMemoryBlock(cro_address, cro_mem, 0, cro_size, Kernel::MemoryState::Code) .Code(); if (result.IsError()) { @@ -278,17 +283,17 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) { return; } - result = Kernel::g_current_process->vm_manager.ReprotectRange(cro_address, cro_size, - Kernel::VMAPermission::Read); + result = + process->vm_manager.ReprotectRange(cro_address, cro_size, Kernel::VMAPermission::Read); if (result.IsError()) { LOG_ERROR(Service_LDR, "Error reprotecting memory block %08X", result.raw); - Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size); + process->vm_manager.UnmapRange(cro_address, cro_size); rb.Push(result); rb.Push(0); return; } - memory_synchronizer.AddMemoryBlock(cro_address, cro_buffer_ptr, cro_size); + slot.memory_synchronizer.AddMemoryBlock(cro_address, cro_buffer_ptr, cro_size); } else { // Do nothing if buffer_ptr == address // TODO(wwylele): verify this behaviour. @@ -303,45 +308,44 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) { result = cro.VerifyHash(cro_size, crr_address); if (result.IsError()) { LOG_ERROR(Service_LDR, "Error verifying CRO in CRR %08X", result.raw); - Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size); + process->vm_manager.UnmapRange(cro_address, cro_size); rb.Push(result); rb.Push(0); return; } - result = cro.Rebase(loaded_crs, cro_size, data_segment_address, data_segment_size, + result = cro.Rebase(slot.loaded_crs, cro_size, data_segment_address, data_segment_size, bss_segment_address, bss_segment_size, false); if (result.IsError()) { LOG_ERROR(Service_LDR, "Error rebasing CRO %08X", result.raw); - Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size); + process->vm_manager.UnmapRange(cro_address, cro_size); rb.Push(result); rb.Push(0); return; } - result = cro.Link(loaded_crs, link_on_load_bug_fix); + result = cro.Link(slot.loaded_crs, link_on_load_bug_fix); if (result.IsError()) { LOG_ERROR(Service_LDR, "Error linking CRO %08X", result.raw); - Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size); + process->vm_manager.UnmapRange(cro_address, cro_size); rb.Push(result); rb.Push(0); return; } - cro.Register(loaded_crs, auto_link); + cro.Register(slot.loaded_crs, auto_link); u32 fix_size = cro.Fix(fix_level); - memory_synchronizer.SynchronizeOriginalMemory(); + slot.memory_synchronizer.SynchronizeOriginalMemory(*process); // TODO(wwylele): verify the behaviour when buffer_ptr == address if (cro_buffer_ptr != cro_address) { if (fix_size != cro_size) { - result = Kernel::g_current_process->vm_manager.UnmapRange(cro_address + fix_size, - cro_size - fix_size); + result = process->vm_manager.UnmapRange(cro_address + fix_size, cro_size - fix_size); if (result.IsError()) { LOG_ERROR(Service_LDR, "Error unmapping memory block %08X", result.raw); - Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size); + process->vm_manager.UnmapRange(cro_address, cro_size); rb.Push(result); rb.Push(0); return; @@ -349,18 +353,18 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) { } // Changes the block size - memory_synchronizer.ResizeMemoryBlock(cro_address, cro_buffer_ptr, fix_size); + slot.memory_synchronizer.ResizeMemoryBlock(cro_address, cro_buffer_ptr, fix_size); } VAddr exe_begin; u32 exe_size; std::tie(exe_begin, exe_size) = cro.GetExecutablePages(); if (exe_begin) { - result = Kernel::g_current_process->vm_manager.ReprotectRange( - exe_begin, exe_size, Kernel::VMAPermission::ReadExecute); + result = process->vm_manager.ReprotectRange(exe_begin, exe_size, + Kernel::VMAPermission::ReadExecute); if (result.IsError()) { LOG_ERROR(Service_LDR, "Error reprotecting memory block %08X", result.raw); - Kernel::g_current_process->vm_manager.UnmapRange(cro_address, fix_size); + process->vm_manager.UnmapRange(cro_address, fix_size); rb.Push(result); rb.Push(0); return; @@ -389,11 +393,13 @@ void RO::UnloadCRO(Kernel::HLERequestContext& ctx) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - if (loaded_crs == 0) { + auto find_result = slots.find(process->process_id); + if (find_result == slots.end()) { LOG_ERROR(Service_LDR, "Not initialized"); rb.Push(ERROR_NOT_INITIALIZED); return; } + auto& slot = find_result->second; if (cro_address & Memory::PAGE_MASK) { LOG_ERROR(Service_LDR, "CRO address is not aligned"); @@ -411,9 +417,9 @@ void RO::UnloadCRO(Kernel::HLERequestContext& ctx) { u32 fixed_size = cro.GetFixedSize(); - cro.Unregister(loaded_crs); + cro.Unregister(slot.loaded_crs); - ResultCode result = cro.Unlink(loaded_crs); + ResultCode result = cro.Unlink(slot.loaded_crs); if (result.IsError()) { LOG_ERROR(Service_LDR, "Error unlinking CRO %08X", result.raw); rb.Push(result); @@ -433,15 +439,15 @@ void RO::UnloadCRO(Kernel::HLERequestContext& ctx) { cro.Unrebase(false); - memory_synchronizer.SynchronizeOriginalMemory(); + slot.memory_synchronizer.SynchronizeOriginalMemory(*process); // TODO(wwylele): verify the behaviour when buffer_ptr == address if (cro_address != cro_buffer_ptr) { - result = Kernel::g_current_process->vm_manager.UnmapRange(cro_address, fixed_size); + result = process->vm_manager.UnmapRange(cro_address, fixed_size); if (result.IsError()) { LOG_ERROR(Service_LDR, "Error unmapping CRO %08X", result.raw); } - memory_synchronizer.RemoveMemoryBlock(cro_address, cro_buffer_ptr); + slot.memory_synchronizer.RemoveMemoryBlock(cro_address, cro_buffer_ptr); } Core::CPU().InvalidateCacheRange(cro_address, fixed_size); @@ -460,11 +466,13 @@ void RO::LinkCRO(Kernel::HLERequestContext& ctx) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - if (loaded_crs == 0) { + auto find_result = slots.find(process->process_id); + if (find_result == slots.end()) { LOG_ERROR(Service_LDR, "Not initialized"); rb.Push(ERROR_NOT_INITIALIZED); return; } + auto& slot = find_result->second; if (cro_address & Memory::PAGE_MASK) { LOG_ERROR(Service_LDR, "CRO address is not aligned"); @@ -480,12 +488,12 @@ void RO::LinkCRO(Kernel::HLERequestContext& ctx) { LOG_INFO(Service_LDR, "Linking CRO \"%s\"", cro.ModuleName().data()); - ResultCode result = cro.Link(loaded_crs, false); + ResultCode result = cro.Link(slot.loaded_crs, false); if (result.IsError()) { LOG_ERROR(Service_LDR, "Error linking CRO %08X", result.raw); } - memory_synchronizer.SynchronizeOriginalMemory(); + slot.memory_synchronizer.SynchronizeOriginalMemory(*process); rb.Push(result); } @@ -501,11 +509,13 @@ void RO::UnlinkCRO(Kernel::HLERequestContext& ctx) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - if (loaded_crs == 0) { + auto find_result = slots.find(process->process_id); + if (find_result == slots.end()) { LOG_ERROR(Service_LDR, "Not initialized"); rb.Push(ERROR_NOT_INITIALIZED); return; } + auto& slot = find_result->second; if (cro_address & Memory::PAGE_MASK) { LOG_ERROR(Service_LDR, "CRO address is not aligned"); @@ -521,12 +531,12 @@ void RO::UnlinkCRO(Kernel::HLERequestContext& ctx) { LOG_INFO(Service_LDR, "Unlinking CRO \"%s\"", cro.ModuleName().data()); - ResultCode result = cro.Unlink(loaded_crs); + ResultCode result = cro.Unlink(slot.loaded_crs); if (result.IsError()) { LOG_ERROR(Service_LDR, "Error unlinking CRO %08X", result.raw); } - memory_synchronizer.SynchronizeOriginalMemory(); + slot.memory_synchronizer.SynchronizeOriginalMemory(*process); rb.Push(result); } @@ -540,29 +550,31 @@ void RO::Shutdown(Kernel::HLERequestContext& ctx) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - if (loaded_crs == 0) { + auto find_result = slots.find(process->process_id); + if (find_result == slots.end()) { LOG_ERROR(Service_LDR, "Not initialized"); rb.Push(ERROR_NOT_INITIALIZED); return; } + auto& slot = find_result->second; - CROHelper crs(loaded_crs); + CROHelper crs(slot.loaded_crs); crs.Unrebase(true); - memory_synchronizer.SynchronizeOriginalMemory(); + slot.memory_synchronizer.SynchronizeOriginalMemory(*process); ResultCode result = RESULT_SUCCESS; // TODO(wwylele): verify the behaviour when buffer_ptr == address - if (loaded_crs != crs_buffer_ptr) { - result = Kernel::g_current_process->vm_manager.UnmapRange(loaded_crs, crs.GetFileSize()); + if (slot.loaded_crs != crs_buffer_ptr) { + result = process->vm_manager.UnmapRange(slot.loaded_crs, crs.GetFileSize()); if (result.IsError()) { LOG_ERROR(Service_LDR, "Error unmapping CRS %08X", result.raw); } - memory_synchronizer.RemoveMemoryBlock(loaded_crs, crs_buffer_ptr); + slot.memory_synchronizer.RemoveMemoryBlock(slot.loaded_crs, crs_buffer_ptr); } - loaded_crs = 0; + slots.erase(find_result); rb.Push(result); } diff --git a/src/core/hle/service/ldr_ro/ldr_ro.h b/src/core/hle/service/ldr_ro/ldr_ro.h index f0aa53af5..b2be17dba 100644 --- a/src/core/hle/service/ldr_ro/ldr_ro.h +++ b/src/core/hle/service/ldr_ro/ldr_ro.h @@ -4,6 +4,7 @@ #pragma once +#include #include "core/hle/service/ldr_ro/memory_synchronizer.h" #include "core/hle/service/service.h" @@ -144,8 +145,11 @@ private: */ void Shutdown(Kernel::HLERequestContext& self); - MemorySynchronizer memory_synchronizer; - VAddr loaded_crs = 0; ///< the virtual address of the static module + struct ClientSlot { + MemorySynchronizer memory_synchronizer; + VAddr loaded_crs = 0; ///< the virtual address of the static module + }; + std::unordered_map slots; }; void InstallInterfaces(SM::ServiceManager& service_manager); diff --git a/src/core/hle/service/ldr_ro/memory_synchronizer.cpp b/src/core/hle/service/ldr_ro/memory_synchronizer.cpp index 0d44bf6bd..9fd44088a 100644 --- a/src/core/hle/service/ldr_ro/memory_synchronizer.cpp +++ b/src/core/hle/service/ldr_ro/memory_synchronizer.cpp @@ -4,6 +4,7 @@ #include #include "common/assert.h" +#include "core/hle/kernel/process.h" #include "core/hle/service/ldr_ro/memory_synchronizer.h" namespace Service { @@ -32,9 +33,9 @@ void MemorySynchronizer::RemoveMemoryBlock(VAddr mapping, VAddr original) { memory_blocks.erase(FindMemoryBlock(mapping, original)); } -void MemorySynchronizer::SynchronizeOriginalMemory() { +void MemorySynchronizer::SynchronizeOriginalMemory(Kernel::Process& process) { for (auto& block : memory_blocks) { - Memory::CopyBlock(block.original, block.mapping, block.size); + Memory::CopyBlock(process, block.original, block.mapping, block.size); } } diff --git a/src/core/hle/service/ldr_ro/memory_synchronizer.h b/src/core/hle/service/ldr_ro/memory_synchronizer.h index 438293a58..2634406ba 100644 --- a/src/core/hle/service/ldr_ro/memory_synchronizer.h +++ b/src/core/hle/service/ldr_ro/memory_synchronizer.h @@ -7,6 +7,10 @@ #include #include "core/memory.h" +namespace Kernel { +class Process; +} + namespace Service { namespace LDR { @@ -24,7 +28,7 @@ public: void ResizeMemoryBlock(VAddr mapping, VAddr original, u32 size); void RemoveMemoryBlock(VAddr mapping, VAddr original); - void SynchronizeOriginalMemory(); + void SynchronizeOriginalMemory(Kernel::Process& process); private: struct MemoryBlock {