diff --git a/src/core/hle/service/ldr_ro/cro_helper.h b/src/core/hle/service/ldr_ro/cro_helper.h index 57b4fb6df..dc14039fb 100644 --- a/src/core/hle/service/ldr_ro/cro_helper.h +++ b/src/core/hle/service/ldr_ro/cro_helper.h @@ -36,6 +36,7 @@ static constexpr u32 CRO_HASH_SIZE = 0x80; /// Represents a loaded module (CRO) with interfaces manipulating it. class CROHelper final { public: + // TODO (wwylele): pass in the process handle for memory access explicit CROHelper(VAddr cro_address) : module_address(cro_address) {} std::string ModuleName() const { diff --git a/src/core/hle/service/ldr_ro/ldr_ro.cpp b/src/core/hle/service/ldr_ro/ldr_ro.cpp index c500d69f1..5c8fedd1e 100644 --- a/src/core/hle/service/ldr_ro/ldr_ro.cpp +++ b/src/core/hle/service/ldr_ro/ldr_ro.cpp @@ -9,10 +9,8 @@ #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/process.h" -#include "core/hle/kernel/vm_manager.h" #include "core/hle/service/ldr_ro/cro_helper.h" #include "core/hle/service/ldr_ro/ldr_ro.h" -#include "core/hle/service/ldr_ro/memory_synchronizer.h" namespace Service { namespace LDR { @@ -42,54 +40,38 @@ static const ResultCode ERROR_NOT_LOADED = // 0xD8A12C0D ResultCode(static_cast(13), ErrorModule::RO, ErrorSummary::InvalidState, ErrorLevel::Permanent); -static MemorySynchronizer memory_synchronizer; - -// TODO(wwylele): this should be in the per-client storage when we implement multi-process -static VAddr loaded_crs; ///< the virtual address of the static module - -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; } -/** - * LDR_RO::Initialize service function - * Inputs: - * 0 : 0x000100C2 - * 1 : CRS buffer pointer - * 2 : CRS Size - * 3 : Process memory address where the CRS will be mapped - * 4 : handle translation descriptor (zero) - * 5 : KProcess handle - * Outputs: - * 0 : Return header - * 1 : Result of function, 0 on success, otherwise error code - */ -static void Initialize(Interface* self) { - IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x01, 3, 2); +void RO::Initialize(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x01, 3, 2); VAddr crs_buffer_ptr = rp.Pop(); u32 crs_size = rp.Pop(); VAddr crs_address = rp.Pop(); // TODO (wwylele): RO service checks the descriptor here and return error 0xD9001830 for // incorrect descriptor. This error return should be probably built in IPC::RequestParser. // All other service functions below have the same issue. - Kernel::Handle process = rp.PopHandle(); + auto process = rp.PopObject(); - LOG_DEBUG(Service_LDR, - "called, crs_buffer_ptr=0x%08X, crs_address=0x%08X, crs_size=0x%X, process=0x%08X", - crs_buffer_ptr, crs_address, crs_size, process); + LOG_DEBUG(Service_LDR, "called, crs_buffer_ptr=0x%08X, crs_address=0x%08X, crs_size=0x%X", + crs_buffer_ptr, crs_address, crs_size); 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); @@ -114,7 +96,7 @@ static void Initialize(Interface* self) { 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; @@ -133,7 +115,7 @@ static void Initialize(Interface* self) { // 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()) { @@ -142,15 +124,15 @@ static void Initialize(Interface* self) { 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, @@ -169,90 +151,39 @@ static void Initialize(Interface* self) { return; } - memory_synchronizer.SynchronizeOriginalMemory(); + slot.memory_synchronizer.SynchronizeOriginalMemory(*process); - loaded_crs = crs_address; + slot.loaded_crs = crs_address; rb.Push(RESULT_SUCCESS); } -/** - * LDR_RO::LoadCRR service function - * Inputs: - * 0 : 0x00020082 - * 1 : CRR buffer pointer - * 2 : CRR Size - * 3 : handle translation descriptor (zero) - * 4 : KProcess handle - * Outputs: - * 0 : Return header - * 1 : Result of function, 0 on success, otherwise error code - */ -static void LoadCRR(Interface* self) { - IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x02, 2, 2); +void RO::LoadCRR(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x02, 2, 2); VAddr crr_buffer_ptr = rp.Pop(); u32 crr_size = rp.Pop(); - Kernel::Handle process = rp.PopHandle(); + auto process = rp.PopObject(); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_LDR, - "(STUBBED) called, crr_buffer_ptr=0x%08X, crr_size=0x%08X, process=0x%08X", - crr_buffer_ptr, crr_size, process); + LOG_WARNING(Service_LDR, "(STUBBED) called, crr_buffer_ptr=0x%08X, crr_size=0x%08X", + crr_buffer_ptr, crr_size); } -/** - * LDR_RO::UnloadCRR service function - * Inputs: - * 0 : 0x00030042 - * 1 : CRR buffer pointer - * 2 : handle translation descriptor (zero) - * 3 : KProcess handle - * Outputs: - * 0 : Return header - * 1 : Result of function, 0 on success, otherwise error code - */ -static void UnloadCRR(Interface* self) { - IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x03, 1, 2); +void RO::UnloadCRR(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x03, 1, 2); u32 crr_buffer_ptr = rp.Pop(); - Kernel::Handle process = rp.PopHandle(); + auto process = rp.PopObject(); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_LDR, "(STUBBED) called, crr_buffer_ptr=0x%08X, process=0x%08X", - crr_buffer_ptr, process); + LOG_WARNING(Service_LDR, "(STUBBED) called, crr_buffer_ptr=0x%08X", crr_buffer_ptr); } -/** - * LDR_RO::LoadCRO service function - * Inputs: - * 0 : 0x000402C2 (old) / 0x000902C2 (new) - * 1 : CRO buffer pointer - * 2 : memory address where the CRO will be mapped - * 3 : CRO Size - * 4 : .data segment buffer pointer - * 5 : must be zero - * 6 : .data segment buffer size - * 7 : .bss segment buffer pointer - * 8 : .bss segment buffer size - * 9 : (bool) register CRO as auto-link module - * 10 : fix level - * 11 : CRR address (zero if use loaded CRR) - * 12 : handle translation descriptor (zero) - * 13 : KProcess handle - * Outputs: - * 0 : Return header - * 1 : Result of function, 0 on success, otherwise error code - * 2 : CRO fixed size - * Note: - * This service function has two versions. The function defined here is a - * unified one of two, with an additional parameter link_on_load_bug_fix. - * There is a dispatcher template below. - */ -static void LoadCRO(Interface* self, bool link_on_load_bug_fix) { - IPC::RequestParser rp(Kernel::GetCommandBuffer(), link_on_load_bug_fix ? 0x09 : 0x04, 11, 2); +void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) { + IPC::RequestParser rp(ctx, link_on_load_bug_fix ? 0x09 : 0x04, 11, 2); VAddr cro_buffer_ptr = rp.Pop(); VAddr cro_address = rp.Pop(); u32 cro_size = rp.Pop(); @@ -264,24 +195,26 @@ static void LoadCRO(Interface* self, bool link_on_load_bug_fix) { bool auto_link = rp.Pop(); u32 fix_level = rp.Pop(); VAddr crr_address = rp.Pop(); - Kernel::Handle process = rp.PopHandle(); + auto process = rp.PopObject(); LOG_DEBUG(Service_LDR, "called (%s), cro_buffer_ptr=0x%08X, cro_address=0x%08X, cro_size=0x%X, " "data_segment_address=0x%08X, zero=%d, data_segment_size=0x%X, " "bss_segment_address=0x%08X, bss_segment_size=0x%X, auto_link=%s, " - "fix_level=%d, crr_address=0x%08X, process=0x%08X", + "fix_level=%d, crr_address=0x%08X", link_on_load_bug_fix ? "new" : "old", cro_buffer_ptr, cro_address, cro_size, data_segment_address, zero, data_segment_size, bss_segment_address, bss_segment_size, - auto_link ? "true" : "false", fix_level, crr_address, process); + auto_link ? "true" : "false", fix_level, crr_address); 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"); @@ -311,7 +244,7 @@ static void LoadCRO(Interface* self, 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); @@ -340,7 +273,7 @@ static void LoadCRO(Interface* self, 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()) { @@ -350,17 +283,17 @@ static void LoadCRO(Interface* self, 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. @@ -375,45 +308,44 @@ static void LoadCRO(Interface* self, 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; @@ -421,18 +353,18 @@ static void LoadCRO(Interface* self, 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; @@ -447,44 +379,27 @@ static void LoadCRO(Interface* self, bool link_on_load_bug_fix) { rb.Push(RESULT_SUCCESS, fix_size); } -template -static void LoadCRO(Interface* self) { - LoadCRO(self, link_on_load_bug_fix); -} - -/** - * LDR_RO::UnloadCRO service function - * Inputs: - * 0 : 0x000500C2 - * 1 : mapped CRO pointer - * 2 : zero? (RO service doesn't care) - * 3 : original CRO pointer - * 4 : handle translation descriptor (zero) - * 5 : KProcess handle - * Outputs: - * 0 : Return header - * 1 : Result of function, 0 on success, otherwise error code - */ -static void UnloadCRO(Interface* self) { - IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x05, 3, 2); +void RO::UnloadCRO(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x05, 3, 2); VAddr cro_address = rp.Pop(); u32 zero = rp.Pop(); VAddr cro_buffer_ptr = rp.Pop(); - Kernel::Handle process = rp.PopHandle(); + auto process = rp.PopObject(); - LOG_DEBUG(Service_LDR, - "called, cro_address=0x%08X, zero=%d, cro_buffer_ptr=0x%08X, process=0x%08X", - cro_address, zero, cro_buffer_ptr, process); + LOG_DEBUG(Service_LDR, "called, cro_address=0x%08X, zero=%d, cro_buffer_ptr=0x%08X", + cro_address, zero, cro_buffer_ptr); CROHelper cro(cro_address); 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"); @@ -502,9 +417,9 @@ static void UnloadCRO(Interface* self) { 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); @@ -524,15 +439,15 @@ static void UnloadCRO(Interface* self) { 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); @@ -540,33 +455,24 @@ static void UnloadCRO(Interface* self) { rb.Push(result); } -/** - * LDR_RO::LinkCRO service function - * Inputs: - * 0 : 0x00060042 - * 1 : mapped CRO pointer - * 2 : handle translation descriptor (zero) - * 3 : KProcess handle - * Outputs: - * 0 : Return header - * 1 : Result of function, 0 on success, otherwise error code - */ -static void LinkCRO(Interface* self) { - IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x06, 1, 2); +void RO::LinkCRO(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x06, 1, 2); VAddr cro_address = rp.Pop(); - Kernel::Handle process = rp.PopHandle(); + auto process = rp.PopObject(); - LOG_DEBUG(Service_LDR, "called, cro_address=0x%08X, process=0x%08X", cro_address, process); + LOG_DEBUG(Service_LDR, "called, cro_address=0x%08X", cro_address); CROHelper cro(cro_address); 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"); @@ -582,43 +488,34 @@ static void LinkCRO(Interface* self) { 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); } -/** - * LDR_RO::UnlinkCRO service function - * Inputs: - * 0 : 0x00070042 - * 1 : mapped CRO pointer - * 2 : handle translation descriptor (zero) - * 3 : KProcess handle - * Outputs: - * 0 : Return header - * 1 : Result of function, 0 on success, otherwise error code - */ -static void UnlinkCRO(Interface* self) { - IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x07, 1, 2); +void RO::UnlinkCRO(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x07, 1, 2); VAddr cro_address = rp.Pop(); - Kernel::Handle process = rp.PopHandle(); + auto process = rp.PopObject(); - LOG_DEBUG(Service_LDR, "called, cro_address=0x%08X, process=0x%08X", cro_address, process); + LOG_DEBUG(Service_LDR, "called, cro_address=0x%08X", cro_address); CROHelper cro(cro_address); 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"); @@ -634,82 +531,70 @@ static void UnlinkCRO(Interface* self) { 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); } -/** - * LDR_RO::Shutdown service function - * Inputs: - * 0 : 0x00080042 - * 1 : original CRS buffer pointer - * 2 : handle translation descriptor (zero) - * 3 : KProcess handle - * Outputs: - * 0 : Return header - * 1 : Result of function, 0 on success, otherwise error code - */ -static void Shutdown(Interface* self) { - IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x08, 1, 2); +void RO::Shutdown(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x08, 1, 2); VAddr crs_buffer_ptr = rp.Pop(); - Kernel::Handle process = rp.PopHandle(); + auto process = rp.PopObject(); - LOG_DEBUG(Service_LDR, "called, crs_buffer_ptr=0x%08X, process=0x%08X", crs_buffer_ptr, - process); + LOG_DEBUG(Service_LDR, "called, crs_buffer_ptr=0x%08X", crs_buffer_ptr); 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); } -const Interface::FunctionInfo FunctionTable[] = { - // clang-format off - {0x000100C2, Initialize, "Initialize"}, - {0x00020082, LoadCRR, "LoadCRR"}, - {0x00030042, UnloadCRR, "UnloadCRR"}, - {0x000402C2, LoadCRO, "LoadCRO"}, - {0x000500C2, UnloadCRO, "UnloadCRO"}, - {0x00060042, LinkCRO, "LinkCRO"}, - {0x00070042, UnlinkCRO, "UnlinkCRO"}, - {0x00080042, Shutdown, "Shutdown"}, - {0x000902C2, LoadCRO, "LoadCRO_New"}, - // clang-format on -}; +RO::RO() : ServiceFramework("ldr:ro", 2) { + static const FunctionInfo functions[] = { + {0x000100C2, &RO::Initialize, "Initialize"}, + {0x00020082, &RO::LoadCRR, "LoadCRR"}, + {0x00030042, &RO::UnloadCRR, "UnloadCRR"}, + {0x000402C2, &RO::LoadCRO, "LoadCRO"}, + {0x000500C2, &RO::UnloadCRO, "UnloadCRO"}, + {0x00060042, &RO::LinkCRO, "LinkCRO"}, + {0x00070042, &RO::UnlinkCRO, "UnlinkCRO"}, + {0x00080042, &RO::Shutdown, "Shutdown"}, + {0x000902C2, &RO::LoadCRO, "LoadCRO_New"}, + }; + RegisterHandlers(functions); +} -LDR_RO::LDR_RO() { - Register(FunctionTable); - - loaded_crs = 0; - memory_synchronizer.Clear(); +void InstallInterfaces(SM::ServiceManager& service_manager) { + std::make_shared()->InstallAsService(service_manager); } } // namespace LDR diff --git a/src/core/hle/service/ldr_ro/ldr_ro.h b/src/core/hle/service/ldr_ro/ldr_ro.h index 0f6fe7b60..b2be17dba 100644 --- a/src/core/hle/service/ldr_ro/ldr_ro.h +++ b/src/core/hle/service/ldr_ro/ldr_ro.h @@ -4,19 +4,155 @@ #pragma once +#include +#include "core/hle/service/ldr_ro/memory_synchronizer.h" #include "core/hle/service/service.h" namespace Service { namespace LDR { -class LDR_RO final : public Interface { +class RO final : public ServiceFramework { public: - LDR_RO(); + RO(); - std::string GetPortName() const override { - return "ldr:ro"; +private: + /** + * RO::Initialize service function + * Inputs: + * 0 : 0x000100C2 + * 1 : CRS buffer pointer + * 2 : CRS Size + * 3 : Process memory address where the CRS will be mapped + * 4 : handle translation descriptor (zero) + * 5 : KProcess handle + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + */ + void Initialize(Kernel::HLERequestContext& ctx); + + /** + * RO::LoadCRR service function + * Inputs: + * 0 : 0x00020082 + * 1 : CRR buffer pointer + * 2 : CRR Size + * 3 : handle translation descriptor (zero) + * 4 : KProcess handle + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + */ + void LoadCRR(Kernel::HLERequestContext& ctx); + + /** + * RO::UnloadCRR service function + * Inputs: + * 0 : 0x00030042 + * 1 : CRR buffer pointer + * 2 : handle translation descriptor (zero) + * 3 : KProcess handle + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + */ + void UnloadCRR(Kernel::HLERequestContext& ctx); + + /** + * RO::LoadCRO service function + * Inputs: + * 0 : 0x000402C2 (old) / 0x000902C2 (new) + * 1 : CRO buffer pointer + * 2 : memory address where the CRO will be mapped + * 3 : CRO Size + * 4 : .data segment buffer pointer + * 5 : must be zero + * 6 : .data segment buffer size + * 7 : .bss segment buffer pointer + * 8 : .bss segment buffer size + * 9 : (bool) register CRO as auto-link module + * 10 : fix level + * 11 : CRR address (zero if use loaded CRR) + * 12 : handle translation descriptor (zero) + * 13 : KProcess handle + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + * 2 : CRO fixed size + * Note: + * This service function has two versions. The function defined here is a + * unified one of two, with an additional parameter link_on_load_bug_fix. + * There is a dispatcher template below. + */ + void LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix); + + template + void LoadCRO(Kernel::HLERequestContext& ctx) { + LoadCRO(ctx, link_on_load_bug_fix); } + + /** + * RO::UnloadCRO service function + * Inputs: + * 0 : 0x000500C2 + * 1 : mapped CRO pointer + * 2 : zero? (RO service doesn't care) + * 3 : original CRO pointer + * 4 : handle translation descriptor (zero) + * 5 : KProcess handle + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + */ + void UnloadCRO(Kernel::HLERequestContext& self); + + /** + * RO::LinkCRO service function + * Inputs: + * 0 : 0x00060042 + * 1 : mapped CRO pointer + * 2 : handle translation descriptor (zero) + * 3 : KProcess handle + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + */ + void LinkCRO(Kernel::HLERequestContext& self); + + /** + * RO::UnlinkCRO service function + * Inputs: + * 0 : 0x00070042 + * 1 : mapped CRO pointer + * 2 : handle translation descriptor (zero) + * 3 : KProcess handle + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + */ + void UnlinkCRO(Kernel::HLERequestContext& self); + + /** + * RO::Shutdown service function + * Inputs: + * 0 : 0x00080042 + * 1 : original CRS buffer pointer + * 2 : handle translation descriptor (zero) + * 3 : KProcess handle + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + */ + void Shutdown(Kernel::HLERequestContext& self); + + 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); + } // namespace LDR } // namespace Service 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 { diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 33de75f39..190792c72 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -263,6 +263,7 @@ void Init() { PXI::InstallInterfaces(*SM::g_service_manager); NS::InstallInterfaces(*SM::g_service_manager); AC::InstallInterfaces(*SM::g_service_manager); + LDR::InstallInterfaces(*SM::g_service_manager); FS::ArchiveInit(); ACT::Init(); @@ -290,7 +291,6 @@ void Init() { AddService(new GSP::GSP_GPU); AddService(new GSP::GSP_LCD); AddService(new HTTP::HTTP_C); - AddService(new LDR::LDR_RO); AddService(new MIC::MIC_U); AddService(new PM::PM_APP); AddService(new SOC::SOC_U); diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 4fc964575..81e60a66f 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -615,7 +615,8 @@ void WriteBlock(const VAddr dest_addr, const void* src_buffer, const size_t size WriteBlock(*Kernel::g_current_process, dest_addr, src_buffer, size); } -void ZeroBlock(const VAddr dest_addr, const size_t size) { +void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const size_t size) { + auto& page_table = process.vm_manager.page_table; size_t remaining_size = size; size_t page_index = dest_addr >> PAGE_BITS; size_t page_offset = dest_addr & PAGE_MASK; @@ -626,36 +627,37 @@ void ZeroBlock(const VAddr dest_addr, const size_t size) { const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size); const VAddr current_vaddr = static_cast((page_index << PAGE_BITS) + page_offset); - switch (current_page_table->attributes[page_index]) { + switch (page_table.attributes[page_index]) { case PageType::Unmapped: { LOG_ERROR(HW_Memory, "unmapped ZeroBlock @ 0x%08X (start address = 0x%08X, size = %zu)", current_vaddr, dest_addr, size); break; } case PageType::Memory: { - DEBUG_ASSERT(current_page_table->pointers[page_index]); + DEBUG_ASSERT(page_table.pointers[page_index]); - u8* dest_ptr = current_page_table->pointers[page_index] + page_offset; + u8* dest_ptr = page_table.pointers[page_index] + page_offset; std::memset(dest_ptr, 0, copy_amount); break; } case PageType::Special: { - DEBUG_ASSERT(GetMMIOHandler(current_vaddr)); - - GetMMIOHandler(current_vaddr)->WriteBlock(current_vaddr, zeros.data(), copy_amount); + MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr); + DEBUG_ASSERT(handler); + handler->WriteBlock(current_vaddr, zeros.data(), copy_amount); break; } case PageType::RasterizerCachedMemory: { RasterizerFlushVirtualRegion(current_vaddr, static_cast(copy_amount), FlushMode::FlushAndInvalidate); - std::memset(GetPointerFromVMA(current_vaddr), 0, copy_amount); + std::memset(GetPointerFromVMA(process, current_vaddr), 0, copy_amount); break; } case PageType::RasterizerCachedSpecial: { - DEBUG_ASSERT(GetMMIOHandler(current_vaddr)); + MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr); + DEBUG_ASSERT(handler); RasterizerFlushVirtualRegion(current_vaddr, static_cast(copy_amount), FlushMode::FlushAndInvalidate); - GetMMIOHandler(current_vaddr)->WriteBlock(current_vaddr, zeros.data(), copy_amount); + handler->WriteBlock(current_vaddr, zeros.data(), copy_amount); break; } default: @@ -668,7 +670,12 @@ void ZeroBlock(const VAddr dest_addr, const size_t size) { } } -void CopyBlock(VAddr dest_addr, VAddr src_addr, const size_t size) { +void ZeroBlock(const VAddr dest_addr, const size_t size) { + ZeroBlock(*Kernel::g_current_process, dest_addr, size); +} + +void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr, const size_t size) { + auto& page_table = process.vm_manager.page_table; size_t remaining_size = size; size_t page_index = src_addr >> PAGE_BITS; size_t page_offset = src_addr & PAGE_MASK; @@ -677,41 +684,42 @@ void CopyBlock(VAddr dest_addr, VAddr src_addr, const size_t size) { const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size); const VAddr current_vaddr = static_cast((page_index << PAGE_BITS) + page_offset); - switch (current_page_table->attributes[page_index]) { + switch (page_table.attributes[page_index]) { case PageType::Unmapped: { LOG_ERROR(HW_Memory, "unmapped CopyBlock @ 0x%08X (start address = 0x%08X, size = %zu)", current_vaddr, src_addr, size); - ZeroBlock(dest_addr, copy_amount); + ZeroBlock(process, dest_addr, copy_amount); break; } case PageType::Memory: { - DEBUG_ASSERT(current_page_table->pointers[page_index]); - const u8* src_ptr = current_page_table->pointers[page_index] + page_offset; - WriteBlock(dest_addr, src_ptr, copy_amount); + DEBUG_ASSERT(page_table.pointers[page_index]); + const u8* src_ptr = page_table.pointers[page_index] + page_offset; + WriteBlock(process, dest_addr, src_ptr, copy_amount); break; } case PageType::Special: { - DEBUG_ASSERT(GetMMIOHandler(current_vaddr)); - + MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr); + DEBUG_ASSERT(handler); std::vector buffer(copy_amount); - GetMMIOHandler(current_vaddr)->ReadBlock(current_vaddr, buffer.data(), buffer.size()); - WriteBlock(dest_addr, buffer.data(), buffer.size()); + handler->ReadBlock(current_vaddr, buffer.data(), buffer.size()); + WriteBlock(process, dest_addr, buffer.data(), buffer.size()); break; } case PageType::RasterizerCachedMemory: { RasterizerFlushVirtualRegion(current_vaddr, static_cast(copy_amount), FlushMode::Flush); - WriteBlock(dest_addr, GetPointerFromVMA(current_vaddr), copy_amount); + WriteBlock(process, dest_addr, GetPointerFromVMA(process, current_vaddr), copy_amount); break; } case PageType::RasterizerCachedSpecial: { - DEBUG_ASSERT(GetMMIOHandler(current_vaddr)); + MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr); + DEBUG_ASSERT(handler); RasterizerFlushVirtualRegion(current_vaddr, static_cast(copy_amount), FlushMode::Flush); std::vector buffer(copy_amount); - GetMMIOHandler(current_vaddr)->ReadBlock(current_vaddr, buffer.data(), buffer.size()); - WriteBlock(dest_addr, buffer.data(), buffer.size()); + handler->ReadBlock(current_vaddr, buffer.data(), buffer.size()); + WriteBlock(process, dest_addr, buffer.data(), buffer.size()); break; } default: @@ -726,6 +734,10 @@ void CopyBlock(VAddr dest_addr, VAddr src_addr, const size_t size) { } } +void CopyBlock(VAddr dest_addr, VAddr src_addr, const size_t size) { + CopyBlock(*Kernel::g_current_process, dest_addr, src_addr, size); +} + template <> u8 ReadMMIO(MMIORegionPointer mmio_handler, VAddr addr) { return mmio_handler->Read8(addr); diff --git a/src/core/memory.h b/src/core/memory.h index dd599f73e..1527ec4c7 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -211,7 +211,9 @@ void ReadBlock(const VAddr src_addr, void* dest_buffer, size_t size); void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer, size_t size); void WriteBlock(const VAddr dest_addr, const void* src_buffer, size_t size); +void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const size_t size); void ZeroBlock(const VAddr dest_addr, const size_t size); +void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr, size_t size); void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size); u8* GetPointer(VAddr virtual_address);