Merge pull request #3192 from wwylele/cro-new-frame
ldr_ro: convert to ServiceFramework
This commit is contained in:
commit
4b8a7eb1ca
8 changed files with 303 additions and 262 deletions
|
@ -36,6 +36,7 @@ static constexpr u32 CRO_HASH_SIZE = 0x80;
|
||||||
/// Represents a loaded module (CRO) with interfaces manipulating it.
|
/// Represents a loaded module (CRO) with interfaces manipulating it.
|
||||||
class CROHelper final {
|
class CROHelper final {
|
||||||
public:
|
public:
|
||||||
|
// TODO (wwylele): pass in the process handle for memory access
|
||||||
explicit CROHelper(VAddr cro_address) : module_address(cro_address) {}
|
explicit CROHelper(VAddr cro_address) : module_address(cro_address) {}
|
||||||
|
|
||||||
std::string ModuleName() const {
|
std::string ModuleName() const {
|
||||||
|
|
|
@ -9,10 +9,8 @@
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/hle/ipc_helpers.h"
|
#include "core/hle/ipc_helpers.h"
|
||||||
#include "core/hle/kernel/process.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/cro_helper.h"
|
||||||
#include "core/hle/service/ldr_ro/ldr_ro.h"
|
#include "core/hle/service/ldr_ro/ldr_ro.h"
|
||||||
#include "core/hle/service/ldr_ro/memory_synchronizer.h"
|
|
||||||
|
|
||||||
namespace Service {
|
namespace Service {
|
||||||
namespace LDR {
|
namespace LDR {
|
||||||
|
@ -42,54 +40,38 @@ static const ResultCode ERROR_NOT_LOADED = // 0xD8A12C0D
|
||||||
ResultCode(static_cast<ErrorDescription>(13), ErrorModule::RO, ErrorSummary::InvalidState,
|
ResultCode(static_cast<ErrorDescription>(13), ErrorModule::RO, ErrorSummary::InvalidState,
|
||||||
ErrorLevel::Permanent);
|
ErrorLevel::Permanent);
|
||||||
|
|
||||||
static MemorySynchronizer memory_synchronizer;
|
static bool VerifyBufferState(Kernel::Process& process, VAddr buffer_ptr, u32 size) {
|
||||||
|
auto vma = process.vm_manager.FindVMA(buffer_ptr);
|
||||||
// TODO(wwylele): this should be in the per-client storage when we implement multi-process
|
return vma != process.vm_manager.vma_map.end() &&
|
||||||
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.base + vma->second.size >= buffer_ptr + size &&
|
||||||
vma->second.permissions == Kernel::VMAPermission::ReadWrite &&
|
vma->second.permissions == Kernel::VMAPermission::ReadWrite &&
|
||||||
vma->second.meminfo_state == Kernel::MemoryState::Private;
|
vma->second.meminfo_state == Kernel::MemoryState::Private;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void RO::Initialize(Kernel::HLERequestContext& ctx) {
|
||||||
* LDR_RO::Initialize service function
|
IPC::RequestParser rp(ctx, 0x01, 3, 2);
|
||||||
* Inputs:
|
|
||||||
* 0 : 0x000100C2
|
|
||||||
* 1 : CRS buffer pointer
|
|
||||||
* 2 : CRS Size
|
|
||||||
* 3 : Process memory address where the CRS will be mapped
|
|
||||||
* 4 : handle translation descriptor (zero)
|
|
||||||
* 5 : KProcess handle
|
|
||||||
* Outputs:
|
|
||||||
* 0 : Return header
|
|
||||||
* 1 : Result of function, 0 on success, otherwise error code
|
|
||||||
*/
|
|
||||||
static void Initialize(Interface* self) {
|
|
||||||
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x01, 3, 2);
|
|
||||||
VAddr crs_buffer_ptr = rp.Pop<u32>();
|
VAddr crs_buffer_ptr = rp.Pop<u32>();
|
||||||
u32 crs_size = rp.Pop<u32>();
|
u32 crs_size = rp.Pop<u32>();
|
||||||
VAddr crs_address = rp.Pop<u32>();
|
VAddr crs_address = rp.Pop<u32>();
|
||||||
// TODO (wwylele): RO service checks the descriptor here and return error 0xD9001830 for
|
// TODO (wwylele): RO service checks the descriptor here and return error 0xD9001830 for
|
||||||
// incorrect descriptor. This error return should be probably built in IPC::RequestParser.
|
// incorrect descriptor. This error return should be probably built in IPC::RequestParser.
|
||||||
// All other service functions below have the same issue.
|
// All other service functions below have the same issue.
|
||||||
Kernel::Handle process = rp.PopHandle();
|
auto process = rp.PopObject<Kernel::Process>();
|
||||||
|
|
||||||
LOG_DEBUG(Service_LDR,
|
LOG_DEBUG(Service_LDR, "called, crs_buffer_ptr=0x%08X, crs_address=0x%08X, crs_size=0x%X",
|
||||||
"called, crs_buffer_ptr=0x%08X, crs_address=0x%08X, crs_size=0x%X, process=0x%08X",
|
crs_buffer_ptr, crs_address, crs_size);
|
||||||
crs_buffer_ptr, crs_address, crs_size, process);
|
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
|
||||||
if (loaded_crs != 0) {
|
auto insert_result = slots.insert({process->process_id, {}});
|
||||||
|
if (!insert_result.second) {
|
||||||
LOG_ERROR(Service_LDR, "Already initialized");
|
LOG_ERROR(Service_LDR, "Already initialized");
|
||||||
rb.Push(ERROR_ALREADY_INITIALIZED);
|
rb.Push(ERROR_ALREADY_INITIALIZED);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto& slot = insert_result.first->second;
|
||||||
|
|
||||||
if (crs_size < CRO_HEADER_SIZE) {
|
if (crs_size < CRO_HEADER_SIZE) {
|
||||||
LOG_ERROR(Service_LDR, "CRS is too small");
|
LOG_ERROR(Service_LDR, "CRS is too small");
|
||||||
rb.Push(ERROR_BUFFER_TOO_SMALL);
|
rb.Push(ERROR_BUFFER_TOO_SMALL);
|
||||||
|
@ -114,7 +96,7 @@ static void Initialize(Interface* self) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!VerifyBufferState(crs_buffer_ptr, crs_size)) {
|
if (!VerifyBufferState(*process, crs_buffer_ptr, crs_size)) {
|
||||||
LOG_ERROR(Service_LDR, "CRS original buffer is in invalid state");
|
LOG_ERROR(Service_LDR, "CRS original buffer is in invalid state");
|
||||||
rb.Push(ERROR_INVALID_MEMORY_STATE);
|
rb.Push(ERROR_INVALID_MEMORY_STATE);
|
||||||
return;
|
return;
|
||||||
|
@ -133,7 +115,7 @@ static void Initialize(Interface* self) {
|
||||||
// TODO(wwylele): should be memory aliasing
|
// TODO(wwylele): should be memory aliasing
|
||||||
std::shared_ptr<std::vector<u8>> crs_mem = std::make_shared<std::vector<u8>>(crs_size);
|
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);
|
Memory::ReadBlock(crs_buffer_ptr, crs_mem->data(), crs_size);
|
||||||
result = Kernel::g_current_process->vm_manager
|
result = process->vm_manager
|
||||||
.MapMemoryBlock(crs_address, crs_mem, 0, crs_size, Kernel::MemoryState::Code)
|
.MapMemoryBlock(crs_address, crs_mem, 0, crs_size, Kernel::MemoryState::Code)
|
||||||
.Code();
|
.Code();
|
||||||
if (result.IsError()) {
|
if (result.IsError()) {
|
||||||
|
@ -142,15 +124,15 @@ static void Initialize(Interface* self) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = Kernel::g_current_process->vm_manager.ReprotectRange(crs_address, crs_size,
|
result =
|
||||||
Kernel::VMAPermission::Read);
|
process->vm_manager.ReprotectRange(crs_address, crs_size, Kernel::VMAPermission::Read);
|
||||||
if (result.IsError()) {
|
if (result.IsError()) {
|
||||||
LOG_ERROR(Service_LDR, "Error reprotecting memory block %08X", result.raw);
|
LOG_ERROR(Service_LDR, "Error reprotecting memory block %08X", result.raw);
|
||||||
rb.Push(result);
|
rb.Push(result);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
memory_synchronizer.AddMemoryBlock(crs_address, crs_buffer_ptr, crs_size);
|
slot.memory_synchronizer.AddMemoryBlock(crs_address, crs_buffer_ptr, crs_size);
|
||||||
} else {
|
} else {
|
||||||
// Do nothing if buffer_ptr == address
|
// Do nothing if buffer_ptr == address
|
||||||
// TODO(wwylele): verify this behaviour. This is only seen in the web browser app,
|
// TODO(wwylele): verify this behaviour. This is only seen in the web browser app,
|
||||||
|
@ -169,90 +151,39 @@ static void Initialize(Interface* self) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
memory_synchronizer.SynchronizeOriginalMemory();
|
slot.memory_synchronizer.SynchronizeOriginalMemory(*process);
|
||||||
|
|
||||||
loaded_crs = crs_address;
|
slot.loaded_crs = crs_address;
|
||||||
|
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void RO::LoadCRR(Kernel::HLERequestContext& ctx) {
|
||||||
* LDR_RO::LoadCRR service function
|
IPC::RequestParser rp(ctx, 0x02, 2, 2);
|
||||||
* Inputs:
|
|
||||||
* 0 : 0x00020082
|
|
||||||
* 1 : CRR buffer pointer
|
|
||||||
* 2 : CRR Size
|
|
||||||
* 3 : handle translation descriptor (zero)
|
|
||||||
* 4 : KProcess handle
|
|
||||||
* Outputs:
|
|
||||||
* 0 : Return header
|
|
||||||
* 1 : Result of function, 0 on success, otherwise error code
|
|
||||||
*/
|
|
||||||
static void LoadCRR(Interface* self) {
|
|
||||||
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x02, 2, 2);
|
|
||||||
VAddr crr_buffer_ptr = rp.Pop<u32>();
|
VAddr crr_buffer_ptr = rp.Pop<u32>();
|
||||||
u32 crr_size = rp.Pop<u32>();
|
u32 crr_size = rp.Pop<u32>();
|
||||||
Kernel::Handle process = rp.PopHandle();
|
auto process = rp.PopObject<Kernel::Process>();
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
|
||||||
LOG_WARNING(Service_LDR,
|
LOG_WARNING(Service_LDR, "(STUBBED) called, crr_buffer_ptr=0x%08X, crr_size=0x%08X",
|
||||||
"(STUBBED) called, crr_buffer_ptr=0x%08X, crr_size=0x%08X, process=0x%08X",
|
crr_buffer_ptr, crr_size);
|
||||||
crr_buffer_ptr, crr_size, process);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void RO::UnloadCRR(Kernel::HLERequestContext& ctx) {
|
||||||
* LDR_RO::UnloadCRR service function
|
IPC::RequestParser rp(ctx, 0x03, 1, 2);
|
||||||
* Inputs:
|
|
||||||
* 0 : 0x00030042
|
|
||||||
* 1 : CRR buffer pointer
|
|
||||||
* 2 : handle translation descriptor (zero)
|
|
||||||
* 3 : KProcess handle
|
|
||||||
* Outputs:
|
|
||||||
* 0 : Return header
|
|
||||||
* 1 : Result of function, 0 on success, otherwise error code
|
|
||||||
*/
|
|
||||||
static void UnloadCRR(Interface* self) {
|
|
||||||
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x03, 1, 2);
|
|
||||||
u32 crr_buffer_ptr = rp.Pop<u32>();
|
u32 crr_buffer_ptr = rp.Pop<u32>();
|
||||||
Kernel::Handle process = rp.PopHandle();
|
auto process = rp.PopObject<Kernel::Process>();
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
|
||||||
LOG_WARNING(Service_LDR, "(STUBBED) called, crr_buffer_ptr=0x%08X, process=0x%08X",
|
LOG_WARNING(Service_LDR, "(STUBBED) called, crr_buffer_ptr=0x%08X", crr_buffer_ptr);
|
||||||
crr_buffer_ptr, process);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) {
|
||||||
* LDR_RO::LoadCRO service function
|
IPC::RequestParser rp(ctx, link_on_load_bug_fix ? 0x09 : 0x04, 11, 2);
|
||||||
* Inputs:
|
|
||||||
* 0 : 0x000402C2 (old) / 0x000902C2 (new)
|
|
||||||
* 1 : CRO buffer pointer
|
|
||||||
* 2 : memory address where the CRO will be mapped
|
|
||||||
* 3 : CRO Size
|
|
||||||
* 4 : .data segment buffer pointer
|
|
||||||
* 5 : must be zero
|
|
||||||
* 6 : .data segment buffer size
|
|
||||||
* 7 : .bss segment buffer pointer
|
|
||||||
* 8 : .bss segment buffer size
|
|
||||||
* 9 : (bool) register CRO as auto-link module
|
|
||||||
* 10 : fix level
|
|
||||||
* 11 : CRR address (zero if use loaded CRR)
|
|
||||||
* 12 : handle translation descriptor (zero)
|
|
||||||
* 13 : KProcess handle
|
|
||||||
* Outputs:
|
|
||||||
* 0 : Return header
|
|
||||||
* 1 : Result of function, 0 on success, otherwise error code
|
|
||||||
* 2 : CRO fixed size
|
|
||||||
* Note:
|
|
||||||
* This service function has two versions. The function defined here is a
|
|
||||||
* unified one of two, with an additional parameter link_on_load_bug_fix.
|
|
||||||
* There is a dispatcher template below.
|
|
||||||
*/
|
|
||||||
static void LoadCRO(Interface* self, bool link_on_load_bug_fix) {
|
|
||||||
IPC::RequestParser rp(Kernel::GetCommandBuffer(), link_on_load_bug_fix ? 0x09 : 0x04, 11, 2);
|
|
||||||
VAddr cro_buffer_ptr = rp.Pop<u32>();
|
VAddr cro_buffer_ptr = rp.Pop<u32>();
|
||||||
VAddr cro_address = rp.Pop<u32>();
|
VAddr cro_address = rp.Pop<u32>();
|
||||||
u32 cro_size = rp.Pop<u32>();
|
u32 cro_size = rp.Pop<u32>();
|
||||||
|
@ -264,24 +195,26 @@ static void LoadCRO(Interface* self, bool link_on_load_bug_fix) {
|
||||||
bool auto_link = rp.Pop<bool>();
|
bool auto_link = rp.Pop<bool>();
|
||||||
u32 fix_level = rp.Pop<u32>();
|
u32 fix_level = rp.Pop<u32>();
|
||||||
VAddr crr_address = rp.Pop<u32>();
|
VAddr crr_address = rp.Pop<u32>();
|
||||||
Kernel::Handle process = rp.PopHandle();
|
auto process = rp.PopObject<Kernel::Process>();
|
||||||
|
|
||||||
LOG_DEBUG(Service_LDR, "called (%s), cro_buffer_ptr=0x%08X, cro_address=0x%08X, cro_size=0x%X, "
|
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, "
|
"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, "
|
"bss_segment_address=0x%08X, bss_segment_size=0x%X, auto_link=%s, "
|
||||||
"fix_level=%d, crr_address=0x%08X, process=0x%08X",
|
"fix_level=%d, crr_address=0x%08X",
|
||||||
link_on_load_bug_fix ? "new" : "old", cro_buffer_ptr, cro_address, cro_size,
|
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,
|
data_segment_address, zero, data_segment_size, bss_segment_address, bss_segment_size,
|
||||||
auto_link ? "true" : "false", fix_level, crr_address, process);
|
auto_link ? "true" : "false", fix_level, crr_address);
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||||
|
|
||||||
if (loaded_crs == 0) {
|
auto find_result = slots.find(process->process_id);
|
||||||
|
if (find_result == slots.end()) {
|
||||||
LOG_ERROR(Service_LDR, "Not initialized");
|
LOG_ERROR(Service_LDR, "Not initialized");
|
||||||
rb.Push(ERROR_NOT_INITIALIZED);
|
rb.Push(ERROR_NOT_INITIALIZED);
|
||||||
rb.Push<u32>(0);
|
rb.Push<u32>(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
auto& slot = find_result->second;
|
||||||
|
|
||||||
if (cro_size < CRO_HEADER_SIZE) {
|
if (cro_size < CRO_HEADER_SIZE) {
|
||||||
LOG_ERROR(Service_LDR, "CRO too small");
|
LOG_ERROR(Service_LDR, "CRO too small");
|
||||||
|
@ -311,7 +244,7 @@ static void LoadCRO(Interface* self, bool link_on_load_bug_fix) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!VerifyBufferState(cro_buffer_ptr, cro_size)) {
|
if (!VerifyBufferState(*process, cro_buffer_ptr, cro_size)) {
|
||||||
LOG_ERROR(Service_LDR, "CRO original buffer is in invalid state");
|
LOG_ERROR(Service_LDR, "CRO original buffer is in invalid state");
|
||||||
rb.Push(ERROR_INVALID_MEMORY_STATE);
|
rb.Push(ERROR_INVALID_MEMORY_STATE);
|
||||||
rb.Push<u32>(0);
|
rb.Push<u32>(0);
|
||||||
|
@ -340,7 +273,7 @@ static void LoadCRO(Interface* self, bool link_on_load_bug_fix) {
|
||||||
// TODO(wwylele): should be memory aliasing
|
// TODO(wwylele): should be memory aliasing
|
||||||
std::shared_ptr<std::vector<u8>> cro_mem = std::make_shared<std::vector<u8>>(cro_size);
|
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);
|
Memory::ReadBlock(cro_buffer_ptr, cro_mem->data(), cro_size);
|
||||||
result = Kernel::g_current_process->vm_manager
|
result = process->vm_manager
|
||||||
.MapMemoryBlock(cro_address, cro_mem, 0, cro_size, Kernel::MemoryState::Code)
|
.MapMemoryBlock(cro_address, cro_mem, 0, cro_size, Kernel::MemoryState::Code)
|
||||||
.Code();
|
.Code();
|
||||||
if (result.IsError()) {
|
if (result.IsError()) {
|
||||||
|
@ -350,17 +283,17 @@ static void LoadCRO(Interface* self, bool link_on_load_bug_fix) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = Kernel::g_current_process->vm_manager.ReprotectRange(cro_address, cro_size,
|
result =
|
||||||
Kernel::VMAPermission::Read);
|
process->vm_manager.ReprotectRange(cro_address, cro_size, Kernel::VMAPermission::Read);
|
||||||
if (result.IsError()) {
|
if (result.IsError()) {
|
||||||
LOG_ERROR(Service_LDR, "Error reprotecting memory block %08X", result.raw);
|
LOG_ERROR(Service_LDR, "Error reprotecting memory block %08X", result.raw);
|
||||||
Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size);
|
process->vm_manager.UnmapRange(cro_address, cro_size);
|
||||||
rb.Push(result);
|
rb.Push(result);
|
||||||
rb.Push<u32>(0);
|
rb.Push<u32>(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
memory_synchronizer.AddMemoryBlock(cro_address, cro_buffer_ptr, cro_size);
|
slot.memory_synchronizer.AddMemoryBlock(cro_address, cro_buffer_ptr, cro_size);
|
||||||
} else {
|
} else {
|
||||||
// Do nothing if buffer_ptr == address
|
// Do nothing if buffer_ptr == address
|
||||||
// TODO(wwylele): verify this behaviour.
|
// TODO(wwylele): verify this behaviour.
|
||||||
|
@ -375,45 +308,44 @@ static void LoadCRO(Interface* self, bool link_on_load_bug_fix) {
|
||||||
result = cro.VerifyHash(cro_size, crr_address);
|
result = cro.VerifyHash(cro_size, crr_address);
|
||||||
if (result.IsError()) {
|
if (result.IsError()) {
|
||||||
LOG_ERROR(Service_LDR, "Error verifying CRO in CRR %08X", result.raw);
|
LOG_ERROR(Service_LDR, "Error verifying CRO in CRR %08X", result.raw);
|
||||||
Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size);
|
process->vm_manager.UnmapRange(cro_address, cro_size);
|
||||||
rb.Push(result);
|
rb.Push(result);
|
||||||
rb.Push<u32>(0);
|
rb.Push<u32>(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = cro.Rebase(loaded_crs, cro_size, data_segment_address, data_segment_size,
|
result = cro.Rebase(slot.loaded_crs, cro_size, data_segment_address, data_segment_size,
|
||||||
bss_segment_address, bss_segment_size, false);
|
bss_segment_address, bss_segment_size, false);
|
||||||
if (result.IsError()) {
|
if (result.IsError()) {
|
||||||
LOG_ERROR(Service_LDR, "Error rebasing CRO %08X", result.raw);
|
LOG_ERROR(Service_LDR, "Error rebasing CRO %08X", result.raw);
|
||||||
Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size);
|
process->vm_manager.UnmapRange(cro_address, cro_size);
|
||||||
rb.Push(result);
|
rb.Push(result);
|
||||||
rb.Push<u32>(0);
|
rb.Push<u32>(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = cro.Link(loaded_crs, link_on_load_bug_fix);
|
result = cro.Link(slot.loaded_crs, link_on_load_bug_fix);
|
||||||
if (result.IsError()) {
|
if (result.IsError()) {
|
||||||
LOG_ERROR(Service_LDR, "Error linking CRO %08X", result.raw);
|
LOG_ERROR(Service_LDR, "Error linking CRO %08X", result.raw);
|
||||||
Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size);
|
process->vm_manager.UnmapRange(cro_address, cro_size);
|
||||||
rb.Push(result);
|
rb.Push(result);
|
||||||
rb.Push<u32>(0);
|
rb.Push<u32>(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cro.Register(loaded_crs, auto_link);
|
cro.Register(slot.loaded_crs, auto_link);
|
||||||
|
|
||||||
u32 fix_size = cro.Fix(fix_level);
|
u32 fix_size = cro.Fix(fix_level);
|
||||||
|
|
||||||
memory_synchronizer.SynchronizeOriginalMemory();
|
slot.memory_synchronizer.SynchronizeOriginalMemory(*process);
|
||||||
|
|
||||||
// TODO(wwylele): verify the behaviour when buffer_ptr == address
|
// TODO(wwylele): verify the behaviour when buffer_ptr == address
|
||||||
if (cro_buffer_ptr != cro_address) {
|
if (cro_buffer_ptr != cro_address) {
|
||||||
if (fix_size != cro_size) {
|
if (fix_size != cro_size) {
|
||||||
result = Kernel::g_current_process->vm_manager.UnmapRange(cro_address + fix_size,
|
result = process->vm_manager.UnmapRange(cro_address + fix_size, cro_size - fix_size);
|
||||||
cro_size - fix_size);
|
|
||||||
if (result.IsError()) {
|
if (result.IsError()) {
|
||||||
LOG_ERROR(Service_LDR, "Error unmapping memory block %08X", result.raw);
|
LOG_ERROR(Service_LDR, "Error unmapping memory block %08X", result.raw);
|
||||||
Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size);
|
process->vm_manager.UnmapRange(cro_address, cro_size);
|
||||||
rb.Push(result);
|
rb.Push(result);
|
||||||
rb.Push<u32>(0);
|
rb.Push<u32>(0);
|
||||||
return;
|
return;
|
||||||
|
@ -421,18 +353,18 @@ static void LoadCRO(Interface* self, bool link_on_load_bug_fix) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Changes the block size
|
// Changes the block size
|
||||||
memory_synchronizer.ResizeMemoryBlock(cro_address, cro_buffer_ptr, fix_size);
|
slot.memory_synchronizer.ResizeMemoryBlock(cro_address, cro_buffer_ptr, fix_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
VAddr exe_begin;
|
VAddr exe_begin;
|
||||||
u32 exe_size;
|
u32 exe_size;
|
||||||
std::tie(exe_begin, exe_size) = cro.GetExecutablePages();
|
std::tie(exe_begin, exe_size) = cro.GetExecutablePages();
|
||||||
if (exe_begin) {
|
if (exe_begin) {
|
||||||
result = Kernel::g_current_process->vm_manager.ReprotectRange(
|
result = process->vm_manager.ReprotectRange(exe_begin, exe_size,
|
||||||
exe_begin, exe_size, Kernel::VMAPermission::ReadExecute);
|
Kernel::VMAPermission::ReadExecute);
|
||||||
if (result.IsError()) {
|
if (result.IsError()) {
|
||||||
LOG_ERROR(Service_LDR, "Error reprotecting memory block %08X", result.raw);
|
LOG_ERROR(Service_LDR, "Error reprotecting memory block %08X", result.raw);
|
||||||
Kernel::g_current_process->vm_manager.UnmapRange(cro_address, fix_size);
|
process->vm_manager.UnmapRange(cro_address, fix_size);
|
||||||
rb.Push(result);
|
rb.Push(result);
|
||||||
rb.Push<u32>(0);
|
rb.Push<u32>(0);
|
||||||
return;
|
return;
|
||||||
|
@ -447,44 +379,27 @@ static void LoadCRO(Interface* self, bool link_on_load_bug_fix) {
|
||||||
rb.Push(RESULT_SUCCESS, fix_size);
|
rb.Push(RESULT_SUCCESS, fix_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool link_on_load_bug_fix>
|
void RO::UnloadCRO(Kernel::HLERequestContext& ctx) {
|
||||||
static void LoadCRO(Interface* self) {
|
IPC::RequestParser rp(ctx, 0x05, 3, 2);
|
||||||
LoadCRO(self, link_on_load_bug_fix);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* LDR_RO::UnloadCRO service function
|
|
||||||
* Inputs:
|
|
||||||
* 0 : 0x000500C2
|
|
||||||
* 1 : mapped CRO pointer
|
|
||||||
* 2 : zero? (RO service doesn't care)
|
|
||||||
* 3 : original CRO pointer
|
|
||||||
* 4 : handle translation descriptor (zero)
|
|
||||||
* 5 : KProcess handle
|
|
||||||
* Outputs:
|
|
||||||
* 0 : Return header
|
|
||||||
* 1 : Result of function, 0 on success, otherwise error code
|
|
||||||
*/
|
|
||||||
static void UnloadCRO(Interface* self) {
|
|
||||||
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x05, 3, 2);
|
|
||||||
VAddr cro_address = rp.Pop<u32>();
|
VAddr cro_address = rp.Pop<u32>();
|
||||||
u32 zero = rp.Pop<u32>();
|
u32 zero = rp.Pop<u32>();
|
||||||
VAddr cro_buffer_ptr = rp.Pop<u32>();
|
VAddr cro_buffer_ptr = rp.Pop<u32>();
|
||||||
Kernel::Handle process = rp.PopHandle();
|
auto process = rp.PopObject<Kernel::Process>();
|
||||||
|
|
||||||
LOG_DEBUG(Service_LDR,
|
LOG_DEBUG(Service_LDR, "called, cro_address=0x%08X, zero=%d, cro_buffer_ptr=0x%08X",
|
||||||
"called, cro_address=0x%08X, zero=%d, cro_buffer_ptr=0x%08X, process=0x%08X",
|
cro_address, zero, cro_buffer_ptr);
|
||||||
cro_address, zero, cro_buffer_ptr, process);
|
|
||||||
|
|
||||||
CROHelper cro(cro_address);
|
CROHelper cro(cro_address);
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
|
||||||
if (loaded_crs == 0) {
|
auto find_result = slots.find(process->process_id);
|
||||||
|
if (find_result == slots.end()) {
|
||||||
LOG_ERROR(Service_LDR, "Not initialized");
|
LOG_ERROR(Service_LDR, "Not initialized");
|
||||||
rb.Push(ERROR_NOT_INITIALIZED);
|
rb.Push(ERROR_NOT_INITIALIZED);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
auto& slot = find_result->second;
|
||||||
|
|
||||||
if (cro_address & Memory::PAGE_MASK) {
|
if (cro_address & Memory::PAGE_MASK) {
|
||||||
LOG_ERROR(Service_LDR, "CRO address is not aligned");
|
LOG_ERROR(Service_LDR, "CRO address is not aligned");
|
||||||
|
@ -502,9 +417,9 @@ static void UnloadCRO(Interface* self) {
|
||||||
|
|
||||||
u32 fixed_size = cro.GetFixedSize();
|
u32 fixed_size = cro.GetFixedSize();
|
||||||
|
|
||||||
cro.Unregister(loaded_crs);
|
cro.Unregister(slot.loaded_crs);
|
||||||
|
|
||||||
ResultCode result = cro.Unlink(loaded_crs);
|
ResultCode result = cro.Unlink(slot.loaded_crs);
|
||||||
if (result.IsError()) {
|
if (result.IsError()) {
|
||||||
LOG_ERROR(Service_LDR, "Error unlinking CRO %08X", result.raw);
|
LOG_ERROR(Service_LDR, "Error unlinking CRO %08X", result.raw);
|
||||||
rb.Push(result);
|
rb.Push(result);
|
||||||
|
@ -524,15 +439,15 @@ static void UnloadCRO(Interface* self) {
|
||||||
|
|
||||||
cro.Unrebase(false);
|
cro.Unrebase(false);
|
||||||
|
|
||||||
memory_synchronizer.SynchronizeOriginalMemory();
|
slot.memory_synchronizer.SynchronizeOriginalMemory(*process);
|
||||||
|
|
||||||
// TODO(wwylele): verify the behaviour when buffer_ptr == address
|
// TODO(wwylele): verify the behaviour when buffer_ptr == address
|
||||||
if (cro_address != cro_buffer_ptr) {
|
if (cro_address != cro_buffer_ptr) {
|
||||||
result = Kernel::g_current_process->vm_manager.UnmapRange(cro_address, fixed_size);
|
result = process->vm_manager.UnmapRange(cro_address, fixed_size);
|
||||||
if (result.IsError()) {
|
if (result.IsError()) {
|
||||||
LOG_ERROR(Service_LDR, "Error unmapping CRO %08X", result.raw);
|
LOG_ERROR(Service_LDR, "Error unmapping CRO %08X", result.raw);
|
||||||
}
|
}
|
||||||
memory_synchronizer.RemoveMemoryBlock(cro_address, cro_buffer_ptr);
|
slot.memory_synchronizer.RemoveMemoryBlock(cro_address, cro_buffer_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::CPU().InvalidateCacheRange(cro_address, fixed_size);
|
Core::CPU().InvalidateCacheRange(cro_address, fixed_size);
|
||||||
|
@ -540,33 +455,24 @@ static void UnloadCRO(Interface* self) {
|
||||||
rb.Push(result);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void RO::LinkCRO(Kernel::HLERequestContext& ctx) {
|
||||||
* LDR_RO::LinkCRO service function
|
IPC::RequestParser rp(ctx, 0x06, 1, 2);
|
||||||
* Inputs:
|
|
||||||
* 0 : 0x00060042
|
|
||||||
* 1 : mapped CRO pointer
|
|
||||||
* 2 : handle translation descriptor (zero)
|
|
||||||
* 3 : KProcess handle
|
|
||||||
* Outputs:
|
|
||||||
* 0 : Return header
|
|
||||||
* 1 : Result of function, 0 on success, otherwise error code
|
|
||||||
*/
|
|
||||||
static void LinkCRO(Interface* self) {
|
|
||||||
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x06, 1, 2);
|
|
||||||
VAddr cro_address = rp.Pop<u32>();
|
VAddr cro_address = rp.Pop<u32>();
|
||||||
Kernel::Handle process = rp.PopHandle();
|
auto process = rp.PopObject<Kernel::Process>();
|
||||||
|
|
||||||
LOG_DEBUG(Service_LDR, "called, cro_address=0x%08X, process=0x%08X", cro_address, process);
|
LOG_DEBUG(Service_LDR, "called, cro_address=0x%08X", cro_address);
|
||||||
|
|
||||||
CROHelper cro(cro_address);
|
CROHelper cro(cro_address);
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
|
||||||
if (loaded_crs == 0) {
|
auto find_result = slots.find(process->process_id);
|
||||||
|
if (find_result == slots.end()) {
|
||||||
LOG_ERROR(Service_LDR, "Not initialized");
|
LOG_ERROR(Service_LDR, "Not initialized");
|
||||||
rb.Push(ERROR_NOT_INITIALIZED);
|
rb.Push(ERROR_NOT_INITIALIZED);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
auto& slot = find_result->second;
|
||||||
|
|
||||||
if (cro_address & Memory::PAGE_MASK) {
|
if (cro_address & Memory::PAGE_MASK) {
|
||||||
LOG_ERROR(Service_LDR, "CRO address is not aligned");
|
LOG_ERROR(Service_LDR, "CRO address is not aligned");
|
||||||
|
@ -582,43 +488,34 @@ static void LinkCRO(Interface* self) {
|
||||||
|
|
||||||
LOG_INFO(Service_LDR, "Linking CRO \"%s\"", cro.ModuleName().data());
|
LOG_INFO(Service_LDR, "Linking CRO \"%s\"", cro.ModuleName().data());
|
||||||
|
|
||||||
ResultCode result = cro.Link(loaded_crs, false);
|
ResultCode result = cro.Link(slot.loaded_crs, false);
|
||||||
if (result.IsError()) {
|
if (result.IsError()) {
|
||||||
LOG_ERROR(Service_LDR, "Error linking CRO %08X", result.raw);
|
LOG_ERROR(Service_LDR, "Error linking CRO %08X", result.raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
memory_synchronizer.SynchronizeOriginalMemory();
|
slot.memory_synchronizer.SynchronizeOriginalMemory(*process);
|
||||||
|
|
||||||
rb.Push(result);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void RO::UnlinkCRO(Kernel::HLERequestContext& ctx) {
|
||||||
* LDR_RO::UnlinkCRO service function
|
IPC::RequestParser rp(ctx, 0x07, 1, 2);
|
||||||
* Inputs:
|
|
||||||
* 0 : 0x00070042
|
|
||||||
* 1 : mapped CRO pointer
|
|
||||||
* 2 : handle translation descriptor (zero)
|
|
||||||
* 3 : KProcess handle
|
|
||||||
* Outputs:
|
|
||||||
* 0 : Return header
|
|
||||||
* 1 : Result of function, 0 on success, otherwise error code
|
|
||||||
*/
|
|
||||||
static void UnlinkCRO(Interface* self) {
|
|
||||||
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x07, 1, 2);
|
|
||||||
VAddr cro_address = rp.Pop<u32>();
|
VAddr cro_address = rp.Pop<u32>();
|
||||||
Kernel::Handle process = rp.PopHandle();
|
auto process = rp.PopObject<Kernel::Process>();
|
||||||
|
|
||||||
LOG_DEBUG(Service_LDR, "called, cro_address=0x%08X, process=0x%08X", cro_address, process);
|
LOG_DEBUG(Service_LDR, "called, cro_address=0x%08X", cro_address);
|
||||||
|
|
||||||
CROHelper cro(cro_address);
|
CROHelper cro(cro_address);
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
|
||||||
if (loaded_crs == 0) {
|
auto find_result = slots.find(process->process_id);
|
||||||
|
if (find_result == slots.end()) {
|
||||||
LOG_ERROR(Service_LDR, "Not initialized");
|
LOG_ERROR(Service_LDR, "Not initialized");
|
||||||
rb.Push(ERROR_NOT_INITIALIZED);
|
rb.Push(ERROR_NOT_INITIALIZED);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
auto& slot = find_result->second;
|
||||||
|
|
||||||
if (cro_address & Memory::PAGE_MASK) {
|
if (cro_address & Memory::PAGE_MASK) {
|
||||||
LOG_ERROR(Service_LDR, "CRO address is not aligned");
|
LOG_ERROR(Service_LDR, "CRO address is not aligned");
|
||||||
|
@ -634,82 +531,70 @@ static void UnlinkCRO(Interface* self) {
|
||||||
|
|
||||||
LOG_INFO(Service_LDR, "Unlinking CRO \"%s\"", cro.ModuleName().data());
|
LOG_INFO(Service_LDR, "Unlinking CRO \"%s\"", cro.ModuleName().data());
|
||||||
|
|
||||||
ResultCode result = cro.Unlink(loaded_crs);
|
ResultCode result = cro.Unlink(slot.loaded_crs);
|
||||||
if (result.IsError()) {
|
if (result.IsError()) {
|
||||||
LOG_ERROR(Service_LDR, "Error unlinking CRO %08X", result.raw);
|
LOG_ERROR(Service_LDR, "Error unlinking CRO %08X", result.raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
memory_synchronizer.SynchronizeOriginalMemory();
|
slot.memory_synchronizer.SynchronizeOriginalMemory(*process);
|
||||||
|
|
||||||
rb.Push(result);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void RO::Shutdown(Kernel::HLERequestContext& ctx) {
|
||||||
* LDR_RO::Shutdown service function
|
IPC::RequestParser rp(ctx, 0x08, 1, 2);
|
||||||
* Inputs:
|
|
||||||
* 0 : 0x00080042
|
|
||||||
* 1 : original CRS buffer pointer
|
|
||||||
* 2 : handle translation descriptor (zero)
|
|
||||||
* 3 : KProcess handle
|
|
||||||
* Outputs:
|
|
||||||
* 0 : Return header
|
|
||||||
* 1 : Result of function, 0 on success, otherwise error code
|
|
||||||
*/
|
|
||||||
static void Shutdown(Interface* self) {
|
|
||||||
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x08, 1, 2);
|
|
||||||
VAddr crs_buffer_ptr = rp.Pop<u32>();
|
VAddr crs_buffer_ptr = rp.Pop<u32>();
|
||||||
Kernel::Handle process = rp.PopHandle();
|
auto process = rp.PopObject<Kernel::Process>();
|
||||||
|
|
||||||
LOG_DEBUG(Service_LDR, "called, crs_buffer_ptr=0x%08X, process=0x%08X", crs_buffer_ptr,
|
LOG_DEBUG(Service_LDR, "called, crs_buffer_ptr=0x%08X", crs_buffer_ptr);
|
||||||
process);
|
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
|
||||||
if (loaded_crs == 0) {
|
auto find_result = slots.find(process->process_id);
|
||||||
|
if (find_result == slots.end()) {
|
||||||
LOG_ERROR(Service_LDR, "Not initialized");
|
LOG_ERROR(Service_LDR, "Not initialized");
|
||||||
rb.Push(ERROR_NOT_INITIALIZED);
|
rb.Push(ERROR_NOT_INITIALIZED);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
auto& slot = find_result->second;
|
||||||
|
|
||||||
CROHelper crs(loaded_crs);
|
CROHelper crs(slot.loaded_crs);
|
||||||
crs.Unrebase(true);
|
crs.Unrebase(true);
|
||||||
|
|
||||||
memory_synchronizer.SynchronizeOriginalMemory();
|
slot.memory_synchronizer.SynchronizeOriginalMemory(*process);
|
||||||
|
|
||||||
ResultCode result = RESULT_SUCCESS;
|
ResultCode result = RESULT_SUCCESS;
|
||||||
|
|
||||||
// TODO(wwylele): verify the behaviour when buffer_ptr == address
|
// TODO(wwylele): verify the behaviour when buffer_ptr == address
|
||||||
if (loaded_crs != crs_buffer_ptr) {
|
if (slot.loaded_crs != crs_buffer_ptr) {
|
||||||
result = Kernel::g_current_process->vm_manager.UnmapRange(loaded_crs, crs.GetFileSize());
|
result = process->vm_manager.UnmapRange(slot.loaded_crs, crs.GetFileSize());
|
||||||
if (result.IsError()) {
|
if (result.IsError()) {
|
||||||
LOG_ERROR(Service_LDR, "Error unmapping CRS %08X", result.raw);
|
LOG_ERROR(Service_LDR, "Error unmapping CRS %08X", result.raw);
|
||||||
}
|
}
|
||||||
memory_synchronizer.RemoveMemoryBlock(loaded_crs, crs_buffer_ptr);
|
slot.memory_synchronizer.RemoveMemoryBlock(slot.loaded_crs, crs_buffer_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
loaded_crs = 0;
|
slots.erase(find_result);
|
||||||
rb.Push(result);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Interface::FunctionInfo FunctionTable[] = {
|
RO::RO() : ServiceFramework("ldr:ro", 2) {
|
||||||
// clang-format off
|
static const FunctionInfo functions[] = {
|
||||||
{0x000100C2, Initialize, "Initialize"},
|
{0x000100C2, &RO::Initialize, "Initialize"},
|
||||||
{0x00020082, LoadCRR, "LoadCRR"},
|
{0x00020082, &RO::LoadCRR, "LoadCRR"},
|
||||||
{0x00030042, UnloadCRR, "UnloadCRR"},
|
{0x00030042, &RO::UnloadCRR, "UnloadCRR"},
|
||||||
{0x000402C2, LoadCRO<false>, "LoadCRO"},
|
{0x000402C2, &RO::LoadCRO<false>, "LoadCRO"},
|
||||||
{0x000500C2, UnloadCRO, "UnloadCRO"},
|
{0x000500C2, &RO::UnloadCRO, "UnloadCRO"},
|
||||||
{0x00060042, LinkCRO, "LinkCRO"},
|
{0x00060042, &RO::LinkCRO, "LinkCRO"},
|
||||||
{0x00070042, UnlinkCRO, "UnlinkCRO"},
|
{0x00070042, &RO::UnlinkCRO, "UnlinkCRO"},
|
||||||
{0x00080042, Shutdown, "Shutdown"},
|
{0x00080042, &RO::Shutdown, "Shutdown"},
|
||||||
{0x000902C2, LoadCRO<true>, "LoadCRO_New"},
|
{0x000902C2, &RO::LoadCRO<true>, "LoadCRO_New"},
|
||||||
// clang-format on
|
};
|
||||||
};
|
RegisterHandlers(functions);
|
||||||
|
}
|
||||||
|
|
||||||
LDR_RO::LDR_RO() {
|
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||||
Register(FunctionTable);
|
std::make_shared<RO>()->InstallAsService(service_manager);
|
||||||
|
|
||||||
loaded_crs = 0;
|
|
||||||
memory_synchronizer.Clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace LDR
|
} // namespace LDR
|
||||||
|
|
|
@ -4,19 +4,155 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
#include "core/hle/service/ldr_ro/memory_synchronizer.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
|
|
||||||
namespace Service {
|
namespace Service {
|
||||||
namespace LDR {
|
namespace LDR {
|
||||||
|
|
||||||
class LDR_RO final : public Interface {
|
class RO final : public ServiceFramework<RO> {
|
||||||
public:
|
public:
|
||||||
LDR_RO();
|
RO();
|
||||||
|
|
||||||
std::string GetPortName() const override {
|
private:
|
||||||
return "ldr:ro";
|
/**
|
||||||
|
* RO::Initialize service function
|
||||||
|
* Inputs:
|
||||||
|
* 0 : 0x000100C2
|
||||||
|
* 1 : CRS buffer pointer
|
||||||
|
* 2 : CRS Size
|
||||||
|
* 3 : Process memory address where the CRS will be mapped
|
||||||
|
* 4 : handle translation descriptor (zero)
|
||||||
|
* 5 : KProcess handle
|
||||||
|
* Outputs:
|
||||||
|
* 0 : Return header
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
void Initialize(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RO::LoadCRR service function
|
||||||
|
* Inputs:
|
||||||
|
* 0 : 0x00020082
|
||||||
|
* 1 : CRR buffer pointer
|
||||||
|
* 2 : CRR Size
|
||||||
|
* 3 : handle translation descriptor (zero)
|
||||||
|
* 4 : KProcess handle
|
||||||
|
* Outputs:
|
||||||
|
* 0 : Return header
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
void LoadCRR(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RO::UnloadCRR service function
|
||||||
|
* Inputs:
|
||||||
|
* 0 : 0x00030042
|
||||||
|
* 1 : CRR buffer pointer
|
||||||
|
* 2 : handle translation descriptor (zero)
|
||||||
|
* 3 : KProcess handle
|
||||||
|
* Outputs:
|
||||||
|
* 0 : Return header
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
void UnloadCRR(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RO::LoadCRO service function
|
||||||
|
* Inputs:
|
||||||
|
* 0 : 0x000402C2 (old) / 0x000902C2 (new)
|
||||||
|
* 1 : CRO buffer pointer
|
||||||
|
* 2 : memory address where the CRO will be mapped
|
||||||
|
* 3 : CRO Size
|
||||||
|
* 4 : .data segment buffer pointer
|
||||||
|
* 5 : must be zero
|
||||||
|
* 6 : .data segment buffer size
|
||||||
|
* 7 : .bss segment buffer pointer
|
||||||
|
* 8 : .bss segment buffer size
|
||||||
|
* 9 : (bool) register CRO as auto-link module
|
||||||
|
* 10 : fix level
|
||||||
|
* 11 : CRR address (zero if use loaded CRR)
|
||||||
|
* 12 : handle translation descriptor (zero)
|
||||||
|
* 13 : KProcess handle
|
||||||
|
* Outputs:
|
||||||
|
* 0 : Return header
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
* 2 : CRO fixed size
|
||||||
|
* Note:
|
||||||
|
* This service function has two versions. The function defined here is a
|
||||||
|
* unified one of two, with an additional parameter link_on_load_bug_fix.
|
||||||
|
* There is a dispatcher template below.
|
||||||
|
*/
|
||||||
|
void LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix);
|
||||||
|
|
||||||
|
template <bool link_on_load_bug_fix>
|
||||||
|
void LoadCRO(Kernel::HLERequestContext& ctx) {
|
||||||
|
LoadCRO(ctx, link_on_load_bug_fix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RO::UnloadCRO service function
|
||||||
|
* Inputs:
|
||||||
|
* 0 : 0x000500C2
|
||||||
|
* 1 : mapped CRO pointer
|
||||||
|
* 2 : zero? (RO service doesn't care)
|
||||||
|
* 3 : original CRO pointer
|
||||||
|
* 4 : handle translation descriptor (zero)
|
||||||
|
* 5 : KProcess handle
|
||||||
|
* Outputs:
|
||||||
|
* 0 : Return header
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
void UnloadCRO(Kernel::HLERequestContext& self);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RO::LinkCRO service function
|
||||||
|
* Inputs:
|
||||||
|
* 0 : 0x00060042
|
||||||
|
* 1 : mapped CRO pointer
|
||||||
|
* 2 : handle translation descriptor (zero)
|
||||||
|
* 3 : KProcess handle
|
||||||
|
* Outputs:
|
||||||
|
* 0 : Return header
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
void LinkCRO(Kernel::HLERequestContext& self);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RO::UnlinkCRO service function
|
||||||
|
* Inputs:
|
||||||
|
* 0 : 0x00070042
|
||||||
|
* 1 : mapped CRO pointer
|
||||||
|
* 2 : handle translation descriptor (zero)
|
||||||
|
* 3 : KProcess handle
|
||||||
|
* Outputs:
|
||||||
|
* 0 : Return header
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
void UnlinkCRO(Kernel::HLERequestContext& self);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RO::Shutdown service function
|
||||||
|
* Inputs:
|
||||||
|
* 0 : 0x00080042
|
||||||
|
* 1 : original CRS buffer pointer
|
||||||
|
* 2 : handle translation descriptor (zero)
|
||||||
|
* 3 : KProcess handle
|
||||||
|
* Outputs:
|
||||||
|
* 0 : Return header
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
void Shutdown(Kernel::HLERequestContext& self);
|
||||||
|
|
||||||
|
struct ClientSlot {
|
||||||
|
MemorySynchronizer memory_synchronizer;
|
||||||
|
VAddr loaded_crs = 0; ///< the virtual address of the static module
|
||||||
|
};
|
||||||
|
std::unordered_map<u32, ClientSlot> slots;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void InstallInterfaces(SM::ServiceManager& service_manager);
|
||||||
|
|
||||||
} // namespace LDR
|
} // namespace LDR
|
||||||
} // namespace Service
|
} // namespace Service
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
#include "core/hle/kernel/process.h"
|
||||||
#include "core/hle/service/ldr_ro/memory_synchronizer.h"
|
#include "core/hle/service/ldr_ro/memory_synchronizer.h"
|
||||||
|
|
||||||
namespace Service {
|
namespace Service {
|
||||||
|
@ -32,9 +33,9 @@ void MemorySynchronizer::RemoveMemoryBlock(VAddr mapping, VAddr original) {
|
||||||
memory_blocks.erase(FindMemoryBlock(mapping, original));
|
memory_blocks.erase(FindMemoryBlock(mapping, original));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MemorySynchronizer::SynchronizeOriginalMemory() {
|
void MemorySynchronizer::SynchronizeOriginalMemory(Kernel::Process& process) {
|
||||||
for (auto& block : memory_blocks) {
|
for (auto& block : memory_blocks) {
|
||||||
Memory::CopyBlock(block.original, block.mapping, block.size);
|
Memory::CopyBlock(process, block.original, block.mapping, block.size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,10 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
class Process;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Service {
|
namespace Service {
|
||||||
namespace LDR {
|
namespace LDR {
|
||||||
|
|
||||||
|
@ -24,7 +28,7 @@ public:
|
||||||
void ResizeMemoryBlock(VAddr mapping, VAddr original, u32 size);
|
void ResizeMemoryBlock(VAddr mapping, VAddr original, u32 size);
|
||||||
void RemoveMemoryBlock(VAddr mapping, VAddr original);
|
void RemoveMemoryBlock(VAddr mapping, VAddr original);
|
||||||
|
|
||||||
void SynchronizeOriginalMemory();
|
void SynchronizeOriginalMemory(Kernel::Process& process);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct MemoryBlock {
|
struct MemoryBlock {
|
||||||
|
|
|
@ -263,6 +263,7 @@ void Init() {
|
||||||
PXI::InstallInterfaces(*SM::g_service_manager);
|
PXI::InstallInterfaces(*SM::g_service_manager);
|
||||||
NS::InstallInterfaces(*SM::g_service_manager);
|
NS::InstallInterfaces(*SM::g_service_manager);
|
||||||
AC::InstallInterfaces(*SM::g_service_manager);
|
AC::InstallInterfaces(*SM::g_service_manager);
|
||||||
|
LDR::InstallInterfaces(*SM::g_service_manager);
|
||||||
|
|
||||||
FS::ArchiveInit();
|
FS::ArchiveInit();
|
||||||
ACT::Init();
|
ACT::Init();
|
||||||
|
@ -290,7 +291,6 @@ void Init() {
|
||||||
AddService(new GSP::GSP_GPU);
|
AddService(new GSP::GSP_GPU);
|
||||||
AddService(new GSP::GSP_LCD);
|
AddService(new GSP::GSP_LCD);
|
||||||
AddService(new HTTP::HTTP_C);
|
AddService(new HTTP::HTTP_C);
|
||||||
AddService(new LDR::LDR_RO);
|
|
||||||
AddService(new MIC::MIC_U);
|
AddService(new MIC::MIC_U);
|
||||||
AddService(new PM::PM_APP);
|
AddService(new PM::PM_APP);
|
||||||
AddService(new SOC::SOC_U);
|
AddService(new SOC::SOC_U);
|
||||||
|
|
|
@ -615,7 +615,8 @@ void WriteBlock(const VAddr dest_addr, const void* src_buffer, const size_t size
|
||||||
WriteBlock(*Kernel::g_current_process, dest_addr, src_buffer, size);
|
WriteBlock(*Kernel::g_current_process, dest_addr, src_buffer, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZeroBlock(const VAddr dest_addr, const size_t size) {
|
void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const size_t size) {
|
||||||
|
auto& page_table = process.vm_manager.page_table;
|
||||||
size_t remaining_size = size;
|
size_t remaining_size = size;
|
||||||
size_t page_index = dest_addr >> PAGE_BITS;
|
size_t page_index = dest_addr >> PAGE_BITS;
|
||||||
size_t page_offset = dest_addr & PAGE_MASK;
|
size_t page_offset = dest_addr & PAGE_MASK;
|
||||||
|
@ -626,36 +627,37 @@ void ZeroBlock(const VAddr dest_addr, const size_t size) {
|
||||||
const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size);
|
const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size);
|
||||||
const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
|
const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
|
||||||
|
|
||||||
switch (current_page_table->attributes[page_index]) {
|
switch (page_table.attributes[page_index]) {
|
||||||
case PageType::Unmapped: {
|
case PageType::Unmapped: {
|
||||||
LOG_ERROR(HW_Memory, "unmapped ZeroBlock @ 0x%08X (start address = 0x%08X, size = %zu)",
|
LOG_ERROR(HW_Memory, "unmapped ZeroBlock @ 0x%08X (start address = 0x%08X, size = %zu)",
|
||||||
current_vaddr, dest_addr, size);
|
current_vaddr, dest_addr, size);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PageType::Memory: {
|
case PageType::Memory: {
|
||||||
DEBUG_ASSERT(current_page_table->pointers[page_index]);
|
DEBUG_ASSERT(page_table.pointers[page_index]);
|
||||||
|
|
||||||
u8* dest_ptr = current_page_table->pointers[page_index] + page_offset;
|
u8* dest_ptr = page_table.pointers[page_index] + page_offset;
|
||||||
std::memset(dest_ptr, 0, copy_amount);
|
std::memset(dest_ptr, 0, copy_amount);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PageType::Special: {
|
case PageType::Special: {
|
||||||
DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
|
MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr);
|
||||||
|
DEBUG_ASSERT(handler);
|
||||||
GetMMIOHandler(current_vaddr)->WriteBlock(current_vaddr, zeros.data(), copy_amount);
|
handler->WriteBlock(current_vaddr, zeros.data(), copy_amount);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PageType::RasterizerCachedMemory: {
|
case PageType::RasterizerCachedMemory: {
|
||||||
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
||||||
FlushMode::FlushAndInvalidate);
|
FlushMode::FlushAndInvalidate);
|
||||||
std::memset(GetPointerFromVMA(current_vaddr), 0, copy_amount);
|
std::memset(GetPointerFromVMA(process, current_vaddr), 0, copy_amount);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PageType::RasterizerCachedSpecial: {
|
case PageType::RasterizerCachedSpecial: {
|
||||||
DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
|
MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr);
|
||||||
|
DEBUG_ASSERT(handler);
|
||||||
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
||||||
FlushMode::FlushAndInvalidate);
|
FlushMode::FlushAndInvalidate);
|
||||||
GetMMIOHandler(current_vaddr)->WriteBlock(current_vaddr, zeros.data(), copy_amount);
|
handler->WriteBlock(current_vaddr, zeros.data(), copy_amount);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -668,7 +670,12 @@ void ZeroBlock(const VAddr dest_addr, const size_t size) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CopyBlock(VAddr dest_addr, VAddr src_addr, const size_t size) {
|
void ZeroBlock(const VAddr dest_addr, const size_t size) {
|
||||||
|
ZeroBlock(*Kernel::g_current_process, dest_addr, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr, const size_t size) {
|
||||||
|
auto& page_table = process.vm_manager.page_table;
|
||||||
size_t remaining_size = size;
|
size_t remaining_size = size;
|
||||||
size_t page_index = src_addr >> PAGE_BITS;
|
size_t page_index = src_addr >> PAGE_BITS;
|
||||||
size_t page_offset = src_addr & PAGE_MASK;
|
size_t page_offset = src_addr & PAGE_MASK;
|
||||||
|
@ -677,41 +684,42 @@ void CopyBlock(VAddr dest_addr, VAddr src_addr, const size_t size) {
|
||||||
const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size);
|
const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size);
|
||||||
const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
|
const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
|
||||||
|
|
||||||
switch (current_page_table->attributes[page_index]) {
|
switch (page_table.attributes[page_index]) {
|
||||||
case PageType::Unmapped: {
|
case PageType::Unmapped: {
|
||||||
LOG_ERROR(HW_Memory, "unmapped CopyBlock @ 0x%08X (start address = 0x%08X, size = %zu)",
|
LOG_ERROR(HW_Memory, "unmapped CopyBlock @ 0x%08X (start address = 0x%08X, size = %zu)",
|
||||||
current_vaddr, src_addr, size);
|
current_vaddr, src_addr, size);
|
||||||
ZeroBlock(dest_addr, copy_amount);
|
ZeroBlock(process, dest_addr, copy_amount);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PageType::Memory: {
|
case PageType::Memory: {
|
||||||
DEBUG_ASSERT(current_page_table->pointers[page_index]);
|
DEBUG_ASSERT(page_table.pointers[page_index]);
|
||||||
const u8* src_ptr = current_page_table->pointers[page_index] + page_offset;
|
const u8* src_ptr = page_table.pointers[page_index] + page_offset;
|
||||||
WriteBlock(dest_addr, src_ptr, copy_amount);
|
WriteBlock(process, dest_addr, src_ptr, copy_amount);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PageType::Special: {
|
case PageType::Special: {
|
||||||
DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
|
MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr);
|
||||||
|
DEBUG_ASSERT(handler);
|
||||||
std::vector<u8> buffer(copy_amount);
|
std::vector<u8> buffer(copy_amount);
|
||||||
GetMMIOHandler(current_vaddr)->ReadBlock(current_vaddr, buffer.data(), buffer.size());
|
handler->ReadBlock(current_vaddr, buffer.data(), buffer.size());
|
||||||
WriteBlock(dest_addr, buffer.data(), buffer.size());
|
WriteBlock(process, dest_addr, buffer.data(), buffer.size());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PageType::RasterizerCachedMemory: {
|
case PageType::RasterizerCachedMemory: {
|
||||||
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
||||||
FlushMode::Flush);
|
FlushMode::Flush);
|
||||||
WriteBlock(dest_addr, GetPointerFromVMA(current_vaddr), copy_amount);
|
WriteBlock(process, dest_addr, GetPointerFromVMA(process, current_vaddr), copy_amount);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PageType::RasterizerCachedSpecial: {
|
case PageType::RasterizerCachedSpecial: {
|
||||||
DEBUG_ASSERT(GetMMIOHandler(current_vaddr));
|
MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr);
|
||||||
|
DEBUG_ASSERT(handler);
|
||||||
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
||||||
FlushMode::Flush);
|
FlushMode::Flush);
|
||||||
|
|
||||||
std::vector<u8> buffer(copy_amount);
|
std::vector<u8> buffer(copy_amount);
|
||||||
GetMMIOHandler(current_vaddr)->ReadBlock(current_vaddr, buffer.data(), buffer.size());
|
handler->ReadBlock(current_vaddr, buffer.data(), buffer.size());
|
||||||
WriteBlock(dest_addr, buffer.data(), buffer.size());
|
WriteBlock(process, dest_addr, buffer.data(), buffer.size());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -726,6 +734,10 @@ void CopyBlock(VAddr dest_addr, VAddr src_addr, const size_t size) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CopyBlock(VAddr dest_addr, VAddr src_addr, const size_t size) {
|
||||||
|
CopyBlock(*Kernel::g_current_process, dest_addr, src_addr, size);
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
u8 ReadMMIO<u8>(MMIORegionPointer mmio_handler, VAddr addr) {
|
u8 ReadMMIO<u8>(MMIORegionPointer mmio_handler, VAddr addr) {
|
||||||
return mmio_handler->Read8(addr);
|
return mmio_handler->Read8(addr);
|
||||||
|
|
|
@ -211,7 +211,9 @@ void ReadBlock(const VAddr src_addr, void* dest_buffer, size_t size);
|
||||||
void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer,
|
void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer,
|
||||||
size_t size);
|
size_t size);
|
||||||
void WriteBlock(const VAddr dest_addr, const void* src_buffer, size_t size);
|
void WriteBlock(const VAddr dest_addr, const void* src_buffer, size_t size);
|
||||||
|
void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const size_t size);
|
||||||
void ZeroBlock(const VAddr dest_addr, const size_t size);
|
void ZeroBlock(const VAddr dest_addr, const size_t size);
|
||||||
|
void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr, size_t size);
|
||||||
void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size);
|
void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size);
|
||||||
|
|
||||||
u8* GetPointer(VAddr virtual_address);
|
u8* GetPointer(VAddr virtual_address);
|
||||||
|
|
Loading…
Reference in a new issue