ldr_ro: properly map CRS/CRO buffer

This commit is contained in:
Weiyi Wang 2018-11-07 12:30:58 -05:00
parent 617b388354
commit 3db8915356
5 changed files with 37 additions and 193 deletions

View file

@ -286,8 +286,6 @@ add_library(core STATIC
hle/service/ldr_ro/cro_helper.h hle/service/ldr_ro/cro_helper.h
hle/service/ldr_ro/ldr_ro.cpp hle/service/ldr_ro/ldr_ro.cpp
hle/service/ldr_ro/ldr_ro.h hle/service/ldr_ro/ldr_ro.h
hle/service/ldr_ro/memory_synchronizer.cpp
hle/service/ldr_ro/memory_synchronizer.h
hle/service/mic_u.cpp hle/service/mic_u.cpp
hle/service/mic_u.h hle/service/mic_u.h
hle/service/mvd/mvd.cpp hle/service/mvd/mvd.cpp

View file

@ -108,36 +108,13 @@ void RO::Initialize(Kernel::HLERequestContext& ctx) {
ResultCode result = RESULT_SUCCESS; ResultCode result = RESULT_SUCCESS;
if (crs_buffer_ptr != crs_address) { result = process->Map(crs_address, crs_buffer_ptr, crs_size, Kernel::VMAPermission::Read, true);
// 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 = process->vm_manager
.MapMemoryBlock(crs_address, crs_mem, 0, crs_size, Kernel::MemoryState::Code)
.Code();
if (result.IsError()) { if (result.IsError()) {
LOG_ERROR(Service_LDR, "Error mapping memory block {:08X}", result.raw); LOG_ERROR(Service_LDR, "Error mapping memory block {:08X}", result.raw);
rb.Push(result); rb.Push(result);
return; return;
} }
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;
}
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,
// 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); CROHelper crs(crs_address);
crs.InitCRS(); crs.InitCRS();
@ -148,8 +125,6 @@ void RO::Initialize(Kernel::HLERequestContext& ctx) {
return; return;
} }
slot->memory_synchronizer.SynchronizeOriginalMemory(*process);
slot->loaded_crs = crs_address; slot->loaded_crs = crs_address;
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
@ -266,13 +241,7 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) {
ResultCode result = RESULT_SUCCESS; ResultCode result = RESULT_SUCCESS;
if (cro_buffer_ptr != cro_address) { result = process->Map(cro_address, cro_buffer_ptr, cro_size, Kernel::VMAPermission::Read, true);
// 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 = process->vm_manager
.MapMemoryBlock(cro_address, cro_mem, 0, cro_size, Kernel::MemoryState::Code)
.Code();
if (result.IsError()) { if (result.IsError()) {
LOG_ERROR(Service_LDR, "Error mapping memory block {:08X}", result.raw); LOG_ERROR(Service_LDR, "Error mapping memory block {:08X}", result.raw);
rb.Push(result); rb.Push(result);
@ -280,32 +249,13 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) {
return; return;
} }
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);
process->vm_manager.UnmapRange(cro_address, cro_size);
rb.Push(result);
rb.Push<u32>(0);
return;
}
slot->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); CROHelper cro(cro_address);
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);
process->vm_manager.UnmapRange(cro_address, cro_size); process->Unmap(cro_address, cro_buffer_ptr, cro_size, Kernel::VMAPermission::ReadWrite,
true);
rb.Push(result); rb.Push(result);
rb.Push<u32>(0); rb.Push<u32>(0);
return; return;
@ -315,7 +265,8 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) {
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);
process->vm_manager.UnmapRange(cro_address, cro_size); process->Unmap(cro_address, cro_buffer_ptr, cro_size, Kernel::VMAPermission::ReadWrite,
true);
rb.Push(result); rb.Push(result);
rb.Push<u32>(0); rb.Push<u32>(0);
return; return;
@ -324,7 +275,8 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) {
result = cro.Link(slot->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);
process->vm_manager.UnmapRange(cro_address, cro_size); process->Unmap(cro_address, cro_buffer_ptr, cro_size, Kernel::VMAPermission::ReadWrite,
true);
rb.Push(result); rb.Push(result);
rb.Push<u32>(0); rb.Push<u32>(0);
return; return;
@ -334,32 +286,27 @@ void RO::LoadCRO(Kernel::HLERequestContext& ctx, bool link_on_load_bug_fix) {
u32 fix_size = cro.Fix(fix_level); u32 fix_size = cro.Fix(fix_level);
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) { if (fix_size != cro_size) {
result = process->vm_manager.UnmapRange(cro_address + fix_size, cro_size - fix_size); result = process->Unmap(cro_address + fix_size, cro_buffer_ptr + fix_size,
cro_size - fix_size, Kernel::VMAPermission::ReadWrite, true);
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);
process->vm_manager.UnmapRange(cro_address, cro_size); process->Unmap(cro_address, cro_buffer_ptr, cro_size, Kernel::VMAPermission::ReadWrite,
true);
rb.Push(result); rb.Push(result);
rb.Push<u32>(0); rb.Push<u32>(0);
return; return;
} }
} }
// Changes the block size
slot->memory_synchronizer.ResizeMemoryBlock(cro_address, cro_buffer_ptr, fix_size);
}
auto [exe_begin, exe_size] = cro.GetExecutablePages(); auto [exe_begin, exe_size] = cro.GetExecutablePages();
if (exe_begin) { if (exe_begin) {
result = process->vm_manager.ReprotectRange(exe_begin, exe_size, result = process->vm_manager.ReprotectRange(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);
process->vm_manager.UnmapRange(cro_address, fix_size); process->Unmap(cro_address, cro_buffer_ptr, cro_size, Kernel::VMAPermission::ReadWrite,
true);
rb.Push(result); rb.Push(result);
rb.Push<u32>(0); rb.Push<u32>(0);
return; return;
@ -433,16 +380,11 @@ void RO::UnloadCRO(Kernel::HLERequestContext& ctx) {
cro.Unrebase(false); cro.Unrebase(false);
slot->memory_synchronizer.SynchronizeOriginalMemory(*process); result = process->Unmap(cro_address, cro_buffer_ptr, fixed_size,
Kernel::VMAPermission::ReadWrite, true);
// TODO(wwylele): verify the behaviour when buffer_ptr == address
if (cro_address != cro_buffer_ptr) {
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);
} }
slot->memory_synchronizer.RemoveMemoryBlock(cro_address, cro_buffer_ptr);
}
Core::CPU().InvalidateCacheRange(cro_address, fixed_size); Core::CPU().InvalidateCacheRange(cro_address, fixed_size);
@ -486,8 +428,6 @@ void RO::LinkCRO(Kernel::HLERequestContext& ctx) {
LOG_ERROR(Service_LDR, "Error linking CRO {:08X}", result.raw); LOG_ERROR(Service_LDR, "Error linking CRO {:08X}", result.raw);
} }
slot->memory_synchronizer.SynchronizeOriginalMemory(*process);
rb.Push(result); rb.Push(result);
} }
@ -528,8 +468,6 @@ void RO::UnlinkCRO(Kernel::HLERequestContext& ctx) {
LOG_ERROR(Service_LDR, "Error unlinking CRO {:08X}", result.raw); LOG_ERROR(Service_LDR, "Error unlinking CRO {:08X}", result.raw);
} }
slot->memory_synchronizer.SynchronizeOriginalMemory(*process);
rb.Push(result); rb.Push(result);
} }
@ -552,18 +490,13 @@ void RO::Shutdown(Kernel::HLERequestContext& ctx) {
CROHelper crs(slot->loaded_crs); CROHelper crs(slot->loaded_crs);
crs.Unrebase(true); crs.Unrebase(true);
slot->memory_synchronizer.SynchronizeOriginalMemory(*process);
ResultCode result = RESULT_SUCCESS; ResultCode result = RESULT_SUCCESS;
// TODO(wwylele): verify the behaviour when buffer_ptr == address result = process->Unmap(slot->loaded_crs, crs_buffer_ptr, crs.GetFileSize(),
if (slot->loaded_crs != crs_buffer_ptr) { Kernel::VMAPermission::ReadWrite, true);
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);
} }
slot->memory_synchronizer.RemoveMemoryBlock(slot->loaded_crs, crs_buffer_ptr);
}
slot->loaded_crs = 0; slot->loaded_crs = 0;
rb.Push(result); rb.Push(result);

