From 0b33134f6ca34c61657dd66491feebbfabaa1f95 Mon Sep 17 00:00:00 2001 From: TuxSH Date: Thu, 18 Apr 2019 19:48:01 +0200 Subject: [PATCH] Loader services refactor --- sysmodules/loader/source/hbldr.c | 84 ++ sysmodules/loader/source/hbldr.h | 13 + sysmodules/loader/source/loader.c | 1041 +++++++------------- sysmodules/loader/source/loader.h | 5 + sysmodules/loader/source/main.c | 126 +++ sysmodules/loader/source/patcher.c | 6 +- sysmodules/loader/source/patcher.h | 2 +- sysmodules/loader/source/service_manager.c | 182 ++++ sysmodules/loader/source/service_manager.h | 22 + sysmodules/loader/source/util.h | 52 + sysmodules/pm/source/util.h | 2 +- sysmodules/rosalina/source/hbloader.c | 28 +- 12 files changed, 845 insertions(+), 718 deletions(-) create mode 100644 sysmodules/loader/source/hbldr.c create mode 100644 sysmodules/loader/source/hbldr.h create mode 100644 sysmodules/loader/source/loader.h create mode 100644 sysmodules/loader/source/main.c create mode 100644 sysmodules/loader/source/service_manager.c create mode 100644 sysmodules/loader/source/service_manager.h create mode 100644 sysmodules/loader/source/util.h diff --git a/sysmodules/loader/source/hbldr.c b/sysmodules/loader/source/hbldr.c new file mode 100644 index 0000000..6820507 --- /dev/null +++ b/sysmodules/loader/source/hbldr.c @@ -0,0 +1,84 @@ +#include <3ds.h> +#include +#include "hbldr.h" + +static u32 hbldrRefcount = 0; +static Handle hbldrHandle = 0; + +Result hbldrInit(void) +{ + Result res; + if (AtomicPostIncrement(&hbldrRefcount)) return 0; + + for(res = 0xD88007FA; res == (Result)0xD88007FA; svcSleepThread(500 * 1000LL)) { + res = svcConnectToPort(&hbldrHandle, "hb:ldr"); + if(R_FAILED(res) && res != (Result)0xD88007FA) { + AtomicDecrement(&hbldrRefcount); + return res; + } + } + + return 0; +} + +void hbldrExit(void) +{ + if (AtomicDecrement(&hbldrRefcount)) return; + svcCloseHandle(hbldrHandle); +} + +Result HBLDR_LoadProcess(Handle *outCodeSet, u32 textAddr, u32 kernelFlags, u64 titleId, const char *name) +{ + u32* cmdbuf = getThreadCommandBuffer(); // 0x11800 + cmdbuf[0] = IPC_MakeHeader(1, 6, 0); + cmdbuf[1] = textAddr; + cmdbuf[2] = kernelFlags & 0xF00; + memcpy(&cmdbuf[3], &titleId, 8); + strncpy((char *)&cmdbuf[5], name, 8); + Result rc = svcSendSyncRequest(hbldrHandle); + if (R_SUCCEEDED(rc)) rc = cmdbuf[1]; + + *outCodeSet = R_SUCCEEDED(rc) ? cmdbuf[3] : 0; + return rc; +} + +Result HBLDR_SetTarget(const char* path) +{ + u32 pathLen = strlen(path) + 1; + u32* cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(2, 0, 2); // 0x20002 + cmdbuf[1] = IPC_Desc_StaticBuffer(pathLen, 0); + cmdbuf[2] = (u32)path; + + Result rc = svcSendSyncRequest(hbldrHandle); + if (R_SUCCEEDED(rc)) rc = cmdbuf[1]; + return rc; +} + +Result HBLDR_SetArgv(const void* buffer, size_t size) +{ + u32* cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(3, 0, 2); // 0x30002 + cmdbuf[1] = IPC_Desc_StaticBuffer(size, 1); + cmdbuf[2] = (u32)buffer; + + Result rc = svcSendSyncRequest(hbldrHandle); + if (R_SUCCEEDED(rc)) rc = cmdbuf[1]; + return rc; +} + +Result HBLDR_PatchExHeaderInfo(ExHeader_Info *exheaderInfo) +{ + u32* cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(4, 0, 2); // 0x40002 + cmdbuf[1] = IPC_Desc_Buffer(sizeof(*exheaderInfo), IPC_BUFFER_RW); + cmdbuf[2] = (u32)exheaderInfo; + + Result rc = svcSendSyncRequest(hbldrHandle); + if (R_SUCCEEDED(rc)) rc = cmdbuf[1]; + + return rc; +} \ No newline at end of file diff --git a/sysmodules/loader/source/hbldr.h b/sysmodules/loader/source/hbldr.h new file mode 100644 index 0000000..cdec83a --- /dev/null +++ b/sysmodules/loader/source/hbldr.h @@ -0,0 +1,13 @@ +#pragma once + +#include <3ds/exheader.h> + +#define HBLDR_3DSX_TID (*(vu64 *)0x1FF81100) + +Result hbldrInit(void); +void hbldrExit(void); + +Result HBLDR_LoadProcess(Handle *outCodeSet, u32 textAddr, u32 kernelFlags, u64 titleId, const char *name); +Result HBLDR_SetTarget(const char* path); +Result HBLDR_SetArgv(const void* buffer, size_t size); +Result HBLDR_PatchExHeaderInfo(ExHeader_Info *exheaderInfo); diff --git a/sysmodules/loader/source/loader.c b/sysmodules/loader/source/loader.c index 3e51ee0..6f1f59f 100644 --- a/sysmodules/loader/source/loader.c +++ b/sysmodules/loader/source/loader.c @@ -2,753 +2,396 @@ #include "memory.h" #include "patcher.h" #include "ifile.h" +#include "util.h" +#include "hbldr.h" -#define MAX_SESSIONS 1 -#define HBLDR_3DSX_TID (*(vu64 *)0x1FF81100) +extern u32 config, multiConfig, bootConfig; +extern bool isN3DS, needToInitSd, isSdMode; -u32 config, multiConfig, bootConfig; -bool isN3DS, needToInitSd, isSdMode; +static u8 g_ret_buf[sizeof(ExHeader_Info)]; +static u64 g_cached_programHandle; +static ExHeader_Info g_exheaderInfo; const char CODE_PATH[] = {0x01, 0x00, 0x00, 0x00, 0x2E, 0x63, 0x6F, 0x64, 0x65, 0x00, 0x00, 0x00}; -typedef struct +typedef struct prog_addrs_t { - u32 text_addr; - u32 text_size; - u32 ro_addr; - u32 ro_size; - u32 data_addr; - u32 data_size; - u32 total_size; + u32 text_addr; + u32 text_size; + u32 ro_addr; + u32 ro_size; + u32 data_addr; + u32 data_size; + u32 total_size; } prog_addrs_t; -static Handle g_handles[MAX_SESSIONS+2]; -static int g_active_handles; -static u64 g_cached_prog_handle; -static ExHeader_Info g_exheader; -static char g_ret_buf[1024]; - -// MAKE SURE fsreg has been init before calling this -static Result fsldrPatchPermissions(void) -{ - u32 pid; - Result res; - FS_ProgramInfo info; - ExHeader_Arm11StorageInfo storageInfo = { - .fs_access_info = FSACCESS_NANDRW | FSACCESS_NANDRO_RO | FSACCESS_SDMC_RW, - }; - - info.programId = 0x0004013000001302LL; // loader PID - info.mediaType = MEDIATYPE_NAND; - res = svcGetProcessId(&pid, CUR_PROCESS_HANDLE); - if (R_SUCCEEDED(res)) - { - res = FSREG_Register(pid, 0xFFFF000000000000LL, &info, &storageInfo); - } - return res; -} - -static inline void loadCFWInfo(void) -{ - s64 out; - - if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 3))) svcBreak(USERBREAK_ASSERT); - config = (u32)out; - if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 4))) svcBreak(USERBREAK_ASSERT); - multiConfig = (u32)out; - if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 5))) svcBreak(USERBREAK_ASSERT); - bootConfig = (u32)out; - - if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 0x201))) svcBreak(USERBREAK_ASSERT); - isN3DS = (bool)out; - if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 0x202))) svcBreak(USERBREAK_ASSERT); - needToInitSd = (bool)out; - if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 0x203))) svcBreak(USERBREAK_ASSERT); - isSdMode = (bool)out; - - IFile file; - if(needToInitSd) fileOpen(&file, ARCHIVE_SDMC, "/", FS_OPEN_READ); //Init SD card if SAFE_MODE is being booted -} - static int lzss_decompress(u8 *end) { - unsigned int v1; // r1@2 - u8 *v2; // r2@2 - u8 *v3; // r3@2 - u8 *v4; // r1@2 - char v5; // r5@4 - char v6; // t1@4 - signed int v7; // r6@4 - int v9; // t1@7 - u8 *v11; // r3@8 - int v12; // r12@8 - int v13; // t1@8 - int v14; // t1@8 - unsigned int v15; // r7@8 - int v16; // r12@8 - int ret; + unsigned int v1; // r1@2 + u8 *v2; // r2@2 + u8 *v3; // r3@2 + u8 *v4; // r1@2 + char v5; // r5@4 + char v6; // t1@4 + signed int v7; // r6@4 + int v9; // t1@7 + u8 *v11; // r3@8 + int v12; // r12@8 + int v13; // t1@8 + int v14; // t1@8 + unsigned int v15; // r7@8 + int v16; // r12@8 + int ret; - ret = 0; - if ( end ) - { - v1 = *((u32 *)end - 2); - v2 = &end[*((u32 *)end - 1)]; - v3 = &end[-(v1 >> 24)]; - v4 = &end[-(v1 & 0xFFFFFF)]; - while ( v3 > v4 ) + ret = 0; + if ( end ) { - v6 = *(v3-- - 1); - v5 = v6; - v7 = 8; - while ( 1 ) - { - if ( (v7-- < 1) ) - break; - if ( v5 & 0x80 ) + v1 = *((u32 *)end - 2); + v2 = &end[*((u32 *)end - 1)]; + v3 = &end[-(v1 >> 24)]; + v4 = &end[-(v1 & 0xFFFFFF)]; + while ( v3 > v4 ) { - v13 = *(v3 - 1); - v11 = v3 - 1; - v12 = v13; - v14 = *(v11 - 1); - v3 = v11 - 1; - v15 = ((v14 | (v12 << 8)) & 0xFFFF0FFF) + 2; - v16 = v12 + 32; - do - { - ret = v2[v15]; - *(v2-- - 1) = ret; - v16 -= 16; - } - while ( !(v16 < 0) ); + v6 = *(v3-- - 1); + v5 = v6; + v7 = 8; + while ( 1 ) + { + if ( (v7-- < 1) ) + break; + if ( v5 & 0x80 ) + { + v13 = *(v3 - 1); + v11 = v3 - 1; + v12 = v13; + v14 = *(v11 - 1); + v3 = v11 - 1; + v15 = ((v14 | (v12 << 8)) & 0xFFFF0FFF) + 2; + v16 = v12 + 32; + do + { + ret = v2[v15]; + *(v2-- - 1) = ret; + v16 -= 16; + } + while ( !(v16 < 0) ); + } + else + { + v9 = *(v3-- - 1); + ret = v9; + *(v2-- - 1) = v9; + } + v5 *= 2; + if ( v3 <= v4 ) + return ret; + } + } + } + return ret; +} + +static Result allocateSharedMem(prog_addrs_t *shared, prog_addrs_t *vaddr, int flags) +{ + u32 dummy; + + memcpy(shared, vaddr, sizeof(prog_addrs_t)); + shared->text_addr = 0x10000000; + shared->ro_addr = shared->text_addr + (shared->text_size << 12); + shared->data_addr = shared->ro_addr + (shared->ro_size << 12); + return svcControlMemory(&dummy, shared->text_addr, 0, shared->total_size << 12, (flags & 0xF00) | MEMOP_ALLOC, MEMPERM_READ | MEMPERM_WRITE); +} + +static Result loadCode(u64 titleId, prog_addrs_t *shared, u64 programHandle, int isCompressed) +{ + IFile file; + FS_Path archivePath; + FS_Path filePath; + u64 size; + u64 total; + + if(!CONFIG(PATCHGAMES) || !loadTitleCodeSection(titleId, (u8 *)shared->text_addr, (u64)shared->total_size << 12)) + { + archivePath.type = PATH_BINARY; + archivePath.data = &programHandle; + archivePath.size = 8; + + filePath.type = PATH_BINARY; + filePath.data = CODE_PATH; + filePath.size = sizeof(CODE_PATH); + if (R_FAILED(IFile_Open(&file, ARCHIVE_SAVEDATA_AND_CONTENT2, archivePath, filePath, FS_OPEN_READ))) + svcBreak(USERBREAK_ASSERT); + + // get file size + assertSuccess(IFile_GetSize(&file, &size)); + + // check size + if (size > (u64)shared->total_size << 12) + { + IFile_Close(&file); + return 0xC900464F; + } + + // read code + assertSuccess(IFile_Read(&file, &total, (void *)shared->text_addr, size)); + IFile_Close(&file); // done reading + + // decompress + if (isCompressed) + lzss_decompress((u8 *)shared->text_addr + size); + } + + ExHeader_CodeSetInfo *csi = &g_exheaderInfo.sci.codeset_info; + + patchCode(titleId, csi->flags.remaster_version, (u8 *)shared->text_addr, shared->total_size << 12, csi->text.size, csi->rodata.size, csi->data.size, csi->rodata.address, csi->data.address); + + return 0; +} + +static Result GetProgramInfo(ExHeader_Info *exheaderInfo, u64 programHandle) +{ + Result res = 0; + + if (programHandle >> 32 == 0xFFFF0000) + res = FSREG_GetProgramInfo(exheaderInfo, 1, programHandle); + else + { + res = FSREG_CheckHostLoadId(programHandle); + //if ((res >= 0 && (unsigned)res >> 27) || (res < 0 && ((unsigned)res >> 27)-32)) + //so use PXIPM if FSREG fails OR returns "info", is the second condition a bug? + if (R_FAILED(res) || (R_SUCCEEDED(res) && R_LEVEL(res) != RL_SUCCESS)) + { + TRY(PXIPM_GetProgramInfo(exheaderInfo, programHandle)); } else { - v9 = *(v3-- - 1); - ret = v9; - *(v2-- - 1) = v9; + TRY(FSREG_GetProgramInfo(exheaderInfo, 1, programHandle)); } - v5 *= 2; - if ( v3 <= v4 ) - return ret; - } - } - } - return ret; -} - -static Result allocate_shared_mem(prog_addrs_t *shared, prog_addrs_t *vaddr, int flags) -{ - u32 dummy; - - memcpy(shared, vaddr, sizeof(prog_addrs_t)); - shared->text_addr = 0x10000000; - shared->ro_addr = shared->text_addr + (shared->text_size << 12); - shared->data_addr = shared->ro_addr + (shared->ro_size << 12); - return svcControlMemory(&dummy, shared->text_addr, 0, shared->total_size << 12, (flags & 0xF00) | MEMOP_ALLOC, MEMPERM_READ | MEMPERM_WRITE); -} - -static Result load_code(u64 progid, prog_addrs_t *shared, u64 prog_handle, int is_compressed) -{ - IFile file; - FS_Path archivePath; - FS_Path filePath; - Result res; - u64 size; - u64 total; - - if(!CONFIG(PATCHGAMES) || !loadTitleCodeSection(progid, (u8 *)shared->text_addr, (u64)shared->total_size << 12)) - { - archivePath.type = PATH_BINARY; - archivePath.data = &prog_handle; - archivePath.size = 8; - - filePath.type = PATH_BINARY; - filePath.data = CODE_PATH; - filePath.size = sizeof(CODE_PATH); - if (R_FAILED(IFile_Open(&file, ARCHIVE_SAVEDATA_AND_CONTENT2, archivePath, filePath, FS_OPEN_READ))) - { - svcBreak(USERBREAK_ASSERT); } - // get file size - if (R_FAILED(IFile_GetSize(&file, &size))) - { - IFile_Close(&file); - svcBreak(USERBREAK_ASSERT); - } - - // check size - if (size > (u64)shared->total_size << 12) - { - IFile_Close(&file); - return 0xC900464F; - } - - // read code - res = IFile_Read(&file, &total, (void *)shared->text_addr, size); - IFile_Close(&file); // done reading - if (R_FAILED(res)) - { - svcBreak(USERBREAK_ASSERT); - } - - // decompress - if (is_compressed) - { - lzss_decompress((u8 *)shared->text_addr + size); - } - } - - ExHeader_CodeSetInfo *csi = &g_exheader.sci.codeset_info; - - patchCode(progid, csi->flags.remaster_version, (u8 *)shared->text_addr, shared->total_size << 12, csi->text.size, csi->rodata.size, csi->data.size, csi->rodata.address, csi->data.address); - - return 0; -} - -static Result HBLDR_Init(Handle *session) -{ - Result res; - while (1) - { - res = svcConnectToPort(session, "hb:ldr"); - if (R_LEVEL(res) != RL_PERMANENT || - R_SUMMARY(res) != RS_NOTFOUND || - R_DESCRIPTION(res) != RD_NOT_FOUND - ) break; - svcSleepThread(500000); - } - return res; -} - -static Result loader_GetProgramInfo(ExHeader_Info *exheader, u64 prog_handle) -{ - Result res; - - if (prog_handle >> 32 == 0xFFFF0000) - { - res = FSREG_GetProgramInfo(exheader, 1, prog_handle); - } - else - { - res = FSREG_CheckHostLoadId(prog_handle); - //if ((res >= 0 && (unsigned)res >> 27) || (res < 0 && ((unsigned)res >> 27)-32)) - //so use PXIPM if FSREG fails OR returns "info", is the second condition a bug? - if (R_FAILED(res) || (R_SUCCEEDED(res) && R_LEVEL(res) != RL_SUCCESS)) - { - res = PXIPM_GetProgramInfo(exheader, prog_handle); - } - else - { - res = FSREG_GetProgramInfo(exheader, 1, prog_handle); - } - } - - if (R_SUCCEEDED(res)) - { s64 nbSection0Modules; svcGetSystemInfo(&nbSection0Modules, 26, 0); // Force always having sdmc:/ and nand:/rw permission - exheader->aci.local_caps.storage_info.fs_access_info |= FSACCESS_NANDRW | FSACCESS_SDMC_RW; + exheaderInfo->aci.local_caps.storage_info.fs_access_info |= FSACCESS_NANDRW | FSACCESS_SDMC_RW; - // Tweak 3dsx placeholder title exheader - if (nbSection0Modules == 6) + // Tweak 3dsx placeholder title exheaderInfo + if (nbSection0Modules == 6 && exheaderInfo->aci.local_caps.title_id == HBLDR_3DSX_TID) { - if(exheader->aci.local_caps.title_id == HBLDR_3DSX_TID) - { - Handle hbldr = 0; - res = HBLDR_Init(&hbldr); - if (R_SUCCEEDED(res)) - { - u32* cmdbuf = getThreadCommandBuffer(); - cmdbuf[0] = IPC_MakeHeader(4,0,2); - cmdbuf[1] = IPC_Desc_Buffer(sizeof(*exheader), IPC_BUFFER_RW); - cmdbuf[2] = (u32)exheader; - res = svcSendSyncRequest(hbldr); - svcCloseHandle(hbldr); - if (R_SUCCEEDED(res)) { - res = cmdbuf[1]; - } - } - } - - u64 originalProgId = exheader->aci.local_caps.title_id; - if(CONFIG(PATCHGAMES) && loadTitleExheader(exheader->aci.local_caps.title_id, exheader)) - { - exheader->aci.local_caps.title_id = originalProgId; - } + assertSuccess(hbldrInit()); + HBLDR_PatchExHeaderInfo(exheaderInfo); + hbldrExit(); } - } - return res; + u64 originaltitleId = exheaderInfo->aci.local_caps.title_id; + if(CONFIG(PATCHGAMES) && loadTitleExheaderInfo(exheaderInfo->aci.local_caps.title_id, exheaderInfo)) + exheaderInfo->aci.local_caps.title_id = originaltitleId; + + return res; } -static Result loader_LoadProcess(Handle *process, u64 prog_handle) +static Result LoadProcess(Handle *process, u64 programHandle) { - Result res; - int count; - u32 flags; - u32 desc; - u32 dummy; - prog_addrs_t shared_addr; - prog_addrs_t vaddr; - Handle codeset; - CodeSetInfo codesetinfo; - u32 data_mem_size; - u64 progid; + Result res; + int count; + u32 flags; + u32 desc; + u32 dummy; + prog_addrs_t sharedAddr; + prog_addrs_t vaddr; + Handle codeset; + CodeSetInfo codesetinfo; + u32 dataMemSize; + u64 titleId; - // make sure the cached info corrosponds to the current prog_handle - if (g_cached_prog_handle != prog_handle || g_exheader.aci.local_caps.title_id == HBLDR_3DSX_TID) - { - res = loader_GetProgramInfo(&g_exheader, prog_handle); - g_cached_prog_handle = prog_handle; - if (res < 0) + // make sure the cached info corrosponds to the current programHandle + if (g_cached_programHandle != programHandle || g_exheaderInfo.aci.local_caps.title_id == HBLDR_3DSX_TID) { - g_cached_prog_handle = 0; - return res; + res = GetProgramInfo(&g_exheaderInfo, programHandle); + g_cached_programHandle = programHandle; + if (R_FAILED(res)) + { + g_cached_programHandle = 0; + return res; + } } - } - // get kernel flags - flags = 0; - for (count = 0; count < 28; count++) - { - desc = g_exheader.aci.kernel_caps.descriptors[count]; - if (0x1FE == desc >> 23) + // get kernel flags + flags = 0; + for (count = 0; count < 28; count++) { - flags = desc & 0xF00; + desc = g_exheaderInfo.aci.kernel_caps.descriptors[count]; + if (0x1FE == desc >> 23) + flags = desc & 0xF00; } - } - if (flags == 0) - { - return MAKERESULT(RL_PERMANENT, RS_INVALIDARG, 1, 2); - } + if (flags == 0) + return MAKERESULT(RL_PERMANENT, RS_INVALIDARG, 1, 2); - // check for 3dsx process - progid = g_exheader.aci.local_caps.title_id; - ExHeader_CodeSetInfo *csi = &g_exheader.sci.codeset_info; + // check for 3dsx process + titleId = g_exheaderInfo.aci.local_caps.title_id; + ExHeader_CodeSetInfo *csi = &g_exheaderInfo.sci.codeset_info; - if (progid == HBLDR_3DSX_TID) - { - Handle hbldr = 0; - res = HBLDR_Init(&hbldr); - if (R_FAILED(res)) + if (titleId == HBLDR_3DSX_TID) { - svcBreak(USERBREAK_ASSERT); - return res; - } - u32* cmdbuf = getThreadCommandBuffer(); - cmdbuf[0] = IPC_MakeHeader(1,6,0); - cmdbuf[1] = csi->text.address; - cmdbuf[2] = flags & 0xF00; - cmdbuf[3] = progid; - cmdbuf[4] = progid>>32; - memcpy(&cmdbuf[5], csi->name, 8); - res = svcSendSyncRequest(hbldr); - svcCloseHandle(hbldr); - if (R_SUCCEEDED(res)) - { - res = cmdbuf[1]; - } - if (R_FAILED(res)) - { - svcBreak(USERBREAK_ASSERT); - return res; - } - codeset = (Handle)cmdbuf[3]; - res = svcCreateProcess(process, codeset, g_exheader.aci.kernel_caps.descriptors, count); - svcCloseHandle(codeset); - return res; - } - - // allocate process memory - vaddr.text_addr = csi->text.address; - vaddr.text_size = (csi->text.size + 4095) >> 12; - vaddr.ro_addr = csi->rodata.address; - vaddr.ro_size = (csi->rodata.size + 4095) >> 12; - vaddr.data_addr = csi->data.address; - vaddr.data_size = (csi->data.size + 4095) >> 12; - data_mem_size = (csi->data.size + csi->bss_size + 4095) >> 12; - vaddr.total_size = vaddr.text_size + vaddr.ro_size + vaddr.data_size; - if ((res = allocate_shared_mem(&shared_addr, &vaddr, flags)) < 0) - { - return res; - } - - // load code - if ((res = load_code(progid, &shared_addr, prog_handle, csi->flags.compress_exefs_code)) >= 0) - { - memcpy(&codesetinfo.name, csi->name, 8); - codesetinfo.program_id = progid; - codesetinfo.text_addr = vaddr.text_addr; - codesetinfo.text_size = vaddr.text_size; - codesetinfo.text_size_total = vaddr.text_size; - codesetinfo.ro_addr = vaddr.ro_addr; - codesetinfo.ro_size = vaddr.ro_size; - codesetinfo.ro_size_total = vaddr.ro_size; - codesetinfo.rw_addr = vaddr.data_addr; - codesetinfo.rw_size = vaddr.data_size; - codesetinfo.rw_size_total = data_mem_size; - res = svcCreateCodeSet(&codeset, &codesetinfo, (void *)shared_addr.text_addr, (void *)shared_addr.ro_addr, (void *)shared_addr.data_addr); - if (res >= 0) - { - res = svcCreateProcess(process, codeset, g_exheader.aci.kernel_caps.descriptors, count); - svcCloseHandle(codeset); - if (res >= 0) - { - return 0; - } - } - } - - svcControlMemory(&dummy, shared_addr.text_addr, 0, shared_addr.total_size << 12, MEMOP_FREE, 0); - return res; -} - -static Result loader_RegisterProgram(u64 *prog_handle, FS_ProgramInfo *title, FS_ProgramInfo *update) -{ - Result res; - u64 prog_id; - - prog_id = title->programId; - if (prog_id >> 32 != 0xFFFF0000) - { - res = FSREG_CheckHostLoadId(prog_id); - //if ((res >= 0 && (unsigned)res >> 27) || (res < 0 && ((unsigned)res >> 27)-32)) - if (R_FAILED(res) || (R_SUCCEEDED(res) && R_LEVEL(res) != RL_SUCCESS)) - { - res = PXIPM_RegisterProgram(prog_handle, title, update); - if (res < 0) - { + assertSuccess(hbldrInit()); + assertSuccess(HBLDR_LoadProcess(&codeset, csi->text.address, flags & 0xF00, titleId, csi->name)); + res = svcCreateProcess(process, codeset, g_exheaderInfo.aci.kernel_caps.descriptors, count); + svcCloseHandle(codeset); + hbldrExit(); return res; - } - if (*prog_handle >> 32 != 0xFFFF0000) - { - res = FSREG_CheckHostLoadId(*prog_handle); + } + + // allocate process memory + vaddr.text_addr = csi->text.address; + vaddr.text_size = (csi->text.size + 4095) >> 12; + vaddr.ro_addr = csi->rodata.address; + vaddr.ro_size = (csi->rodata.size + 4095) >> 12; + vaddr.data_addr = csi->data.address; + vaddr.data_size = (csi->data.size + 4095) >> 12; + dataMemSize = (csi->data.size + csi->bss_size + 4095) >> 12; + vaddr.total_size = vaddr.text_size + vaddr.ro_size + vaddr.data_size; + TRY(allocateSharedMem(&sharedAddr, &vaddr, flags)); + + // load code + if (R_SUCCEEDED(res = loadCode(titleId, &sharedAddr, programHandle, csi->flags.compress_exefs_code))) + { + memcpy(&codesetinfo.name, csi->name, 8); + codesetinfo.program_id = titleId; + codesetinfo.text_addr = vaddr.text_addr; + codesetinfo.text_size = vaddr.text_size; + codesetinfo.text_size_total = vaddr.text_size; + codesetinfo.ro_addr = vaddr.ro_addr; + codesetinfo.ro_size = vaddr.ro_size; + codesetinfo.ro_size_total = vaddr.ro_size; + codesetinfo.rw_addr = vaddr.data_addr; + codesetinfo.rw_size = vaddr.data_size; + codesetinfo.rw_size_total = dataMemSize; + res = svcCreateCodeSet(&codeset, &codesetinfo, (void *)sharedAddr.text_addr, (void *)sharedAddr.ro_addr, (void *)sharedAddr.data_addr); + if (R_SUCCEEDED(res)) + { + res = svcCreateProcess(process, codeset, g_exheaderInfo.aci.kernel_caps.descriptors, count); + svcCloseHandle(codeset); + res = R_SUCCEEDED(res) ? 0 : res; + } + } + + svcControlMemory(&dummy, sharedAddr.text_addr, 0, sharedAddr.total_size << 12, MEMOP_FREE, 0); + return res; +} + +static Result RegisterProgram(u64 *programHandle, FS_ProgramInfo *title, FS_ProgramInfo *update) +{ + Result res; + u64 titleId; + + titleId = title->programId; + if (titleId >> 32 != 0xFFFF0000) + { + res = FSREG_CheckHostLoadId(titleId); //if ((res >= 0 && (unsigned)res >> 27) || (res < 0 && ((unsigned)res >> 27)-32)) if (R_FAILED(res) || (R_SUCCEEDED(res) && R_LEVEL(res) != RL_SUCCESS)) { - return 0; - } - } - svcBreak(USERBREAK_ASSERT); - } - } - - if ((title->mediaType != update->mediaType) || (prog_id != update->programId)) - { - svcBreak(USERBREAK_ASSERT); - } - res = FSREG_LoadProgram(prog_handle, title); - if (R_SUCCEEDED(res)) - { - if (*prog_handle >> 32 == 0xFFFF0000) - { - return 0; - } - res = FSREG_CheckHostLoadId(*prog_handle); - //if ((res >= 0 && (unsigned)res >> 27) || (res < 0 && ((unsigned)res >> 27)-32)) - if (R_FAILED(res) || (R_SUCCEEDED(res) && R_LEVEL(res) != RL_SUCCESS)) - { - svcBreak(USERBREAK_ASSERT); - } - } - return res; -} - -static Result loader_UnregisterProgram(u64 prog_handle) -{ - Result res; - - if (prog_handle >> 32 == 0xFFFF0000) - { - return FSREG_UnloadProgram(prog_handle); - } - else - { - res = FSREG_CheckHostLoadId(prog_handle); - //if ((res >= 0 && (unsigned)res >> 27) || (res < 0 && ((unsigned)res >> 27)-32)) - if (R_FAILED(res) || (R_SUCCEEDED(res) && R_LEVEL(res) != RL_SUCCESS)) - { - return PXIPM_UnregisterProgram(prog_handle); - } - else - { - return FSREG_UnloadProgram(prog_handle); - } - } -} - -static void handle_commands(void) -{ - FS_ProgramInfo title; - FS_ProgramInfo update; - u32* cmdbuf; - u16 cmdid; - int res; - Handle handle; - u64 prog_handle; - - cmdbuf = getThreadCommandBuffer(); - cmdid = cmdbuf[0] >> 16; - res = 0; - switch (cmdid) - { - case 1: // LoadProcess - { - res = loader_LoadProcess(&handle, *(u64 *)&cmdbuf[1]); - cmdbuf[0] = 0x10042; - cmdbuf[1] = res; - cmdbuf[2] = 16; - cmdbuf[3] = handle; - break; - } - case 2: // RegisterProgram - { - memcpy(&title, &cmdbuf[1], sizeof(FS_ProgramInfo)); - memcpy(&update, &cmdbuf[5], sizeof(FS_ProgramInfo)); - res = loader_RegisterProgram(&prog_handle, &title, &update); - cmdbuf[0] = 0x200C0; - cmdbuf[1] = res; - *(u64 *)&cmdbuf[2] = prog_handle; - break; - } - case 3: // UnregisterProgram - { - prog_handle = *(u64 *)&cmdbuf[1]; - - if (g_cached_prog_handle == prog_handle) - { - g_cached_prog_handle = 0; - } - cmdbuf[0] = 0x30040; - cmdbuf[1] = loader_UnregisterProgram(prog_handle); - break; - } - case 4: // GetProgramInfo - { - prog_handle = *(u64 *)&cmdbuf[1]; - if (prog_handle != g_cached_prog_handle || g_exheader.aci.local_caps.title_id == HBLDR_3DSX_TID) - { - res = loader_GetProgramInfo(&g_exheader, prog_handle); - if (res >= 0) - { - g_cached_prog_handle = prog_handle; - } - else - { - g_cached_prog_handle = 0; - } - } - memcpy(&g_ret_buf, &g_exheader, 1024); - cmdbuf[0] = 0x40042; - cmdbuf[1] = res; - cmdbuf[2] = 0x1000002; - cmdbuf[3] = (u32) &g_ret_buf; - break; - } - default: // error - { - cmdbuf[0] = 0x40; - cmdbuf[1] = 0xD900182F; - break; - } - } -} - -static Result should_terminate(int *term_request) -{ - u32 notid; - Result ret; - - ret = srvReceiveNotification(¬id); - if (R_FAILED(ret)) - { - return ret; - } - if (notid == 0x100) // term request - { - *term_request = 1; - } - return 0; -} - -// this is called before main -void __appInit() -{ - Result res; - for(res = 0xD88007FA; res == (Result)0xD88007FA; svcSleepThread(500 * 1000LL)) - { - res = srvInit(); - if(R_FAILED(res) && res != (Result)0xD88007FA) - svcBreak(USERBREAK_PANIC); - } - - fsRegInit(); - fsldrPatchPermissions(); - - //fsldrInit(); - res = srvGetServiceHandle(fsGetSessionHandle(), "fs:LDR"); - if (R_FAILED(res)) svcBreak(USERBREAK_PANIC); - res = FSUSER_InitializeWithSdkVersion(*fsGetSessionHandle(), 0x70200C8); - if (R_FAILED(res)) svcBreak(USERBREAK_PANIC); - res = FSUSER_SetPriority(0); - if (R_FAILED(res)) svcBreak(USERBREAK_PANIC); - - pxiPmInit(); -} - -// this is called after main exits -void __appExit() -{ - pxiPmExit(); - //fsldrExit(); - svcCloseHandle(*fsGetSessionHandle()); - fsRegExit(); - srvExit(); -} - -// stubs for non-needed pre-main functions -void __sync_init(); -void __sync_fini(); -void __system_initSyscalls(); - -void __ctru_exit() -{ - void __libc_fini_array(void); - - __libc_fini_array(); - __appExit(); - __sync_fini(); - svcExitProcess(); -} - -void initSystem() -{ - void __libc_init_array(void); - - __sync_init(); - __system_initSyscalls(); - __appInit(); - - __libc_init_array(); -} - -int main() -{ - Result ret; - Handle handle; - Handle reply_target; - Handle *srv_handle; - Handle *notification_handle; - s32 index; - int i; - int term_request; - u32* cmdbuf; - - ret = 0; - - srv_handle = &g_handles[1]; - notification_handle = &g_handles[0]; - - if (R_FAILED(srvRegisterService(srv_handle, "Loader", MAX_SESSIONS))) - { - svcBreak(USERBREAK_ASSERT); - } - - if (R_FAILED(srvEnableNotification(notification_handle))) - { - svcBreak(USERBREAK_ASSERT); - } - - loadCFWInfo(); - - g_active_handles = 2; - g_cached_prog_handle = 0; - index = 1; - - reply_target = 0; - term_request = 0; - do - { - if (reply_target == 0) - { - cmdbuf = getThreadCommandBuffer(); - cmdbuf[0] = 0xFFFF0000; - } - ret = svcReplyAndReceive(&index, g_handles, g_active_handles, reply_target); - - if (R_FAILED(ret)) - { - // check if any handle has been closed - if (ret == (int)0xC920181A) - { - if (index == -1) - { - for (i = 2; i < MAX_SESSIONS+2; i++) - { - if (g_handles[i] == reply_target) + TRY(PXIPM_RegisterProgram(programHandle, title, update)); + if (*programHandle >> 32 != 0xFFFF0000) { - index = i; - break; + res = FSREG_CheckHostLoadId(*programHandle); + //if ((res >= 0 && (unsigned)res >> 27) || (res < 0 && ((unsigned)res >> 27)-32)) + if (R_FAILED(res) || (R_SUCCEEDED(res) && R_LEVEL(res) != RL_SUCCESS)) + { + return 0; + } } - } + panic(0); } - svcCloseHandle(g_handles[index]); - g_handles[index] = g_handles[g_active_handles-1]; - g_active_handles--; - reply_target = 0; - } - else - { - svcBreak(USERBREAK_ASSERT); - } } + + if ((title->mediaType != update->mediaType) || (titleId != update->programId)) + panic(1); + + TRY(FSREG_LoadProgram(programHandle, title)); + if (*programHandle >> 32 == 0xFFFF0000) + return 0; + + res = FSREG_CheckHostLoadId(*programHandle); + //if ((res >= 0 && (unsigned)res >> 27) || (res < 0 && ((unsigned)res >> 27)-32)) + if (R_FAILED(res) || (R_SUCCEEDED(res) && R_LEVEL(res) != RL_SUCCESS)) + panic(2); + + return res; +} + +static Result UnregisterProgram(u64 programHandle) +{ + Result res; + + if (programHandle >> 32 == 0xFFFF0000) + return FSREG_UnloadProgram(programHandle); else { - // process responses - reply_target = 0; - switch (index) - { - case 0: // notification - { - if (R_FAILED(should_terminate(&term_request))) - { - svcBreak(USERBREAK_ASSERT); - } - break; - } - case 1: // new session - { - if (R_FAILED(svcAcceptSession(&handle, *srv_handle))) - { - svcBreak(USERBREAK_ASSERT); - } - if (g_active_handles < MAX_SESSIONS+2) - { - g_handles[g_active_handles] = handle; - g_active_handles++; - } - else - { - svcCloseHandle(handle); - } - break; - } - default: // session - { - handle_commands(); - reply_target = g_handles[index]; - break; - } - } + res = FSREG_CheckHostLoadId(programHandle); + //if ((res >= 0 && (unsigned)res >> 27) || (res < 0 && ((unsigned)res >> 27)-32)) + if (R_FAILED(res) || (R_SUCCEEDED(res) && R_LEVEL(res) != RL_SUCCESS)) + return PXIPM_UnregisterProgram(programHandle); + else + return FSREG_UnloadProgram(programHandle); + } +} + +void loaderHandleCommands(void *ctx) +{ + (void)ctx; + FS_ProgramInfo title; + FS_ProgramInfo update; + u32* cmdbuf; + u16 cmdid; + int res; + Handle handle; + u64 programHandle; + + cmdbuf = getThreadCommandBuffer(); + cmdid = cmdbuf[0] >> 16; + res = 0; + switch (cmdid) + { + case 1: // LoadProcess + memcpy(&programHandle, &cmdbuf[1], 8); + handle = 0; + cmdbuf[1] = LoadProcess(&handle, programHandle); + cmdbuf[0] = IPC_MakeHeader(1, 1, 2); + cmdbuf[2] = IPC_Desc_MoveHandles(1); + cmdbuf[3] = handle; + break; + case 2: // RegisterProgram + memcpy(&title, &cmdbuf[1], sizeof(FS_ProgramInfo)); + memcpy(&update, &cmdbuf[5], sizeof(FS_ProgramInfo)); + cmdbuf[1] = RegisterProgram(&programHandle, &title, &update); + cmdbuf[0] = IPC_MakeHeader(2, 3, 0); + memcpy(&cmdbuf[2], &programHandle, 8); + break; + case 3: // UnregisterProgram + memcpy(&programHandle, &cmdbuf[1], 8); + + if (g_cached_programHandle == programHandle) + g_cached_programHandle = 0; + cmdbuf[1] = UnregisterProgram(programHandle); + cmdbuf[0] = IPC_MakeHeader(3, 1, 0); + break; + case 4: // GetProgramInfo + memcpy(&programHandle, &cmdbuf[1], 8); + if (programHandle != g_cached_programHandle || g_exheaderInfo.aci.local_caps.title_id == HBLDR_3DSX_TID) + { + res = GetProgramInfo(&g_exheaderInfo, programHandle); + g_cached_programHandle = R_SUCCEEDED(res) ? programHandle : 0; + } + memcpy(&g_ret_buf, &g_exheaderInfo, sizeof(ExHeader_Info)); + cmdbuf[0] = IPC_MakeHeader(4, 1, 2); + cmdbuf[1] = res; + cmdbuf[2] = IPC_Desc_StaticBuffer(sizeof(ExHeader_Info), 0); //0x1000002; + cmdbuf[3] = (u32)&g_ret_buf; + break; + default: // error + cmdbuf[0] = IPC_MakeHeader(0, 1, 0); + cmdbuf[1] = 0xD900182F; + break; } - } while (!term_request || g_active_handles != 2); - - srvUnregisterService("Loader"); - svcCloseHandle(*srv_handle); - svcCloseHandle(*notification_handle); - - return 0; } diff --git a/sysmodules/loader/source/loader.h b/sysmodules/loader/source/loader.h new file mode 100644 index 0000000..624e424 --- /dev/null +++ b/sysmodules/loader/source/loader.h @@ -0,0 +1,5 @@ +#pragma once + +#include <3ds/types.h> + +void loaderHandleCommands(void *ctx); diff --git a/sysmodules/loader/source/main.c b/sysmodules/loader/source/main.c new file mode 100644 index 0000000..af1c10c --- /dev/null +++ b/sysmodules/loader/source/main.c @@ -0,0 +1,126 @@ +#include <3ds.h> +#include "memory.h" +#include "patcher.h" +#include "ifile.h" +#include "util.h" +#include "loader.h" +#include "service_manager.h" + +u32 config, multiConfig, bootConfig; +bool isN3DS, needToInitSd, isSdMode; + +// MAKE SURE fsreg has been init before calling this +static Result fsldrPatchPermissions(void) +{ + u32 pid; + Result res = 0; + FS_ProgramInfo info; + ExHeader_Arm11StorageInfo storageInfo = { + .fs_access_info = FSACCESS_NANDRW | FSACCESS_NANDRO_RO | FSACCESS_SDMC_RW, + }; + + info.programId = 0x0004013000001302LL; // loader PID + info.mediaType = MEDIATYPE_NAND; + TRY(svcGetProcessId(&pid, CUR_PROCESS_HANDLE)); + return FSREG_Register(pid, 0xFFFF000000000000LL, &info, &storageInfo); +} + +static inline void loadCFWInfo(void) +{ + s64 out; + + assertSuccess(svcGetSystemInfo(&out, 0x10000, 3)); + config = (u32)out; + assertSuccess(svcGetSystemInfo(&out, 0x10000, 4)); + multiConfig = (u32)out; + assertSuccess(svcGetSystemInfo(&out, 0x10000, 5)); + bootConfig = (u32)out; + + assertSuccess(svcGetSystemInfo(&out, 0x10000, 0x201)); + isN3DS = (bool)out; + assertSuccess(svcGetSystemInfo(&out, 0x10000, 0x202)); + needToInitSd = (bool)out; + assertSuccess(svcGetSystemInfo(&out, 0x10000, 0x203)); + isSdMode = (bool)out; + + IFile file; + if(needToInitSd) fileOpen(&file, ARCHIVE_SDMC, "/", FS_OPEN_READ); //Init SD card if SAFE_MODE is being booted +} + +// this is called before main +void __appInit() +{ + loadCFWInfo(); + + Result res; + for(res = 0xD88007FA; res == (Result)0xD88007FA; svcSleepThread(500 * 1000LL)) + { + res = srvInit(); + if(R_FAILED(res) && res != (Result)0xD88007FA) + panic(res); + } + + assertSuccess(fsRegInit()); + assertSuccess(fsldrPatchPermissions()); + + //fsldrInit(); + assertSuccess(srvGetServiceHandle(fsGetSessionHandle(), "fs:LDR")); + + // Hackjob + assertSuccess(FSUSER_InitializeWithSdkVersion(*fsGetSessionHandle(), 0x70200C8)); + assertSuccess(FSUSER_SetPriority(0)); + + assertSuccess(pxiPmInit()); +} + +// this is called after main exits +void __appExit() +{ + pxiPmExit(); + //fsldrExit(); + svcCloseHandle(*fsGetSessionHandle()); + fsRegExit(); + srvExit(); +} + +// stubs for non-needed pre-main functions +void __sync_init(); +void __sync_fini(); +void __system_initSyscalls(); + +void __ctru_exit() +{ + void __libc_fini_array(void); + + __libc_fini_array(); + __appExit(); + __sync_fini(); + svcExitProcess(); +} + +void initSystem() +{ + void __libc_init_array(void); + + __sync_init(); + __system_initSyscalls(); + __appInit(); + + __libc_init_array(); +} + +static const ServiceManagerServiceEntry services[] = { + { "Loader", 1, loaderHandleCommands, false }, + { NULL }, +}; + +static const ServiceManagerNotificationEntry notifications[] = { + { 0x000, NULL }, +}; + +int main(void) +{ + loadCFWInfo(); + assertSuccess(ServiceManager_Run(services, notifications, NULL)); + return 0; +} diff --git a/sysmodules/loader/source/patcher.c b/sysmodules/loader/source/patcher.c index 9fbfee6..0a3970a 100644 --- a/sysmodules/loader/source/patcher.c +++ b/sysmodules/loader/source/patcher.c @@ -355,10 +355,10 @@ error: while(true); } -bool loadTitleExheader(u64 progId, ExHeader_Info *exheader) +bool loadTitleExheaderInfo(u64 progId, ExHeader_Info *exheaderInfo) { /* Here we look for "/luma/titles/[u64 titleID in hex, uppercase]/exheader.bin" - If it exists it should be a decrypted exheader */ + If it exists it should be a decrypted exheader / exheader info */ char path[] = "/luma/titles/0000000000000000/exheader.bin"; progIdToStr(path + 28, progId); @@ -374,7 +374,7 @@ bool loadTitleExheader(u64 progId, ExHeader_Info *exheader) { u64 total; - if(R_FAILED(IFile_Read(&file, &total, exheader, sizeof(ExHeader_Info))) || total != sizeof(ExHeader_Info)) goto error; + if(R_FAILED(IFile_Read(&file, &total, exheaderInfo, sizeof(ExHeader_Info))) || total != sizeof(ExHeader_Info)) goto error; } IFile_Close(&file); diff --git a/sysmodules/loader/source/patcher.h b/sysmodules/loader/source/patcher.h index 3869903..87762ac 100644 --- a/sysmodules/loader/source/patcher.h +++ b/sysmodules/loader/source/patcher.h @@ -44,4 +44,4 @@ extern bool isN3DS, needToInitSd, isSdMode; void patchCode(u64 progId, u16 progVer, u8 *code, u32 size, u32 textSize, u32 roSize, u32 dataSize, u32 roAddress, u32 dataAddress); Result fileOpen(IFile *file, FS_ArchiveID archiveId, const char *path, int flags); bool loadTitleCodeSection(u64 progId, u8 *code, u32 size); -bool loadTitleExheader(u64 progId, ExHeader_Info *exheader); +bool loadTitleExheaderInfo(u64 progId, ExHeader_Info *exheaderInfo); diff --git a/sysmodules/loader/source/service_manager.c b/sysmodules/loader/source/service_manager.c new file mode 100644 index 0000000..33cc78e --- /dev/null +++ b/sysmodules/loader/source/service_manager.c @@ -0,0 +1,182 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2016-2019 Aurora Wright, TuxSH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b and 7.c of GPLv3 apply to this file: +* * Requiring preservation of specified reasonable legal notices or +* author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +* * Prohibiting misrepresentation of the origin of that material, +* or requiring that modified versions of such material be marked in +* reasonable ways as different from the original version. +*/ + +#include <3ds.h> +#include "service_manager.h" + +#define TRY(expr) if(R_FAILED(res = (expr))) goto cleanup; + +Result ServiceManager_Run(const ServiceManagerServiceEntry *services, const ServiceManagerNotificationEntry *notifications, const ServiceManagerContextAllocator *allocator) +{ + Result res = 0; + + u32 numServices = 0; + u32 maxSessionsTotal = 0; + u32 numActiveSessions = 0; + bool terminationRequested = false; + + for (u32 i = 0; services[i].name != NULL; i++) { + numServices++; + maxSessionsTotal += services[i].maxSessions; + } + + Handle waitHandles[1 + numServices + maxSessionsTotal]; + void *ctxs[maxSessionsTotal]; + u8 handlerIds[maxSessionsTotal]; + + Handle replyTarget = 0; + s32 id = -1; + u32 *cmdbuf = getThreadCommandBuffer(); + + TRY(srvEnableNotification(&waitHandles[0])); + + // Subscribe to notifications if needed. + for (u32 i = 0; notifications[i].handler != NULL; i++) { + // Termination & ready for reboot events send by PM using PublishToProcess don't require subscription. + if (notifications[i].id != 0x100 && notifications[i].id != 0x179) { + TRY(srvSubscribe(notifications[i].id)); + } + } + + for (u32 i = 0; i < numServices; i++) { + if (!services[i].isGlobalPort) { + TRY(srvRegisterService(&waitHandles[1 + i], services[i].name, (s32)services[i].maxSessions)); + } else { + Handle clientPort; + TRY(svcCreatePort(&waitHandles[1 + i], &clientPort, services[i].name, (s32)services[i].maxSessions)); + svcCloseHandle(clientPort); + } + } + + while (!terminationRequested) { + if (replyTarget == 0) { + cmdbuf[0] = 0xFFFF0000; + } + + id = -1; + res = svcReplyAndReceive(&id, waitHandles, 1 + numServices + numActiveSessions, replyTarget); + + if (res == (Result)0xC920181A) { + // Session has been closed + u32 off; + if (id == -1) { + for (off = 0; off < numActiveSessions && waitHandles[1 + numServices + off] != replyTarget; off++); + if (off >= numActiveSessions) { + return res; + } + + id = 1 + numServices + off; + } else if ((u32)id < 1 + numServices) { + return res; + } + + off = id - 1 - numServices; + + Handle h = waitHandles[id]; + void *ctx = ctxs[off]; + waitHandles[id] = waitHandles[1 + numServices + --numActiveSessions]; + handlerIds[off] = handlerIds[numActiveSessions]; + ctxs[off] = ctxs[numActiveSessions]; + + svcCloseHandle(h); + if (allocator != NULL) { + allocator->freeSessionContext(ctx); + } + + replyTarget = 0; + res = 0; + } else if (R_FAILED(res)) { + return res; + } + + else { + // Ok, no session closed and no error + replyTarget = 0; + if (id == 0) { + // Notification + u32 notificationId = 0; + TRY(srvReceiveNotification(¬ificationId)); + terminationRequested = notificationId == 0x100; + + for (u32 i = 0; notifications[i].handler != NULL; i++) { + if (notifications[i].id == notificationId) { + notifications[i].handler(notificationId); + break; + } + } + } else if ((u32)id < 1 + numServices) { + // New session + Handle session; + void *ctx = NULL; + TRY(svcAcceptSession(&session, waitHandles[id])); + + if (allocator) { + ctx = allocator->newSessionContext((u8)(id - 1)); + if (ctx == NULL) { + svcCloseHandle(session); + return 0xDEAD0000; + } + } + + waitHandles[1 + numServices + numActiveSessions] = session; + handlerIds[numActiveSessions] = (u8)(id - 1); + ctxs[numActiveSessions++] = ctx; + } else { + // Service command + u32 off = id - 1 - numServices; + services[handlerIds[off]].handler(ctxs[off]); + replyTarget = waitHandles[id]; + } + } + } + +cleanup: + for (u32 i = 0; i < 1 + numServices + numActiveSessions; i++) { + svcCloseHandle(waitHandles[i]); + } + + // Subscribe to notifications if needed. + for (u32 i = 0; notifications[i].handler != NULL; i++) { + // Termination & ready for reboot events send by PM using PublishToProcess don't require subscription. + if (notifications[i].id != 0x100 && notifications[i].id != 0x179) { + TRY(srvUnsubscribe(notifications[i].id)); + } + } + + for (u32 i = 0; i < numServices; i++) { + if (!services[i].isGlobalPort) { + srvUnregisterService(services[i].name); + } + } + + if (allocator) { + for (u32 i = 0; i < numActiveSessions; i++) { + allocator->freeSessionContext(ctxs[i]); + } + } + + return res; +} diff --git a/sysmodules/loader/source/service_manager.h b/sysmodules/loader/source/service_manager.h new file mode 100644 index 0000000..1c2c1fb --- /dev/null +++ b/sysmodules/loader/source/service_manager.h @@ -0,0 +1,22 @@ +#pragma once + +#include <3ds/types.h> + +typedef struct ServiceManagerServiceEntry { + const char *name; + u32 maxSessions; + void (*handler)(void *ctx); + bool isGlobalPort; +} ServiceManagerServiceEntry; + +typedef struct ServiceManagerNotificationEntry { + u32 id; + void (*handler)(u32 id); +} ServiceManagerNotificationEntry; + +typedef struct ServiceManagerContextAllocator { + void* (*newSessionContext)(u8 serviceId); + void (*freeSessionContext)(void *ctx); +} ServiceManagerContextAllocator; + +Result ServiceManager_Run(const ServiceManagerServiceEntry *services, const ServiceManagerNotificationEntry *notifications, const ServiceManagerContextAllocator *allocator); diff --git a/sysmodules/loader/source/util.h b/sysmodules/loader/source/util.h new file mode 100644 index 0000000..dfe5340 --- /dev/null +++ b/sysmodules/loader/source/util.h @@ -0,0 +1,52 @@ +#pragma once + +#include <3ds/types.h> +#include <3ds/result.h> +#include <3ds/os.h> +#include <3ds/srv.h> + + +#define REG32(reg) (*(vu32 *)reg) +#define REG64(reg) (*(vu64 *)reg) + +#define TRY(expr) if(R_FAILED(res = (expr))) return res; +#define TRYG(expr, label) if(R_FAILED(res = (expr))) goto label; + +#ifdef XDS +static void hexItoa(u64 number, char *out, u32 digits, bool uppercase) +{ + const char hexDigits[] = "0123456789ABCDEF"; + const char hexDigitsLowercase[] = "0123456789abcdef"; + u32 i = 0; + + while(number > 0) + { + out[digits - 1 - i++] = uppercase ? hexDigits[number & 0xF] : hexDigitsLowercase[number & 0xF]; + number >>= 4; + } + + while(i < digits) out[digits - 1 - i++] = '0'; +} +#endif + +static void __attribute__((noinline)) panic(Result res) +{ +#ifndef XDS + (void)res; + __builtin_trap(); +#else + char buf[32] = {0}; + hexItoa(res, buf, 8, false); + svcOutputDebugString(buf, 8); + svcBreak(USERBREAK_PANIC); +#endif +} + +static inline Result assertSuccess(Result res) +{ + if(R_FAILED(res)) { + panic(res); + } + + return res; +} diff --git a/sysmodules/pm/source/util.h b/sysmodules/pm/source/util.h index d84b2b7..0a3cf20 100644 --- a/sysmodules/pm/source/util.h +++ b/sysmodules/pm/source/util.h @@ -3,6 +3,7 @@ #include <3ds/types.h> #include <3ds/result.h> #include <3ds/os.h> +#include <3ds/srv.h> #define REG32(reg) (*(vu32 *)reg) #define REG64(reg) (*(vu64 *)reg) @@ -20,7 +21,6 @@ #define TRY(expr) if(R_FAILED(res = (expr))) return res; #define TRYG(expr, label) if(R_FAILED(res = (expr))) goto label; -#define XDS #ifdef XDS static void hexItoa(u64 number, char *out, u32 digits, bool uppercase) { diff --git a/sysmodules/rosalina/source/hbloader.c b/sysmodules/rosalina/source/hbloader.c index 2aef35e..564dfba 100644 --- a/sysmodules/rosalina/source/hbloader.c +++ b/sysmodules/rosalina/source/hbloader.c @@ -149,7 +149,7 @@ void HBLDR_HandleCommands(void *ctx) u32 *cmdbuf = getThreadCommandBuffer(); switch (cmdbuf[0] >> 16) { - case 1: + case 1: // LoadProcess { if (cmdbuf[0] != IPC_MakeHeader(1, 6, 0)) { @@ -212,7 +212,7 @@ void HBLDR_HandleCommands(void *ctx) cmdbuf[3] = hCodeset; break; } - case 2: + case 2: // SetTarget { if (cmdbuf[0] != IPC_MakeHeader(2, 0, 2) || (cmdbuf[1] & 0x3FFF) != 0x0002) { @@ -232,7 +232,7 @@ void HBLDR_HandleCommands(void *ctx) cmdbuf[1] = 0; break; } - case 3: + case 3: // SetArgv { if (cmdbuf[0] != IPC_MakeHeader(3, 0, 2) || (cmdbuf[1] & 0x3FFF) != (0x2 | (1<<10))) { @@ -244,7 +244,7 @@ void HBLDR_HandleCommands(void *ctx) cmdbuf[1] = 0; break; } - case 4: + case 4: // PatchExHeaderInfo { if (cmdbuf[0] != IPC_MakeHeader(4, 0, 2) || cmdbuf[1] != IPC_Desc_Buffer(sizeof(ExHeader_Info), IPC_BUFFER_RW)) { @@ -253,14 +253,14 @@ void HBLDR_HandleCommands(void *ctx) } // Perform ExHeader patches - ExHeader_Info* exh = (ExHeader_Info*)cmdbuf[2]; + ExHeader_Info* exhi = (ExHeader_Info*)cmdbuf[2]; u32 stacksize = 4096; // 3dsx/libctru don't require anymore than this - memcpy(exh->sci.codeset_info.name, "3dsx_app", 8); - memcpy(&exh->sci.codeset_info.stack_size, &stacksize, 4); - memset(&exh->sci.dependencies, 0, sizeof(exh->sci.dependencies)); - memcpy(exh->sci.dependencies, dependencyList, sizeof(dependencyList)); + memcpy(exhi->sci.codeset_info.name, "3dsx_app", 8); + memcpy(&exhi->sci.codeset_info.stack_size, &stacksize, 4); + memset(&exhi->sci.dependencies, 0, sizeof(exhi->sci.dependencies)); + memcpy(exhi->sci.dependencies, dependencyList, sizeof(dependencyList)); - ExHeader_Arm11SystemLocalCapabilities* localcaps0 = &exh->aci.local_caps; + ExHeader_Arm11SystemLocalCapabilities* localcaps0 = &exhi->aci.local_caps; localcaps0->core_info.core_version = 2; localcaps0->core_info.use_cpu_clockrate_804MHz = false; @@ -285,20 +285,20 @@ void HBLDR_HandleCommands(void *ctx) localcaps0->reslimit_category = RESLIMIT_CATEGORY_APPLICATION; - ExHeader_Arm11KernelCapabilities* kcaps0 = &exh->aci.kernel_caps; + ExHeader_Arm11KernelCapabilities* kcaps0 = &exhi->aci.kernel_caps; memset(kcaps0->descriptors, 0xFF, sizeof(kcaps0->descriptors)); memcpy(kcaps0->descriptors, kernelCaps, sizeof(kernelCaps)); u64 lastdep = sizeof(dependencyList)/8; if (osGetFirmVersion() >= SYSTEM_VERSION(2,50,0)) // 9.6+ FIRM { - exh->sci.dependencies[lastdep++] = 0x0004013000004002ULL; // nfc + exhi->sci.dependencies[lastdep++] = 0x0004013000004002ULL; // nfc strncpy((char*)&localcaps0->service_access[0x20], "nfc:u", 8); s64 dummy = 0; bool isN3DS = svcGetSystemInfo(&dummy, 0x10001, 0) == 0; if (isN3DS) { - exh->sci.dependencies[lastdep++] = 0x0004013020004102ULL; // mvd + exhi->sci.dependencies[lastdep++] = 0x0004013020004102ULL; // mvd strncpy((char*)&localcaps0->service_access[0x21], "mvd:STD", 8); } } @@ -306,7 +306,7 @@ void HBLDR_HandleCommands(void *ctx) cmdbuf[0] = IPC_MakeHeader(4, 1, 2); cmdbuf[1] = 0; cmdbuf[2] = IPC_Desc_Buffer(sizeof(ExHeader_Info), IPC_BUFFER_RW); - cmdbuf[3] = (u32)exh; + cmdbuf[3] = (u32)exhi; break; } default: