mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40:04 +00:00 
			
		
		
		
	
						commit
						474586bc53
					
				
					 14 changed files with 3041 additions and 99 deletions
				
			
		|  | @ -95,7 +95,9 @@ set(SRCS | ||||||
|             hle/service/ir/ir_rst.cpp |             hle/service/ir/ir_rst.cpp | ||||||
|             hle/service/ir/ir_u.cpp |             hle/service/ir/ir_u.cpp | ||||||
|             hle/service/ir/ir_user.cpp |             hle/service/ir/ir_user.cpp | ||||||
|             hle/service/ldr_ro.cpp |             hle/service/ldr_ro/cro_helper.cpp | ||||||
|  |             hle/service/ldr_ro/ldr_ro.cpp | ||||||
|  |             hle/service/ldr_ro/memory_synchronizer.cpp | ||||||
|             hle/service/mic_u.cpp |             hle/service/mic_u.cpp | ||||||
|             hle/service/ndm/ndm.cpp |             hle/service/ndm/ndm.cpp | ||||||
|             hle/service/ndm/ndm_u.cpp |             hle/service/ndm/ndm_u.cpp | ||||||
|  | @ -238,7 +240,9 @@ set(HEADERS | ||||||
|             hle/service/ir/ir_rst.h |             hle/service/ir/ir_rst.h | ||||||
|             hle/service/ir/ir_u.h |             hle/service/ir/ir_u.h | ||||||
|             hle/service/ir/ir_user.h |             hle/service/ir/ir_user.h | ||||||
|             hle/service/ldr_ro.h |             hle/service/ldr_ro/cro_helper.h | ||||||
|  |             hle/service/ldr_ro/ldr_ro.h | ||||||
|  |             hle/service/ldr_ro/memory_synchronizer.h | ||||||
|             hle/service/mic_u.h |             hle/service/mic_u.h | ||||||
|             hle/service/ndm/ndm.h |             hle/service/ndm/ndm.h | ||||||
|             hle/service/ndm/ndm_u.h |             hle/service/ndm/ndm_u.h | ||||||
|  |  | ||||||
|  | @ -32,6 +32,9 @@ public: | ||||||
|         Run(1); |         Run(1); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /// Clear all instruction cache
 | ||||||
|  |     virtual void ClearInstructionCache() = 0; | ||||||
|  | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Set the Program Counter to an address |      * Set the Program Counter to an address | ||||||
|      * @param addr Address to set PC to |      * @param addr Address to set PC to | ||||||
|  |  | ||||||
|  | @ -12,6 +12,7 @@ | ||||||
| #include "core/arm/dyncom/arm_dyncom.h" | #include "core/arm/dyncom/arm_dyncom.h" | ||||||
| #include "core/arm/dyncom/arm_dyncom_interpreter.h" | #include "core/arm/dyncom/arm_dyncom_interpreter.h" | ||||||
| #include "core/arm/dyncom/arm_dyncom_run.h" | #include "core/arm/dyncom/arm_dyncom_run.h" | ||||||
|  | #include "core/arm/dyncom/arm_dyncom_trans.h" | ||||||
| 
 | 
 | ||||||
| #include "core/core.h" | #include "core/core.h" | ||||||
| #include "core/core_timing.h" | #include "core/core_timing.h" | ||||||
|  | @ -23,6 +24,11 @@ ARM_DynCom::ARM_DynCom(PrivilegeMode initial_mode) { | ||||||
| ARM_DynCom::~ARM_DynCom() { | ARM_DynCom::~ARM_DynCom() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void ARM_DynCom::ClearInstructionCache() { | ||||||
|  |     state->instruction_cache.clear(); | ||||||
|  |     trans_cache_buf_top = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void ARM_DynCom::SetPC(u32 pc) { | void ARM_DynCom::SetPC(u32 pc) { | ||||||
|     state->Reg[15] = pc; |     state->Reg[15] = pc; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -21,6 +21,8 @@ public: | ||||||
|     ARM_DynCom(PrivilegeMode initial_mode); |     ARM_DynCom(PrivilegeMode initial_mode); | ||||||
|     ~ARM_DynCom(); |     ~ARM_DynCom(); | ||||||
| 
 | 
 | ||||||
|  |     void ClearInstructionCache() override; | ||||||
|  | 
 | ||||||
|     void SetPC(u32 pc) override; |     void SetPC(u32 pc) override; | ||||||
|     u32 GetPC() const override; |     u32 GetPC() const override; | ||||||
|     u32 GetReg(int index) const override; |     u32 GetReg(int index) const override; | ||||||
|  |  | ||||||
|  | @ -1,96 +0,0 @@ | ||||||
| // Copyright 2014 Citra Emulator Project
 |  | ||||||
| // Licensed under GPLv2 or any later version
 |  | ||||||
| // Refer to the license.txt file included.
 |  | ||||||
| 
 |  | ||||||
| #include "common/common_types.h" |  | ||||||
| #include "common/logging/log.h" |  | ||||||
| 
 |  | ||||||
| #include "core/hle/service/ldr_ro.h" |  | ||||||
| 
 |  | ||||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 |  | ||||||
| // Namespace LDR_RO
 |  | ||||||
| 
 |  | ||||||
| namespace LDR_RO { |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * LDR_RO::Initialize service function |  | ||||||
|  *  Inputs: |  | ||||||
|  *      1 : CRS buffer pointer |  | ||||||
|  *      2 : CRS Size |  | ||||||
|  *      3 : Process memory address where the CRS will be mapped |  | ||||||
|  *      4 : Value, must be zero |  | ||||||
|  *      5 : KProcess handle |  | ||||||
|  *  Outputs: |  | ||||||
|  *      0 : Return header |  | ||||||
|  *      1 : Result of function, 0 on success, otherwise error code |  | ||||||
|  */ |  | ||||||
| static void Initialize(Service::Interface* self) { |  | ||||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); |  | ||||||
|     u32 crs_buffer_ptr = cmd_buff[1]; |  | ||||||
|     u32 crs_size       = cmd_buff[2]; |  | ||||||
|     u32 address        = cmd_buff[3]; |  | ||||||
|     u32 value          = cmd_buff[4]; |  | ||||||
|     u32 process        = cmd_buff[5]; |  | ||||||
| 
 |  | ||||||
|     if (value != 0) { |  | ||||||
|         LOG_ERROR(Service_LDR, "This value should be zero, but is actually %u!", value); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // TODO(purpasmart96): Verify return header on HW
 |  | ||||||
| 
 |  | ||||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; // No error
 |  | ||||||
| 
 |  | ||||||
|     LOG_WARNING(Service_LDR, "(STUBBED) called. crs_buffer_ptr=0x%08X, crs_size=0x%08X, address=0x%08X, value=0x%08X, process=0x%08X", |  | ||||||
|                 crs_buffer_ptr, crs_size, address, value, process); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * LDR_RO::LoadCRR service function |  | ||||||
|  *  Inputs: |  | ||||||
|  *      1 : CRS buffer pointer |  | ||||||
|  *      2 : CRS Size |  | ||||||
|  *      3 : Value, must be zero |  | ||||||
|  *      4 : KProcess handle |  | ||||||
|  *  Outputs: |  | ||||||
|  *      0 : Return header |  | ||||||
|  *      1 : Result of function, 0 on success, otherwise error code |  | ||||||
|  */ |  | ||||||
| static void LoadCRR(Service::Interface* self) { |  | ||||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); |  | ||||||
|     u32 crs_buffer_ptr = cmd_buff[1]; |  | ||||||
|     u32 crs_size       = cmd_buff[2]; |  | ||||||
|     u32 value          = cmd_buff[3]; |  | ||||||
|     u32 process        = cmd_buff[4]; |  | ||||||
| 
 |  | ||||||
|     if (value != 0) { |  | ||||||
|         LOG_ERROR(Service_LDR, "This value should be zero, but is actually %u!", value); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // TODO(purpasmart96): Verify return header on HW
 |  | ||||||
| 
 |  | ||||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; // No error
 |  | ||||||
| 
 |  | ||||||
|     LOG_WARNING(Service_LDR, "(STUBBED) called. crs_buffer_ptr=0x%08X, crs_size=0x%08X, value=0x%08X, process=0x%08X", |  | ||||||
|                 crs_buffer_ptr, crs_size, value, process); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| const Interface::FunctionInfo FunctionTable[] = { |  | ||||||
|     {0x000100C2, Initialize,            "Initialize"}, |  | ||||||
|     {0x00020082, LoadCRR,               "LoadCRR"}, |  | ||||||
|     {0x00030042, nullptr,               "UnloadCCR"}, |  | ||||||
|     {0x000402C2, nullptr,               "LoadExeCRO"}, |  | ||||||
|     {0x000500C2, nullptr,               "LoadCROSymbols"}, |  | ||||||
|     {0x00060042, nullptr,               "CRO_Load?"}, |  | ||||||
|     {0x00070042, nullptr,               "LoadCROSymbols"}, |  | ||||||
|     {0x00080042, nullptr,               "Shutdown"}, |  | ||||||
|     {0x000902C2, nullptr,               "LoadExeCRO_New?"}, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 |  | ||||||
| // Interface class
 |  | ||||||
| 
 |  | ||||||
| Interface::Interface() { |  | ||||||
|     Register(FunctionTable); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| } // namespace
 |  | ||||||
							
								
								
									
										1477
									
								
								src/core/hle/service/ldr_ro/cro_helper.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1477
									
								
								src/core/hle/service/ldr_ro/cro_helper.cpp
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										691
									
								
								src/core/hle/service/ldr_ro/cro_helper.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										691
									
								
								src/core/hle/service/ldr_ro/cro_helper.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,691 @@ | ||||||
|  | // Copyright 2016 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <array> | ||||||
|  | #include <tuple> | ||||||
|  | 
 | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "common/swap.h" | ||||||
|  | 
 | ||||||
|  | #include "core/memory.h" | ||||||
|  | #include "core/hle/result.h" | ||||||
|  | 
 | ||||||
|  | ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | // Namespace LDR_RO
 | ||||||
|  | 
 | ||||||
|  | namespace LDR_RO { | ||||||
|  | 
 | ||||||
|  | // GCC versions < 5.0 do not implement std::is_trivially_copyable.
 | ||||||
|  | // Excluding MSVC because it has weird behaviour for std::is_trivially_copyable.
 | ||||||
|  | #if (__GNUC__ >= 5) || defined(__clang__) | ||||||
|  |     #define ASSERT_CRO_STRUCT(name, size) \ | ||||||
|  |         static_assert(std::is_standard_layout<name>::value, "CRO structure " #name " doesn't use standard layout"); \ | ||||||
|  |         static_assert(std::is_trivially_copyable<name>::value, "CRO structure " #name " isn't trivially copyable"); \ | ||||||
|  |         static_assert(sizeof(name) == (size), "Unexpected struct size for CRO structure " #name) | ||||||
|  | #else | ||||||
|  |     #define ASSERT_CRO_STRUCT(name, size) \ | ||||||
|  |         static_assert(std::is_standard_layout<name>::value, "CRO structure " #name " doesn't use standard layout"); \ | ||||||
|  |         static_assert(sizeof(name) == (size), "Unexpected struct size for CRO structure " #name) | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | static constexpr u32 CRO_HEADER_SIZE = 0x138; | ||||||
|  | static constexpr u32 CRO_HASH_SIZE = 0x80; | ||||||
|  | 
 | ||||||
|  | /// Represents a loaded module (CRO) with interfaces manipulating it.
 | ||||||
|  | class CROHelper final { | ||||||
|  | public: | ||||||
|  |     explicit CROHelper(VAddr cro_address) : module_address(cro_address) { | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     std::string ModuleName() const { | ||||||
|  |         return Memory::ReadCString(GetField(ModuleNameOffset), GetField(ModuleNameSize)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     u32 GetFileSize() const { | ||||||
|  |         return GetField(FileSize); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Rebases the module according to its address. | ||||||
|  |      * @param crs_address the virtual address of the static module | ||||||
|  |      * @param cro_size the size of the CRO file | ||||||
|  |      * @param data_segment_address buffer address for .data segment | ||||||
|  |      * @param data_segment_size the buffer size for .data segment | ||||||
|  |      * @param bss_segment_address the buffer address for .bss segment | ||||||
|  |      * @param bss_segment_size the buffer size for .bss segment | ||||||
|  |      * @param is_crs true if the module itself is the static module | ||||||
|  |      * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||||||
|  |      */ | ||||||
|  |     ResultCode Rebase(VAddr crs_address, u32 cro_size, | ||||||
|  |         VAddr data_segment_addresss, u32 data_segment_size, | ||||||
|  |         VAddr bss_segment_address, u32 bss_segment_size, bool is_crs); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Unrebases the module. | ||||||
|  |      * @param is_crs true if the module itself is the static module | ||||||
|  |      */ | ||||||
|  |     void Unrebase(bool is_crs); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Verifies module hash by CRR. | ||||||
|  |      * @param cro_size the size of the CRO | ||||||
|  |      * @param crr the virtual address of the CRR | ||||||
|  |      * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||||||
|  |      */ | ||||||
|  |     ResultCode VerifyHash(u32 cro_size, VAddr crr) const; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Links this module with all registered auto-link module. | ||||||
|  |      * @param crs_address the virtual address of the static module | ||||||
|  |      * @param link_on_load_bug_fix true if links when loading and fixes the bug | ||||||
|  |      * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||||||
|  |      */ | ||||||
|  |     ResultCode Link(VAddr crs_address, bool link_on_load_bug_fix); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Unlinks this module with other modules. | ||||||
|  |      * @param crs_address the virtual address of the static module | ||||||
|  |      * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||||||
|  |      */ | ||||||
|  |     ResultCode Unlink(VAddr crs_address); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Clears all relocations to zero. | ||||||
|  |      * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||||||
|  |      */ | ||||||
|  |     ResultCode ClearRelocations(); | ||||||
|  | 
 | ||||||
|  |     /// Initialize this module as the static module (CRS)
 | ||||||
|  |     void InitCRS(); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Registers this module and adds it to the module list. | ||||||
|  |      * @param crs_address the virtual address of the static module | ||||||
|  |      * @auto_link whether to register as an auto link module | ||||||
|  |      */ | ||||||
|  |     void Register(VAddr crs_address, bool auto_link); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Unregisters this module and removes from the module list. | ||||||
|  |      * @param crs_address the virtual address of the static module | ||||||
|  |      */ | ||||||
|  |     void Unregister(VAddr crs_address); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Gets the end of reserved data according to the fix level. | ||||||
|  |      * @param fix_level fix level from 0 to 3 | ||||||
|  |      * @returns the end of reserved data. | ||||||
|  |      */ | ||||||
|  |     u32 GetFixEnd(u32 fix_level) const; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Zeros offsets to cropped data according to the fix level and marks as fixed. | ||||||
|  |      * @param fix_level fix level from 0 to 3 | ||||||
|  |      * @returns page-aligned size of the module after fixing. | ||||||
|  |      */ | ||||||
|  |     u32 Fix(u32 fix_level); | ||||||
|  | 
 | ||||||
|  |     bool IsFixed() const { | ||||||
|  |         return GetField(Magic) == MAGIC_FIXD; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     u32 GetFixedSize() const { | ||||||
|  |         return GetField(FixedSize); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool IsLoaded() const; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Gets the page address and size of the code segment. | ||||||
|  |      * @returns a tuple of (address, size); (0, 0) if the code segment doesn't exist. | ||||||
|  |      */ | ||||||
|  |     std::tuple<VAddr, u32> GetExecutablePages() const; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     const VAddr module_address; ///< the virtual address of this module
 | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Each item in this enum represents a u32 field in the header begin from address+0x80, successively. | ||||||
|  |      * We don't directly use a struct here, to avoid GetPointer, reinterpret_cast, or Read/WriteBlock repeatedly. | ||||||
|  |      */ | ||||||
|  |     enum HeaderField { | ||||||
|  |         Magic = 0, | ||||||
|  |         NameOffset, | ||||||
|  |         NextCRO, | ||||||
|  |         PreviousCRO, | ||||||
|  |         FileSize, | ||||||
|  |         BssSize, | ||||||
|  |         FixedSize, | ||||||
|  |         UnknownZero, | ||||||
|  |         UnkSegmentTag, | ||||||
|  |         OnLoadSegmentTag, | ||||||
|  |         OnExitSegmentTag, | ||||||
|  |         OnUnresolvedSegmentTag, | ||||||
|  | 
 | ||||||
|  |         CodeOffset, | ||||||
|  |         CodeSize, | ||||||
|  |         DataOffset, | ||||||
|  |         DataSize, | ||||||
|  |         ModuleNameOffset, | ||||||
|  |         ModuleNameSize, | ||||||
|  |         SegmentTableOffset, | ||||||
|  |         SegmentNum, | ||||||
|  | 
 | ||||||
|  |         ExportNamedSymbolTableOffset, | ||||||
|  |         ExportNamedSymbolNum, | ||||||
|  |         ExportIndexedSymbolTableOffset, | ||||||
|  |         ExportIndexedSymbolNum, | ||||||
|  |         ExportStringsOffset, | ||||||
|  |         ExportStringsSize, | ||||||
|  |         ExportTreeTableOffset, | ||||||
|  |         ExportTreeNum, | ||||||
|  | 
 | ||||||
|  |         ImportModuleTableOffset, | ||||||
|  |         ImportModuleNum, | ||||||
|  |         ExternalRelocationTableOffset, | ||||||
|  |         ExternalRelocationNum, | ||||||
|  |         ImportNamedSymbolTableOffset, | ||||||
|  |         ImportNamedSymbolNum, | ||||||
|  |         ImportIndexedSymbolTableOffset, | ||||||
|  |         ImportIndexedSymbolNum, | ||||||
|  |         ImportAnonymousSymbolTableOffset, | ||||||
|  |         ImportAnonymousSymbolNum, | ||||||
|  |         ImportStringsOffset, | ||||||
|  |         ImportStringsSize, | ||||||
|  | 
 | ||||||
|  |         StaticAnonymousSymbolTableOffset, | ||||||
|  |         StaticAnonymousSymbolNum, | ||||||
|  |         InternalRelocationTableOffset, | ||||||
|  |         InternalRelocationNum, | ||||||
|  |         StaticRelocationTableOffset, | ||||||
|  |         StaticRelocationNum, | ||||||
|  |         Fix0Barrier, | ||||||
|  | 
 | ||||||
|  |         Fix3Barrier = ExportNamedSymbolTableOffset, | ||||||
|  |         Fix2Barrier = ImportModuleTableOffset, | ||||||
|  |         Fix1Barrier = StaticAnonymousSymbolTableOffset, | ||||||
|  |     }; | ||||||
|  |     static_assert(Fix0Barrier == (CRO_HEADER_SIZE - CRO_HASH_SIZE) / 4, "CRO Header fields are wrong!"); | ||||||
|  | 
 | ||||||
|  |     enum class SegmentType : u32 { | ||||||
|  |         Code   = 0, | ||||||
|  |         ROData = 1, | ||||||
|  |         Data   = 2, | ||||||
|  |         BSS    = 3, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Identifies a program location inside of a segment. | ||||||
|  |      * Required to refer to program locations because individual segments may be relocated independently of each other. | ||||||
|  |      */ | ||||||
|  |     union SegmentTag { | ||||||
|  |         u32_le raw; | ||||||
|  |         BitField<0, 4, u32_le> segment_index; | ||||||
|  |         BitField<4, 28, u32_le> offset_into_segment; | ||||||
|  | 
 | ||||||
|  |         SegmentTag() = default; | ||||||
|  |         explicit SegmentTag(u32 raw_) : raw(raw_) {} | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     /// Information of a segment in this module.
 | ||||||
|  |     struct SegmentEntry { | ||||||
|  |         u32_le offset; | ||||||
|  |         u32_le size; | ||||||
|  |         SegmentType type; | ||||||
|  | 
 | ||||||
|  |         static constexpr HeaderField TABLE_OFFSET_FIELD = SegmentTableOffset; | ||||||
|  |     }; | ||||||
|  |     ASSERT_CRO_STRUCT(SegmentEntry, 12); | ||||||
|  | 
 | ||||||
|  |     /// Identifies a named symbol exported from this module.
 | ||||||
|  |     struct ExportNamedSymbolEntry { | ||||||
|  |         u32_le name_offset;         // pointing to a substring in ExportStrings
 | ||||||
|  |         SegmentTag symbol_position; // to self's segment
 | ||||||
|  | 
 | ||||||
|  |         static constexpr HeaderField TABLE_OFFSET_FIELD = ExportNamedSymbolTableOffset; | ||||||
|  |     }; | ||||||
|  |     ASSERT_CRO_STRUCT(ExportNamedSymbolEntry, 8); | ||||||
|  | 
 | ||||||
|  |     /// Identifies an indexed symbol exported from this module.
 | ||||||
|  |     struct ExportIndexedSymbolEntry { | ||||||
|  |         SegmentTag symbol_position; // to self's segment
 | ||||||
|  | 
 | ||||||
|  |         static constexpr HeaderField TABLE_OFFSET_FIELD = ExportIndexedSymbolTableOffset; | ||||||
|  |     }; | ||||||
|  |     ASSERT_CRO_STRUCT(ExportIndexedSymbolEntry, 4); | ||||||
|  | 
 | ||||||
|  |     /// A tree node in the symbol lookup tree.
 | ||||||
|  |     struct ExportTreeEntry { | ||||||
|  |         u16_le test_bit; // bit address into the name to test
 | ||||||
|  |         union Child { | ||||||
|  |             u16_le raw; | ||||||
|  |             BitField<0, 15, u16_le> next_index; | ||||||
|  |             BitField<15, 1, u16_le> is_end; | ||||||
|  |         } left, right; | ||||||
|  |         u16_le export_table_index; // index of an ExportNamedSymbolEntry
 | ||||||
|  | 
 | ||||||
|  |         static constexpr HeaderField TABLE_OFFSET_FIELD = ExportTreeTableOffset; | ||||||
|  |     }; | ||||||
|  |     ASSERT_CRO_STRUCT(ExportTreeEntry, 8); | ||||||
|  | 
 | ||||||
|  |     /// Identifies a named symbol imported from another module.
 | ||||||
|  |     struct ImportNamedSymbolEntry { | ||||||
|  |         u32_le name_offset;             // pointing to a substring in ImportStrings
 | ||||||
|  |         u32_le relocation_batch_offset; // pointing to a relocation batch in ExternalRelocationTable
 | ||||||
|  | 
 | ||||||
|  |         static constexpr HeaderField TABLE_OFFSET_FIELD = ImportNamedSymbolTableOffset; | ||||||
|  |     }; | ||||||
|  |     ASSERT_CRO_STRUCT(ImportNamedSymbolEntry, 8); | ||||||
|  | 
 | ||||||
|  |     /// Identifies an indexed symbol imported from another module.
 | ||||||
|  |     struct ImportIndexedSymbolEntry { | ||||||
|  |         u32_le index;                   // index of an ExportIndexedSymbolEntry in the exporting module
 | ||||||
|  |         u32_le relocation_batch_offset; // pointing to a relocation batch in ExternalRelocationTable
 | ||||||
|  | 
 | ||||||
|  |         static constexpr HeaderField TABLE_OFFSET_FIELD = ImportIndexedSymbolTableOffset; | ||||||
|  |     }; | ||||||
|  |     ASSERT_CRO_STRUCT(ImportIndexedSymbolEntry, 8); | ||||||
|  | 
 | ||||||
|  |     /// Identifies an anonymous symbol imported from another module.
 | ||||||
|  |     struct ImportAnonymousSymbolEntry { | ||||||
|  |         SegmentTag symbol_position;      // in the exporting segment
 | ||||||
|  |         u32_le relocation_batch_offset;  // pointing to a relocation batch in ExternalRelocationTable
 | ||||||
|  | 
 | ||||||
|  |         static constexpr HeaderField TABLE_OFFSET_FIELD = ImportAnonymousSymbolTableOffset; | ||||||
|  |     }; | ||||||
|  |     ASSERT_CRO_STRUCT(ImportAnonymousSymbolEntry, 8); | ||||||
|  | 
 | ||||||
|  |     /// Information of a imported module and symbols imported from it.
 | ||||||
|  |     struct ImportModuleEntry { | ||||||
|  |         u32_le name_offset;                          // pointing to a substring in ImportStrings
 | ||||||
|  |         u32_le import_indexed_symbol_table_offset;   // pointing to a subtable in ImportIndexedSymbolTable
 | ||||||
|  |         u32_le import_indexed_symbol_num; | ||||||
|  |         u32_le import_anonymous_symbol_table_offset; // pointing to a subtable in ImportAnonymousSymbolTable
 | ||||||
|  |         u32_le import_anonymous_symbol_num; | ||||||
|  | 
 | ||||||
|  |         static constexpr HeaderField TABLE_OFFSET_FIELD = ImportModuleTableOffset; | ||||||
|  | 
 | ||||||
|  |         void GetImportIndexedSymbolEntry(u32 index, ImportIndexedSymbolEntry& entry) { | ||||||
|  |             Memory::ReadBlock(import_indexed_symbol_table_offset + index * sizeof(ImportIndexedSymbolEntry), | ||||||
|  |                 &entry, sizeof(ImportIndexedSymbolEntry)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         void GetImportAnonymousSymbolEntry(u32 index, ImportAnonymousSymbolEntry& entry) { | ||||||
|  |             Memory::ReadBlock(import_anonymous_symbol_table_offset + index * sizeof(ImportAnonymousSymbolEntry), | ||||||
|  |                 &entry, sizeof(ImportAnonymousSymbolEntry)); | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |     ASSERT_CRO_STRUCT(ImportModuleEntry, 20); | ||||||
|  | 
 | ||||||
|  |     enum class RelocationType : u8 { | ||||||
|  |         Nothing                = 0, | ||||||
|  |         AbsoluteAddress        = 2, | ||||||
|  |         RelativeAddress        = 3, | ||||||
|  |         ThumbBranch            = 10, | ||||||
|  |         ArmBranch              = 28, | ||||||
|  |         ModifyArmBranch        = 29, | ||||||
|  |         AbsoluteAddress2       = 38, | ||||||
|  |         AlignedRelativeAddress = 42, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     struct RelocationEntry { | ||||||
|  |         SegmentTag target_position; // to self's segment as an ExternalRelocationEntry; to static module segment as a StaticRelocationEntry
 | ||||||
|  |         RelocationType type; | ||||||
|  |         u8 is_batch_end; | ||||||
|  |         u8 is_batch_resolved;       // set at a batch beginning if the batch is resolved
 | ||||||
|  |         INSERT_PADDING_BYTES(1); | ||||||
|  |         u32_le addend; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     /// Identifies a normal cross-module relocation.
 | ||||||
|  |     struct ExternalRelocationEntry : RelocationEntry { | ||||||
|  |         static constexpr HeaderField TABLE_OFFSET_FIELD = ExternalRelocationTableOffset; | ||||||
|  |     }; | ||||||
|  |     ASSERT_CRO_STRUCT(ExternalRelocationEntry, 12); | ||||||
|  | 
 | ||||||
|  |     /// Identifies a special static relocation (no game is known using this).
 | ||||||
|  |     struct StaticRelocationEntry : RelocationEntry { | ||||||
|  |         static constexpr HeaderField TABLE_OFFSET_FIELD = StaticRelocationTableOffset; | ||||||
|  |     }; | ||||||
|  |     ASSERT_CRO_STRUCT(StaticRelocationEntry, 12); | ||||||
|  | 
 | ||||||
|  |     /// Identifies a in-module relocation.
 | ||||||
|  |     struct InternalRelocationEntry { | ||||||
|  |         SegmentTag target_position; // to self's segment
 | ||||||
|  |         RelocationType type; | ||||||
|  |         u8 symbol_segment; | ||||||
|  |         INSERT_PADDING_BYTES(2); | ||||||
|  |         u32_le addend; | ||||||
|  | 
 | ||||||
|  |         static constexpr HeaderField TABLE_OFFSET_FIELD = InternalRelocationTableOffset; | ||||||
|  |     }; | ||||||
|  |     ASSERT_CRO_STRUCT(InternalRelocationEntry, 12); | ||||||
|  | 
 | ||||||
|  |     /// Identifies a special static anonymous symbol (no game is known using this).
 | ||||||
|  |     struct StaticAnonymousSymbolEntry { | ||||||
|  |         SegmentTag symbol_position;      // to self's segment
 | ||||||
|  |         u32_le relocation_batch_offset;  // pointing to a relocation batch in StaticRelocationTable
 | ||||||
|  | 
 | ||||||
|  |         static constexpr HeaderField TABLE_OFFSET_FIELD = StaticAnonymousSymbolTableOffset; | ||||||
|  |     }; | ||||||
|  |     ASSERT_CRO_STRUCT(StaticAnonymousSymbolEntry, 8); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Entry size of each table, from Code to StaticRelocationTable. | ||||||
|  |      * Byte string contents (such as Code) are treated with entries of size 1. | ||||||
|  |      * This is used for verifying the size of each table and calculating the fix end. | ||||||
|  |      */ | ||||||
|  |     static const std::array<int, 17> ENTRY_SIZE; | ||||||
|  | 
 | ||||||
|  |     /// The offset field of the table where to crop for each fix level
 | ||||||
|  |     static const std::array<HeaderField, 4> FIX_BARRIERS; | ||||||
|  | 
 | ||||||
|  |     static constexpr u32 MAGIC_CRO0 = 0x304F5243; | ||||||
|  |     static constexpr u32 MAGIC_FIXD = 0x44584946; | ||||||
|  | 
 | ||||||
|  |     VAddr Field(HeaderField field) const { | ||||||
|  |         return module_address + CRO_HASH_SIZE + field * 4; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     u32 GetField(HeaderField field) const { | ||||||
|  |         return Memory::Read32(Field(field)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void SetField(HeaderField field, u32 value) { | ||||||
|  |         Memory::Write32(Field(field), value); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Reads an entry in one of module tables. | ||||||
|  |      * @param index index of the entry | ||||||
|  |      * @param data where to put the read entry | ||||||
|  |      * @note the entry type must have the static member TABLE_OFFSET_FIELD | ||||||
|  |      *       indicating which table the entry is in. | ||||||
|  |      */ | ||||||
|  |     template <typename T> | ||||||
|  |     void GetEntry(std::size_t index, T& data) const { | ||||||
|  |         Memory::ReadBlock(GetField(T::TABLE_OFFSET_FIELD) + index * sizeof(T), &data, sizeof(T)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Writes an entry to one of module tables. | ||||||
|  |      * @param index index of the entry | ||||||
|  |      * @param data the entry data to write | ||||||
|  |      * @note the entry type must have the static member TABLE_OFFSET_FIELD | ||||||
|  |      *       indicating which table the entry is in. | ||||||
|  |      */ | ||||||
|  |     template <typename T> | ||||||
|  |     void SetEntry(std::size_t index, const T& data) { | ||||||
|  |         Memory::WriteBlock(GetField(T::TABLE_OFFSET_FIELD) + index * sizeof(T), &data, sizeof(T)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Converts a segment tag to virtual address in this module. | ||||||
|  |      * @param segment_tag the segment tag to convert | ||||||
|  |      * @returns VAddr the virtual address the segment tag points to; 0 if invalid. | ||||||
|  |      */ | ||||||
|  |     VAddr SegmentTagToAddress(SegmentTag segment_tag) const; | ||||||
|  | 
 | ||||||
|  |     VAddr NextModule() const { | ||||||
|  |         return GetField(NextCRO); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     VAddr PreviousModule() const { | ||||||
|  |         return GetField(PreviousCRO); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void SetNextModule(VAddr next) { | ||||||
|  |         SetField(NextCRO, next); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void SetPreviousModule(VAddr previous) { | ||||||
|  |         SetField(PreviousCRO, previous); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * A helper function iterating over all registered auto-link modules, including the static module. | ||||||
|  |      * @param crs_address the virtual address of the static module | ||||||
|  |      * @param func a function object to operate on a module. It accepts one parameter | ||||||
|  |      *        CROHelper and returns ResultVal<bool>. It should return true to continue the iteration, | ||||||
|  |      *        false to stop the iteration, or an error code (which will also stop the iteration). | ||||||
|  |      * @returns ResultCode indicating the result of the operation, RESULT_SUCCESS if all iteration success, | ||||||
|  |      *         otherwise error code of the last iteration. | ||||||
|  |      */ | ||||||
|  |     template <typename FunctionObject> | ||||||
|  |     static ResultCode ForEachAutoLinkCRO(VAddr crs_address, FunctionObject func) { | ||||||
|  |         VAddr current = crs_address; | ||||||
|  |         while (current != 0) { | ||||||
|  |             CROHelper cro(current); | ||||||
|  |             CASCADE_RESULT(bool next, func(cro)); | ||||||
|  |             if (!next) | ||||||
|  |                 break; | ||||||
|  |             current = cro.NextModule(); | ||||||
|  |         } | ||||||
|  |         return RESULT_SUCCESS; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Applies a relocation | ||||||
|  |      * @param target_address where to apply the relocation | ||||||
|  |      * @param relocation_type the type of the relocation | ||||||
|  |      * @param addend address addend applied to the relocated symbol | ||||||
|  |      * @param symbol_address the symbol address to be relocated with | ||||||
|  |      * @param target_future_address the future address of the target. | ||||||
|  |      *        Usually equals to target_address, but will be different for a target in .data segment | ||||||
|  |      * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||||||
|  |      */ | ||||||
|  |     ResultCode ApplyRelocation(VAddr target_address, RelocationType relocation_type, | ||||||
|  |         u32 addend, u32 symbol_address, u32 target_future_address); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Clears a relocation to zero | ||||||
|  |      * @param target_address where to apply the relocation | ||||||
|  |      * @param relocation_type the type of the relocation | ||||||
|  |      * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||||||
|  |      */ | ||||||
|  |     ResultCode ClearRelocation(VAddr target_address, RelocationType relocation_type); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Applies or resets a batch of relocations | ||||||
|  |      * @param batch the virtual address of the first relocation in the batch | ||||||
|  |      * @param symbol_address the symbol address to be relocated with | ||||||
|  |      * @param reset false to set the batch to resolved state, true to reset the batch to unresolved state | ||||||
|  |      * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||||||
|  |      */ | ||||||
|  |     ResultCode ApplyRelocationBatch(VAddr batch, u32 symbol_address, bool reset = false); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Finds an exported named symbol in this module. | ||||||
|  |      * @param name the name of the symbol to find | ||||||
|  |      * @return VAddr the virtual address of the symbol; 0 if not found. | ||||||
|  |      */ | ||||||
|  |     VAddr FindExportNamedSymbol(const std::string& name) const; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Rebases offsets in module header according to module address. | ||||||
|  |      * @param cro_size the size of the CRO file | ||||||
|  |      * @returns ResultCode RESULT_SUCCESS if all offsets are verified as valid, otherwise error code. | ||||||
|  |      */ | ||||||
|  |     ResultCode RebaseHeader(u32 cro_size); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Rebases offsets in segment table according to module address. | ||||||
|  |      * @param cro_size the size of the CRO file | ||||||
|  |      * @param data_segment_address the buffer address for .data segment | ||||||
|  |      * @param data_segment_size the buffer size for .data segment | ||||||
|  |      * @param bss_segment_address the buffer address for .bss segment | ||||||
|  |      * @param bss_segment_size the buffer size for .bss segment | ||||||
|  |      * @returns ResultVal<VAddr> with the virtual address of .data segment in CRO. | ||||||
|  |      */ | ||||||
|  |     ResultVal<VAddr> RebaseSegmentTable(u32 cro_size, | ||||||
|  |         VAddr data_segment_address, u32 data_segment_size, | ||||||
|  |         VAddr bss_segment_address, u32 bss_segment_size); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Rebases offsets in exported named symbol table according to module address. | ||||||
|  |      * @returns ResultCode RESULT_SUCCESS if all offsets are verified as valid, otherwise error code. | ||||||
|  |      */ | ||||||
|  |     ResultCode RebaseExportNamedSymbolTable(); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Verifies indices in export tree table. | ||||||
|  |      * @returns ResultCode RESULT_SUCCESS if all indices are verified as valid, otherwise error code. | ||||||
|  |      */ | ||||||
|  |     ResultCode VerifyExportTreeTable() const; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Rebases offsets in exported module table according to module address. | ||||||
|  |      * @returns ResultCode RESULT_SUCCESS if all offsets are verified as valid, otherwise error code. | ||||||
|  |      */ | ||||||
|  |     ResultCode RebaseImportModuleTable(); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Rebases offsets in imported named symbol table according to module address. | ||||||
|  |      * @returns ResultCode RESULT_SUCCESS if all offsets are verified as valid, otherwise error code. | ||||||
|  |      */ | ||||||
|  |     ResultCode RebaseImportNamedSymbolTable(); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Rebases offsets in imported indexed symbol table according to module address. | ||||||
|  |      * @returns ResultCode RESULT_SUCCESS if all offsets are verified as valid, otherwise error code. | ||||||
|  |      */ | ||||||
|  |     ResultCode RebaseImportIndexedSymbolTable(); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Rebases offsets in imported anonymous symbol table according to module address. | ||||||
|  |      * @returns ResultCode RESULT_SUCCESS if all offsets are verified as valid, otherwise error code. | ||||||
|  |      */ | ||||||
|  |     ResultCode RebaseImportAnonymousSymbolTable(); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Gets the address of OnUnresolved function in this module. | ||||||
|  |      * Used as the applied symbol for reset relocation. | ||||||
|  |      * @returns the virtual address of OnUnresolved. 0 if not provided. | ||||||
|  |      */ | ||||||
|  |     VAddr GetOnUnresolvedAddress(); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Resets all external relocations to unresolved state. | ||||||
|  |      * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||||||
|  |      */ | ||||||
|  |     ResultCode ResetExternalRelocations(); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Clears all external relocations to zero. | ||||||
|  |      * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||||||
|  |      */ | ||||||
|  |     ResultCode ClearExternalRelocations(); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Applies all static anonymous symbol to the static module. | ||||||
|  |      * @param crs_address the virtual address of the static module | ||||||
|  |      * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||||||
|  |      */ | ||||||
|  |     ResultCode ApplyStaticAnonymousSymbolToCRS(VAddr crs_address); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Applies all internal relocations to the module itself. | ||||||
|  |      * @param old_data_segment_address the virtual address of data segment in CRO buffer | ||||||
|  |      * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||||||
|  |      */ | ||||||
|  |     ResultCode ApplyInternalRelocations(u32 old_data_segment_address); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Clears all internal relocations to zero. | ||||||
|  |      * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||||||
|  |      */ | ||||||
|  |     ResultCode ClearInternalRelocations(); | ||||||
|  | 
 | ||||||
|  |     /// Unrebases offsets in imported anonymous symbol table
 | ||||||
|  |     void UnrebaseImportAnonymousSymbolTable(); | ||||||
|  | 
 | ||||||
|  |     /// Unrebases offsets in imported indexed symbol table
 | ||||||
|  |     void UnrebaseImportIndexedSymbolTable(); | ||||||
|  | 
 | ||||||
|  |     /// Unrebases offsets in imported named symbol table
 | ||||||
|  |     void UnrebaseImportNamedSymbolTable(); | ||||||
|  | 
 | ||||||
|  |     /// Unrebases offsets in imported module table
 | ||||||
|  |     void UnrebaseImportModuleTable(); | ||||||
|  | 
 | ||||||
|  |     /// Unrebases offsets in exported named symbol table
 | ||||||
|  |     void UnrebaseExportNamedSymbolTable(); | ||||||
|  | 
 | ||||||
|  |     /// Unrebases offsets in segment table
 | ||||||
|  |     void UnrebaseSegmentTable(); | ||||||
|  | 
 | ||||||
|  |     /// Unrebases offsets in module header
 | ||||||
|  |     void UnrebaseHeader(); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Looks up all imported named symbols of this module in all registered auto-link modules, and resolves them if found. | ||||||
|  |      * @param crs_address the virtual address of the static module | ||||||
|  |      * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||||||
|  |      */ | ||||||
|  |     ResultCode ApplyImportNamedSymbol(VAddr crs_address); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Resets all imported named symbols of this module to unresolved state. | ||||||
|  |      * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||||||
|  |      */ | ||||||
|  |     ResultCode ResetImportNamedSymbol(); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Resets all imported indexed symbols of this module to unresolved state. | ||||||
|  |      * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||||||
|  |      */ | ||||||
|  |     ResultCode ResetImportIndexedSymbol(); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Resets all imported anonymous symbols of this module to unresolved state. | ||||||
|  |      * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||||||
|  |      */ | ||||||
|  |     ResultCode ResetImportAnonymousSymbol(); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Finds registered auto-link modules that this module imports, and resolves indexed and anonymous symbols exported by them. | ||||||
|  |      * @param crs_address the virtual address of the static module | ||||||
|  |      * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||||||
|  |      */ | ||||||
|  |     ResultCode ApplyModuleImport(VAddr crs_address); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Resolves target module's imported named symbols that exported by this module. | ||||||
|  |      * @param target the module to resolve. | ||||||
|  |      * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||||||
|  |      */ | ||||||
|  |     ResultCode ApplyExportNamedSymbol(CROHelper target); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Resets target's named symbols imported from this module to unresolved state. | ||||||
|  |      * @param target the module to reset. | ||||||
|  |      * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||||||
|  |      */ | ||||||
|  |     ResultCode ResetExportNamedSymbol(CROHelper target); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Resolves imported indexed and anonymous symbols in the target module which imports this module. | ||||||
|  |      * @param target the module to resolve. | ||||||
|  |      * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||||||
|  |      */ | ||||||
|  |     ResultCode ApplyModuleExport(CROHelper target); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Resets target's indexed and anonymous symbol imported from this module to unresolved state. | ||||||
|  |      * @param target the module to reset. | ||||||
|  |      * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||||||
|  |      */ | ||||||
|  |     ResultCode ResetModuleExport(CROHelper target); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Resolves the exit function in this module | ||||||
|  |      * @param crs_address the virtual address of the static module. | ||||||
|  |      * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. | ||||||
|  |      */ | ||||||
|  |     ResultCode ApplyExitRelocations(VAddr crs_address); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace
 | ||||||
							
								
								
									
										748
									
								
								src/core/hle/service/ldr_ro/ldr_ro.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										748
									
								
								src/core/hle/service/ldr_ro/ldr_ro.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,748 @@ | ||||||
|  | // Copyright 2014 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include "common/alignment.h" | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "common/logging/log.h" | ||||||
|  | 
 | ||||||
|  | #include "core/arm/arm_interface.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 LDR_RO
 | ||||||
|  | 
 | ||||||
|  | namespace LDR_RO { | ||||||
|  | 
 | ||||||
|  | static const ResultCode ERROR_ALREADY_INITIALIZED =   // 0xD9612FF9
 | ||||||
|  |     ResultCode(ErrorDescription::AlreadyInitialized,         ErrorModule::RO, ErrorSummary::Internal,        ErrorLevel::Permanent); | ||||||
|  | static const ResultCode ERROR_NOT_INITIALIZED =       // 0xD9612FF8
 | ||||||
|  |     ResultCode(ErrorDescription::NotInitialized,             ErrorModule::RO, ErrorSummary::Internal,        ErrorLevel::Permanent); | ||||||
|  | static const ResultCode ERROR_BUFFER_TOO_SMALL =      // 0xE0E12C1F
 | ||||||
|  |     ResultCode(static_cast<ErrorDescription>(31),            ErrorModule::RO, ErrorSummary::InvalidArgument, ErrorLevel::Usage); | ||||||
|  | static const ResultCode ERROR_MISALIGNED_ADDRESS =    // 0xD9012FF1
 | ||||||
|  |     ResultCode(ErrorDescription::MisalignedAddress,          ErrorModule::RO, ErrorSummary::WrongArgument,   ErrorLevel::Permanent); | ||||||
|  | static const ResultCode ERROR_MISALIGNED_SIZE =       // 0xD9012FF2
 | ||||||
|  |     ResultCode(ErrorDescription::MisalignedSize,             ErrorModule::RO, ErrorSummary::WrongArgument,   ErrorLevel::Permanent); | ||||||
|  | static const ResultCode ERROR_ILLEGAL_ADDRESS =       // 0xE1612C0F
 | ||||||
|  |     ResultCode(static_cast<ErrorDescription>(15),            ErrorModule::RO, ErrorSummary::Internal,        ErrorLevel::Usage); | ||||||
|  | static const ResultCode ERROR_INVALID_MEMORY_STATE =  // 0xD8A12C08
 | ||||||
|  |     ResultCode(static_cast<ErrorDescription>(8),             ErrorModule::RO, ErrorSummary::InvalidState,    ErrorLevel::Permanent); | ||||||
|  | static const ResultCode ERROR_NOT_LOADED =            // 0xD8A12C0D
 | ||||||
|  |     ResultCode(static_cast<ErrorDescription>(13),            ErrorModule::RO, ErrorSummary::InvalidState,    ErrorLevel::Permanent); | ||||||
|  | static const ResultCode ERROR_INVALID_DESCRIPTOR =    // 0xD9001830
 | ||||||
|  |     ResultCode(ErrorDescription::OS_InvalidBufferDescriptor, ErrorModule::OS, ErrorSummary::WrongArgument,   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() | ||||||
|  |         && 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(Service::Interface* self) { | ||||||
|  |     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||||
|  |     VAddr crs_buffer_ptr = cmd_buff[1]; | ||||||
|  |     u32 crs_size         = cmd_buff[2]; | ||||||
|  |     VAddr crs_address    = cmd_buff[3]; | ||||||
|  |     u32 descriptor       = cmd_buff[4]; | ||||||
|  |     u32 process          = cmd_buff[5]; | ||||||
|  | 
 | ||||||
|  |     LOG_DEBUG(Service_LDR, "called, crs_buffer_ptr=0x%08X, crs_address=0x%08X, crs_size=0x%X, descriptor=0x%08X, process=0x%08X", | ||||||
|  |                 crs_buffer_ptr, crs_address, crs_size, descriptor, process); | ||||||
|  | 
 | ||||||
|  |     if (descriptor != 0) { | ||||||
|  |         LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor); | ||||||
|  |         cmd_buff[0] = IPC::MakeHeader(0, 1, 0); | ||||||
|  |         cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     cmd_buff[0] = IPC::MakeHeader(1, 1, 0); | ||||||
|  | 
 | ||||||
|  |     if (loaded_crs != 0) { | ||||||
|  |         LOG_ERROR(Service_LDR, "Already initialized"); | ||||||
|  |         cmd_buff[1] = ERROR_ALREADY_INITIALIZED.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (crs_size < CRO_HEADER_SIZE) { | ||||||
|  |         LOG_ERROR(Service_LDR, "CRS is too small"); | ||||||
|  |         cmd_buff[1] = ERROR_BUFFER_TOO_SMALL.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (crs_buffer_ptr & Memory::PAGE_MASK) { | ||||||
|  |         LOG_ERROR(Service_LDR, "CRS original address is not aligned"); | ||||||
|  |         cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (crs_address & Memory::PAGE_MASK) { | ||||||
|  |         LOG_ERROR(Service_LDR, "CRS mapping address is not aligned"); | ||||||
|  |         cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (crs_size & Memory::PAGE_MASK) { | ||||||
|  |         LOG_ERROR(Service_LDR, "CRS size is not aligned"); | ||||||
|  |         cmd_buff[1] = ERROR_MISALIGNED_SIZE.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!VerifyBufferState(crs_buffer_ptr, crs_size)) { | ||||||
|  |         LOG_ERROR(Service_LDR, "CRS original buffer is in invalid state"); | ||||||
|  |         cmd_buff[1] = ERROR_INVALID_MEMORY_STATE.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (crs_address < Memory::PROCESS_IMAGE_VADDR || crs_address + crs_size > Memory::PROCESS_IMAGE_VADDR_END) { | ||||||
|  |         LOG_ERROR(Service_LDR, "CRS mapping address is not in the process image region"); | ||||||
|  |         cmd_buff[1] = ERROR_ILLEGAL_ADDRESS.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     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 = Kernel::g_current_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); | ||||||
|  |             cmd_buff[1] = result.raw; | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         result = Kernel::g_current_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); | ||||||
|  |             cmd_buff[1] = result.raw; | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         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); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     CROHelper crs(crs_address); | ||||||
|  |     crs.InitCRS(); | ||||||
|  | 
 | ||||||
|  |     result = crs.Rebase(0, crs_size, 0, 0, 0, 0, true); | ||||||
|  |     if (result.IsError()) { | ||||||
|  |         LOG_ERROR(Service_LDR, "Error rebasing CRS 0x%08X", result.raw); | ||||||
|  |         cmd_buff[1] = result.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     memory_synchronizer.SynchronizeOriginalMemory(); | ||||||
|  | 
 | ||||||
|  |     loaded_crs = crs_address; | ||||||
|  | 
 | ||||||
|  |     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * 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(Service::Interface* self) { | ||||||
|  |     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||||
|  |     u32 crr_buffer_ptr = cmd_buff[1]; | ||||||
|  |     u32 crr_size       = cmd_buff[2]; | ||||||
|  |     u32 descriptor     = cmd_buff[3]; | ||||||
|  |     u32 process        = cmd_buff[4]; | ||||||
|  | 
 | ||||||
|  |     if (descriptor != 0) { | ||||||
|  |         LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor); | ||||||
|  |         cmd_buff[0] = IPC::MakeHeader(0, 1, 0); | ||||||
|  |         cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     cmd_buff[0] = IPC::MakeHeader(2, 1, 0); | ||||||
|  |     cmd_buff[1] = RESULT_SUCCESS.raw; // No error
 | ||||||
|  | 
 | ||||||
|  |     LOG_WARNING(Service_LDR, "(STUBBED) called, crr_buffer_ptr=0x%08X, crr_size=0x%08X, descriptor=0x%08X, process=0x%08X", | ||||||
|  |                 crr_buffer_ptr, crr_size, descriptor, process); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * 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(Service::Interface* self) { | ||||||
|  |     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||||
|  |     u32 crr_buffer_ptr = cmd_buff[1]; | ||||||
|  |     u32 descriptor     = cmd_buff[2]; | ||||||
|  |     u32 process        = cmd_buff[3]; | ||||||
|  | 
 | ||||||
|  |     if (descriptor != 0) { | ||||||
|  |         LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor); | ||||||
|  |         cmd_buff[0] = IPC::MakeHeader(0, 1, 0); | ||||||
|  |         cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     cmd_buff[0] = IPC::MakeHeader(3, 1, 0); | ||||||
|  |     cmd_buff[1] = RESULT_SUCCESS.raw; // No error
 | ||||||
|  | 
 | ||||||
|  |     LOG_WARNING(Service_LDR, "(STUBBED) called, crr_buffer_ptr=0x%08X, descriptor=0x%08X, process=0x%08X", | ||||||
|  |                 crr_buffer_ptr, descriptor, process); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * 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(Service::Interface* self, bool link_on_load_bug_fix) { | ||||||
|  |     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||||
|  |     VAddr cro_buffer_ptr       = cmd_buff[1]; | ||||||
|  |     VAddr cro_address          = cmd_buff[2]; | ||||||
|  |     u32 cro_size               = cmd_buff[3]; | ||||||
|  |     VAddr data_segment_address = cmd_buff[4]; | ||||||
|  |     u32 zero                   = cmd_buff[5]; | ||||||
|  |     u32 data_segment_size      = cmd_buff[6]; | ||||||
|  |     u32 bss_segment_address    = cmd_buff[7]; | ||||||
|  |     u32 bss_segment_size       = cmd_buff[8]; | ||||||
|  |     bool auto_link             = (cmd_buff[9] & 0xFF) != 0; | ||||||
|  |     u32 fix_level              = cmd_buff[10]; | ||||||
|  |     VAddr crr_address          = cmd_buff[11]; | ||||||
|  |     u32 descriptor             = cmd_buff[12]; | ||||||
|  |     u32 process                = cmd_buff[13]; | ||||||
|  | 
 | ||||||
|  |     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, descriptor=0x%08X, process=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, descriptor, process | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |     if (descriptor != 0) { | ||||||
|  |         LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor); | ||||||
|  |         cmd_buff[0] = IPC::MakeHeader(0, 1, 0); | ||||||
|  |         cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     cmd_buff[0] = IPC::MakeHeader(link_on_load_bug_fix ? 9 : 4, 2, 0); | ||||||
|  | 
 | ||||||
|  |     if (loaded_crs == 0) { | ||||||
|  |         LOG_ERROR(Service_LDR, "Not initialized"); | ||||||
|  |         cmd_buff[1] = ERROR_NOT_INITIALIZED.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (cro_size < CRO_HEADER_SIZE) { | ||||||
|  |         LOG_ERROR(Service_LDR, "CRO too small"); | ||||||
|  |         cmd_buff[1] = ERROR_BUFFER_TOO_SMALL.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (cro_buffer_ptr & Memory::PAGE_MASK) { | ||||||
|  |         LOG_ERROR(Service_LDR, "CRO original address is not aligned"); | ||||||
|  |         cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (cro_address & Memory::PAGE_MASK) { | ||||||
|  |         LOG_ERROR(Service_LDR, "CRO mapping address is not aligned"); | ||||||
|  |         cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (cro_size & Memory::PAGE_MASK) { | ||||||
|  |         LOG_ERROR(Service_LDR, "CRO size is not aligned"); | ||||||
|  |         cmd_buff[1] = ERROR_MISALIGNED_SIZE.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!VerifyBufferState(cro_buffer_ptr, cro_size)) { | ||||||
|  |         LOG_ERROR(Service_LDR, "CRO original buffer is in invalid state"); | ||||||
|  |         cmd_buff[1] = ERROR_INVALID_MEMORY_STATE.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (cro_address < Memory::PROCESS_IMAGE_VADDR | ||||||
|  |         || cro_address + cro_size > Memory::PROCESS_IMAGE_VADDR_END) { | ||||||
|  |         LOG_ERROR(Service_LDR, "CRO mapping address is not in the process image region"); | ||||||
|  |         cmd_buff[1] = ERROR_ILLEGAL_ADDRESS.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (zero) { | ||||||
|  |         LOG_ERROR(Service_LDR, "Zero is not zero %d", zero); | ||||||
|  |         cmd_buff[1] = ResultCode(static_cast<ErrorDescription>(29), ErrorModule::RO, ErrorSummary::Internal, ErrorLevel::Usage).raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     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 = Kernel::g_current_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); | ||||||
|  |             cmd_buff[1] = result.raw; | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         result = Kernel::g_current_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); | ||||||
|  |             cmd_buff[1] = result.raw; | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         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); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     CROHelper cro(cro_address); | ||||||
|  | 
 | ||||||
|  |     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); | ||||||
|  |         cmd_buff[1] = result.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     result = cro.Rebase(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); | ||||||
|  |         cmd_buff[1] = result.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     result = cro.Link(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); | ||||||
|  |         cmd_buff[1] = result.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     cro.Register(loaded_crs, auto_link); | ||||||
|  | 
 | ||||||
|  |     u32 fix_size = cro.Fix(fix_level); | ||||||
|  | 
 | ||||||
|  |     memory_synchronizer.SynchronizeOriginalMemory(); | ||||||
|  | 
 | ||||||
|  |     // 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); | ||||||
|  |             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); | ||||||
|  |                 cmd_buff[1] = result.raw; | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Changes the block size
 | ||||||
|  |         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); | ||||||
|  |         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); | ||||||
|  |             cmd_buff[1] = result.raw; | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     Core::g_app_core->ClearInstructionCache(); | ||||||
|  | 
 | ||||||
|  |     LOG_INFO(Service_LDR, "CRO \"%s\" loaded at 0x%08X, fixed_end=0x%08X", | ||||||
|  |         cro.ModuleName().data(), cro_address, cro_address+fix_size); | ||||||
|  | 
 | ||||||
|  |     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||||
|  |     cmd_buff[2] = fix_size; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | template <bool link_on_load_bug_fix> | ||||||
|  | static void LoadCRO(Service::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(Service::Interface* self) { | ||||||
|  |     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||||
|  |     VAddr cro_address      = cmd_buff[1]; | ||||||
|  |     u32 zero               = cmd_buff[2]; | ||||||
|  |     VAddr cro_buffer_ptr   = cmd_buff[3]; | ||||||
|  |     u32 descriptor         = cmd_buff[4]; | ||||||
|  |     u32 process            = cmd_buff[5]; | ||||||
|  | 
 | ||||||
|  |     LOG_DEBUG(Service_LDR, "called, cro_address=0x%08X, zero=%d, cro_buffer_ptr=0x%08X, descriptor=0x%08X, process=0x%08X", | ||||||
|  |         cro_address, zero, cro_buffer_ptr, descriptor, process); | ||||||
|  | 
 | ||||||
|  |     if (descriptor != 0) { | ||||||
|  |         LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor); | ||||||
|  |         cmd_buff[0] = IPC::MakeHeader(0, 1, 0); | ||||||
|  |         cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     CROHelper cro(cro_address); | ||||||
|  | 
 | ||||||
|  |     cmd_buff[0] = IPC::MakeHeader(5, 1, 0); | ||||||
|  | 
 | ||||||
|  |     if (loaded_crs == 0) { | ||||||
|  |         LOG_ERROR(Service_LDR, "Not initialized"); | ||||||
|  |         cmd_buff[1] = ERROR_NOT_INITIALIZED.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (cro_address & Memory::PAGE_MASK) { | ||||||
|  |         LOG_ERROR(Service_LDR, "CRO address is not aligned"); | ||||||
|  |         cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!cro.IsLoaded()) { | ||||||
|  |         LOG_ERROR(Service_LDR, "Invalid or not loaded CRO"); | ||||||
|  |         cmd_buff[1] = ERROR_NOT_LOADED.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     LOG_INFO(Service_LDR, "Unloading CRO \"%s\"", cro.ModuleName().data()); | ||||||
|  | 
 | ||||||
|  |     u32 fixed_size = cro.GetFixedSize(); | ||||||
|  | 
 | ||||||
|  |     cro.Unregister(loaded_crs); | ||||||
|  | 
 | ||||||
|  |     ResultCode result = cro.Unlink(loaded_crs); | ||||||
|  |     if (result.IsError()) { | ||||||
|  |         LOG_ERROR(Service_LDR, "Error unlinking CRO %08X", result.raw); | ||||||
|  |         cmd_buff[1] = result.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // If the module is not fixed, clears all external/internal relocations
 | ||||||
|  |     // to restore the state before loading, so that it can be loaded again(?)
 | ||||||
|  |     if (!cro.IsFixed()) { | ||||||
|  |         result = cro.ClearRelocations(); | ||||||
|  |         if (result.IsError()) { | ||||||
|  |             LOG_ERROR(Service_LDR, "Error clearing relocations %08X", result.raw); | ||||||
|  |             cmd_buff[1] = result.raw; | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     cro.Unrebase(false); | ||||||
|  | 
 | ||||||
|  |     memory_synchronizer.SynchronizeOriginalMemory(); | ||||||
|  | 
 | ||||||
|  |     // 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); | ||||||
|  |         if (result.IsError()) { | ||||||
|  |             LOG_ERROR(Service_LDR, "Error unmapping CRO %08X", result.raw); | ||||||
|  |         } | ||||||
|  |         memory_synchronizer.RemoveMemoryBlock(cro_address, cro_buffer_ptr); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     Core::g_app_core->ClearInstructionCache(); | ||||||
|  | 
 | ||||||
|  |     cmd_buff[1] = result.raw; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * 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(Service::Interface* self) { | ||||||
|  |     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||||
|  |     VAddr cro_address = cmd_buff[1]; | ||||||
|  |     u32 descriptor    = cmd_buff[2]; | ||||||
|  |     u32 process       = cmd_buff[3]; | ||||||
|  | 
 | ||||||
|  |     LOG_DEBUG(Service_LDR, "called, cro_address=0x%08X, descriptor=0x%08X, process=0x%08X", | ||||||
|  |         cro_address, descriptor, process); | ||||||
|  | 
 | ||||||
|  |     if (descriptor != 0) { | ||||||
|  |         LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor); | ||||||
|  |         cmd_buff[0] = IPC::MakeHeader(0, 1, 0); | ||||||
|  |         cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     CROHelper cro(cro_address); | ||||||
|  | 
 | ||||||
|  |     cmd_buff[0] = IPC::MakeHeader(6, 1, 0); | ||||||
|  | 
 | ||||||
|  |     if (loaded_crs == 0) { | ||||||
|  |         LOG_ERROR(Service_LDR, "Not initialized"); | ||||||
|  |         cmd_buff[1] = ERROR_NOT_INITIALIZED.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (cro_address & Memory::PAGE_MASK) { | ||||||
|  |         LOG_ERROR(Service_LDR, "CRO address is not aligned"); | ||||||
|  |         cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!cro.IsLoaded()) { | ||||||
|  |         LOG_ERROR(Service_LDR, "Invalid or not loaded CRO"); | ||||||
|  |         cmd_buff[1] = ERROR_NOT_LOADED.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     LOG_INFO(Service_LDR, "Linking CRO \"%s\"", cro.ModuleName().data()); | ||||||
|  | 
 | ||||||
|  |     ResultCode result = cro.Link(loaded_crs, false); | ||||||
|  |     if (result.IsError()) { | ||||||
|  |         LOG_ERROR(Service_LDR, "Error linking CRO %08X", result.raw); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     memory_synchronizer.SynchronizeOriginalMemory(); | ||||||
|  |     Core::g_app_core->ClearInstructionCache(); | ||||||
|  | 
 | ||||||
|  |     cmd_buff[1] = result.raw; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * 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(Service::Interface* self) { | ||||||
|  |     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||||
|  |     VAddr cro_address = cmd_buff[1]; | ||||||
|  |     u32 descriptor    = cmd_buff[2]; | ||||||
|  |     u32 process       = cmd_buff[3]; | ||||||
|  | 
 | ||||||
|  |     LOG_DEBUG(Service_LDR, "called, cro_address=0x%08X, descriptor=0x%08X, process=0x%08X", | ||||||
|  |         cro_address, descriptor, process); | ||||||
|  | 
 | ||||||
|  |     if (descriptor != 0) { | ||||||
|  |         LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor); | ||||||
|  |         cmd_buff[0] = IPC::MakeHeader(0, 1, 0); | ||||||
|  |         cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     CROHelper cro(cro_address); | ||||||
|  | 
 | ||||||
|  |     cmd_buff[0] = IPC::MakeHeader(7, 1, 0); | ||||||
|  | 
 | ||||||
|  |     if (loaded_crs == 0) { | ||||||
|  |         LOG_ERROR(Service_LDR, "Not initialized"); | ||||||
|  |         cmd_buff[1] = ERROR_NOT_INITIALIZED.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (cro_address & Memory::PAGE_MASK) { | ||||||
|  |         LOG_ERROR(Service_LDR, "CRO address is not aligned"); | ||||||
|  |         cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!cro.IsLoaded()) { | ||||||
|  |         LOG_ERROR(Service_LDR, "Invalid or not loaded CRO"); | ||||||
|  |         cmd_buff[1] = ERROR_NOT_LOADED.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     LOG_INFO(Service_LDR, "Unlinking CRO \"%s\"", cro.ModuleName().data()); | ||||||
|  | 
 | ||||||
|  |     ResultCode result = cro.Unlink(loaded_crs); | ||||||
|  |     if (result.IsError()) { | ||||||
|  |         LOG_ERROR(Service_LDR, "Error unlinking CRO %08X", result.raw); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     memory_synchronizer.SynchronizeOriginalMemory(); | ||||||
|  |     Core::g_app_core->ClearInstructionCache(); | ||||||
|  | 
 | ||||||
|  |     cmd_buff[1] = result.raw; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * 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(Service::Interface* self) { | ||||||
|  |     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||||
|  |     VAddr crs_buffer_ptr = cmd_buff[1]; | ||||||
|  |     u32 descriptor       = cmd_buff[2]; | ||||||
|  |     u32 process          = cmd_buff[3]; | ||||||
|  | 
 | ||||||
|  |     LOG_DEBUG(Service_LDR, "called, crs_buffer_ptr=0x%08X, descriptor=0x%08X, process=0x%08X", | ||||||
|  |         crs_buffer_ptr, descriptor, process); | ||||||
|  | 
 | ||||||
|  |     if (descriptor != 0) { | ||||||
|  |         LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor); | ||||||
|  |         cmd_buff[0] = IPC::MakeHeader(0, 1, 0); | ||||||
|  |         cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (loaded_crs == 0) { | ||||||
|  |         LOG_ERROR(Service_LDR, "Not initialized"); | ||||||
|  |         cmd_buff[1] = ERROR_NOT_INITIALIZED.raw; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     cmd_buff[0] = IPC::MakeHeader(8, 1, 0); | ||||||
|  | 
 | ||||||
|  |     CROHelper crs(loaded_crs); | ||||||
|  |     crs.Unrebase(true); | ||||||
|  | 
 | ||||||
|  |     memory_synchronizer.SynchronizeOriginalMemory(); | ||||||
|  | 
 | ||||||
|  |     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 (result.IsError()) { | ||||||
|  |             LOG_ERROR(Service_LDR, "Error unmapping CRS %08X", result.raw); | ||||||
|  |         } | ||||||
|  |         memory_synchronizer.RemoveMemoryBlock(loaded_crs, crs_buffer_ptr); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     loaded_crs = 0; | ||||||
|  |     cmd_buff[1] = result.raw; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const Interface::FunctionInfo FunctionTable[] = { | ||||||
|  |     {0x000100C2, Initialize,            "Initialize"}, | ||||||
|  |     {0x00020082, LoadCRR,               "LoadCRR"}, | ||||||
|  |     {0x00030042, UnloadCRR,             "UnloadCRR"}, | ||||||
|  |     {0x000402C2, LoadCRO<false>,        "LoadCRO"}, | ||||||
|  |     {0x000500C2, UnloadCRO,             "UnloadCRO"}, | ||||||
|  |     {0x00060042, LinkCRO,               "LinkCRO"}, | ||||||
|  |     {0x00070042, UnlinkCRO,             "UnlinkCRO"}, | ||||||
|  |     {0x00080042, Shutdown,              "Shutdown"}, | ||||||
|  |     {0x000902C2, LoadCRO<true>,         "LoadCRO_New"}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | // Interface class
 | ||||||
|  | 
 | ||||||
|  | Interface::Interface() { | ||||||
|  |     Register(FunctionTable); | ||||||
|  | 
 | ||||||
|  |     loaded_crs = 0; | ||||||
|  |     memory_synchronizer.Clear(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace
 | ||||||
							
								
								
									
										46
									
								
								src/core/hle/service/ldr_ro/memory_synchronizer.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/core/hle/service/ldr_ro/memory_synchronizer.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,46 @@ | ||||||
|  | // 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/service/ldr_ro/memory_synchronizer.h" | ||||||
|  | 
 | ||||||
|  | ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | // Namespace LDR_RO
 | ||||||
|  | 
 | ||||||
|  | namespace LDR_RO { | ||||||
|  | 
 | ||||||
|  | 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() { | ||||||
|  |     for (auto& block : memory_blocks) { | ||||||
|  |         Memory::CopyBlock(block.original, block.mapping, block.size); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace
 | ||||||
							
								
								
									
										44
									
								
								src/core/hle/service/ldr_ro/memory_synchronizer.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/core/hle/service/ldr_ro/memory_synchronizer.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,44 @@ | ||||||
|  | // 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 LDR_RO
 | ||||||
|  | 
 | ||||||
|  | namespace LDR_RO { | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * 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(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     struct MemoryBlock { | ||||||
|  |         VAddr mapping; | ||||||
|  |         VAddr original; | ||||||
|  |         u32 size; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     std::vector<MemoryBlock> memory_blocks; | ||||||
|  | 
 | ||||||
|  |     auto FindMemoryBlock(VAddr mapping, VAddr original); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace
 | ||||||
|  | @ -15,7 +15,6 @@ | ||||||
| #include "core/hle/service/gsp_gpu.h" | #include "core/hle/service/gsp_gpu.h" | ||||||
| #include "core/hle/service/gsp_lcd.h" | #include "core/hle/service/gsp_lcd.h" | ||||||
| #include "core/hle/service/http_c.h" | #include "core/hle/service/http_c.h" | ||||||
| #include "core/hle/service/ldr_ro.h" |  | ||||||
| #include "core/hle/service/mic_u.h" | #include "core/hle/service/mic_u.h" | ||||||
| #include "core/hle/service/ns_s.h" | #include "core/hle/service/ns_s.h" | ||||||
| #include "core/hle/service/nwm_uds.h" | #include "core/hle/service/nwm_uds.h" | ||||||
|  | @ -36,6 +35,7 @@ | ||||||
| #include "core/hle/service/cfg/cfg.h" | #include "core/hle/service/cfg/cfg.h" | ||||||
| #include "core/hle/service/hid/hid.h" | #include "core/hle/service/hid/hid.h" | ||||||
| #include "core/hle/service/ir/ir.h" | #include "core/hle/service/ir/ir.h" | ||||||
|  | #include "core/hle/service/ldr_ro/ldr_ro.h" | ||||||
| #include "core/hle/service/ndm/ndm.h" | #include "core/hle/service/ndm/ndm.h" | ||||||
| #include "core/hle/service/news/news.h" | #include "core/hle/service/news/news.h" | ||||||
| #include "core/hle/service/nim/nim.h" | #include "core/hle/service/nim/nim.h" | ||||||
|  |  | ||||||
|  | @ -280,6 +280,20 @@ u8* GetPointer(const VAddr vaddr) { | ||||||
|     return nullptr; |     return nullptr; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | std::string ReadCString(VAddr vaddr, std::size_t max_length) { | ||||||
|  |     std::string string; | ||||||
|  |     string.reserve(max_length); | ||||||
|  |     for (std::size_t i = 0; i < max_length; ++i) { | ||||||
|  |         char c = Read8(vaddr); | ||||||
|  |         if (c == '\0') | ||||||
|  |             break; | ||||||
|  |         string.push_back(c); | ||||||
|  |         ++vaddr; | ||||||
|  |     } | ||||||
|  |     string.shrink_to_fit(); | ||||||
|  |     return string; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| u8* GetPhysicalPointer(PAddr address) { | u8* GetPhysicalPointer(PAddr address) { | ||||||
|     // TODO(Subv): This call should not go through the application's memory mapping.
 |     // TODO(Subv): This call should not go through the application's memory mapping.
 | ||||||
|     return GetPointer(PhysicalToVirtualAddress(address)); |     return GetPointer(PhysicalToVirtualAddress(address)); | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <cstddef> | #include <cstddef> | ||||||
|  | #include <string> | ||||||
| 
 | 
 | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| 
 | 
 | ||||||
|  | @ -130,6 +131,8 @@ void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size); | ||||||
| 
 | 
 | ||||||
| u8* GetPointer(VAddr virtual_address); | u8* GetPointer(VAddr virtual_address); | ||||||
| 
 | 
 | ||||||
|  | std::string ReadCString(VAddr virtual_address, std::size_t max_length); | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
| * Converts a virtual address inside a region with 1:1 mapping to physical memory to a physical | * Converts a virtual address inside a region with 1:1 mapping to physical memory to a physical | ||||||
| * address. This should be used by services to translate addresses for use by the hardware. | * address. This should be used by services to translate addresses for use by the hardware. | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue