kernel/svc: Implement svcMapProcessCodeMemory

This is utilized for mapping code modules into memory. Notably, the
ldr service would call this in order to map objects into memory.
This commit is contained in:
Lioncash 2019-04-11 23:21:13 -04:00
parent ea80e2bc57
commit 76a2465655
4 changed files with 131 additions and 1 deletions

View file

@ -1189,6 +1189,74 @@ static ResultCode QueryMemory(Core::System& system, VAddr memory_info_address,
query_address); query_address);
} }
static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
u64 src_address, u64 size) {
LOG_DEBUG(Kernel_SVC,
"called. process_handle=0x{:08X}, dst_address=0x{:016X}, "
"src_address=0x{:016X}, size=0x{:016X}",
process_handle, dst_address, src_address, size);
if (!Common::Is4KBAligned(src_address)) {
LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
src_address);
return ERR_INVALID_ADDRESS;
}
if (!Common::Is4KBAligned(dst_address)) {
LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
dst_address);
return ERR_INVALID_ADDRESS;
}
if (size == 0 || !Common::Is4KBAligned(size)) {
LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size);
return ERR_INVALID_SIZE;
}
if (!IsValidAddressRange(dst_address, size)) {
LOG_ERROR(Kernel_SVC,
"Destination address range overflows the address space (dst_address=0x{:016X}, "
"size=0x{:016X}).",
dst_address, size);
return ERR_INVALID_ADDRESS_STATE;
}
if (!IsValidAddressRange(src_address, size)) {
LOG_ERROR(Kernel_SVC,
"Source address range overflows the address space (src_address=0x{:016X}, "
"size=0x{:016X}).",
src_address, size);
return ERR_INVALID_ADDRESS_STATE;
}
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
auto process = handle_table.Get<Process>(process_handle);
if (!process) {
LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
process_handle);
return ERR_INVALID_HANDLE;
}
auto& vm_manager = process->VMManager();
if (!vm_manager.IsWithinAddressSpace(src_address, size)) {
LOG_ERROR(Kernel_SVC,
"Source address range is not within the address space (src_address=0x{:016X}, "
"size=0x{:016X}).",
src_address, size);
return ERR_INVALID_ADDRESS_STATE;
}
if (!vm_manager.IsWithinASLRRegion(dst_address, size)) {
LOG_ERROR(Kernel_SVC,
"Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
"size=0x{:016X}).",
dst_address, size);
return ERR_INVALID_MEMORY_RANGE;
}
return vm_manager.MapCodeMemory(dst_address, src_address, size);
}
/// Exits the current process /// Exits the current process
static void ExitProcess(Core::System& system) { static void ExitProcess(Core::System& system) {
auto* current_process = system.Kernel().CurrentProcess(); auto* current_process = system.Kernel().CurrentProcess();
@ -2217,7 +2285,7 @@ static const FunctionDef SVC_Table[] = {
{0x74, nullptr, "MapProcessMemory"}, {0x74, nullptr, "MapProcessMemory"},
{0x75, nullptr, "UnmapProcessMemory"}, {0x75, nullptr, "UnmapProcessMemory"},
{0x76, SvcWrap<QueryProcessMemory>, "QueryProcessMemory"}, {0x76, SvcWrap<QueryProcessMemory>, "QueryProcessMemory"},
{0x77, nullptr, "MapProcessCodeMemory"}, {0x77, SvcWrap<MapProcessCodeMemory>, "MapProcessCodeMemory"},
{0x78, nullptr, "UnmapProcessCodeMemory"}, {0x78, nullptr, "UnmapProcessCodeMemory"},
{0x79, nullptr, "CreateProcess"}, {0x79, nullptr, "CreateProcess"},
{0x7A, nullptr, "StartProcess"}, {0x7A, nullptr, "StartProcess"},

View file

@ -44,6 +44,13 @@ void SvcWrap(Core::System& system) {
func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw); func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw);
} }
template <ResultCode func(Core::System&, u32, u64, u64, u64)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
Param(system, 2), Param(system, 3))
.raw);
}
template <ResultCode func(Core::System&, u32*)> template <ResultCode func(Core::System&, u32*)>
void SvcWrap(Core::System& system) { void SvcWrap(Core::System& system) {
u32 param = 0; u32 param = 0;

View file

@ -302,6 +302,35 @@ ResultVal<VAddr> VMManager::SetHeapSize(u64 size) {
return MakeResult<VAddr>(heap_region_base); return MakeResult<VAddr>(heap_region_base);
} }
ResultCode VMManager::MapCodeMemory(VAddr dst_address, VAddr src_address, u64 size) {
constexpr auto ignore_attribute = MemoryAttribute::LockedForIPC | MemoryAttribute::DeviceMapped;
const auto src_check_result = CheckRangeState(
src_address, size, MemoryState::All, MemoryState::Heap, VMAPermission::All,
VMAPermission::ReadWrite, MemoryAttribute::Mask, MemoryAttribute::None, ignore_attribute);
if (src_check_result.Failed()) {
return src_check_result.Code();
}
const auto mirror_result =
MirrorMemory(dst_address, src_address, size, MemoryState::ModuleCode);
if (mirror_result.IsError()) {
return mirror_result;
}
// Ensure we lock the source memory region.
const auto src_vma_result = CarveVMARange(src_address, size);
if (src_vma_result.Failed()) {
return src_vma_result.Code();
}
auto src_vma_iter = *src_vma_result;
src_vma_iter->second.attribute = MemoryAttribute::Locked;
Reprotect(src_vma_iter, VMAPermission::Read);
// The destination memory region is fine as is, however we need to make it read-only.
return ReprotectRange(dst_address, size, VMAPermission::Read);
}
MemoryInfo VMManager::QueryMemory(VAddr address) const { MemoryInfo VMManager::QueryMemory(VAddr address) const {
const auto vma = FindVMA(address); const auto vma = FindVMA(address);
MemoryInfo memory_info{}; MemoryInfo memory_info{};

View file

@ -43,6 +43,9 @@ enum class VMAPermission : u8 {
ReadExecute = Read | Execute, ReadExecute = Read | Execute,
WriteExecute = Write | Execute, WriteExecute = Write | Execute,
ReadWriteExecute = Read | Write | Execute, ReadWriteExecute = Read | Write | Execute,
// Used as a wildcard when checking permissions across memory ranges
All = 0xFF,
}; };
constexpr VMAPermission operator|(VMAPermission lhs, VMAPermission rhs) { constexpr VMAPermission operator|(VMAPermission lhs, VMAPermission rhs) {
@ -152,6 +155,9 @@ enum class MemoryState : u32 {
FlagUncached = 1U << 24, FlagUncached = 1U << 24,
FlagCodeMemory = 1U << 25, FlagCodeMemory = 1U << 25,
// Wildcard used in range checking to indicate all states.
All = 0xFFFFFFFF,
// Convenience flag sets to reduce repetition // Convenience flag sets to reduce repetition
IPCFlags = FlagIPC0 | FlagIPC3 | FlagIPC1, IPCFlags = FlagIPC0 | FlagIPC3 | FlagIPC1,
@ -415,6 +421,26 @@ public:
/// ///
ResultVal<VAddr> SetHeapSize(u64 size); ResultVal<VAddr> SetHeapSize(u64 size);
/// Maps a region of memory as code memory.
///
/// @param dst_address The base address of the region to create the aliasing memory region.
/// @param src_address The base address of the region to be aliased.
/// @param size The total amount of memory to map in bytes.
///
/// @pre Both memory regions lie within the actual addressable address space.
///
/// @post After this function finishes execution, assuming success, then the address range
/// [dst_address, dst_address+size) will alias the memory region,
/// [src_address, src_address+size).
/// <p>
/// What this also entails is as follows:
/// 1. The aliased region gains the Locked memory attribute.
/// 2. The aliased region becomes read-only.
/// 3. The aliasing region becomes read-only.
/// 4. The aliasing region is created with a memory state of MemoryState::CodeModule.
///
ResultCode MapCodeMemory(VAddr dst_address, VAddr src_address, u64 size);
/// Queries the memory manager for information about the given address. /// Queries the memory manager for information about the given address.
/// ///
/// @param address The address to query the memory manager about for information. /// @param address The address to query the memory manager about for information.