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:
parent
6fa80c959d
commit
7074ac1166
@ -25,7 +25,7 @@ AccessControlInfo:
|
|||||||
|
|
||||||
DisableDebug : true
|
DisableDebug : true
|
||||||
EnableForceDebug : false
|
EnableForceDebug : false
|
||||||
CanWriteSharedPage : false
|
CanWriteSharedPage : true
|
||||||
CanUsePrivilegedPriority : false
|
CanUsePrivilegedPriority : false
|
||||||
CanUseNonAlphabetAndNumber : false
|
CanUseNonAlphabetAndNumber : false
|
||||||
PermitMainFunctionArgument : false
|
PermitMainFunctionArgument : false
|
||||||
|
@ -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 <3ds.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include "util.h"
|
||||||
#include "hbldr.h"
|
#include "hbldr.h"
|
||||||
|
#include "3dsx.h"
|
||||||
|
|
||||||
static u32 hbldrRefcount = 0;
|
static const char serviceList[32][8] =
|
||||||
static Handle hbldrHandle = 0;
|
|
||||||
|
|
||||||
Result hbldrInit(void)
|
|
||||||
{
|
{
|
||||||
Result res;
|
"APT:U",
|
||||||
if (AtomicPostIncrement(&hbldrRefcount)) return 0;
|
"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)) {
|
static const u64 dependencyListNativeFirm[] =
|
||||||
res = svcConnectToPort(&hbldrHandle, "hb:ldr");
|
{
|
||||||
if(R_FAILED(res) && res != (Result)0xD88007FA) {
|
0x0004013000002402LL, //ac
|
||||||
AtomicDecrement(&hbldrRefcount);
|
0x0004013000001502LL, //am
|
||||||
return res;
|
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;
|
IFile file;
|
||||||
svcCloseHandle(hbldrHandle);
|
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
|
(void)ctx;
|
||||||
cmdbuf[0] = IPC_MakeHeader(1, 6, 0);
|
u32 *cmdbuf = getThreadCommandBuffer();
|
||||||
cmdbuf[1] = textAddr;
|
switch (cmdbuf[0] >> 16)
|
||||||
cmdbuf[2] = kernelFlags & 0xF00;
|
{
|
||||||
memcpy(&cmdbuf[3], &titleId, 8);
|
case 2: // SetTarget
|
||||||
strncpy((char *)&cmdbuf[5], name, 8);
|
{
|
||||||
Result rc = svcSendSyncRequest(hbldrHandle);
|
if (cmdbuf[0] != IPC_MakeHeader(2, 0, 2) || (cmdbuf[1] & 0x3FFF) != 0x0002)
|
||||||
if (R_SUCCEEDED(rc)) rc = cmdbuf[1];
|
{
|
||||||
|
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;
|
hbldrTarget[units] = 0;
|
||||||
return rc;
|
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;
|
|
||||||
}
|
|
@ -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
|
#pragma once
|
||||||
|
|
||||||
#include <3ds/exheader.h>
|
#include <3ds.h>
|
||||||
|
#include "luma_shared_config.h"
|
||||||
|
|
||||||
Result hbldrInit(void);
|
Result hbldrLoadProcess(Handle *outProcessHandle, const ExHeader_Info *exhi);
|
||||||
void hbldrExit(void);
|
void hbldrPatchExHeaderInfo(ExHeader_Info *exhi);
|
||||||
|
void hbldrHandleCommands(void *ctx);
|
||||||
|
|
||||||
Result HBLDR_LoadProcess(Handle *outCodeSet, u32 textAddr, u32 kernelFlags, u64 titleId, const char *name);
|
static inline bool hbldrIs3dsxTitle(u64 tid)
|
||||||
Result HBLDR_SetTarget(const char* path);
|
{
|
||||||
Result HBLDR_SetArgv(const void* buffer, size_t size);
|
return Luma_SharedConfig->use_hbldr && tid == Luma_SharedConfig->hbldr_3dsx_tid;
|
||||||
Result HBLDR_PatchExHeaderInfo(ExHeader_Info *exheaderInfo);
|
}
|
||||||
Result HBLDR_DebugNextApplicationByForce(bool dryRun);
|
|
||||||
|
void HBLDR_RestartHbApplication(void *p);
|
||||||
|
void HBLDR_HandleCommands(void *ctx);
|
||||||
|
@ -13,7 +13,15 @@ static u8 g_ret_buf[sizeof(ExHeader_Info)];
|
|||||||
static u64 g_cached_programHandle;
|
static u64 g_cached_programHandle;
|
||||||
static ExHeader_Info g_exheaderInfo;
|
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
|
typedef struct prog_addrs_t
|
||||||
{
|
{
|
||||||
@ -92,12 +100,7 @@ static int lzss_decompress(u8 *end)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool hbldrIs3dsxTitle(u64 tid)
|
static Result allocateSharedMem(prog_addrs_t *shared, const prog_addrs_t *vaddr, int flags)
|
||||||
{
|
|
||||||
return Luma_SharedConfig->use_hbldr && tid == Luma_SharedConfig->hbldr_3dsx_tid;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Result allocateSharedMem(prog_addrs_t *shared, prog_addrs_t *vaddr, int flags)
|
|
||||||
{
|
{
|
||||||
u32 dummy;
|
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);
|
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;
|
IFile file;
|
||||||
FS_Path archivePath;
|
FS_Path archivePath;
|
||||||
@ -116,17 +119,22 @@ static Result loadCode(u64 titleId, prog_addrs_t *shared, u64 programHandle, int
|
|||||||
u64 size;
|
u64 size;
|
||||||
u64 total;
|
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))
|
if(!CONFIG(PATCHGAMES) || !loadTitleCodeSection(titleId, (u8 *)shared->text_addr, (u64)shared->total_size << 12))
|
||||||
{
|
{
|
||||||
archivePath.type = PATH_BINARY;
|
archivePath.type = PATH_BINARY;
|
||||||
archivePath.data = &programHandle;
|
archivePath.data = &programHandle;
|
||||||
archivePath.size = 8;
|
archivePath.size = sizeof(programHandle);
|
||||||
|
|
||||||
filePath.type = PATH_BINARY;
|
filePath.type = PATH_BINARY;
|
||||||
filePath.data = CODE_PATH;
|
filePath.data = &codeContentPath;
|
||||||
filePath.size = sizeof(CODE_PATH);
|
filePath.size = sizeof(codeContentPath);
|
||||||
if (R_FAILED(IFile_Open(&file, ARCHIVE_SAVEDATA_AND_CONTENT2, archivePath, filePath, FS_OPEN_READ)))
|
Result res;
|
||||||
svcBreak(USERBREAK_ASSERT);
|
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
|
// get file size
|
||||||
assertSuccess(IFile_GetSize(&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);
|
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);
|
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;
|
return 0;
|
||||||
@ -173,70 +179,40 @@ static Result GetProgramInfo(ExHeader_Info *exheaderInfo, u64 programHandle)
|
|||||||
// Tweak 3dsx placeholder title exheaderInfo
|
// Tweak 3dsx placeholder title exheaderInfo
|
||||||
if (hbldrIs3dsxTitle(exheaderInfo->aci.local_caps.title_id))
|
if (hbldrIs3dsxTitle(exheaderInfo->aci.local_caps.title_id))
|
||||||
{
|
{
|
||||||
assertSuccess(hbldrInit());
|
hbldrPatchExHeaderInfo(exheaderInfo);
|
||||||
HBLDR_PatchExHeaderInfo(exheaderInfo);
|
|
||||||
hbldrExit();
|
|
||||||
}
|
}
|
||||||
else
|
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))
|
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;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Result LoadProcess(Handle *process, u64 programHandle)
|
static Result LoadProcessImpl(Handle *outProcessHandle, const ExHeader_Info *exhi, u64 programHandle)
|
||||||
{
|
{
|
||||||
Result res;
|
const ExHeader_CodeSetInfo *csi = &exhi->sci.codeset_info;
|
||||||
int count;
|
|
||||||
u32 flags;
|
Result res = 0;
|
||||||
u32 desc;
|
|
||||||
u32 dummy;
|
u32 dummy;
|
||||||
prog_addrs_t sharedAddr;
|
prog_addrs_t sharedAddr;
|
||||||
prog_addrs_t vaddr;
|
prog_addrs_t vaddr;
|
||||||
Handle codeset;
|
Handle codeset;
|
||||||
CodeSetInfo codesetinfo;
|
CodeSetInfo codesetinfo;
|
||||||
u32 dataMemSize;
|
u32 dataMemSize;
|
||||||
u64 titleId;
|
|
||||||
|
|
||||||
// make sure the cached info corrosponds to the current programHandle
|
u32 region = 0;
|
||||||
if (g_cached_programHandle != programHandle || hbldrIs3dsxTitle(g_exheaderInfo.aci.local_caps.title_id))
|
u32 count;
|
||||||
{
|
|
||||||
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++)
|
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)
|
if (0x1FE == desc >> 23)
|
||||||
flags = desc & 0xF00;
|
region = 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;
|
|
||||||
}
|
}
|
||||||
|
if (region == 0)
|
||||||
|
return MAKERESULT(RL_PERMANENT, RS_INVALIDARG, 1, 2);
|
||||||
|
|
||||||
// allocate process memory
|
// allocate process memory
|
||||||
vaddr.text_addr = csi->text.address;
|
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;
|
vaddr.data_size = (csi->data.size + 4095) >> 12;
|
||||||
dataMemSize = (csi->data.size + csi->bss_size + 4095) >> 12;
|
dataMemSize = (csi->data.size + csi->bss_size + 4095) >> 12;
|
||||||
vaddr.total_size = vaddr.text_size + vaddr.ro_size + vaddr.data_size;
|
vaddr.total_size = vaddr.text_size + vaddr.ro_size + vaddr.data_size;
|
||||||
TRY(allocateSharedMem(&sharedAddr, &vaddr, flags));
|
TRY(allocateSharedMem(&sharedAddr, &vaddr, region));
|
||||||
|
|
||||||
// load code
|
// 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);
|
memcpy(&codesetinfo.name, csi->name, 8);
|
||||||
codesetinfo.program_id = titleId;
|
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);
|
res = svcCreateCodeSet(&codeset, &codesetinfo, (void *)sharedAddr.text_addr, (void *)sharedAddr.ro_addr, (void *)sharedAddr.data_addr);
|
||||||
if (R_SUCCEEDED(res))
|
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);
|
svcCloseHandle(codeset);
|
||||||
res = R_SUCCEEDED(res) ? 0 : res;
|
res = R_SUCCEEDED(res) ? 0 : res;
|
||||||
}
|
}
|
||||||
@ -276,6 +254,28 @@ static Result LoadProcess(Handle *process, u64 programHandle)
|
|||||||
return res;
|
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)
|
static Result RegisterProgram(u64 *programHandle, FS_ProgramInfo *title, FS_ProgramInfo *update)
|
||||||
{
|
{
|
||||||
Result res;
|
Result res;
|
||||||
|
@ -18,10 +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.
|
/// Luma shared config type.
|
||||||
typedef struct LumaSharedConfig {
|
typedef struct LumaSharedConfig {
|
||||||
u64 hbldr_3dsx_tid; ///< Title ID to use for 3DSX loading.
|
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;
|
} LumaSharedConfig;
|
||||||
|
|
||||||
/// Luma shared config.
|
/// Luma shared config.
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
#include <3ds.h>
|
#include <3ds.h>
|
||||||
|
#include <assert.h>
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "patcher.h"
|
#include "patcher.h"
|
||||||
#include "ifile.h"
|
#include "ifile.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "loader.h"
|
#include "loader.h"
|
||||||
#include "service_manager.h"
|
#include "service_manager.h"
|
||||||
|
#include "3dsx.h"
|
||||||
|
#include "hbldr.h"
|
||||||
|
|
||||||
u32 config, multiConfig, bootConfig;
|
u32 config, multiConfig, bootConfig;
|
||||||
bool isN3DS, isSdMode;
|
bool isN3DS, isSdMode;
|
||||||
@ -28,20 +31,43 @@ static Result fsldrPatchPermissions(void)
|
|||||||
static inline void loadCFWInfo(void)
|
static inline void loadCFWInfo(void)
|
||||||
{
|
{
|
||||||
s64 out;
|
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);
|
svcGetSystemInfo(&out, 0x10000, 0x100);
|
||||||
config = (u32)out;
|
hbldrTid = (u64)out;
|
||||||
svcGetSystemInfo(&out, 0x10000, 4);
|
svcGetSystemInfo(&out, 0x10000, 0x201);
|
||||||
multiConfig = (u32)out;
|
isN3DS = (bool)out;
|
||||||
svcGetSystemInfo(&out, 0x10000, 5);
|
svcGetSystemInfo(&out, 0x10000, 0x203);
|
||||||
bootConfig = (u32)out;
|
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);
|
if (numKips >= 6)
|
||||||
isN3DS = (bool)out;
|
panic(0xDEADCAFE);
|
||||||
svcGetSystemInfo(&out, 0x10000, 0x203);
|
|
||||||
isSdMode = (bool)out;
|
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
|
void __ctru_exit(int rc) { (void)rc; } // needed to avoid linking error
|
||||||
@ -88,6 +114,7 @@ void initSystem(void)
|
|||||||
|
|
||||||
static const ServiceManagerServiceEntry services[] = {
|
static const ServiceManagerServiceEntry services[] = {
|
||||||
{ "Loader", 1, loaderHandleCommands, false },
|
{ "Loader", 1, loaderHandleCommands, false },
|
||||||
|
{ "hb:ldr", 2, hbldrHandleCommands, true },
|
||||||
{ NULL },
|
{ NULL },
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -95,9 +122,18 @@ static const ServiceManagerNotificationEntry notifications[] = {
|
|||||||
{ 0x000, NULL },
|
{ 0x000, NULL },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static u8 ALIGN(4) staticBufferForHbldr[0x400];
|
||||||
|
static_assert(ARGVBUF_SIZE > 2 * PATH_MAX, "Wrong 3DSX argv buffer size");
|
||||||
|
|
||||||
int main(void)
|
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));
|
assertSuccess(ServiceManager_Run(services, notifications, NULL));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -290,7 +290,7 @@ static Result launchTitleImpl(Handle *debug, ProcessData **outProcessData, const
|
|||||||
return res;
|
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();
|
ExHeader_Info *exheaderInfo = ExHeaderInfoHeap_New();
|
||||||
if (exheaderInfo == NULL) {
|
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)
|
Result LaunchTitleUpdate(const FS_ProgramInfo *programInfo, const FS_ProgramInfo *programInfoUpdate, u32 launchFlags)
|
||||||
{
|
{
|
||||||
ProcessList_Lock(&g_manager.processList);
|
|
||||||
if (g_manager.preparingForReboot) {
|
if (g_manager.preparingForReboot) {
|
||||||
return 0xC8A05801;
|
return 0xC8A05801;
|
||||||
}
|
}
|
||||||
if (g_manager.runningApplicationData != NULL) {
|
|
||||||
ProcessList_Unlock(&g_manager.processList);
|
|
||||||
return 0xC8A05BF0;
|
|
||||||
}
|
|
||||||
if (!(launchFlags & ~PMLAUNCHFLAG_NORMAL_APPLICATION)) {
|
if (!(launchFlags & ~PMLAUNCHFLAG_NORMAL_APPLICATION)) {
|
||||||
return 0xD8E05802;
|
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;
|
bool originallyDebugged = launchFlags & PMLAUNCHFLAG_QUEUE_DEBUG_APPLICATION;
|
||||||
|
|
||||||
|
@ -20,3 +20,5 @@ Result autolaunchSysmodules(void);
|
|||||||
// Custom
|
// Custom
|
||||||
Result DebugNextApplicationByForce(bool debug);
|
Result DebugNextApplicationByForce(bool debug);
|
||||||
Result LaunchTitleDebug(Handle *outDebug, const FS_ProgramInfo *programInfo, u32 launchFlags);
|
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);
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include "reslimit.h"
|
#include "reslimit.h"
|
||||||
#include "launch.h"
|
#include "launch.h"
|
||||||
#include "firmlaunch.h"
|
#include "firmlaunch.h"
|
||||||
|
#include "termination.h"
|
||||||
#include "exheader_info_heap.h"
|
#include "exheader_info_heap.h"
|
||||||
#include "task_runner.h"
|
#include "task_runner.h"
|
||||||
#include "process_monitor.h"
|
#include "process_monitor.h"
|
||||||
@ -57,8 +58,19 @@ static const ServiceManagerServiceEntry services[] = {
|
|||||||
{ NULL },
|
{ 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[] = {
|
static const ServiceManagerNotificationEntry notifications[] = {
|
||||||
{ 0x000, NULL },
|
{ 0x3000, handleRestartHbAppNotification },
|
||||||
|
{ 0x0000, NULL },
|
||||||
};
|
};
|
||||||
|
|
||||||
void __ctru_exit(int rc) { (void)rc; } // needed to avoid linking error
|
void __ctru_exit(int rc) { (void)rc; } // needed to avoid linking error
|
||||||
|
@ -90,27 +90,3 @@ Result UnregisterProcess(u64 titleId)
|
|||||||
ProcessList_Unlock(&g_manager.processList);
|
ProcessList_Unlock(&g_manager.processList);
|
||||||
return 0;
|
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;
|
|
||||||
}
|
|
||||||
|
@ -21,4 +21,3 @@ extern Manager g_manager;
|
|||||||
void Manager_Init(void *procBuf, size_t numProc);
|
void Manager_Init(void *procBuf, size_t numProc);
|
||||||
void Manager_RegisterKips(void);
|
void Manager_RegisterKips(void);
|
||||||
Result UnregisterProcess(u64 titleId);
|
Result UnregisterProcess(u64 titleId);
|
||||||
Result PrepareToChainloadHomebrew(u64 titleId);
|
|
||||||
|
@ -12,7 +12,6 @@ void pmDbgHandleCommands(void *ctx)
|
|||||||
u32 cmdhdr = cmdbuf[0];
|
u32 cmdhdr = cmdbuf[0];
|
||||||
|
|
||||||
FS_ProgramInfo programInfo;
|
FS_ProgramInfo programInfo;
|
||||||
u64 titleId;
|
|
||||||
Handle debug;
|
Handle debug;
|
||||||
u32 pid;
|
u32 pid;
|
||||||
u32 launchFlags;
|
u32 launchFlags;
|
||||||
@ -59,11 +58,7 @@ void pmDbgHandleCommands(void *ctx)
|
|||||||
cmdbuf[2] = IPC_Desc_MoveHandles(1);
|
cmdbuf[2] = IPC_Desc_MoveHandles(1);
|
||||||
cmdbuf[3] = debug;
|
cmdbuf[3] = debug;
|
||||||
break;
|
break;
|
||||||
case 0x103:
|
case 0x103: // PrepareToChainloadHomebrew (removed)
|
||||||
memcpy(&titleId, cmdbuf + 1, 8);
|
|
||||||
cmdbuf[1] = PrepareToChainloadHomebrew(titleId);
|
|
||||||
cmdbuf[0] = IPC_MakeHeader(0x103, 1, 0);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
cmdbuf[0] = IPC_MakeHeader(0, 1, 0);
|
cmdbuf[0] = IPC_MakeHeader(0, 1, 0);
|
||||||
cmdbuf[1] = 0xD900182F;
|
cmdbuf[1] = 0xD900182F;
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "exheader_info_heap.h"
|
#include "exheader_info_heap.h"
|
||||||
#include "task_runner.h"
|
#include "task_runner.h"
|
||||||
|
#include "launch.h"
|
||||||
|
|
||||||
void forceMountSdCard(void)
|
void forceMountSdCard(void)
|
||||||
{
|
{
|
||||||
@ -387,3 +388,78 @@ Result PrepareForReboot(u32 pid, s64 timeout)
|
|||||||
TaskRunner_RunTask(PrepareForRebootAsync, &args, sizeof(args));
|
TaskRunner_RunTask(PrepareForRebootAsync, &args, sizeof(args));
|
||||||
return 0;
|
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;
|
||||||
|
}
|
||||||
|
@ -10,3 +10,5 @@ Result TerminateApplication(s64 timeout);
|
|||||||
Result TerminateTitle(u64 titleId, s64 timeout);
|
Result TerminateTitle(u64 titleId, s64 timeout);
|
||||||
Result TerminateProcess(u32 pid, s64 timeout);
|
Result TerminateProcess(u32 pid, s64 timeout);
|
||||||
Result PrepareForReboot(u32 pid, s64 timeout);
|
Result PrepareForReboot(u32 pid, s64 timeout);
|
||||||
|
|
||||||
|
Result ChainloadHomebrewDirty(void);
|
||||||
|
@ -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);
|
|
@ -18,12 +18,13 @@
|
|||||||
|
|
||||||
#include <3ds/types.h>
|
#include <3ds/types.h>
|
||||||
|
|
||||||
#include <3ds/types.h>
|
/// Default TitleID for 3DSX loading
|
||||||
|
#define HBLDR_DEFAULT_3DSX_TID 0x000400000D921E00ULL
|
||||||
|
|
||||||
/// Luma shared config type.
|
/// Luma shared config type.
|
||||||
typedef struct LumaSharedConfig {
|
typedef struct LumaSharedConfig {
|
||||||
u64 hbldr_3dsx_tid; ///< Title ID to use for 3DSX loading.
|
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;
|
} LumaSharedConfig;
|
||||||
|
|
||||||
/// Luma shared config.
|
/// Luma shared config.
|
||||||
|
@ -14,4 +14,3 @@ enum {
|
|||||||
Result PMDBG_GetCurrentAppInfo(FS_ProgramInfo *outProgramInfo, u32 *outPid, u32 *outLaunchFlags);
|
Result PMDBG_GetCurrentAppInfo(FS_ProgramInfo *outProgramInfo, u32 *outPid, u32 *outLaunchFlags);
|
||||||
Result PMDBG_DebugNextApplicationByForce(bool debug);
|
Result PMDBG_DebugNextApplicationByForce(bool debug);
|
||||||
Result PMDBG_LaunchTitleDebug(Handle *outDebug, const FS_ProgramInfo *programInfo, u32 launchFlags);
|
Result PMDBG_LaunchTitleDebug(Handle *outDebug, const FS_ProgramInfo *programInfo, u32 launchFlags);
|
||||||
Result PMDBG_PrepareToChainloadHomebrew(u64 titleId);
|
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -29,8 +29,6 @@
|
|||||||
#include "menu.h"
|
#include "menu.h"
|
||||||
#include "service_manager.h"
|
#include "service_manager.h"
|
||||||
#include "errdisp.h"
|
#include "errdisp.h"
|
||||||
#include "hbloader.h"
|
|
||||||
#include "3dsx.h"
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "MyThread.h"
|
#include "MyThread.h"
|
||||||
#include "menus/miscellaneous.h"
|
#include "menus/miscellaneous.h"
|
||||||
@ -81,9 +79,6 @@ void initSystem(void)
|
|||||||
mappableInit(0x10000000, 0x14000000);
|
mappableInit(0x10000000, 0x14000000);
|
||||||
|
|
||||||
isN3DS = svcGetSystemInfo(&out, 0x10001, 0) == 0;
|
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);
|
svcGetSystemInfo(&out, 0x10000, 0x101);
|
||||||
menuCombo = out == 0 ? DEFAULT_MENU_COMBO : (u32)out;
|
menuCombo = out == 0 ? DEFAULT_MENU_COMBO : (u32)out;
|
||||||
@ -216,14 +211,15 @@ static void handleNextApplicationDebuggedByForce(u32 notificationId)
|
|||||||
TaskRunner_RunTask(debuggerFetchAndSetNextApplicationDebugHandleTask, NULL, 0);
|
TaskRunner_RunTask(debuggerFetchAndSetNextApplicationDebugHandleTask, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
static void handleRestartHbAppNotification(u32 notificationId)
|
static void handleRestartHbAppNotification(u32 notificationId)
|
||||||
{
|
{
|
||||||
(void)notificationId;
|
(void)notificationId;
|
||||||
TaskRunner_RunTask(HBLDR_RestartHbApplication, NULL, 0);
|
TaskRunner_RunTask(HBLDR_RestartHbApplication, NULL, 0);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static const ServiceManagerServiceEntry services[] = {
|
static const ServiceManagerServiceEntry services[] = {
|
||||||
{ "hb:ldr", 2, HBLDR_HandleCommands, true },
|
|
||||||
{ NULL },
|
{ NULL },
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -240,22 +236,11 @@ static const ServiceManagerNotificationEntry notifications[] = {
|
|||||||
{ 0x214, handleShellNotification },
|
{ 0x214, handleShellNotification },
|
||||||
{ 0x1000, handleNextApplicationDebuggedByForce },
|
{ 0x1000, handleNextApplicationDebuggedByForce },
|
||||||
{ 0x2000, handlePreTermNotification },
|
{ 0x2000, handlePreTermNotification },
|
||||||
{ 0x3000, handleRestartHbAppNotification },
|
|
||||||
{ 0x000, NULL },
|
{ 0x000, NULL },
|
||||||
};
|
};
|
||||||
|
|
||||||
int main(void)
|
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)))
|
if(R_FAILED(svcCreateEvent(&preTerminationEvent, RESET_STICKY)))
|
||||||
svcBreak(USERBREAK_ASSERT);
|
svcBreak(USERBREAK_ASSERT);
|
||||||
|
|
||||||
|
@ -30,7 +30,6 @@
|
|||||||
#include "ntp.h"
|
#include "ntp.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "draw.h"
|
#include "draw.h"
|
||||||
#include "hbloader.h"
|
|
||||||
#include "fmt.h"
|
#include "fmt.h"
|
||||||
#include "utils.h" // for makeArmBranch
|
#include "utils.h" // for makeArmBranch
|
||||||
#include "minisoc.h"
|
#include "minisoc.h"
|
||||||
|
@ -48,16 +48,3 @@ Result PMDBG_LaunchTitleDebug(Handle *outDebug, const FS_ProgramInfo *programInf
|
|||||||
*outDebug = cmdbuf[3];
|
*outDebug = cmdbuf[3];
|
||||||
return (Result)cmdbuf[1];
|
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];
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user