|
|
|
@@ -40,9 +40,9 @@ static const ResultCode ERROR_NOT_LOADED = // 0xD8A12C0D
|
|
|
|
|
ResultCode(static_cast<ErrorDescription>(13), ErrorModule::RO, ErrorSummary::InvalidState,
|
|
|
|
|
ErrorLevel::Permanent);
|
|
|
|
|
|
|
|
|
|
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() &&
|
|
|
|
|
static bool VerifyBufferState(Kernel::Process& process, VAddr buffer_ptr, u32 size) {
|
|
|
|
|
auto vma = process.vm_manager.FindVMA(buffer_ptr);
|
|
|
|
|
return vma != 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;
|
|
|
|
@@ -63,12 +63,15 @@ void RO::Initialize(Kernel::HLERequestContext& ctx) {
|
|
|
|
|
|
|
|
|
|
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");
|
|
|
|
|
rb.Push(ERROR_ALREADY_INITIALIZED);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto& slot = insert_result.first->second;
|
|
|
|
|
|
|
|
|
|
if (crs_size < CRO_HEADER_SIZE) {
|
|
|
|
|
LOG_ERROR(Service_LDR, "CRS is too small");
|
|
|
|
|
rb.Push(ERROR_BUFFER_TOO_SMALL);
|
|
|
|
@@ -93,7 +96,7 @@ void RO::Initialize(Kernel::HLERequestContext& ctx) {
|
|
|
|
|
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");
|
|
|
|
|
rb.Push(ERROR_INVALID_MEMORY_STATE);
|
|
|
|
|
return;
|
|
|
|
@@ -112,7 +115,7 @@ void RO::Initialize(Kernel::HLERequestContext& ctx) {
|
|
|
|
|
// 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
|
|
|
|
|
result = process->vm_manager
|
|
|
|
|
.MapMemoryBlock(crs_address, crs_mem, 0, crs_size, Kernel::MemoryState::Code)
|
|
|
|
|
.Code();
|
|
|
|
|
if (result.IsError()) {
|
|
|
|
@@ -121,15 +124,15 @@ void RO::Initialize(Kernel::HLERequestContext& ctx) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result = Kernel::g_current_process->vm_manager.ReprotectRange(crs_address, crs_size,
|
|
|
|
|
Kernel::VMAPermission::Read);
|
|
|
|
|
result =
|
|
|
|
|
process->vm_manager.ReprotectRange(crs_address, crs_size, Kernel::VMAPermission::Read);
|
|
|
|
|
if (result.IsError()) {
|
|
|
|
|
LOG_ERROR(Service_LDR, "Error reprotecting memory block %08X", result.raw);
|
|
|
|
|
rb.Push(result);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memory_synchronizer.AddMemoryBlock(crs_address, crs_buffer_ptr, crs_size);
|
|
|
|
|
slot.memory_synchronizer.AddMemoryBlock(crs_address, crs_buffer_ptr, crs_size);
|
|
|
|
|
} else {
|
|
|
|
|
// Do nothing if buffer_ptr == address
|
|
|
|
|
// TODO(wwylele): verify this behaviour. This is only seen in the web browser app,
|
|
|
|
@@ -148,9 +151,9 @@ void RO::Initialize(Kernel::HLERequestContext& ctx) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memory_synchronizer.SynchronizeOriginalMemory();
|
|
|
|
|
slot.memory_synchronizer.SynchronizeOriginalMemory(*process);
|
|
|
|
|
|
|
|
|
|
loaded_crs = crs_address;
|
|
|
|
|
slot.loaded_crs = crs_address;
|
|
|
|
|
|
|
|
|
|
rb.Push(RESULT_SUCCESS);
|
|
|
|
|
}
|
|
|
|
@@ -204,12 +207,14 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) {
|
|
|
|
|
|
|
|
|
|
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");
|
|
|
|
|
rb.Push(ERROR_NOT_INITIALIZED);
|
|
|
|
|
rb.Push<u32>(0);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
auto& slot = find_result->second;
|
|
|
|
|
|
|
|
|
|
if (cro_size < CRO_HEADER_SIZE) {
|
|
|
|
|
LOG_ERROR(Service_LDR, "CRO too small");
|
|
|
|
@@ -239,7 +244,7 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) {
|
|
|
|
|
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");
|
|
|
|
|
rb.Push(ERROR_INVALID_MEMORY_STATE);
|
|
|
|
|
rb.Push<u32>(0);
|
|
|
|
@@ -268,7 +273,7 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) {
|
|
|
|
|
// 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
|
|
|
|
|
result = process->vm_manager
|
|
|
|
|
.MapMemoryBlock(cro_address, cro_mem, 0, cro_size, Kernel::MemoryState::Code)
|
|
|
|
|
.Code();
|
|
|
|
|
if (result.IsError()) {
|
|
|
|
@@ -278,17 +283,17 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result = Kernel::g_current_process->vm_manager.ReprotectRange(cro_address, cro_size,
|
|
|
|
|
Kernel::VMAPermission::Read);
|
|
|
|
|
result =
|
|
|
|
|
process->vm_manager.ReprotectRange(cro_address, cro_size, Kernel::VMAPermission::Read);
|
|
|
|
|
if (result.IsError()) {
|
|
|
|
|
LOG_ERROR(Service_LDR, "Error reprotecting memory block %08X", result.raw);
|
|
|
|
|
Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size);
|
|
|
|
|
process->vm_manager.UnmapRange(cro_address, cro_size);
|
|
|
|
|
rb.Push(result);
|
|
|
|
|
rb.Push<u32>(0);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memory_synchronizer.AddMemoryBlock(cro_address, cro_buffer_ptr, cro_size);
|
|
|
|
|
slot.memory_synchronizer.AddMemoryBlock(cro_address, cro_buffer_ptr, cro_size);
|
|
|
|
|
} else {
|
|
|
|
|
// Do nothing if buffer_ptr == address
|
|
|
|
|
// TODO(wwylele): verify this behaviour.
|
|
|
|
@@ -303,45 +308,44 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) {
|
|
|
|
|
result = cro.VerifyHash(cro_size, crr_address);
|
|
|
|
|
if (result.IsError()) {
|
|
|
|
|
LOG_ERROR(Service_LDR, "Error verifying CRO in CRR %08X", result.raw);
|
|
|
|
|
Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size);
|
|
|
|
|
process->vm_manager.UnmapRange(cro_address, cro_size);
|
|
|
|
|
rb.Push(result);
|
|
|
|
|
rb.Push<u32>(0);
|
|
|
|
|
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);
|
|
|
|
|
if (result.IsError()) {
|
|
|
|
|
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<u32>(0);
|
|
|
|
|
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()) {
|
|
|
|
|
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<u32>(0);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cro.Register(loaded_crs, auto_link);
|
|
|
|
|
cro.Register(slot.loaded_crs, auto_link);
|
|
|
|
|
|
|
|
|
|
u32 fix_size = cro.Fix(fix_level);
|
|
|
|
|
|
|
|
|
|
memory_synchronizer.SynchronizeOriginalMemory();
|
|
|
|
|
slot.memory_synchronizer.SynchronizeOriginalMemory(*process);
|
|
|
|
|
|
|
|
|
|
// TODO(wwylele): verify the behaviour when buffer_ptr == address
|
|
|
|
|
if (cro_buffer_ptr != cro_address) {
|
|
|
|
|
if (fix_size != cro_size) {
|
|
|
|
|
result = Kernel::g_current_process->vm_manager.UnmapRange(cro_address + fix_size,
|
|
|
|
|
cro_size - fix_size);
|
|
|
|
|
result = process->vm_manager.UnmapRange(cro_address + fix_size, cro_size - fix_size);
|
|
|
|
|
if (result.IsError()) {
|
|
|
|
|
LOG_ERROR(Service_LDR, "Error unmapping memory block %08X", result.raw);
|
|
|
|
|
Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size);
|
|
|
|
|
process->vm_manager.UnmapRange(cro_address, cro_size);
|
|
|
|
|
rb.Push(result);
|
|
|
|
|
rb.Push<u32>(0);
|
|
|
|
|
return;
|
|
|
|
@@ -349,18 +353,18 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
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);
|
|
|
|
|
result = 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);
|
|
|
|
|
process->vm_manager.UnmapRange(cro_address, fix_size);
|
|
|
|
|
rb.Push(result);
|
|
|
|
|
rb.Push<u32>(0);
|
|
|
|
|
return;
|
|
|
|
@@ -389,11 +393,13 @@ void RO::UnloadCRO(Kernel::HLERequestContext& ctx) {
|
|
|
|
|
|
|
|
|
|
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");
|
|
|
|
|
rb.Push(ERROR_NOT_INITIALIZED);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
auto& slot = find_result->second;
|
|
|
|
|
|
|
|
|
|
if (cro_address & Memory::PAGE_MASK) {
|
|
|
|
|
LOG_ERROR(Service_LDR, "CRO address is not aligned");
|
|
|
|
@@ -411,9 +417,9 @@ void RO::UnloadCRO(Kernel::HLERequestContext& ctx) {
|
|
|
|
|
|
|
|
|
|
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()) {
|
|
|
|
|
LOG_ERROR(Service_LDR, "Error unlinking CRO %08X", result.raw);
|
|
|
|
|
rb.Push(result);
|
|
|
|
@@ -433,15 +439,15 @@ void RO::UnloadCRO(Kernel::HLERequestContext& ctx) {
|
|
|
|
|
|
|
|
|
|
cro.Unrebase(false);
|
|
|
|
|
|
|
|
|
|
memory_synchronizer.SynchronizeOriginalMemory();
|
|
|
|
|
slot.memory_synchronizer.SynchronizeOriginalMemory(*process);
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
result = 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);
|
|
|
|
|
slot.memory_synchronizer.RemoveMemoryBlock(cro_address, cro_buffer_ptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Core::CPU().InvalidateCacheRange(cro_address, fixed_size);
|
|
|
|
@@ -460,11 +466,13 @@ void RO::LinkCRO(Kernel::HLERequestContext& ctx) {
|
|
|
|
|
|
|
|
|
|
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");
|
|
|
|
|
rb.Push(ERROR_NOT_INITIALIZED);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
auto& slot = find_result->second;
|
|
|
|
|
|
|
|
|
|
if (cro_address & Memory::PAGE_MASK) {
|
|
|
|
|
LOG_ERROR(Service_LDR, "CRO address is not aligned");
|
|
|
|
@@ -480,12 +488,12 @@ void RO::LinkCRO(Kernel::HLERequestContext& ctx) {
|
|
|
|
|
|
|
|
|
|
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()) {
|
|
|
|
|
LOG_ERROR(Service_LDR, "Error linking CRO %08X", result.raw);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memory_synchronizer.SynchronizeOriginalMemory();
|
|
|
|
|
slot.memory_synchronizer.SynchronizeOriginalMemory(*process);
|
|
|
|
|
|
|
|
|
|
rb.Push(result);
|
|
|
|
|
}
|
|
|
|
@@ -501,11 +509,13 @@ void RO::UnlinkCRO(Kernel::HLERequestContext& ctx) {
|
|
|
|
|
|
|
|
|
|
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");
|
|
|
|
|
rb.Push(ERROR_NOT_INITIALIZED);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
auto& slot = find_result->second;
|
|
|
|
|
|
|
|
|
|
if (cro_address & Memory::PAGE_MASK) {
|
|
|
|
|
LOG_ERROR(Service_LDR, "CRO address is not aligned");
|
|
|
|
@@ -521,12 +531,12 @@ void RO::UnlinkCRO(Kernel::HLERequestContext& ctx) {
|
|
|
|
|
|
|
|
|
|
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()) {
|
|
|
|
|
LOG_ERROR(Service_LDR, "Error unlinking CRO %08X", result.raw);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memory_synchronizer.SynchronizeOriginalMemory();
|
|
|
|
|
slot.memory_synchronizer.SynchronizeOriginalMemory(*process);
|
|
|
|
|
|
|
|
|
|
rb.Push(result);
|
|
|
|
|
}
|
|
|
|
@@ -540,29 +550,31 @@ void RO::Shutdown(Kernel::HLERequestContext& ctx) {
|
|
|
|
|
|
|
|
|
|
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");
|
|
|
|
|
rb.Push(ERROR_NOT_INITIALIZED);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
auto& slot = find_result->second;
|
|
|
|
|
|
|
|
|
|
CROHelper crs(loaded_crs);
|
|
|
|
|
CROHelper crs(slot.loaded_crs);
|
|
|
|
|
crs.Unrebase(true);
|
|
|
|
|
|
|
|
|
|
memory_synchronizer.SynchronizeOriginalMemory();
|
|
|
|
|
slot.memory_synchronizer.SynchronizeOriginalMemory(*process);
|
|
|
|
|
|
|
|
|
|
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 (slot.loaded_crs != crs_buffer_ptr) {
|
|
|
|
|
result = process->vm_manager.UnmapRange(slot.loaded_crs, crs.GetFileSize());
|
|
|
|
|
if (result.IsError()) {
|
|
|
|
|
LOG_ERROR(Service_LDR, "Error unmapping CRS %08X", result.raw);
|
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|