View file

@ -4,7 +4,6 @@
#pragma once #pragma once
#include "core/hle/service/ldr_ro/memory_synchronizer.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
namespace Core { namespace Core {
@ -14,7 +13,6 @@ class System;
namespace Service::LDR { namespace Service::LDR {
struct ClientSlot : public Kernel::SessionRequestHandler::SessionDataBase { struct ClientSlot : public Kernel::SessionRequestHandler::SessionDataBase {
MemorySynchronizer memory_synchronizer;
VAddr loaded_crs = 0; ///< the virtual address of the static module VAddr loaded_crs = 0; ///< the virtual address of the static module
}; };

View file

@ -1,41 +0,0 @@
// 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/kernel/process.h"
#include "core/hle/service/ldr_ro/memory_synchronizer.h"
namespace Service::LDR {
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(Kernel::Process& process) {
for (auto& block : memory_blocks) {
Memory::CopyBlock(process, block.original, block.mapping, block.size);
}
}
} // namespace Service::LDR

View file

@ -1,44 +0,0 @@
// 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 Kernel {
class Process;
}
namespace Service::LDR {
/**
* 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(Kernel::Process& process);
private:
struct MemoryBlock {
VAddr mapping;
VAddr original;
u32 size;
};
std::vector<MemoryBlock> memory_blocks;
auto FindMemoryBlock(VAddr mapping, VAddr original);
};
} // namespace Service::LDR