mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	ldr_ro: properly map CRS/CRO buffer
This commit is contained in:
		
							parent
							
								
									617b388354
								
							
						
					
					
						commit
						3db8915356
					
				
					 5 changed files with 37 additions and 193 deletions
				
			
		|  | @ -286,8 +286,6 @@ add_library(core STATIC | |||
|     hle/service/ldr_ro/cro_helper.h | ||||
|     hle/service/ldr_ro/ldr_ro.cpp | ||||
|     hle/service/ldr_ro/ldr_ro.h | ||||
|     hle/service/ldr_ro/memory_synchronizer.cpp | ||||
|     hle/service/ldr_ro/memory_synchronizer.h | ||||
|     hle/service/mic_u.cpp | ||||
|     hle/service/mic_u.h | ||||
|     hle/service/mvd/mvd.cpp | ||||
|  |  | |||
|  | @ -108,34 +108,11 @@ void RO::Initialize(Kernel::HLERequestContext& ctx) { | |||
| 
 | ||||
|     ResultCode result = RESULT_SUCCESS; | ||||
| 
 | ||||
|     if (crs_buffer_ptr != crs_address) { | ||||
|         // TODO(wwylele): should be memory aliasing
 | ||||
|         std::shared_ptr<std::vector<u8>> crs_mem = std::make_shared<std::vector<u8>>(crs_size); | ||||
|         Memory::ReadBlock(crs_buffer_ptr, crs_mem->data(), crs_size); | ||||
|         result = process->vm_manager | ||||
|                      .MapMemoryBlock(crs_address, crs_mem, 0, crs_size, Kernel::MemoryState::Code) | ||||
|                      .Code(); | ||||
|         if (result.IsError()) { | ||||
|             LOG_ERROR(Service_LDR, "Error mapping memory block {:08X}", result.raw); | ||||
|             rb.Push(result); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         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; | ||||
|         } | ||||
| 
 | ||||
|         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,
 | ||||
|         //     and the actual behaviour is unclear. "Do nothing" is probably an incorrect implement.
 | ||||
|         //     There is also a chance that another issue causes the app passing wrong arguments.
 | ||||
|         LOG_WARNING(Service_LDR, "crs_buffer_ptr == crs_address (0x{:08X})", crs_address); | ||||
|     result = process->Map(crs_address, crs_buffer_ptr, crs_size, Kernel::VMAPermission::Read, true); | ||||
|     if (result.IsError()) { | ||||
|         LOG_ERROR(Service_LDR, "Error mapping memory block {:08X}", result.raw); | ||||
|         rb.Push(result); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     CROHelper crs(crs_address); | ||||
|  | @ -148,8 +125,6 @@ void RO::Initialize(Kernel::HLERequestContext& ctx) { | |||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     slot->memory_synchronizer.SynchronizeOriginalMemory(*process); | ||||
| 
 | ||||
|     slot->loaded_crs = crs_address; | ||||
| 
 | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|  | @ -266,38 +241,12 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) { | |||
| 
 | ||||
|     ResultCode result = RESULT_SUCCESS; | ||||
| 
 | ||||
|     if (cro_buffer_ptr != cro_address) { | ||||
|         // TODO(wwylele): should be memory aliasing
 | ||||
|         std::shared_ptr<std::vector<u8>> cro_mem = std::make_shared<std::vector<u8>>(cro_size); | ||||
|         Memory::ReadBlock(cro_buffer_ptr, cro_mem->data(), cro_size); | ||||
|         result = process->vm_manager | ||||
|                      .MapMemoryBlock(cro_address, cro_mem, 0, cro_size, Kernel::MemoryState::Code) | ||||
|                      .Code(); | ||||
|         if (result.IsError()) { | ||||
|             LOG_ERROR(Service_LDR, "Error mapping memory block {:08X}", result.raw); | ||||
|             rb.Push(result); | ||||
|             rb.Push<u32>(0); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         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); | ||||
|             process->vm_manager.UnmapRange(cro_address, cro_size); | ||||
|             rb.Push(result); | ||||
|             rb.Push<u32>(0); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         slot->memory_synchronizer.AddMemoryBlock(cro_address, cro_buffer_ptr, cro_size); | ||||
|     } else { | ||||
|         // Do nothing if buffer_ptr == address
 | ||||
|         // TODO(wwylele): verify this behaviour.
 | ||||
|         //     This is derived from the case of LoadCRS with buffer_ptr==address,
 | ||||
|         //     and is never seen in any game. "Do nothing" is probably an incorrect implement.
 | ||||
|         //     There is also a chance that this case is just prohibited.
 | ||||
|         LOG_WARNING(Service_LDR, "cro_buffer_ptr == cro_address (0x{:08X})", cro_address); | ||||
|     result = process->Map(cro_address, cro_buffer_ptr, cro_size, Kernel::VMAPermission::Read, true); | ||||
|     if (result.IsError()) { | ||||
|         LOG_ERROR(Service_LDR, "Error mapping memory block {:08X}", result.raw); | ||||
|         rb.Push(result); | ||||
|         rb.Push<u32>(0); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     CROHelper cro(cro_address); | ||||
|  | @ -305,7 +254,8 @@ 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); | ||||
|         process->vm_manager.UnmapRange(cro_address, cro_size); | ||||
|         process->Unmap(cro_address, cro_buffer_ptr, cro_size, Kernel::VMAPermission::ReadWrite, | ||||
|                        true); | ||||
|         rb.Push(result); | ||||
|         rb.Push<u32>(0); | ||||
|         return; | ||||
|  | @ -315,7 +265,8 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) { | |||
|                         bss_segment_address, bss_segment_size, false); | ||||
|     if (result.IsError()) { | ||||
|         LOG_ERROR(Service_LDR, "Error rebasing CRO {:08X}", result.raw); | ||||
|         process->vm_manager.UnmapRange(cro_address, cro_size); | ||||
|         process->Unmap(cro_address, cro_buffer_ptr, cro_size, Kernel::VMAPermission::ReadWrite, | ||||
|                        true); | ||||
|         rb.Push(result); | ||||
|         rb.Push<u32>(0); | ||||
|         return; | ||||
|  | @ -324,7 +275,8 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool 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); | ||||
|         process->vm_manager.UnmapRange(cro_address, cro_size); | ||||
|         process->Unmap(cro_address, cro_buffer_ptr, cro_size, Kernel::VMAPermission::ReadWrite, | ||||
|                        true); | ||||
|         rb.Push(result); | ||||
|         rb.Push<u32>(0); | ||||
|         return; | ||||
|  | @ -334,23 +286,17 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) { | |||
| 
 | ||||
|     u32 fix_size = cro.Fix(fix_level); | ||||
| 
 | ||||
|     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 = 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); | ||||
|                 process->vm_manager.UnmapRange(cro_address, cro_size); | ||||
|                 rb.Push(result); | ||||
|                 rb.Push<u32>(0); | ||||
|                 return; | ||||
|             } | ||||
|     if (fix_size != cro_size) { | ||||
|         result = process->Unmap(cro_address + fix_size, cro_buffer_ptr + fix_size, | ||||
|                                 cro_size - fix_size, Kernel::VMAPermission::ReadWrite, true); | ||||
|         if (result.IsError()) { | ||||
|             LOG_ERROR(Service_LDR, "Error unmapping memory block {:08X}", result.raw); | ||||
|             process->Unmap(cro_address, cro_buffer_ptr, cro_size, Kernel::VMAPermission::ReadWrite, | ||||
|                            true); | ||||
|             rb.Push(result); | ||||
|             rb.Push<u32>(0); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         // Changes the block size
 | ||||
|         slot->memory_synchronizer.ResizeMemoryBlock(cro_address, cro_buffer_ptr, fix_size); | ||||
|     } | ||||
| 
 | ||||
|     auto [exe_begin, exe_size] = cro.GetExecutablePages(); | ||||
|  | @ -359,7 +305,8 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) { | |||
|                                                     Kernel::VMAPermission::ReadExecute); | ||||
|         if (result.IsError()) { | ||||
|             LOG_ERROR(Service_LDR, "Error reprotecting memory block {:08X}", result.raw); | ||||
|             process->vm_manager.UnmapRange(cro_address, fix_size); | ||||
|             process->Unmap(cro_address, cro_buffer_ptr, cro_size, Kernel::VMAPermission::ReadWrite, | ||||
|                            true); | ||||
|             rb.Push(result); | ||||
|             rb.Push<u32>(0); | ||||
|             return; | ||||
|  | @ -433,15 +380,10 @@ void RO::UnloadCRO(Kernel::HLERequestContext& ctx) { | |||
| 
 | ||||
|     cro.Unrebase(false); | ||||
| 
 | ||||
|     slot->memory_synchronizer.SynchronizeOriginalMemory(*process); | ||||
| 
 | ||||
|     // TODO(wwylele): verify the behaviour when buffer_ptr == address
 | ||||
|     if (cro_address != cro_buffer_ptr) { | ||||
|         result = process->vm_manager.UnmapRange(cro_address, fixed_size); | ||||
|         if (result.IsError()) { | ||||
|             LOG_ERROR(Service_LDR, "Error unmapping CRO {:08X}", result.raw); | ||||
|         } | ||||
|         slot->memory_synchronizer.RemoveMemoryBlock(cro_address, cro_buffer_ptr); | ||||
|     result = process->Unmap(cro_address, cro_buffer_ptr, fixed_size, | ||||
|                             Kernel::VMAPermission::ReadWrite, true); | ||||
|     if (result.IsError()) { | ||||
|         LOG_ERROR(Service_LDR, "Error unmapping CRO {:08X}", result.raw); | ||||
|     } | ||||
| 
 | ||||
|     Core::CPU().InvalidateCacheRange(cro_address, fixed_size); | ||||
|  | @ -486,8 +428,6 @@ void RO::LinkCRO(Kernel::HLERequestContext& ctx) { | |||
|         LOG_ERROR(Service_LDR, "Error linking CRO {:08X}", result.raw); | ||||
|     } | ||||
| 
 | ||||
|     slot->memory_synchronizer.SynchronizeOriginalMemory(*process); | ||||
| 
 | ||||
|     rb.Push(result); | ||||
| } | ||||
| 
 | ||||
|  | @ -528,8 +468,6 @@ void RO::UnlinkCRO(Kernel::HLERequestContext& ctx) { | |||
|         LOG_ERROR(Service_LDR, "Error unlinking CRO {:08X}", result.raw); | ||||
|     } | ||||
| 
 | ||||
|     slot->memory_synchronizer.SynchronizeOriginalMemory(*process); | ||||
| 
 | ||||
|     rb.Push(result); | ||||
| } | ||||
| 
 | ||||
|  | @ -552,17 +490,12 @@ void RO::Shutdown(Kernel::HLERequestContext& ctx) { | |||
|     CROHelper crs(slot->loaded_crs); | ||||
|     crs.Unrebase(true); | ||||
| 
 | ||||
|     slot->memory_synchronizer.SynchronizeOriginalMemory(*process); | ||||
| 
 | ||||
|     ResultCode result = RESULT_SUCCESS; | ||||
| 
 | ||||
|     // TODO(wwylele): verify the behaviour when buffer_ptr == address
 | ||||
|     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); | ||||
|         } | ||||
|         slot->memory_synchronizer.RemoveMemoryBlock(slot->loaded_crs, crs_buffer_ptr); | ||||
|     result = process->Unmap(slot->loaded_crs, crs_buffer_ptr, crs.GetFileSize(), | ||||
|                             Kernel::VMAPermission::ReadWrite, true); | ||||
|     if (result.IsError()) { | ||||
|         LOG_ERROR(Service_LDR, "Error unmapping CRS {:08X}", result.raw); | ||||
|     } | ||||
| 
 | ||||
|     slot->loaded_crs = 0; | ||||
|  |  | |||
|  | @ -4,7 +4,6 @@ | |||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "core/hle/service/ldr_ro/memory_synchronizer.h" | ||||
| #include "core/hle/service/service.h" | ||||
| 
 | ||||
| namespace Core { | ||||
|  | @ -14,7 +13,6 @@ class System; | |||
| namespace Service::LDR { | ||||
| 
 | ||||
| struct ClientSlot : public Kernel::SessionRequestHandler::SessionDataBase { | ||||
|     MemorySynchronizer memory_synchronizer; | ||||
|     VAddr loaded_crs = 0; ///< the virtual address of the static module
 | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,41 +0,0 @@ | |||
| // Copyright 2016 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include "common/assert.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/service/ldr_ro/memory_synchronizer.h" | ||||
| 
 | ||||
| namespace Service::LDR { | ||||
| 
 | ||||
| auto MemorySynchronizer::FindMemoryBlock(VAddr mapping, VAddr original) { | ||||
|     auto block = std::find_if(memory_blocks.begin(), memory_blocks.end(), | ||||
|                               [=](MemoryBlock& b) { return b.original == original; }); | ||||
|     ASSERT(block->mapping == mapping); | ||||
|     return block; | ||||
| } | ||||
| 
 | ||||
| void MemorySynchronizer::Clear() { | ||||
|     memory_blocks.clear(); | ||||
| } | ||||
| 
 | ||||
| void MemorySynchronizer::AddMemoryBlock(VAddr mapping, VAddr original, u32 size) { | ||||
|     memory_blocks.push_back(MemoryBlock{mapping, original, size}); | ||||
| } | ||||
| 
 | ||||
| void MemorySynchronizer::ResizeMemoryBlock(VAddr mapping, VAddr original, u32 size) { | ||||
|     FindMemoryBlock(mapping, original)->size = size; | ||||
| } | ||||
| 
 | ||||
| void MemorySynchronizer::RemoveMemoryBlock(VAddr mapping, VAddr original) { | ||||
|     memory_blocks.erase(FindMemoryBlock(mapping, original)); | ||||
| } | ||||
| 
 | ||||
| void MemorySynchronizer::SynchronizeOriginalMemory(Kernel::Process& process) { | ||||
|     for (auto& block : memory_blocks) { | ||||
|         Memory::CopyBlock(process, block.original, block.mapping, block.size); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| } // namespace Service::LDR
 | ||||
|  | @ -1,44 +0,0 @@ | |||
| // Copyright 2016 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <vector> | ||||
| #include "core/memory.h" | ||||
| 
 | ||||
| namespace Kernel { | ||||
| class Process; | ||||
| } | ||||
| 
 | ||||
| namespace Service::LDR { | ||||
| 
 | ||||
| /**
 | ||||
|  * This is a work-around before we implement memory aliasing. | ||||
|  * CRS and CRO are mapped (aliased) to another memory when loading. Games can read | ||||
|  * from both the original buffer and the mapping memory. So we use this to synchronize | ||||
|  * all original buffers with mapping memory after modifying the content. | ||||
|  */ | ||||
| class MemorySynchronizer { | ||||
| public: | ||||
|     void Clear(); | ||||
| 
 | ||||
|     void AddMemoryBlock(VAddr mapping, VAddr original, u32 size); | ||||
|     void ResizeMemoryBlock(VAddr mapping, VAddr original, u32 size); | ||||
|     void RemoveMemoryBlock(VAddr mapping, VAddr original); | ||||
| 
 | ||||
|     void SynchronizeOriginalMemory(Kernel::Process& process); | ||||
| 
 | ||||
| private: | ||||
|     struct MemoryBlock { | ||||
|         VAddr mapping; | ||||
|         VAddr original; | ||||
|         u32 size; | ||||
|     }; | ||||
| 
 | ||||
|     std::vector<MemoryBlock> memory_blocks; | ||||
| 
 | ||||
|     auto FindMemoryBlock(VAddr mapping, VAddr original); | ||||
| }; | ||||
| 
 | ||||
| } // namespace Service::LDR
 | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue