Move hb:ldr from Rosalina to Loader

Let's not pretend in 2022 that it needed things from rosalina sysmodule
- it did not.

This moves 3DSX loading from Rosalina to Loader, and also removes all
the dependencies Loader had to other Luma3DS components (if kernel ext.
is missing, a default config will be used). This means that, as long as
you replace Loader to the one in here, you will be able to properly load
3DSX files.

Changes:
- hb:ldr is now hosted in loader
- hb:ldr LoadProcess, PatchExHeaderInfo, DebugNextApplicationByForce:
  all removed
- fix a bug where some malformed 3DSX files were not rejected
- grant access to CONFIG11 registers to 3DSX homebrew
- move dirty homebrew chainload (when HM. isn't loaded nor loadable) to
  pm
- pm:dbg (ext.) PrepareToChainloadHomebrew: removed
This commit is contained in:
TuxSH 2022-12-25 18:27:32 +01:00
parent 6fa80c959d
commit 7074ac1166
23 changed files with 594 additions and 697 deletions

View File

@ -25,7 +25,7 @@ AccessControlInfo:
DisableDebug : true
EnableForceDebug : false
CanWriteSharedPage : false
CanWriteSharedPage : true
CanUsePrivilegedPriority : false
CanUseNonAlphabetAndNumber : false
PermitMainFunctionArgument : false

View File

@ -1,97 +1,352 @@
/*
* This file is part of Luma3DS
* Copyright (C) 2016-2020, 2022 Aurora Wright, TuxSH, fincs
*
* 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 <http://www.gnu.org/licenses/>.
*
* 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 <string.h>
#include <assert.h>
#include "util.h"
#include "hbldr.h"
#include "3dsx.h"
static u32 hbldrRefcount = 0;
static Handle hbldrHandle = 0;
Result hbldrInit(void)
static const char serviceList[32][8] =
{
Result res;
if (AtomicPostIncrement(&hbldrRefcount)) return 0;
"APT:U",
"ac:u",
"am:net",
"boss:P",
"cam:u",
"cfg:nor",
"cfg:u",
"csnd:SND",
"dsp::DSP",
"fs:USER",
"gsp::Lcd",
"gsp::Gpu",
"hid:USER",
"http:C",
"ir:USER",
"ir:rst",
"ir:u",
"ldr:ro",
"mic:u",
"ndm:u",
"news:s",
"nim:s",
"ns:s",
"nwm::UDS",
"nwm::EXT",
"ptm:u",
"ptm:sysm",
"pxi:dev",
"qtm:u",
"soc:U",
"ssl:C",
"y2r:u",
};
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;
static const u64 dependencyListNativeFirm[] =
{
0x0004013000002402LL, //ac
0x0004013000001502LL, //am
0x0004013000001702LL, //cfg
0x0004013000001802LL, //codec
0x0004013000002702LL, //csnd
0x0004013000001A02LL, //dsp
0x0004013000001B02LL, //gpio
0x0004013000001C02LL, //gsp
0x0004013000001D02LL, //hid
0x0004013000002902LL, //http
0x0004013000001E02LL, //i2c
0x0004013000003302LL, //ir
0x0004013000001F02LL, //mcu
0x0004013000002C02LL, //nim
0x0004013000002D02LL, //nwm
0x0004013000002102LL, //pdn
0x0004013000003102LL, //ps
0x0004013000002202LL, //ptm
0x0004013000002E02LL, //socket
0x0004013000002302LL, //spi
0x0004013000002F02LL, //ssl
// Not present on SAFE_FIRM:
0x0004013000003402LL, //boss
0x0004013000001602LL, //camera
0x0004013000002802LL, //dlp
0x0004013000002002LL, //mic
0x0004013000002B02LL, //ndm
0x0004013000003502LL, //news
0x0004013000003702LL, //ro
};
static const u64 dependencyListSafeFirm[] =
{
0x0004013000002403LL, //ac
0x0004013000001503LL, //am
0x0004013000001703LL, //cfg
0x0004013000001803LL, //codec
0x0004013000002703LL, //csnd
0x0004013000001A03LL, //dsp
0x0004013000001B03LL, //gpio
0x0004013000001C03LL, //gsp
0x0004013000001D03LL, //hid
0x0004013000002903LL, //http
0x0004013000001E03LL, //i2c
0x0004013000003303LL, //ir
0x0004013000001F03LL, //mcu
0x0004013000002C03LL, //nim
0x0004013000002D03LL, //nwm
0x0004013000002103LL, //pdn
0x0004013000003103LL, //ps
0x0004013000002203LL, //ptm
0x0004013000002E03LL, //socket
0x0004013000002303LL, //spi
0x0004013000002F03LL, //ssl
0x0004013000003203LL, //friends (wouldn't be launched otherwise)
};
static const u32 kernelCaps[] =
{
0xFC00022C, // Kernel release version 8.0 is necessary for using the new linear mapping. Modified below.
0xFF81FF50, // RW static range mapping: 0x1FF50000 (DSP Shared Mem 1, start)
0xFF81FF58, // RW static range mapping: 0x1FF58000 (DSP Shared Mem 1, end)
0xFF81FF70, // RW static range mapping: 0x1FF70000 (DSP Shared Mem 2, start)
0xFF81FF78, // RW static range mapping: 0x1FF78000 (DSP Shared Mem 2, end)
0xFF91F000, // RO static range mapping: 0x1F000000 (VRAM, start)
0xFF91F600, // RO static range mapping: 0x1F600000 (VRAM, end)
0xFF002109, // Exflags: APPLICATION memtype + "Shared page writing" + "Allow debug" + "Access core2"
0xFE000200, // Handle table size: 0x200
// In case kernel ext isn't loaded:
0xFFE1EC40, // RW I/O page mapping: 0x1EC40000 (CONFIG11, for kernel takeover)
0xF0FFFFFF, // SVC ACL 0 to 0x7F (even if some SVC numbers don't exist)...
0xF1FFFFFF,
0xF2FFFFFF,
0xF3FFFFFF,
0xF4FFFFFF,
0xF5FFFFFF,
0xF6FFFFFF,
0xF7FFFFFF,
};
static u16 hbldrTarget[PATH_MAX+1];
static inline void error(u32* cmdbuf, Result rc)
{
cmdbuf[0] = IPC_MakeHeader(0, 1, 0);
cmdbuf[1] = rc;
}
static u16 *u16_strncpy(u16 *dest, const u16 *src, u32 size)
{
u32 i;
for (i = 0; i < size && src[i] != 0; i++)
dest[i] = src[i];
while (i < size)
dest[i++] = 0;
return dest;
}
void hbldrPatchExHeaderInfo(ExHeader_Info *exhi)
{
u32 stacksize = 4096; // 3dsx/libctru don't require anymore than this
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));
u32 coreVer = OS_KernelConfig->kernel_syscore_ver;
if (coreVer == 2)
memcpy(exhi->sci.dependencies, dependencyListNativeFirm, sizeof(dependencyListNativeFirm));
else if (coreVer == 3)
memcpy(exhi->sci.dependencies, dependencyListSafeFirm, sizeof(dependencyListSafeFirm));
ExHeader_Arm11SystemLocalCapabilities* localcaps0 = &exhi->aci.local_caps;
localcaps0->core_info.core_version = coreVer;
localcaps0->core_info.use_cpu_clockrate_804MHz = false;
localcaps0->core_info.enable_l2c = false;
localcaps0->core_info.ideal_processor = 0;
localcaps0->core_info.affinity_mask = BIT(0);
localcaps0->core_info.priority = 0x30;
u32 appmemtype = OS_KernelConfig->app_memtype;
localcaps0->core_info.o3ds_system_mode = appmemtype < 6 ? (SystemMode)appmemtype : SYSMODE_O3DS_PROD;
localcaps0->core_info.n3ds_system_mode = appmemtype >= 6 ? (SystemMode)(appmemtype - 6 + 1) : SYSMODE_N3DS_PROD;
memset(localcaps0->reslimits, 0, sizeof(localcaps0->reslimits));
// Set mode1 preemption mode for core1, max. 89% of CPU time (default 0, requires a APT_SetAppCpuTimeLimit call)
// See the big comment in sysmodules/pm/source/reslimit.c for technical details.
localcaps0->reslimits[0] = BIT(7) | 89;
localcaps0->storage_info.fs_access_info = 0xFFFFFFFF; // Give access to everything
localcaps0->storage_info.no_romfs = true;
localcaps0->storage_info.use_extended_savedata_access = true; // Whatever
// We have a patched SM, so whatever...
memset(localcaps0->service_access, 0, sizeof(localcaps0->service_access));
memcpy(localcaps0->service_access, serviceList, sizeof(serviceList));
localcaps0->reslimit_category = RESLIMIT_CATEGORY_APPLICATION;
ExHeader_Arm11KernelCapabilities* kcaps0 = &exhi->aci.kernel_caps;
memset(kcaps0->descriptors, 0xFF, sizeof(kcaps0->descriptors));
memcpy(kcaps0->descriptors, kernelCaps, sizeof(kernelCaps));
// Set kernel release version to the current kernel version
kcaps0->descriptors[0] = 0xFC000000 | (osGetKernelVersion() >> 16);
if (GET_VERSION_MINOR(osGetKernelVersion()) >= 50 && coreVer == 2) // 9.6+ NFIRM
{
u64 lastdep = sizeof(dependencyListNativeFirm)/8;
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)
{
exhi->sci.dependencies[lastdep++] = 0x0004013020004102ULL; // mvd
strncpy((char*)&localcaps0->service_access[0x21], "mvd:STD", 8);
}
}
return 0;
}
void hbldrExit(void)
Result hbldrLoadProcess(Handle *outProcessHandle, const ExHeader_Info *exhi)
{
if (AtomicDecrement(&hbldrRefcount)) return;
svcCloseHandle(hbldrHandle);
IFile file;
Result res;
const ExHeader_CodeSetInfo *csi = &exhi->sci.codeset_info;
u32 region = 0;
u32 count;
for (count = 0; count < 28; count++)
{
u32 desc = exhi->aci.kernel_caps.descriptors[count];
if (0x1FE == desc >> 23)
region = desc & 0xF00;
}
if (region == 0)
return MAKERESULT(RL_PERMANENT, RS_INVALIDARG, 1, 2);
if (hbldrTarget[0] == 0)
{
u16_strncpy(hbldrTarget, u"/boot.3dsx", PATH_MAX);
ldrArgvBuf[0] = 1;
strncpy((char*)&ldrArgvBuf[1], "sdmc:/boot.3dsx", sizeof(ldrArgvBuf)-4);
}
res = IFile_Open(&file, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_UTF16, hbldrTarget), FS_OPEN_READ);
hbldrTarget[0] = 0;
if (R_FAILED(res))
return res;
u32 totalSize = 0;
if (!Ldr_Get3dsxSize(&totalSize, &file))
{
IFile_Close(&file);
return (Result)-1;
}
u32 tmp = 0;
u32 addr = 0x10000000;
res = svcControlMemory(&tmp, addr, 0, totalSize, MEMOP_ALLOC | region, MEMPERM_READ | MEMPERM_WRITE);
if (R_FAILED(res))
{
IFile_Close(&file);
return res;
}
Handle hCodeset = Ldr_CodesetFrom3dsx(csi->name, (u32 *)addr, csi->text.address, &file, exhi->aci.local_caps.title_id);
IFile_Close(&file);
if (hCodeset != 0)
{
// There are always 28 descriptors
res = svcCreateProcess(outProcessHandle, hCodeset, exhi->aci.kernel_caps.descriptors, count);
svcCloseHandle(hCodeset);
}
else
res = MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_LDR, RD_NOT_FOUND);
svcControlMemory(&tmp, addr, 0, totalSize, MEMOP_FREE, 0);
return res;
}
Result HBLDR_LoadProcess(Handle *outCodeSet, u32 textAddr, u32 kernelFlags, u64 titleId, const char *name)
void hbldrHandleCommands(void *ctx)
{
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];
(void)ctx;
u32 *cmdbuf = getThreadCommandBuffer();
switch (cmdbuf[0] >> 16)
{
case 2: // SetTarget
{
if (cmdbuf[0] != IPC_MakeHeader(2, 0, 2) || (cmdbuf[1] & 0x3FFF) != 0x0002)
{
error(cmdbuf, 0xD9001830);
break;
}
size_t inSize = cmdbuf[1] >> 14;
inSize = inSize > PATH_MAX ? inSize : PATH_MAX;
ssize_t units = utf8_to_utf16(hbldrTarget, (const uint8_t*)cmdbuf[2], inSize);
if (units < 0 || units > PATH_MAX)
{
hbldrTarget[0] = 0;
error(cmdbuf, 0xD9001830);
break;
}
*outCodeSet = R_SUCCEEDED(rc) ? cmdbuf[3] : 0;
return rc;
hbldrTarget[units] = 0;
cmdbuf[0] = IPC_MakeHeader(2, 1, 0);
cmdbuf[1] = 0;
break;
}
case 3: // SetArgv
{
if (cmdbuf[0] != IPC_MakeHeader(3, 0, 2) || (cmdbuf[1] & 0x3FFF) != (0x2 | (1<<10)))
{
error(cmdbuf, 0xD9001830);
break;
}
size_t inSize = cmdbuf[1] >> 14;
inSize = inSize > ARGVBUF_SIZE ? inSize : ARGVBUF_SIZE;
memcpy(ldrArgvBuf, (const u8 *)cmdbuf[2], inSize);
cmdbuf[0] = IPC_MakeHeader(3, 1, 0);
cmdbuf[1] = 0;
break;
}
case 1: // LoadProcess (removed)
case 4: // PatchExHeaderInfo (removed)
case 5: // DebugNextApplicationByForce (removed)
default:
{
error(cmdbuf, 0xD900182F);
break;
}
}
}
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;
}
Result HBLDR_DebugNextApplicationByForce(bool dryRun)
{
u32* cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(5, 1, 0); // 0x50040
cmdbuf[1] = dryRun ? 1 : 0;
Result rc = svcSendSyncRequest(hbldrHandle);
if (R_SUCCEEDED(rc)) rc = cmdbuf[1];
return rc;
}

View File

@ -1,12 +1,42 @@
/*
* This file is part of Luma3DS
* Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
*
* 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.
*/
#pragma once
#include <3ds/exheader.h>
#include <3ds.h>
#include "luma_shared_config.h"
Result hbldrInit(void);
void hbldrExit(void);
Result hbldrLoadProcess(Handle *outProcessHandle, const ExHeader_Info *exhi);
void hbldrPatchExHeaderInfo(ExHeader_Info *exhi);
void hbldrHandleCommands(void *ctx);
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);
Result HBLDR_DebugNextApplicationByForce(bool dryRun);
static inline bool hbldrIs3dsxTitle(u64 tid)
{
return Luma_SharedConfig->use_hbldr && tid == Luma_SharedConfig->hbldr_3dsx_tid;
}
void HBLDR_RestartHbApplication(void *p);
void HBLDR_HandleCommands(void *ctx);

View File

@ -13,7 +13,15 @@ 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 ContentPath {
u32 contentType;
char fileName[8]; // for exefs
} ContentPath;
static const ContentPath codeContentPath = {
.contentType = 1, // ExeFS (with code)
.fileName = ".code", // last 3 bytes have to be 0, but this is guaranteed here.s
};
typedef struct prog_addrs_t
{
@ -92,12 +100,7 @@ static int lzss_decompress(u8 *end)
return ret;
}
static inline bool hbldrIs3dsxTitle(u64 tid)
{
return Luma_SharedConfig->use_hbldr && tid == Luma_SharedConfig->hbldr_3dsx_tid;
}
static Result allocateSharedMem(prog_addrs_t *shared, prog_addrs_t *vaddr, int flags)
static Result allocateSharedMem(prog_addrs_t *shared, const prog_addrs_t *vaddr, int flags)
{
u32 dummy;
@ -108,7 +111,7 @@ static Result allocateSharedMem(prog_addrs_t *shared, prog_addrs_t *vaddr, int f
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)
static Result loadCode(const ExHeader_Info *exhi, u64 programHandle, const prog_addrs_t *shared)
{
IFile file;
FS_Path archivePath;
@ -116,17 +119,22 @@ static Result loadCode(u64 titleId, prog_addrs_t *shared, u64 programHandle, int
u64 size;
u64 total;
u64 titleId = exhi->aci.local_caps.title_id;
const ExHeader_CodeSetInfo *csi = &exhi->sci.codeset_info;
bool isCompressed = csi->flags.compress_exefs_code;
if(!CONFIG(PATCHGAMES) || !loadTitleCodeSection(titleId, (u8 *)shared->text_addr, (u64)shared->total_size << 12))
{
archivePath.type = PATH_BINARY;
archivePath.data = &programHandle;
archivePath.size = 8;
archivePath.size = sizeof(programHandle);
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);
filePath.data = &codeContentPath;
filePath.size = sizeof(codeContentPath);
Result res;
if (R_FAILED(res = IFile_Open(&file, ARCHIVE_SAVEDATA_AND_CONTENT2, archivePath, filePath, FS_OPEN_READ)))
*(u64 *)0x1238 = programHandle;//panic(programHandle);//svcBreak(USERBREAK_ASSERT);
// get file size
assertSuccess(IFile_GetSize(&file, &size));
@ -147,8 +155,6 @@ static Result loadCode(u64 titleId, prog_addrs_t *shared, u64 programHandle, int
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;
@ -173,70 +179,40 @@ static Result GetProgramInfo(ExHeader_Info *exheaderInfo, u64 programHandle)
// Tweak 3dsx placeholder title exheaderInfo
if (hbldrIs3dsxTitle(exheaderInfo->aci.local_caps.title_id))
{
assertSuccess(hbldrInit());
HBLDR_PatchExHeaderInfo(exheaderInfo);
hbldrExit();
hbldrPatchExHeaderInfo(exheaderInfo);
}
else
{
u64 originaltitleId = exheaderInfo->aci.local_caps.title_id;
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;
exheaderInfo->aci.local_caps.title_id = originalTitleId;
}
return res;
}
static Result LoadProcess(Handle *process, u64 programHandle)
static Result LoadProcessImpl(Handle *outProcessHandle, const ExHeader_Info *exhi, u64 programHandle)
{
Result res;
int count;
u32 flags;
u32 desc;
const ExHeader_CodeSetInfo *csi = &exhi->sci.codeset_info;
Result res = 0;
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 programHandle
if (g_cached_programHandle != programHandle || hbldrIs3dsxTitle(g_exheaderInfo.aci.local_caps.title_id))
{
res = GetProgramInfo(&g_exheaderInfo, programHandle);
g_cached_programHandle = programHandle;
if (R_FAILED(res))
{
g_cached_programHandle = 0;
return res;
}
}
// get kernel flags
flags = 0;
u32 region = 0;
u32 count;
for (count = 0; count < 28; count++)
{
desc = g_exheaderInfo.aci.kernel_caps.descriptors[count];
u32 desc = exhi->aci.kernel_caps.descriptors[count];
if (0x1FE == desc >> 23)
flags = desc & 0xF00;
}
if (flags == 0)
return MAKERESULT(RL_PERMANENT, RS_INVALIDARG, 1, 2);
// check for 3dsx process
titleId = g_exheaderInfo.aci.local_caps.title_id;
ExHeader_CodeSetInfo *csi = &g_exheaderInfo.sci.codeset_info;
if (hbldrIs3dsxTitle(titleId))
{
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;
region = desc & 0xF00;
}
if (region == 0)
return MAKERESULT(RL_PERMANENT, RS_INVALIDARG, 1, 2);
// allocate process memory
vaddr.text_addr = csi->text.address;
@ -247,10 +223,11 @@ static Result LoadProcess(Handle *process, u64 programHandle)
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));
TRY(allocateSharedMem(&sharedAddr, &vaddr, region));
// load code
if (R_SUCCEEDED(res = loadCode(titleId, &sharedAddr, programHandle, csi->flags.compress_exefs_code)))
u64 titleId = exhi->aci.local_caps.title_id;
if (R_SUCCEEDED(res = loadCode(exhi, programHandle, &sharedAddr)))
{
memcpy(&codesetinfo.name, csi->name, 8);
codesetinfo.program_id = titleId;
@ -266,7 +243,8 @@ static Result LoadProcess(Handle *process, u64 programHandle)
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);
// There are always 28 descriptors
res = svcCreateProcess(outProcessHandle, codeset, exhi->aci.kernel_caps.descriptors, count);
svcCloseHandle(codeset);
res = R_SUCCEEDED(res) ? 0 : res;
}
@ -276,6 +254,28 @@ static Result LoadProcess(Handle *process, u64 programHandle)
return res;
}
static Result LoadProcess(Handle *process, u64 programHandle)
{
Result res;
// make sure the cached info corresponds to the current programHandle
if (g_cached_programHandle != programHandle || hbldrIs3dsxTitle(g_exheaderInfo.aci.local_caps.title_id))
{
res = GetProgramInfo(&g_exheaderInfo, programHandle);
g_cached_programHandle = programHandle;
if (R_FAILED(res))
{
g_cached_programHandle = 0;
return res;
}
}
if (hbldrIs3dsxTitle(g_exheaderInfo.aci.local_caps.title_id))
return hbldrLoadProcess(process, &g_exheaderInfo);
else
return LoadProcessImpl(process, &g_exheaderInfo, programHandle);
}
static Result RegisterProgram(u64 *programHandle, FS_ProgramInfo *title, FS_ProgramInfo *update)
{
Result res;

View File

@ -18,10 +18,13 @@
#include <3ds/types.h>
/// Default TitleID for 3DSX loading
#define HBLDR_DEFAULT_3DSX_TID 0x000400000D921E00ULL
/// Luma shared config type.
typedef struct LumaSharedConfig {
u64 hbldr_3dsx_tid; ///< Title ID to use for 3DSX loading.
bool use_hbldr; ///< Whether or not Loader should use hb:ldr (Rosalina writes 1).
bool use_hbldr; ///< Whether or not Loader should use hb:ldr (reset to true).
} LumaSharedConfig;
/// Luma shared config.

View File

@ -1,10 +1,13 @@
#include <3ds.h>
#include <assert.h>
#include "memory.h"
#include "patcher.h"
#include "ifile.h"
#include "util.h"
#include "loader.h"
#include "service_manager.h"
#include "3dsx.h"
#include "hbldr.h"
u32 config, multiConfig, bootConfig;
bool isN3DS, isSdMode;
@ -28,20 +31,43 @@ static Result fsldrPatchPermissions(void)
static inline void loadCFWInfo(void)
{
s64 out;
u64 hbldrTid = 0;
if(svcGetSystemInfo(&out, 0x20000, 0) != 1) panic(0xDEADCAFE);
bool isLumaWithKext = svcGetSystemInfo(&out, 0x20000, 0) == 1;
if (isLumaWithKext)
{
svcGetSystemInfo(&out, 0x10000, 3);
config = (u32)out;
svcGetSystemInfo(&out, 0x10000, 4);
multiConfig = (u32)out;
svcGetSystemInfo(&out, 0x10000, 5);
bootConfig = (u32)out;
svcGetSystemInfo(&out, 0x10000, 3);
config = (u32)out;
svcGetSystemInfo(&out, 0x10000, 4);
multiConfig = (u32)out;
svcGetSystemInfo(&out, 0x10000, 5);
bootConfig = (u32)out;
svcGetSystemInfo(&out, 0x10000, 0x100);
hbldrTid = (u64)out;
svcGetSystemInfo(&out, 0x10000, 0x201);
isN3DS = (bool)out;
svcGetSystemInfo(&out, 0x10000, 0x203);
isSdMode = (bool)out;
}
else
{
// Try to support non-Luma or builds where kext is disabled
s64 numKips = 0;
svcGetSystemInfo(&numKips, 26, 0);
svcGetSystemInfo(&out, 0x10000, 0x201);
isN3DS = (bool)out;
svcGetSystemInfo(&out, 0x10000, 0x203);
isSdMode = (bool)out;
if (numKips >= 6)
panic(0xDEADCAFE);
config = 0; // all options 0
multiConfig = 0;
bootConfig = 0;
isN3DS = OS_KernelConfig->app_memtype >= 6;
isSdMode = true;
}
Luma_SharedConfig->hbldr_3dsx_tid = hbldrTid == 0 ? HBLDR_DEFAULT_3DSX_TID : hbldrTid;
Luma_SharedConfig->use_hbldr = true;
}
void __ctru_exit(int rc) { (void)rc; } // needed to avoid linking error
@ -88,6 +114,7 @@ void initSystem(void)
static const ServiceManagerServiceEntry services[] = {
{ "Loader", 1, loaderHandleCommands, false },
{ "hb:ldr", 2, hbldrHandleCommands, true },
{ NULL },
};
@ -95,9 +122,18 @@ static const ServiceManagerNotificationEntry notifications[] = {
{ 0x000, NULL },
};
static u8 ALIGN(4) staticBufferForHbldr[0x400];
static_assert(ARGVBUF_SIZE > 2 * PATH_MAX, "Wrong 3DSX argv buffer size");
int main(void)
{
loadCFWInfo();
// Loader doesn't use any input static buffer, so we should be fine
u32 *sbuf = getThreadStaticBuffers();
sbuf[0] = IPC_Desc_StaticBuffer(sizeof(staticBufferForHbldr), 0);
sbuf[1] = (u32)staticBufferForHbldr;
sbuf[2] = IPC_Desc_StaticBuffer(sizeof(staticBufferForHbldr), 1);
sbuf[3] = (u32)staticBufferForHbldr;
assertSuccess(ServiceManager_Run(services, notifications, NULL));
return 0;
}

View File

@ -290,7 +290,7 @@ static Result launchTitleImpl(Handle *debug, ProcessData **outProcessData, const
return res;
}
static Result launchTitleImplWrapper(Handle *outDebug, u32 *outPid, const FS_ProgramInfo *programInfo, const FS_ProgramInfo *programInfoUpdate, u32 launchFlags)
Result launchTitleImplWrapper(Handle *outDebug, u32 *outPid, const FS_ProgramInfo *programInfo, const FS_ProgramInfo *programInfoUpdate, u32 launchFlags)
{
ExHeader_Info *exheaderInfo = ExHeaderInfoHeap_New();
if (exheaderInfo == NULL) {
@ -389,18 +389,18 @@ Result LaunchTitle(u32 *outPid, const FS_ProgramInfo *programInfo, u32 launchFla
Result LaunchTitleUpdate(const FS_ProgramInfo *programInfo, const FS_ProgramInfo *programInfoUpdate, u32 launchFlags)
{
ProcessList_Lock(&g_manager.processList);
if (g_manager.preparingForReboot) {
return 0xC8A05801;
}
if (g_manager.runningApplicationData != NULL) {
ProcessList_Unlock(&g_manager.processList);
return 0xC8A05BF0;
}
if (!(launchFlags & ~PMLAUNCHFLAG_NORMAL_APPLICATION)) {
return 0xD8E05802;
}
ProcessList_Unlock(&g_manager.processList);
ProcessList_Lock(&g_manager.processList);
if (g_manager.runningApplicationData != NULL) {
ProcessList_Unlock(&g_manager.processList);
return 0xC8A05BF0;
}
bool originallyDebugged = launchFlags & PMLAUNCHFLAG_QUEUE_DEBUG_APPLICATION;

View File

@ -20,3 +20,5 @@ Result autolaunchSysmodules(void);
// Custom
Result DebugNextApplicationByForce(bool debug);
Result LaunchTitleDebug(Handle *outDebug, const FS_ProgramInfo *programInfo, u32 launchFlags);
Result launchTitleImplWrapper(Handle *outDebug, u32 *outPid, const FS_ProgramInfo *programInfo, const FS_ProgramInfo *programInfoUpdate, u32 launchFlags);

View File

@ -4,6 +4,7 @@
#include "reslimit.h"
#include "launch.h"
#include "firmlaunch.h"
#include "termination.h"
#include "exheader_info_heap.h"
#include "task_runner.h"
#include "process_monitor.h"
@ -57,8 +58,19 @@ static const ServiceManagerServiceEntry services[] = {
{ NULL },
};
static void handleRestartHbAppNotification(u32 notificationId)
{
// Dirty workaround to support hbmenu on SAFE_FIRM
// Cleaning up dependencies would mean terminating most sysmodules,
// and letting app term. notif. go through would cause NS to svcBreak,
// this is not what we want.
(void)notificationId;
ChainloadHomebrewDirty();
}
static const ServiceManagerNotificationEntry notifications[] = {
{ 0x000, NULL },
{ 0x3000, handleRestartHbAppNotification },
{ 0x0000, NULL },
};
void __ctru_exit(int rc) { (void)rc; } // needed to avoid linking error

View File

@ -90,27 +90,3 @@ Result UnregisterProcess(u64 titleId)
ProcessList_Unlock(&g_manager.processList);
return 0;
}
Result PrepareToChainloadHomebrew(u64 titleId)
{
// Note: I'm allowing this command to be called for non-applications, maybe that'll be useful
// in the future...
ProcessData *foundProcess = NULL;
Result res;
ProcessList_Lock(&g_manager.processList);
foundProcess = ProcessList_FindProcessByTitleId(&g_manager.processList, titleId & ~N3DS_TID_MASK);
if (foundProcess != NULL) {
// Clear the "notify on termination, don't cleanup" flag, so that for ex. APT isn't notified & no need for UnregisterProcess,
// and the "dependencies loaded" flag, so that the dependencies aren't killed (for ex. when
// booting hbmenu instead of Home Menu, in which case the same title is going to be launched...)
foundProcess->flags &= ~(PROCESSFLAG_DEPENDENCIES_LOADED | PROCESSFLAG_NOTIFY_TERMINATION);
res = 0;
} else {
res = MAKERESULT(RL_TEMPORARY, RS_NOTFOUND, RM_PM, 0x100);
}
ProcessList_Unlock(&g_manager.processList);
return res;
}

View File

@ -21,4 +21,3 @@ extern Manager g_manager;
void Manager_Init(void *procBuf, size_t numProc);
void Manager_RegisterKips(void);
Result UnregisterProcess(u64 titleId);
Result PrepareToChainloadHomebrew(u64 titleId);

View File

@ -12,7 +12,6 @@ void pmDbgHandleCommands(void *ctx)
u32 cmdhdr = cmdbuf[0];
FS_ProgramInfo programInfo;
u64 titleId;
Handle debug;
u32 pid;
u32 launchFlags;
@ -59,11 +58,7 @@ void pmDbgHandleCommands(void *ctx)
cmdbuf[2] = IPC_Desc_MoveHandles(1);
cmdbuf[3] = debug;
break;
case 0x103:
memcpy(&titleId, cmdbuf + 1, 8);
cmdbuf[1] = PrepareToChainloadHomebrew(titleId);
cmdbuf[0] = IPC_MakeHeader(0x103, 1, 0);
break;
case 0x103: // PrepareToChainloadHomebrew (removed)
default:
cmdbuf[0] = IPC_MakeHeader(0, 1, 0);
cmdbuf[1] = 0xD900182F;

View File

@ -5,6 +5,7 @@
#include "util.h"
#include "exheader_info_heap.h"
#include "task_runner.h"
#include "launch.h"
void forceMountSdCard(void)
{
@ -387,3 +388,78 @@ Result PrepareForReboot(u32 pid, s64 timeout)
TaskRunner_RunTask(PrepareForRebootAsync, &args, sizeof(args));
return 0;
}
static void ChainloadHomebrewDirtyAsync(void *argdata)
{
(void)argdata;
Result res = 0;
ProcessData *app = NULL;
u32 launchFlags;
FS_ProgramInfo programInfo;
ExHeader_Info *exheaderInfo = ExHeaderInfoHeap_New();
if (exheaderInfo == NULL) {
panic(0);
}
assertSuccess(svcClearEvent(g_manager.allNotifiedTerminationEvent));
g_manager.waitingForTermination = true;
ProcessList_Lock(&g_manager.processList);
app = g_manager.runningApplicationData;
if (app != NULL) {
// Clear the "notify on termination, don't cleanup" flag, so that for ex. APT isn't notified & no need for
// UnregisterProcess, and the "dependencies loaded" flag, so that the dependencies aren't killed (for ex. when
// booting hbmenu instead of Home Menu, in which case the same title is going to be launched...)
launchFlags = app->launchFlags;
programInfo.programId = app->titleId;
programInfo.mediaType = app->mediaType;
app->flags &= ~(PROCESSFLAG_DEPENDENCIES_LOADED | PROCESSFLAG_NOTIFY_TERMINATION);
terminateProcessImpl(app, exheaderInfo);
}
ProcessList_Unlock(&g_manager.processList);
res = commitPendingTerminations(3 * 1000 * 1000 * 1000LL); // 3s, what NS is using
ExHeaderInfoHeap_Delete(exheaderInfo);
g_manager.waitingForTermination = false;
if (app == NULL) {
res = MAKERESULT(RL_TEMPORARY, RS_NOTFOUND, RM_PM, 0x100);
} else if (R_SUCCEEDED(res)) {
// Wait for process monitor thread to clean the terminated process up
app = NULL;
do {
svcSleepThread(100 * 1000 * 1000LL);
ProcessList_Lock(&g_manager.processList);
app = g_manager.runningApplicationData;
ProcessList_Unlock(&g_manager.processList);
} while (app != NULL);
// Since this is a dirty workaround for hb support on SAFE_FIRM, we can opt not to support
// launch-from-gamecard/update support.
launchFlags &= ~PMLAUNCHFLAG_USE_UPDATE_TITLE;
launchFlags |= PMLAUNCHFLAGEXT_FAKE_DEPENDENCY_LOADING;
res = launchTitleImplWrapper(NULL, NULL, &programInfo, NULL, launchFlags);
}
}
Result ChainloadHomebrewDirty(void) {
ProcessData *app = NULL;
Result res = 0;
if (g_manager.preparingForReboot) {
return 0xC8A05801;
}
ProcessList_Lock(&g_manager.processList);
app = g_manager.runningApplicationData;
ProcessList_Unlock(&g_manager.processList);
if (app == NULL) {
res = MAKERESULT(RL_TEMPORARY, RS_NOTFOUND, RM_PM, 0x100);
} else {
TaskRunner_RunTask(ChainloadHomebrewDirtyAsync, NULL, 0);
res = 0;
}
return res;
}

View File

@ -10,3 +10,5 @@ Result TerminateApplication(s64 timeout);
Result TerminateTitle(u64 titleId, s64 timeout);
Result TerminateProcess(u32 pid, s64 timeout);
Result PrepareForReboot(u32 pid, s64 timeout);
Result ChainloadHomebrewDirty(void);

View File

@ -1,36 +0,0 @@
/*
* This file is part of Luma3DS
* Copyright (C) 2016-2020 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 <http://www.gnu.org/licenses/>.
*
* 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.
*/
/* File mainly written by fincs */
#pragma once
#include <3ds/types.h>
#include "MyThread.h"
#define HBLDR_DEFAULT_3DSX_TID 0x000400000D921E00ULL
void HBLDR_RestartHbApplication(void *p);
void HBLDR_HandleCommands(void *ctx);

View File

@ -18,12 +18,13 @@
#include <3ds/types.h>
#include <3ds/types.h>
/// Default TitleID for 3DSX loading
#define HBLDR_DEFAULT_3DSX_TID 0x000400000D921E00ULL
/// Luma shared config type.
typedef struct LumaSharedConfig {
u64 hbldr_3dsx_tid; ///< Title ID to use for 3DSX loading.
bool use_hbldr; ///< Whether or not Loader should use hb:ldr (Rosalina writes 1).
bool use_hbldr; ///< Whether or not Loader should use hb:ldr (reset to true).
} LumaSharedConfig;
/// Luma shared config.

View File

@ -14,4 +14,3 @@ enum {
Result PMDBG_GetCurrentAppInfo(FS_ProgramInfo *outProgramInfo, u32 *outPid, u32 *outLaunchFlags);
Result PMDBG_DebugNextApplicationByForce(bool debug);
Result PMDBG_LaunchTitleDebug(Handle *outDebug, const FS_ProgramInfo *programInfo, u32 launchFlags);
Result PMDBG_PrepareToChainloadHomebrew(u64 titleId);

View File

@ -1,424 +0,0 @@
/*
* This file is part of Luma3DS
* Copyright (C) 2016-2020 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 <http://www.gnu.org/licenses/>.
*
* 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.
*/
/* This file was mostly written by fincs */
#include <3ds.h>
#include "hbloader.h"
#include "3dsx.h"
#include "menu.h"
#include "csvc.h"
#include "memory.h"
#include "gdb/server.h"
#include "pmdbgext.h"
extern GDBContext *nextApplicationGdbCtx;
extern GDBServer gdbServer;
static const char serviceList[32][8] =
{
"APT:U",
"ac:u",
"am:net",
"boss:P",
"cam:u",
"cfg:nor",
"cfg:u",
"csnd:SND",
"dsp::DSP",
"fs:USER",
"gsp::Lcd",
"gsp::Gpu",
"hid:USER",
"http:C",
"ir:USER",
"ir:rst",
"ir:u",
"ldr:ro",
"mic:u",
"ndm:u",
"news:s",
"nim:s",
"ns:s",
"nwm::UDS",
"nwm::EXT",
"ptm:u",
"ptm:sysm",
"pxi:dev",
"qtm:u",
"soc:U",
"ssl:C",
"y2r:u",
};
static const u64 dependencyListNativeFirm[] =
{
0x0004013000002402LL, //ac
0x0004013000001502LL, //am
0x0004013000001702LL, //cfg
0x0004013000001802LL, //codec
0x0004013000002702LL, //csnd
0x0004013000001A02LL, //dsp
0x0004013000001B02LL, //gpio
0x0004013000001C02LL, //gsp
0x0004013000001D02LL, //hid
0x0004013000002902LL, //http
0x0004013000001E02LL, //i2c
0x0004013000003302LL, //ir
0x0004013000001F02LL, //mcu
0x0004013000002C02LL, //nim
0x0004013000002D02LL, //nwm
0x0004013000002102LL, //pdn
0x0004013000003102LL, //ps
0x0004013000002202LL, //ptm
0x0004013000002E02LL, //socket
0x0004013000002302LL, //spi
0x0004013000002F02LL, //ssl
// Not present on SAFE_FIRM:
0x0004013000003402LL, //boss
0x0004013000001602LL, //camera
0x0004013000002802LL, //dlp
0x0004013000002002LL, //mic
0x0004013000002B02LL, //ndm
0x0004013000003502LL, //news
0x0004013000003702LL, //ro
};
static const u64 dependencyListSafeFirm[] =
{
0x0004013000002403LL, //ac
0x0004013000001503LL, //am
0x0004013000001703LL, //cfg
0x0004013000001803LL, //codec
0x0004013000002703LL, //csnd
0x0004013000001A03LL, //dsp
0x0004013000001B03LL, //gpio
0x0004013000001C03LL, //gsp
0x0004013000001D03LL, //hid
0x0004013000002903LL, //http
0x0004013000001E03LL, //i2c
0x0004013000003303LL, //ir
0x0004013000001F03LL, //mcu
0x0004013000002C03LL, //nim
0x0004013000002D03LL, //nwm
0x0004013000002103LL, //pdn
0x0004013000003103LL, //ps
0x0004013000002203LL, //ptm
0x0004013000002E03LL, //socket
0x0004013000002303LL, //spi
0x0004013000002F03LL, //ssl
0x0004013000003203LL, //friends (wouldn't be launched otherwise)
};
static const u32 kernelCaps[] =
{
0xFC00022C, // Kernel release version 8.0 is necessary for using the new linear mapping. Modified below.
0xFF81FF50, // RW static mapping: 0x1FF50000
0xFF81FF58, // RW static mapping: 0x1FF58000
0xFF81FF70, // RW static mapping: 0x1FF70000
0xFF81FF78, // RW static mapping: 0x1FF78000
0xFF91F000, // RO static mapping: 0x1F000000
0xFF91F600, // RO static mapping: 0x1F600000
0xFF002109, // Exflags: APPLICATION memtype + "Shared page writing" + "Allow debug" + "Access core2"
0xFE000200, // Handle table size: 0x200
};
static inline void assertSuccess(Result res)
{
if(R_FAILED(res))
svcBreak(USERBREAK_PANIC);
}
static u16 hbldrTarget[PATH_MAX+1];
static inline void error(u32* cmdbuf, Result rc)
{
cmdbuf[0] = IPC_MakeHeader(0, 1, 0);
cmdbuf[1] = rc;
}
static u16 *u16_strncpy(u16 *dest, const u16 *src, u32 size)
{
u32 i;
for (i = 0; i < size && src[i] != 0; i++)
dest[i] = src[i];
while (i < size)
dest[i++] = 0;
return dest;
}
void HBLDR_RestartHbApplication(void *p)
{
(void)p;
// Don't crash if we fail
FS_ProgramInfo programInfo;
u32 pid;
u32 launchFlags;
Result res = PMDBG_GetCurrentAppInfo(&programInfo, &pid, &launchFlags);
if (R_FAILED(res)) return;
res = PMDBG_PrepareToChainloadHomebrew(programInfo.programId);
if (R_FAILED(res)) return;
res = PMAPP_TerminateCurrentApplication(3 * 1000 * 1000 *1000LL); // 3s, like what NS uses
if (R_FAILED(res)) return;
if (R_SUCCEEDED(res))
{
do
{
svcSleepThread(100 * 1000 * 1000LL);
res = PMAPP_LaunchTitle(&programInfo, PMLAUNCHFLAGEXT_FAKE_DEPENDENCY_LOADING | launchFlags);
} while (res == (Result)0xC8A05BF0);
}
}
void HBLDR_HandleCommands(void *ctx)
{
(void)ctx;
Result res = 0;
IFile file;
u32 *cmdbuf = getThreadCommandBuffer();
switch (cmdbuf[0] >> 16)
{
case 1: // LoadProcess
{
if (cmdbuf[0] != IPC_MakeHeader(1, 6, 0))
{
error(cmdbuf, 0xD9001830);
break;
}
u32 baseAddr = cmdbuf[1];
u32 flags = cmdbuf[2] & 0xF00;
u64 tid = (u64)cmdbuf[3] | ((u64)cmdbuf[4]<<32);
char name[8];
memcpy(name, &cmdbuf[5], sizeof(name));
if (hbldrTarget[0] == 0)
{
u16_strncpy(hbldrTarget, u"/boot.3dsx", PATH_MAX);
ldrArgvBuf[0] = 1;
strncpy((char*)&ldrArgvBuf[1], "sdmc:/boot.3dsx", sizeof(ldrArgvBuf)-4);
}
res = IFile_Open(&file, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_UTF16, hbldrTarget), FS_OPEN_READ);
hbldrTarget[0] = 0;
if (R_FAILED(res))
{
error(cmdbuf, res);
break;
}
u32 totalSize = 0;
res = Ldr_Get3dsxSize(&totalSize, &file);
if (R_FAILED(res))
{
IFile_Close(&file);
error(cmdbuf, res);
break;
}
// note: mappableFree doesn't do anything
u32 tmp = 0;
u32 *addr = mappableAlloc(totalSize);
res = svcControlMemoryEx(&tmp, (u32)addr, 0, totalSize, MEMOP_ALLOC | flags, MEMPERM_READ | MEMPERM_WRITE, true);
if (R_FAILED(res))
{
IFile_Close(&file);
error(cmdbuf, res);
break;
}
Handle hCodeset = Ldr_CodesetFrom3dsx(name, addr, baseAddr, &file, tid);
IFile_Close(&file);
if (!hCodeset)
{
svcControlMemory(&tmp, (u32)addr, 0, totalSize, MEMOP_FREE, 0);
error(cmdbuf, MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_LDR, RD_NOT_FOUND));
break;
}
cmdbuf[0] = IPC_MakeHeader(1, 1, 2);
cmdbuf[1] = 0;
cmdbuf[2] = IPC_Desc_MoveHandles(1);
cmdbuf[3] = hCodeset;
break;
}
case 2: // SetTarget
{
if (cmdbuf[0] != IPC_MakeHeader(2, 0, 2) || (cmdbuf[1] & 0x3FFF) != 0x0002)
{
error(cmdbuf, 0xD9001830);
break;
}
ssize_t units = utf8_to_utf16(hbldrTarget, (const uint8_t*)cmdbuf[2], PATH_MAX);
if (units < 0 || units > PATH_MAX)
{
hbldrTarget[0] = 0;
error(cmdbuf, 0xD9001830);
break;
}
hbldrTarget[units] = 0;
cmdbuf[0] = IPC_MakeHeader(2, 1, 0);
cmdbuf[1] = 0;
break;
}
case 3: // SetArgv
{
if (cmdbuf[0] != IPC_MakeHeader(3, 0, 2) || (cmdbuf[1] & 0x3FFF) != (0x2 | (1<<10)))
{
error(cmdbuf, 0xD9001830);
break;
}
// No need to do anything - the kernel already copied the data to our buffer
cmdbuf[0] = IPC_MakeHeader(3, 1, 0);
cmdbuf[1] = 0;
break;
}
case 4: // PatchExHeaderInfo
{
if (cmdbuf[0] != IPC_MakeHeader(4, 0, 2) || cmdbuf[1] != IPC_Desc_Buffer(sizeof(ExHeader_Info), IPC_BUFFER_RW))
{
error(cmdbuf, 0xD9001830);
break;
}
// Perform ExHeader patches
ExHeader_Info* exhi = (ExHeader_Info*)cmdbuf[2];
u32 stacksize = 4096; // 3dsx/libctru don't require anymore than this
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));
u32 coreVer = OS_KernelConfig->kernel_syscore_ver;
if (coreVer == 2)
memcpy(exhi->sci.dependencies, dependencyListNativeFirm, sizeof(dependencyListNativeFirm));
else if (coreVer == 3)
memcpy(exhi->sci.dependencies, dependencyListSafeFirm, sizeof(dependencyListSafeFirm));
ExHeader_Arm11SystemLocalCapabilities* localcaps0 = &exhi->aci.local_caps;
localcaps0->core_info.core_version = coreVer;
localcaps0->core_info.use_cpu_clockrate_804MHz = false;
localcaps0->core_info.enable_l2c = false;
localcaps0->core_info.ideal_processor = 0;
localcaps0->core_info.affinity_mask = BIT(0);
localcaps0->core_info.priority = 0x30;
u32 appmemtype = OS_KernelConfig->app_memtype;
localcaps0->core_info.o3ds_system_mode = appmemtype < 6 ? (SystemMode)appmemtype : SYSMODE_O3DS_PROD;
localcaps0->core_info.n3ds_system_mode = appmemtype >= 6 ? (SystemMode)(appmemtype - 6 + 1) : SYSMODE_N3DS_PROD;
memset(localcaps0->reslimits, 0, sizeof(localcaps0->reslimits));
// Set mode1 preemption mode for core1, max. 89% of CPU time (default 0, requires a APT_SetAppCpuTimeLimit call)
// See the big comment in sysmodules/pm/source/reslimit.c for technical details.
localcaps0->reslimits[0] = BIT(7) | 89;
localcaps0->storage_info.fs_access_info = 0xFFFFFFFF; // Give access to everything
localcaps0->storage_info.no_romfs = true;
localcaps0->storage_info.use_extended_savedata_access = true; // Whatever
// We have a patched SM, so whatever...
memset(localcaps0->service_access, 0, sizeof(localcaps0->service_access));
memcpy(localcaps0->service_access, serviceList, sizeof(serviceList));
localcaps0->reslimit_category = RESLIMIT_CATEGORY_APPLICATION;
ExHeader_Arm11KernelCapabilities* kcaps0 = &exhi->aci.kernel_caps;
memset(kcaps0->descriptors, 0xFF, sizeof(kcaps0->descriptors));
memcpy(kcaps0->descriptors, kernelCaps, sizeof(kernelCaps));
// Set kernel release version to the current kernel version
kcaps0->descriptors[0] = 0xFC000000 | (osGetKernelVersion() >> 16);
if (GET_VERSION_MINOR(osGetKernelVersion()) >= 50 && coreVer == 2) // 9.6+ NFIRM
{
u64 lastdep = sizeof(dependencyListNativeFirm)/8;
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)
{
exhi->sci.dependencies[lastdep++] = 0x0004013020004102ULL; // mvd
strncpy((char*)&localcaps0->service_access[0x21], "mvd:STD", 8);
}
}
cmdbuf[0] = IPC_MakeHeader(4, 1, 2);
cmdbuf[1] = 0;
cmdbuf[2] = IPC_Desc_Buffer(sizeof(ExHeader_Info), IPC_BUFFER_RW);
cmdbuf[3] = (u32)exhi;
break;
}
case 5: // DebugNextApplicationByForce
{
res = 0;
if (gdbServer.referenceCount == 0)
res = MAKERESULT(RL_PERMANENT, RS_INVALIDSTATE, RM_LDR, RD_NOT_INITIALIZED);
else if (cmdbuf[1] == 0)
{
GDB_LockAllContexts(&gdbServer);
if (nextApplicationGdbCtx != NULL)
res = MAKERESULT(RL_PERMANENT, RS_NOP, RM_LDR, RD_ALREADY_DONE);
else
{
nextApplicationGdbCtx = GDB_SelectAvailableContext(&gdbServer, GDB_PORT_BASE + 3, GDB_PORT_BASE + 4);
if (nextApplicationGdbCtx != NULL)
{
nextApplicationGdbCtx->debug = 0;
nextApplicationGdbCtx->pid = 0xFFFFFFFF;
res = PMDBG_DebugNextApplicationByForce(true);
if (R_FAILED(res))
{
nextApplicationGdbCtx->flags = 0;
nextApplicationGdbCtx->localPort = 0;
nextApplicationGdbCtx = NULL;
}
}
else
res = MAKERESULT(RL_PERMANENT, RS_OUTOFRESOURCE, RM_LDR, RD_OUT_OF_RANGE);
}
GDB_UnlockAllContexts(&gdbServer);
}
cmdbuf[1] = res;
cmdbuf[0] = IPC_MakeHeader(5, 1, 0);
break;
}
default:
{
error(cmdbuf, 0xD900182F);
break;
}
}
}

