committed by
GitHub
14 changed files with 3041 additions and 99 deletions
-
8src/core/CMakeLists.txt
-
3src/core/arm/arm_interface.h
-
6src/core/arm/dyncom/arm_dyncom.cpp
-
2src/core/arm/dyncom/arm_dyncom.h
-
96src/core/hle/service/ldr_ro.cpp
-
1477src/core/hle/service/ldr_ro/cro_helper.cpp
-
691src/core/hle/service/ldr_ro/cro_helper.h
-
748src/core/hle/service/ldr_ro/ldr_ro.cpp
-
0src/core/hle/service/ldr_ro/ldr_ro.h
-
46src/core/hle/service/ldr_ro/memory_synchronizer.cpp
-
44src/core/hle/service/ldr_ro/memory_synchronizer.h
-
2src/core/hle/service/service.cpp
-
14src/core/memory.cpp
-
3src/core/memory.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
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View 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 |
|||
@ -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
|
|||
@ -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
|
|||
@ -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 |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue