Compare commits

...

3 Commits

Author SHA1 Message Date
Subv
d356a8eceb Services/LDR: Implemented the ldr:ro LoadNRO andLoadNRR service functions.
The implementation was based on Atmosphere's implementation of the ldr:ro service.
This is an incomplete commit, the error condition checks are missing.
2018-10-10 19:10:04 -05:00
Subv
9ff787bf61 Kernel/Memory: Added a function to first a suitable guest address at which to allocate a region of a given size. 2018-10-10 19:10:03 -05:00
Subv
5fb8854a11 Kernel/Memory: Allow specifying a MemoryState when mirroring memory.
Not all mirrored memory should have the Mapped memory state.
2018-10-10 19:10:02 -05:00
5 changed files with 144 additions and 7 deletions

View File

@@ -295,7 +295,7 @@ ResultCode Process::HeapFree(VAddr target, u32 size) {
return RESULT_SUCCESS;
}
ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) {
auto vma = vm_manager.FindVMA(src_addr);
ASSERT_MSG(vma != vm_manager.vma_map.end(), "Invalid memory address");
@@ -311,7 +311,7 @@ ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
CASCADE_RESULT(auto new_vma,
vm_manager.MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size,
MemoryState::Mapped));
state));
// Protect mirror with permissions from old region
vm_manager.Reprotect(new_vma, vma->second.permissions);
// Remove permissions from old region

View File

@@ -233,7 +233,8 @@ public:
ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
ResultCode HeapFree(VAddr target, u32 size);
ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size);
ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size,
MemoryState state = MemoryState::Mapped);
ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size);

View File

@@ -119,6 +119,28 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target,
return MakeResult<VMAHandle>(MergeAdjacent(vma_handle));
}
ResultVal<VAddr> VMManager::FindFreeRegion(u32 size) {
VAddr base = GetAddressSpaceBaseAddress();
// Find the first Free VMA.
VMAHandle vma_handle = std::find_if(vma_map.begin(), vma_map.end(), [&](const auto& vma) {
if (vma.second.type != VMAType::Free)
return false;
VAddr vma_end = vma.second.base + vma.second.size;
return vma_end > base && vma_end >= base + size;
});
if (vma_handle == vma_map.end()) {
// TODO(Subv): Find the correct error code here.
return ResultCode(-1);
}
VAddr target = std::max(base, vma_handle->second.base);
return MakeResult<VAddr>(target);
}
ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* memory, u64 size,
MemoryState state) {
ASSERT(memory != nullptr);

View File

@@ -147,6 +147,14 @@ public:
ResultVal<VMAHandle> MapMemoryBlock(VAddr target, std::shared_ptr<std::vector<u8>> block,
std::size_t offset, u64 size, MemoryState state);
/**
* Finds the first free address that can hold a region of the desired size.
*
* @param size Size of the desired region.
* @returns The found free address.
*/
ResultVal<VAddr> FindFreeRegion(u32 size);
/**
* Maps an unmanaged host memory pointer at a given address.
*

View File

@@ -1,9 +1,11 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <memory>
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/ldr/ldr.h"
#include "core/hle/service/service.h"
@@ -59,16 +61,120 @@ public:
explicit RelocatableObject() : ServiceFramework{"ldr:ro"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "LoadNro"},
{0, &RelocatableObject::LoadNro, "LoadNro"},
{1, nullptr, "UnloadNro"},
{2, nullptr, "LoadNrr"},
{2, &RelocatableObject::LoadNrr, "LoadNrr"},
{3, nullptr, "UnloadNrr"},
{4, nullptr, "Initialize"},
{4, &RelocatableObject::Initialize, "Initialize"},
};
// clang-format on
RegisterHandlers(functions);
}
void LoadNrr(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
rp.Skip(2, false);
auto address = rp.Pop<VAddr>();
auto size = rp.Pop<u64>();
auto process = Core::CurrentProcess();
auto& vm_manager = process->VMManager();
// Find a region we can use to mirror the NRR memory.
auto map_address = vm_manager.FindFreeRegion(size);
ASSERT(map_address.Succeeded());
auto result = process->MirrorMemory(*map_address, address, size,
Kernel::MemoryState::ModuleCodeStatic);
ASSERT(result == RESULT_SUCCESS);
// TODO(Subv): Reprotect the source memory to make it inaccessible.
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void LoadNro(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
rp.Skip(2, false);
auto nro_address = rp.Pop<VAddr>();
auto nro_size = rp.Pop<u64>();
auto bss_address = rp.Pop<VAddr>();
auto bss_size = rp.Pop<u64>();
// TODO(Subv): Verify the NRO with the currently-loaded NRR.
NroHeader nro_header;
Memory::ReadBlock(nro_address, &nro_header, sizeof(nro_header));
auto process = Core::CurrentProcess();
auto& vm_manager = process->VMManager();
auto map_address = vm_manager.FindFreeRegion(nro_size + bss_size);
ASSERT(map_address.Succeeded());
auto result = process->MirrorMemory(*map_address, nro_address, nro_size,
Kernel::MemoryState::ModuleCodeStatic);
ASSERT(result == RESULT_SUCCESS);
if (bss_size > 0) {
result = process->MirrorMemory(*map_address + nro_size, bss_address, bss_size,
Kernel::MemoryState::ModuleCodeStatic);
ASSERT(result == RESULT_SUCCESS);
}
vm_manager.ReprotectRange(*map_address, nro_header.text_size,
Kernel::VMAPermission::ReadExecute);
vm_manager.ReprotectRange(*map_address + nro_header.ro_offset, nro_header.ro_size,
Kernel::VMAPermission::Read);
vm_manager.ReprotectRange(*map_address + nro_header.rw_offset,
nro_header.rw_size + bss_size, Kernel::VMAPermission::ReadWrite);
Core::System::GetInstance().ArmInterface(0).ClearInstructionCache();
Core::System::GetInstance().ArmInterface(1).ClearInstructionCache();
Core::System::GetInstance().ArmInterface(2).ClearInstructionCache();
Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
// TODO(Subv): Reprotect the source memory to make it inaccessible.
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push(nro_address);
LOG_WARNING(Service, "called");
}
void Initialize(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
private:
struct NroHeader {
u32_le entrypoint_insn;
u32_le mod_offset;
INSERT_PADDING_WORDS(2);
u32_le magic;
INSERT_PADDING_WORDS(1);
u32_le nro_size;
INSERT_PADDING_WORDS(1);
u32_le text_offset;
u32_le text_size;
u32_le ro_offset;
u32_le ro_size;
u32_le rw_offset;
u32_le rw_size;
u32_le bss_size;
INSERT_PADDING_WORDS(1);
std::array<u8, 0x20> build_id;
INSERT_PADDING_BYTES(0x20);
};
static_assert(sizeof(NroHeader) == 128, "NroHeader has invalid size.");
};
void InstallInterfaces(SM::ServiceManager& sm) {