mirror of
				https://git.suyu.dev/suyu/suyu
				synced 2025-10-30 15:39:02 -05:00 
			
		
		
		
	LDR: Implement CRO
This commit is contained in:
		| @@ -95,7 +95,9 @@ set(SRCS | ||||
|             hle/service/ir/ir_rst.cpp | ||||
|             hle/service/ir/ir_u.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/ndm/ndm.cpp | ||||
|             hle/service/ndm/ndm_u.cpp | ||||
| @@ -238,7 +240,9 @@ set(HEADERS | ||||
|             hle/service/ir/ir_rst.h | ||||
|             hle/service/ir/ir_u.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/ndm/ndm.h | ||||
|             hle/service/ndm/ndm_u.h | ||||
|   | ||||
| @@ -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_lcd.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/ns_s.h" | ||||
| #include "core/hle/service/nwm_uds.h" | ||||
| @@ -36,6 +35,7 @@ | ||||
| #include "core/hle/service/cfg/cfg.h" | ||||
| #include "core/hle/service/hid/hid.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/news/news.h" | ||||
| #include "core/hle/service/nim/nim.h" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 wwylele
					wwylele