View File

@ -29,8 +29,6 @@
#include "menu.h"
#include "service_manager.h"
#include "errdisp.h"
#include "hbloader.h"
#include "3dsx.h"
#include "utils.h"
#include "MyThread.h"
#include "menus/miscellaneous.h"
@ -81,9 +79,6 @@ void initSystem(void)
mappableInit(0x10000000, 0x14000000);
isN3DS = svcGetSystemInfo(&out, 0x10001, 0) == 0;
svcGetSystemInfo(&out, 0x10000, 0x100);
Luma_SharedConfig->hbldr_3dsx_tid = out == 0 ? HBLDR_DEFAULT_3DSX_TID : (u64)out;
Luma_SharedConfig->use_hbldr = true;
svcGetSystemInfo(&out, 0x10000, 0x101);
menuCombo = out == 0 ? DEFAULT_MENU_COMBO : (u32)out;
@ -216,14 +211,15 @@ static void handleNextApplicationDebuggedByForce(u32 notificationId)
TaskRunner_RunTask(debuggerFetchAndSetNextApplicationDebugHandleTask, NULL, 0);
}
#if 0
static void handleRestartHbAppNotification(u32 notificationId)
{
(void)notificationId;
TaskRunner_RunTask(HBLDR_RestartHbApplication, NULL, 0);
}
#endif
static const ServiceManagerServiceEntry services[] = {
{ "hb:ldr", 2, HBLDR_HandleCommands, true },
{ NULL },
};
@ -240,22 +236,11 @@ static const ServiceManagerNotificationEntry notifications[] = {
{ 0x214, handleShellNotification },
{ 0x1000, handleNextApplicationDebuggedByForce },
{ 0x2000, handlePreTermNotification },
{ 0x3000, handleRestartHbAppNotification },
{ 0x000, NULL },
};
int main(void)
{
static u8 ipcBuf[0x100] = {0}; // used by both err:f and hb:ldr
// Set up static buffers for IPC
u32* bufPtrs = getThreadStaticBuffers();
memset(bufPtrs, 0, 16 * 2 * 4);
bufPtrs[0] = IPC_Desc_StaticBuffer(sizeof(ipcBuf), 0);
bufPtrs[1] = (u32)ipcBuf;
bufPtrs[2] = IPC_Desc_StaticBuffer(sizeof(ldrArgvBuf), 1);
bufPtrs[3] = (u32)ldrArgvBuf;
if(R_FAILED(svcCreateEvent(&preTerminationEvent, RESET_STICKY)))
svcBreak(USERBREAK_ASSERT);

View File

@ -30,7 +30,6 @@
#include "ntp.h"
#include "memory.h"
#include "draw.h"
#include "hbloader.h"
#include "fmt.h"
#include "utils.h" // for makeArmBranch
#include "minisoc.h"

View File

@ -48,16 +48,3 @@ Result PMDBG_LaunchTitleDebug(Handle *outDebug, const FS_ProgramInfo *programInf
*outDebug = cmdbuf[3];
return (Result)cmdbuf[1];
}
Result PMDBG_PrepareToChainloadHomebrew(u64 titleId)
{
Result ret = 0;
u32 *cmdbuf = getThreadCommandBuffer();
cmdbuf[0] = IPC_MakeHeader(0x103, 2, 0);
memcpy(&cmdbuf[1], &titleId, 8);
if(R_FAILED(ret = svcSendSyncRequest(*pmDbgGetSessionHandle()))) return ret;
return (Result)cmdbuf[1];
}