From ff87ff259250a807044d852699926371dde51847 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 8 Jun 2020 18:11:16 -0700 Subject: [PATCH] emummc: update for exo2 --- emummc/emummc.json | 2 + emummc/source/main.c | 120 ++++++++++++++++++++++++++++++++++-- emummc/source/nx/smc.c | 76 +++++++++++------------ emummc/source/nx/smc.h | 8 +-- emummc/source/nx/svc.h | 98 +++++++++++++++++++++++++++++ emummc/source/nx/svc.s | 63 +++++++++++++++++++ emummc/source/utils/types.h | 4 ++ emummc/source/utils/util.h | 6 ++ 8 files changed, 328 insertions(+), 49 deletions(-) diff --git a/emummc/emummc.json b/emummc/emummc.json index 6cb2d5c90..19471c53d 100644 --- a/emummc/emummc.json +++ b/emummc/emummc.json @@ -128,6 +128,8 @@ "svcUnmapDeviceAddressSpace": "0x5c", "svcGetSystemInfo": "0x6f", "svcSetProcessMemoryPermission": "0x73", + "svcMapProcessMemory": "0x74", + "svcUnmapProcessMemory": "0x75", "svcCallSecureMonitor": "0x7f" } } diff --git a/emummc/source/main.c b/emummc/source/main.c index 520c1ea22..e93d3761d 100644 --- a/emummc/source/main.c +++ b/emummc/source/main.c @@ -39,8 +39,11 @@ void hook_function(uintptr_t source, uintptr_t target); void *__stack_top; uintptr_t text_base; +size_t fs_code_size; char inner_heap[INNER_HEAP_SIZE]; size_t inner_heap_size = INNER_HEAP_SIZE; +Handle self_proc_handle = 0; +u8 *fs_rw_mapping = NULL; extern char _start; extern char __argdata__; @@ -148,15 +151,117 @@ void __initheap(void) fake_heap_end = (char *)addr + size; } +static void _receive_process_handle_thread(void *_session_handle) { + Result rc; + + // Convert the argument to a handle we can use. + Handle session_handle = (Handle)(uintptr_t)_session_handle; + + // Receive the request from the client thread. + memset(armGetTls(), 0, 0x10); + s32 idx = 0; + rc = svcReplyAndReceive(&idx, &session_handle, 1, INVALID_HANDLE, UINT64_MAX); + if (rc != 0) + { + fatal_abort(Fatal_BadResult); + } + + // Set the process handle. + self_proc_handle = ((u32 *)armGetTls())[3]; + + // Close the session. + svcCloseHandle(session_handle); + + // Terminate ourselves. + svcExitThread(); + + // This code will never execute. + while (true); +} + +static void _init_process_handle(void) { + Result rc; + u8 temp_thread_stack[0x1000]; + + // Create a new session to transfer our process handle to ourself + Handle server_handle, client_handle; + rc = svcCreateSession(&server_handle, &client_handle, 0, 0); + if (rc != 0) + { + fatal_abort(Fatal_BadResult); + } + + // Create a new thread to receive our handle. + Handle thread_handle; + rc = svcCreateThread(&thread_handle, _receive_process_handle_thread, (void *)(uintptr_t)server_handle, temp_thread_stack + sizeof(temp_thread_stack), 0x20, 3); + if (rc != 0) + { + fatal_abort(Fatal_BadResult); + } + + // Start the new thread. + rc = svcStartThread(thread_handle); + if (rc != 0) + { + fatal_abort(Fatal_BadResult); + } + + // Send the message. + static const u32 SendProcessHandleMessage[4] = { 0x00000000, 0x80000000, 0x00000002, CUR_PROCESS_HANDLE }; + memcpy(armGetTls(), SendProcessHandleMessage, sizeof(SendProcessHandleMessage)); + svcSendSyncRequest(client_handle); + + // Close the session handle. + svcCloseHandle(client_handle); + + // Wait for the thread to be done. + rc = svcWaitSynchronizationSingle(thread_handle, UINT64_MAX); + if (rc != 0) + { + fatal_abort(Fatal_BadResult); + } + + // Close the thread handle. + svcCloseHandle(thread_handle); +} + +static void _map_fs_rw(void) { + Result rc; + + do { + fs_rw_mapping = (u8 *)(smcGenerateRandomU64() & 0xFFFFFF000ull); + rc = svcMapProcessMemory(fs_rw_mapping, self_proc_handle, INJECT_OFFSET(u64, 0), fs_code_size); + } while (rc == 0xDC01 || rc == 0xD401); + + if (rc != 0) + { + fatal_abort(Fatal_BadResult); + } +} + +static void _unmap_fs_rw(void) { + Result rc = svcUnmapProcessMemory(fs_rw_mapping, self_proc_handle, INJECT_OFFSET(u64, 0), fs_code_size); + if (rc != 0) + { + fatal_abort(Fatal_BadResult); + } + + fs_rw_mapping = NULL; +} + +static void _write32(uintptr_t source, u32 value) { + *((u32 *)(fs_rw_mapping + (source - INJECT_OFFSET(u64, 0)))) = value; +} + void hook_function(uintptr_t source, uintptr_t target) { u32 branch_opcode = GENERATE_BRANCH(source, target); - smcWriteAddress32((void *)source, branch_opcode); + _write32(source, branch_opcode); } void write_nop(uintptr_t source) { - smcWriteAddress32((void *)source, GENERATE_NOP()); + _write32(source, GENERATE_NOP()); } void write_adrp_add(int reg, uintptr_t pc, uintptr_t add_rel_offset, intptr_t destination) @@ -167,8 +272,8 @@ void write_adrp_add(int reg, uintptr_t pc, uintptr_t add_rel_offset, intptr_t de uint32_t opcode_adrp = GENERATE_ADRP(reg, offset); uint32_t opcode_add = GENERATE_ADD(reg, reg, (destination & 0x00000FFF)); - smcWriteAddress32((void *)pc, opcode_adrp); - smcWriteAddress32((void *)add_opcode_location, opcode_add); + _write32(pc, opcode_adrp); + _write32(add_opcode_location, opcode_add); } void setup_hooks(void) @@ -306,14 +411,21 @@ void __init() text_base = meminfo.addr; + // Get code size + svcQueryMemory(&meminfo, &pageinfo, INJECT_OFFSET(u64, 0)); + fs_code_size = meminfo.size; + load_emummc_ctx(); fs_offsets = get_fs_offsets(emuMMC_ctx.fs_ver); + _init_process_handle(); + _map_fs_rw(); setup_hooks(); populate_function_pointers(); write_nops(); setup_nintendo_paths(); + _unmap_fs_rw(); clock_enable_i2c5(); i2c_init(); diff --git a/emummc/source/nx/smc.c b/emummc/source/nx/smc.c index 921d9be69..dcb9fce87 100644 --- a/emummc/source/nx/smc.c +++ b/emummc/source/nx/smc.c @@ -6,6 +6,7 @@ #include #include #include "smc.h" +#include "../utils/fatal.h" void smcRebootToRcm(void) { @@ -117,45 +118,6 @@ Result smcReadWriteRegister(u32 phys_addr, u32 value, u32 mask) return rc; } -static Result _smcWriteAddress(void *dst_addr, u64 val, u32 size) -{ - SecmonArgs args; - args.X[0] = 0xF0000003; /* smcAmsWriteAddress */ - args.X[1] = (u64)dst_addr; /* DRAM address */ - args.X[2] = val; /* value */ - args.X[3] = size; /* Amount to write */ - Result rc = svcCallSecureMonitor(&args); - if (rc == 0) - { - if (args.X[0] != 0) - { - /* SPL result n = SMC result n */ - rc = (26u | ((u32)args.X[0] << 9u)); - } - } - return rc; -} - -Result smcWriteAddress8(void *dst_addr, u8 val) -{ - return _smcWriteAddress(dst_addr, val, 1); -} - -Result smcWriteAddress16(void *dst_addr, u16 val) -{ - return _smcWriteAddress(dst_addr, val, 2); -} - -Result smcWriteAddress32(void *dst_addr, u32 val) -{ - return _smcWriteAddress(dst_addr, val, 4); -} - -Result smcWriteAddress64(void *dst_addr, u64 val) -{ - return _smcWriteAddress(dst_addr, val, 8); -} - Result smcGetEmummcConfig(exo_emummc_mmc_t mmc_id, exo_emummc_config_t *out_cfg, void *out_paths) { SecmonArgs args; @@ -177,4 +139,38 @@ Result smcGetEmummcConfig(exo_emummc_mmc_t mmc_id, exo_emummc_config_t *out_cfg, } return rc; -} \ No newline at end of file +} + +Result smcGenerateRandomBytes(void *dst, u32 size) +{ + SecmonArgs args; + args.X[0] = 0xC3000006; /* smcGenerateRandomBytes */ + args.X[1] = size; + Result rc = svcCallSecureMonitor(&args); + if (rc == 0) + { + if (args.X[0] != 0) + { + /* SPL result n = SMC result n */ + rc = (26u | ((u32)args.X[0] << 9u)); + } + if (rc == 0) + { + memcpy(dst, &args.X[1], size); + } + } + return rc; +} + +u64 smcGenerateRandomU64(void) +{ + u64 random; + + Result rc = smcGenerateRandomBytes(&random, sizeof(random)); + if (rc != 0) + { + fatal_abort(Fatal_BadResult); + } + + return random; +} diff --git a/emummc/source/nx/smc.h b/emummc/source/nx/smc.h index cb8069b28..215877f97 100644 --- a/emummc/source/nx/smc.h +++ b/emummc/source/nx/smc.h @@ -78,13 +78,11 @@ Result smcCopyFromIram(void *dst_addr, uintptr_t iram_addr, u32 size); Result smcReadWriteRegister(u32 phys_addr, u32 value, u32 mask); -Result smcWriteAddress8(void *dst_addr, u8 val); -Result smcWriteAddress16(void *dst_addr, u16 val); -Result smcWriteAddress32(void *dst_addr, u32 val); -Result smcWriteAddress64(void *dst_addr, u64 val); - Result smcGetEmummcConfig(exo_emummc_mmc_t mmc_id, exo_emummc_config_t *out_cfg, void *out_paths); +Result smcGenerateRandomBytes(void *dst, u32 size); +u64 smcGenerateRandomU64(void); + #ifdef __cplusplus } #endif diff --git a/emummc/source/nx/svc.h b/emummc/source/nx/svc.h index 05d51b9ec..320666390 100644 --- a/emummc/source/nx/svc.h +++ b/emummc/source/nx/svc.h @@ -106,6 +106,104 @@ Result svcSetProcessMemoryPermission(Handle proc, u64 addr, u64 size, u32 perm); */ Result svcSetMemoryPermission(void* addr, u64 size, u32 perm); +/** + * @brief Creates a thread. + * @return Result code. + * @note Syscall number 0x08. + */ +Result svcCreateThread(Handle* out, void* entry, void* arg, void* stack_top, int prio, int cpuid); + +/** + * @brief Starts a freshly created thread. + * @return Result code. + * @note Syscall number 0x09. + */ +Result svcStartThread(Handle handle); + +/** + * @brief Exits the current thread. + * @note Syscall number 0x0A. + */ +void __attribute__((noreturn)) svcExitThread(void); + +/** + * @brief Closes a handle, decrementing the reference count of the corresponding kernel object. + * This might result in the kernel freeing the object. + * @param handle Handle to close. + * @return Result code. + * @note Syscall number 0x16. + */ +Result svcCloseHandle(Handle handle); + +/** + * @brief Waits on one or more synchronization objects, optionally with a timeout. + * @return Result code. + * @note Syscall number 0x18. + * @note \p handleCount must not be greater than \ref MAX_WAIT_OBJECTS. This is a Horizon kernel limitation. + * @note This is the raw syscall, which can be cancelled by \ref svcCancelSynchronization or other means. \ref waitHandles or \ref waitMultiHandle should normally be used instead. + */ +Result svcWaitSynchronization(s32* index, const Handle* handles, s32 handleCount, u64 timeout); + +/** + * @brief Waits on a single synchronization object, optionally with a timeout. + * @return Result code. + * @note Wrapper for \ref svcWaitSynchronization. + * @note This is the raw syscall, which can be cancelled by \ref svcCancelSynchronization or other means. \ref waitSingleHandle should normally be used instead. + */ +static inline Result svcWaitSynchronizationSingle(Handle handle, u64 timeout) { + s32 tmp; + return svcWaitSynchronization(&tmp, &handle, 1, timeout); +} + +/** + * @brief Creates an IPC session. + * @return Result code. + * @note Syscall number 0x40. + * @warning This is a privileged syscall. Use \ref envIsSyscallHinted to check if it is available. + */ +Result svcCreateSession(Handle *server_handle, Handle *client_handle, u32 unk0, u64 unk1);//unk* are normally 0? + +/** + * @brief Sends an IPC synchronization request to a session. + * @return Result code. + * @note Syscall number 0x21. + */ +Result svcSendSyncRequest(Handle session); + +/** + * @brief Performs IPC input/output. + * @return Result code. + * @note Syscall number 0x43. + * @warning This is a privileged syscall. Use \ref envIsSyscallHinted to check if it is available. + */ +Result svcReplyAndReceive(s32* index, const Handle* handles, s32 handleCount, Handle replyTarget, u64 timeout); + +/** + * @brief Maps the src address from the supplied process handle into the current process. + * @param[in] dst Address to which map the memory in the current process. + * @param[in] proc Process handle. + * @param[in] src Source mapping address. + * @param[in] size Size of the memory. + * @return Result code. + * @remark This allows mapping code and rodata with RW- permission. + * @note Syscall number 0x74. + * @warning This is a privileged syscall. Use \ref envIsSyscallHinted to check if it is available. + */ +Result svcMapProcessMemory(void* dst, Handle proc, u64 src, u64 size); + +/** + * @brief Undoes the effects of \ref svcMapProcessMemory. + * @param[in] dst Destination mapping address + * @param[in] proc Process handle. + * @param[in] src Address of the memory in the process. + * @param[in] size Size of the memory. + * @return Result code. + * @remark This allows mapping code and rodata with RW- permission. + * @note Syscall number 0x75. + * @warning This is a privileged syscall. Use \ref envIsSyscallHinted to check if it is available. + */ +Result svcUnmapProcessMemory(void* dst, Handle proc, u64 src, u64 size); + /** * @brief Calls a secure monitor function (TrustZone, EL3). * @param regs Arguments to pass to the secure monitor. diff --git a/emummc/source/nx/svc.s b/emummc/source/nx/svc.s index 9a2c61524..fc03f96c3 100644 --- a/emummc/source/nx/svc.s +++ b/emummc/source/nx/svc.s @@ -56,6 +56,69 @@ SVC_BEGIN svcSetProcessMemoryPermission RET SVC_END +SVC_BEGIN svcCreateThread + STR X0, [SP, #-16]! + SVC 0x8 + LDR X2, [SP], #16 + STR W1, [X2] + RET +SVC_END + +SVC_BEGIN svcStartThread + SVC 0x9 + RET +SVC_END + +SVC_BEGIN svcExitThread + SVC 0xA + RET +SVC_END + +SVC_BEGIN svcCloseHandle + SVC 0x16 + RET +SVC_END + +SVC_BEGIN svcWaitSynchronization + STR X0, [SP, #-16]! + SVC 0x18 + LDR X2, [SP], #16 + STR W1, [X2] + RET +SVC_END + +SVC_BEGIN svcCreateSession + STP X0, X1, [SP, #-16]! + SVC 0x40 + LDP X3, X4, [SP], #16 + STR W1, [X3] + STR W2, [X4] + RET +SVC_END + +SVC_BEGIN svcSendSyncRequest + SVC 0x21 + RET +SVC_END + +SVC_BEGIN svcReplyAndReceive + STR X0, [SP, #-16]! + SVC 0x43 + LDR X2, [SP], #16 + STR W1, [X2] + RET +SVC_END + +SVC_BEGIN svcMapProcessMemory + SVC 0x74 + RET +SVC_END + +SVC_BEGIN svcUnmapProcessMemory + SVC 0x75 + RET +SVC_END + SVC_BEGIN svcCallSecureMonitor STR X0, [SP, #-16]! MOV X8, X0 diff --git a/emummc/source/utils/types.h b/emummc/source/utils/types.h index b39359e8f..5af01d48e 100644 --- a/emummc/source/utils/types.h +++ b/emummc/source/utils/types.h @@ -55,6 +55,10 @@ typedef volatile unsigned int vu32; typedef u32 Handle; ///< Kernel object handle. typedef u32 Result; ///< Function error code result type. +#define INVALID_HANDLE ((Handle) 0) +#define CUR_PROCESS_HANDLE ((Handle) 0xFFFF8001) + + #ifndef __cplusplus typedef int bool; #define true 1 diff --git a/emummc/source/utils/util.h b/emummc/source/utils/util.h index bf13c8958..0c36d06ab 100644 --- a/emummc/source/utils/util.h +++ b/emummc/source/utils/util.h @@ -38,6 +38,12 @@ void usleep(u64 ticks); void msleep(u64 milliseconds); void exec_cfg(u32 *base, const cfg_op_t *ops, u32 num_ops); +static inline void *armGetTls(void) { + void *ret; + __asm__ __volatile__("MRS %x[data], TPIDRRO_EL0" : [data]"=r"(ret)); + return ret; +} + extern volatile emuMMC_ctx_t emuMMC_ctx; #endif