Compare commits

...

80 Commits
v6.3.1 ... v6.5

Author SHA1 Message Date
Aurora
cef947d67d Fix NS version 2016-11-03 19:36:08 +01:00
Aurora
b6640d118d Fix derp 2016-11-03 19:29:28 +01:00
Aurora
273d0b2ce9 Minor stuff 2016-11-03 19:02:38 +01:00
Aurora
9b724d776e Add title version support to loader, only apply userland patches to suitable title versions, report used console and safe mode boots to loader separately from the config, remove eShop update check patch as older eShop version would fail anyway and it causes issues with background updates 2016-11-03 18:56:32 +01:00
Aurora
3eaa706ccf Minor stuff (2) 2016-11-02 15:36:20 +01:00
Aurora
b4c854dfe8 Minor stuff 2016-11-01 20:52:04 +01:00
Aurora
9cdadbe834 Cleanup some userland patches, add/cleanup credits for patches 2016-11-01 19:07:53 +01:00
TuxSH
b3f38a8764 Update cache.s
Comments
2016-10-31 14:18:29 +01:00
Aurora
5d868284c6 Fix 11.2 FIRM support 2016-10-25 15:47:04 +02:00
Aurora
035751980d Minor stuff 2016-10-23 18:44:26 +02:00
Aurora
6b80bc08d5 Simplify some patches 2016-10-23 15:54:03 +02:00
Aurora
2089959d1b Minor stuff 2016-10-23 04:03:41 +02:00
Aurora
67b00ec28d Not needed 2016-10-23 03:56:55 +02:00
Aurora
127ae6b945 Minor stuff 2016-10-23 03:50:38 +02:00
Aurora
4f53b3ce35 Merge branch 'master' of https://github.com/AuroraWright/Luma3DS 2016-10-23 03:44:12 +02:00
Aurora
c3092b482a This is actually Thumb code 2016-10-23 03:42:52 +02:00
TuxSH
080219f88d Update reboot.s 2016-10-22 23:38:26 +02:00
Aurora
3fd783cd01 Cleanup 2016-10-22 18:00:58 +02:00
Aurora
211cd964d7 Move hexAtoi to strings.c, add bound check to decAtoi 2016-10-22 16:57:18 +02:00
Aurora
04f42f0be4 Fix derp 2016-10-22 16:31:31 +02:00
Aurora
e00ef893d0 Introduce a decimal itoa, fixed findDumpFile not working properly with more than 100 dumps 2016-10-22 16:25:27 +02:00
Aurora
858efa604e Minor stuff (2) 2016-10-17 23:39:13 +02:00
Aurora
b63b17c54f Minor stuff 2016-10-17 18:27:48 +02:00
Aurora
f0e111c20e It seems we don't need it anymore after all 2016-10-17 01:21:12 +02:00
Aurora
7339f57138 Minor stuff (2) 2016-10-16 23:35:30 +02:00
Aurora
1e39c999f9 Minor stuff 2016-10-16 04:03:00 +02:00
Aurora
62f7a06192 Add safety checks for loaded FIRM modules 2016-10-16 02:47:53 +02:00
Aurora
a0531b7930 Fix FIRM size check 2016-10-16 00:21:18 +02:00
Aurora
0619d04939 Divide Process9 and Kernel9 patches on non-NATIVE FIRMs too 2016-10-15 19:22:32 +02:00
Aurora
cd76476d26 Fix derp 2016-10-15 18:05:46 +02:00
Aurora
53b6c17e33 Fix UNITINFO patch, limit kernel9 pattern searches to kernel9 2016-10-15 16:16:53 +02:00
Aurora
3b7b66b272 Fix fs patch to work on old FIRMs 2016-10-15 15:04:48 +02:00
Aurora
a795a45c34 Cleanup the modules patch (makes it considerably smaller too 2016-10-15 05:00:09 +02:00
Aurora
b58cbd228c We don't really need two sets of blank spaces 2016-10-15 00:32:00 +02:00
Aurora
fc994285f9 Do the same with the reboot patch 2016-10-14 19:14:46 +02:00
Aurora
d5e74b91c7 Make loader read from CTRNAND just if SD is not mounted 2016-10-14 18:03:17 +02:00
Aurora
c5eb2e1070 Not needed 2016-10-14 02:06:01 +02:00
Aurora
d613cb057e Minor stuff 2016-10-13 18:45:38 +02:00
Aurora
121792bebe Fix A9LH patches not being applied after firmlaunch 2016-10-13 15:19:37 +02:00
Aurora
e07c230106 Simplify the main logic, remove assumption that if not using A9LH, SysNAND can't have a newer FIRM than EmuNAND, fix derp 2016-10-13 15:08:30 +02:00
Aurora
d5ce3044c8 Figured I might as well do this 2016-10-13 00:52:18 +02:00
Aurora
98ff43b4d2 Minor stuff (2) 2016-10-12 23:05:26 +02:00
Aurora
1704fbcd62 Minor stuff 2016-10-12 05:08:30 +02:00
Aurora
014ac1cf72 Fix derp 2016-10-12 02:45:49 +02:00
Aurora
b499c7ee75 Use f_chdir and relative paths 2016-10-12 02:32:36 +02:00
Aurora
2e069e326c Not needed anymore 2016-10-11 17:16:59 +02:00
Aurora
e47d42da22 Rename vars 2016-10-11 16:59:55 +02:00
Aurora
615e5dfaa7 Fixed CTRNAND writing leaving encrypted data (thanks to d0k3), added path.txt support for CTRNAND, have the firmlaunch patch panic if both payloads cannot be found 2016-10-11 16:55:37 +02:00
Aurora
fde2c371ef Minor stuff 2016-10-11 02:44:17 +02:00
Aurora
a0b4e7fd5d Fix other derps 2016-10-11 00:41:58 +02:00
Aurora
45c36bbcae Fix derp 2016-10-10 23:46:25 +02:00
Aurora
66c041ad93 Minor stuff 2016-10-10 18:56:19 +02:00
Aurora
32d5c52b5f Reinstate this check 2016-10-10 18:31:56 +02:00
Aurora
9cf5b01633 Minor stuff 2016-10-10 17:30:53 +02:00
Aurora
d4cf22d370 Display the number of failed patches, make loader svcBreak on failed patch, minor cleanup 2016-10-10 16:27:21 +02:00
Aurora
973640f023 Remove assumptions 2016-10-10 13:29:34 +02:00
Aurora
248ea82f76 One more check, fix non-unique pattern 2016-10-10 13:27:19 +02:00
Aurora
a868079a93 Don't close the directory if not already opened 2016-10-10 03:50:24 +02:00
Aurora
d270d5b9ca This can be here 2016-10-10 03:39:23 +02:00
Aurora
e9692a438b More sanity checks 2016-10-10 03:10:53 +02:00
Aurora
06ea123dbd Minor stuff 2016-10-10 02:51:44 +02:00
Aurora
85141d5eda Fix another derp 2016-10-10 02:39:19 +02:00
Aurora
fa13b8fbd0 Fix derp 2016-10-10 02:19:15 +02:00
Aurora
5b4712644a Lots of refactoring, main() has its own file yet again, properly handle failed patches/decryption steps, support TWL and AGB FIRM since 3.0 2016-10-10 02:10:47 +02:00
Aurora
aa422914bd Does not seem to work 2016-10-08 14:46:57 +02:00
Aurora
22c453e297 Fix derp 2016-10-08 14:44:25 +02:00
Aurora
e5f40cec5a This is a char 2016-10-08 14:27:36 +02:00
Aurora
50b24bf6c2 Switch to structs where possible 2016-10-08 14:23:08 +02:00
Aurora
b575ee9e28 Minor stuff 2016-10-08 01:58:58 +02:00
Aurora
37030621ac Revamp CTRNAND support, add proper support for hiding options in config menu 2016-10-08 01:47:39 +02:00
Aurora
f005da4d12 Prevent reading FIRM files if SD is not mounted 2016-10-07 16:25:41 +02:00
Aurora
6295559d9c Change SAFE_MODE detection for <= 2.1 2016-10-07 16:14:16 +02:00
Aurora
f36ff303d9 Fix diskio.c stuff 2016-10-07 14:51:32 +02:00
Aurora
1a62e91c01 Minor stuff 2016-10-07 14:31:15 +02:00
Aurora
7f314dfe11 Merge branch 'master' of https://github.com/AuroraWright/Luma3DS 2016-10-07 14:27:54 +02:00
Aurora
0caf9f4214 Add safety checks, support booting from CTRNAND 2016-10-07 14:27:30 +02:00
TuxSH
1ad600c81a Make the exception dump parser output the disassembly of the dumped code (when it's possible) 2016-10-06 00:16:21 +02:00
TuxSH
bd6c7b7fdb Fix the arm11 exception handlers on < 7.x 2016-09-30 22:46:48 +02:00
Aurora
5f93724845 Fix some unrelated files being detected as FIRMs due to uncleared FatFs strings 2016-09-28 15:37:58 +02:00
Aurora
ad60eac6ef Avoid overwriting the fb struct 2016-09-28 15:01:38 +02:00
48 changed files with 2282 additions and 1526 deletions

View File

@@ -22,8 +22,6 @@
#include "handlers.h"
#define FINAL_BUFFER 0xE5000000 //0x25000000
#define REG_DUMP_SIZE 4 * 23
#define CODE_DUMP_SIZE 48
@@ -49,7 +47,8 @@ void __attribute__((noreturn)) mainHandler(u32 *regs, u32 type, u32 cpuId)
u32 registerDump[REG_DUMP_SIZE / 4];
u8 codeDump[CODE_DUMP_SIZE];
u8 *final = (u8 *)FINAL_BUFFER;
u8 *finalBuffer = cannotAccessVA((void *)0xE5000000) ? (u8 *)0xF5000000 : (u8 *)0xE5000000; //VA for 0x25000000
u8 *final = finalBuffer;
while(*(vu32 *)final == 0xDEADC0DE && *((vu32 *)final + 1) == 0xDEADCAFE);
@@ -80,8 +79,8 @@ void __attribute__((noreturn)) mainHandler(u32 *regs, u32 type, u32 cpuId)
u8 *instr = (u8 *)pc + ((cpsr & 0x20) ? 2 : 4) - dumpHeader.codeDumpSize; //Doesn't work well on 32-bit Thumb instructions, but it isn't much of a problem
dumpHeader.codeDumpSize = copyMemory(codeDump, instr, dumpHeader.codeDumpSize, ((cpsr & 0x20) != 0) ? 2 : 4);
//Copy register dump and code dump
final = (u8 *)(FINAL_BUFFER + sizeof(ExceptionDumpHeader));
//Copy register dump and code dump
final = (u8 *)(finalBuffer + sizeof(ExceptionDumpHeader));
final += copyMemory(final, registerDump, dumpHeader.registerDumpSize, 1);
final += copyMemory(final, codeDump, dumpHeader.codeDumpSize, 1);
@@ -89,13 +88,13 @@ void __attribute__((noreturn)) mainHandler(u32 *regs, u32 type, u32 cpuId)
dumpHeader.stackDumpSize = copyMemory(final, (const void *)registerDump[13], 0x1000 - (registerDump[13] & 0xFFF), 1);
final += dumpHeader.stackDumpSize;
if(!cannotAccessVA((u8 *)0xFFFF9004))
if(!cannotAccessVA((void *)0xFFFF9004))
{
vu64 *additionalData = (vu64 *)final;
dumpHeader.additionalDataSize = 16;
vu8 *currentKCodeSet = *(vu8 **)(*(vu8 **)0xFFFF9004 + CODESET_OFFSET); //currentKProcess + CodeSet
additionalData[0] = *(vu64 *)(currentKCodeSet + 0x50); //Process name
additionalData[0] = *(vu64 *)(currentKCodeSet + 0x50); //Process name
additionalData[1] = *(vu64 *)(currentKCodeSet + 0x5C); //Title ID
}
else dumpHeader.additionalDataSize = 0;
@@ -103,7 +102,7 @@ void __attribute__((noreturn)) mainHandler(u32 *regs, u32 type, u32 cpuId)
dumpHeader.totalSize = sizeof(ExceptionDumpHeader) + dumpHeader.registerDumpSize + dumpHeader.codeDumpSize + dumpHeader.stackDumpSize + dumpHeader.additionalDataSize;
//Copy header (actually optimized by the compiler)
*(ExceptionDumpHeader *)FINAL_BUFFER = dumpHeader;
*(ExceptionDumpHeader *)finalBuffer = dumpHeader;
cleanInvalidateDCacheAndDMB();
mcuReboot(); //Also contains DCache-cleaning code

View File

@@ -22,9 +22,9 @@
# Notices displayed by works containing it.
__author__ = "TuxSH"
__copyright__ = "Copyright (c) 2016 TuxSH"
__copyright__ = "Copyright (c) 2016 TuxSH"
__license__ = "GPLv3"
__version__ = "v1.0"
__version__ = "v1.2"
"""
Parses Luma3DS exception dumps
@@ -33,6 +33,9 @@ Parses Luma3DS exception dumps
import argparse
from struct import unpack_from
import os
import subprocess
# Source of hexdump: https://gist.github.com/ImmortalPC/c340564823f283fe530b
# Credits for hexdump go to the original authors
# Slightly edited by TuxSH
@@ -76,14 +79,14 @@ def hexdump(addr, src, length=16, sep='.' ):
text += chr(c)
else:
text += sep
result.append(('%08X: %-'+str(length*(2+1)+1)+'s |%s|') % (addr + i, hexa, text))
result.append(('%08x: %-'+str(length*(2+1)+1)+'s |%s|') % (addr + i, hexa, text))
return '\n'.join(result)
def makeRegisterLine(A, rA, B, rB):
return "{0:<15}{1:<20}{2:<15}{3:<20}".format(A, "{0:08x}".format(rA), B, "{0:08x}".format(rB))
handledExceptionNames = ("FIQ", "undefined instruction", "prefetch abort", "data abort")
registerNames = tuple("r{0}".format(i) for i in range(13)) + ("sp", "lr", "pc", "cpsr") + ("dfsr", "ifsr", "far") + ("fpexc", "fpinst", "fpinst2")
svcBreakReasons = ("(svcBreak: panic)", "(svcBreak: assertion failed)", "(svcBreak: user-related)")
@@ -96,23 +99,24 @@ if __name__ == "__main__":
with open(args.filename, "rb") as f: data = f.read()
if unpack_from("<2I", data) != (0xdeadc0de, 0xdeadcafe):
raise SystemExit("Invalid file format")
version, processor, exceptionType, _, nbRegisters, codeDumpSize, stackDumpSize, additionalDataSize = unpack_from("<8I", data, 8)
nbRegisters //= 4
if version < (1 << 16) | 2:
raise SystemExit("Incompatible format version, please use the appropriate parser.")
registers = unpack_from("<{0}I".format(nbRegisters), data, 40)
codeDump = data[40 + 4 * nbRegisters : 40 + 4 * nbRegisters + codeDumpSize]
stackOffset = 40 + 4 * nbRegisters + codeDumpSize
codeOffset = 40 + 4 * nbRegisters
codeDump = data[codeOffset : codeOffset + codeDumpSize]
stackOffset = codeOffset + codeDumpSize
stackDump = data[stackOffset : stackOffset + stackDumpSize]
addtionalDataOffset = stackOffset + stackDumpSize
additionalData = data[addtionalDataOffset : addtionalDataOffset + additionalDataSize]
if processor == 9: print("Processor: ARM9")
else: print("Processor: ARM11 (core {0})".format(processor >> 16))
typeDetailsStr = ""
if exceptionType == 2:
if (registers[16] & 0x20) == 0 and codeDumpSize >= 4:
@@ -125,23 +129,41 @@ if __name__ == "__main__":
instr = unpack_from("<I", codeDump[-4:])[0]
if instr == 0xdf3c:
typeDetailsStr = " " + (svcBreakReasons[registers[0]] if registers[0] < 3 else "(svcBreak)")
elif processor != 9 and (registers[20] & 0x80000000) != 0:
typeDetailsStr = " (VFP exception)"
print("Exception type: {0}{1}".format("unknown" if exceptionType >= len(handledExceptionNames) else handledExceptionNames[exceptionType], typeDetailsStr))
if additionalDataSize != 0:
print("Current process: {0} ({1:016x})".format(additionalData[:8].decode("ascii"), unpack_from("<Q", additionalData, 8)[0]))
print("\nRegister dump:\n")
for i in range(0, nbRegisters - (nbRegisters % 2), 2):
if i == 16: print("")
print(makeRegisterLine(registerNames[i], registers[i], registerNames[i+1], registers[i+1]))
if nbRegisters % 2 == 1: print("{0:<15}{1:<20}".format(registerNames[nbRegisters - 1], "{0:08x}".format(registers[nbRegisters - 1])))
thumb = registers[16] & 0x20 != 0
addr = registers[15] - codeDumpSize + (2 if thumb else 4)
print("\nCode dump:\n")
print(hexdump(registers[15] - codeDumpSize + (4 if (registers[16] & 0x20 == 0) else 2), codeDump))
objdump_res = ""
try:
path = os.path.join(os.environ["DEVKITARM"], "bin", "arm-none-eabi-objdump")
if os.name == "nt":
path = ''.join((path[1], ':', path[2:])).replace('/', '\\')
objdump_res = subprocess.check_output((
path, "-marm", "-b", "binary",
"--adjust-vma="+hex(addr - codeOffset), "--start-address="+hex(addr),
"--stop-address="+hex(addr + codeDumpSize), "-D", "-z", "-M",
"reg-names-std" + (",force-thumb" if thumb else ""), args.filename
)).decode("utf-8")
objdump_res = '\n'.join(objdump_res[objdump_res.find('<.data+'):].split('\n')[1:])
except: objdump_res = ""
print(objdump_res if objdump_res != "" else hexdump(addr, codeDump))
print("\nStack dump:\n")
print(hexdump(registers[13], stackDump))

View File

@@ -33,9 +33,7 @@ DSTATUS disk_initialize (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
sdmmc_sdcard_init();
return RES_OK;
return sdmmc_sdcard_init() ? RES_OK : RES_PARERR;
}
@@ -52,10 +50,7 @@ DRESULT disk_read (
UINT count /* Number of sectors to read */
)
{
if(sdmmc_sdcard_readsectors(sector, count, (BYTE *)buff))
return RES_PARERR;
return RES_OK;
return (!sdmmc_sdcard_readsectors(sector, count, buff)) ? RES_OK : RES_PARERR;
}

View File

@@ -274,17 +274,17 @@ static u32 calcSDSize(u8 *csd, int type)
switch(type)
{
case 0:
{
u32 block_len = csd[9] & 0xF;
block_len = 1u << block_len;
u32 mult = (u32)((csd[4] >> 7) | ((csd[5] & 3) << 1));
mult = 1u << (mult + 2);
result = csd[8] & 3;
result = (result << 8) | csd[7];
result = (result << 2) | (csd[6] >> 6);
result = (result + 1) * mult * block_len / 512;
}
{
u32 block_len = csd[9] & 0xF;
block_len = 1u << block_len;
u32 mult = (u32)((csd[4] >> 7) | ((csd[5] & 3) << 1));
mult = 1u << (mult + 2);
result = csd[8] & 3;
result = (result << 8) | csd[7];
result = (result << 2) | (csd[6] >> 6);
result = (result + 1) * mult * block_len / 512;
break;
}
case 1:
result = csd[7] & 0x3F;
result = (result << 8) | csd[6];
@@ -482,9 +482,9 @@ void sdmmc_get_cid(bool isNand, u32 *info)
}
*/
void sdmmc_sdcard_init()
bool sdmmc_sdcard_init()
{
InitSD();
//Nand_Init();
SD_Init();
return SD_Init() == 0;
}

View File

@@ -91,7 +91,7 @@ typedef struct mmcdevice {
u32 res;
} mmcdevice;
void sdmmc_sdcard_init();
bool sdmmc_sdcard_init();
int sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, u8 *out);
//int sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, const u8 *in);
//int sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, u8 *out);

View File

@@ -37,7 +37,7 @@ void main(void)
payload;
bool foundPayload = false;
if(f_open(&pathFile, "/luma/path.txt", FA_READ) == FR_OK)
if(f_open(&pathFile, "luma/path.txt", FA_READ) == FR_OK)
{
u32 pathSize = f_size(&pathFile);
@@ -74,10 +74,13 @@ void main(void)
f_read(&payload, payloadAddress, payloadSize, &read);
f_close(&payload);
flushDCacheRange(loaderAddress, loader_bin_size);
flushICacheRange(loaderAddress, loader_bin_size);
if((u32)read == payloadSize)
{
flushDCacheRange(loaderAddress, loader_bin_size);
flushICacheRange(loaderAddress, loader_bin_size);
((void (*)())loaderAddress)();
((void (*)())loaderAddress)();
}
}
}

View File

@@ -24,14 +24,14 @@
.align 4
.global _start
_start:
@ Change the stack pointer
mov sp, #0x27000000
@ Disable interrupts
mrs r0, cpsr
orr r0, #0x1C0
msr cpsr_cx, r0
@ Change the stack pointer
mov sp, #0x27000000
@ Disable caches / MPU
mrc p15, 0, r0, c1, c0, 0 @ read control register
bic r0, #(1<<12) @ - instruction cache disable

View File

@@ -9,7 +9,7 @@ typedef struct __attribute__((packed))
u8 versionMajor;
u8 versionMinor;
u8 versionBuild;
u8 flags; /* bit 0: dev branch; bit 1: is release */
u8 flags;
u32 commitHash;

View File

@@ -63,4 +63,4 @@ Result IFile_Read(IFile *file, u64 *total, void *buffer, u32 len)
*total = cur;
return res;
}
}

View File

@@ -155,8 +155,10 @@ static Result load_code(u64 progid, prog_addrs_t *shared, u64 prog_handle, int i
lzss_decompress((u8 *)shared->text_addr + size);
}
u16 progver = g_exheader.codesetinfo.flags.remasterversion[0] | (g_exheader.codesetinfo.flags.remasterversion[1] << 8);
// patch
patchCode(progid, (u8 *)shared->text_addr, shared->total_size << 12);
patchCode(progid, progver, (u8 *)shared->text_addr, shared->total_size << 12);
return 0;
}

View File

@@ -13,7 +13,7 @@ static void patchMemory(u8 *start, u32 size, const void *pattern, u32 patSize, i
{
u8 *found = memsearch(start, pattern, size, patSize);
if(found == NULL) break;
if(found == NULL) svcBreak(USERBREAK_ASSERT);
memcpy(found + offset, replace, repSize);
@@ -26,7 +26,7 @@ static void patchMemory(u8 *start, u32 size, const void *pattern, u32 patSize, i
}
}
static int fileOpen(IFile *file, FS_ArchiveID archiveId, const char *path, int flags)
static Result fileOpen(IFile *file, FS_ArchiveID archiveId, const char *path, int flags)
{
FS_Path filePath = {PATH_ASCII, strnlen(path, 255) + 1, path},
archivePath = {PATH_EMPTY, 1, (u8 *)""};
@@ -34,7 +34,16 @@ static int fileOpen(IFile *file, FS_ArchiveID archiveId, const char *path, int f
return IFile_Open(file, archiveId, archivePath, filePath, flags);
}
static void loadCFWInfo(void)
static Result openLumaFile(IFile *file, const char *path)
{
Result res = fileOpen(file, ARCHIVE_SDMC, path, FS_OPEN_READ);
if((u32)res == 0xC88044AB) res = fileOpen(file, ARCHIVE_NAND_RW, path, FS_OPEN_READ); //Returned if SD is not mounted
return res;
}
static inline void loadCFWInfo(void)
{
static bool infoLoaded = false;
@@ -43,7 +52,7 @@ static void loadCFWInfo(void)
svcGetCFWInfo(&info);
IFile file;
if(BOOTCFG_SAFEMODE != 0 && R_SUCCEEDED(fileOpen(&file, ARCHIVE_SDMC, "/", FS_OPEN_READ))) //Init SD card if SAFE_MODE is being booted
if(LOADERFLAG(ISSAFEMODE) && R_SUCCEEDED(fileOpen(&file, ARCHIVE_SDMC, "/", FS_OPEN_READ))) //Init SD card if SAFE_MODE is being booted
IFile_Close(&file);
infoLoaded = true;
@@ -77,7 +86,7 @@ static void loadCustomVerString(u16 *out, u32 *verStringSize, u32 currentNand)
IFile file;
if(R_SUCCEEDED(fileOpen(&file, ARCHIVE_SDMC, paths[currentNand], FS_OPEN_READ)))
if(R_SUCCEEDED(openLumaFile(&file, paths[currentNand])))
{
u64 fileSize;
@@ -135,7 +144,7 @@ static void loadTitleCodeSection(u64 progId, u8 *code, u32 size)
IFile file;
if(R_SUCCEEDED(fileOpen(&file, ARCHIVE_SDMC, path, FS_OPEN_READ)))
if(R_SUCCEEDED(openLumaFile(&file, path)))
{
u64 fileSize;
@@ -143,6 +152,8 @@ static void loadTitleCodeSection(u64 progId, u8 *code, u32 size)
{
u64 total;
IFile_Read(&file, &total, code, fileSize);
if(total != fileSize) svcBreak(USERBREAK_ASSERT);
}
IFile_Close(&file);
@@ -159,7 +170,7 @@ static void loadTitleLocaleConfig(u64 progId, u8 *regionId, u8 *languageId)
IFile file;
if(R_SUCCEEDED(fileOpen(&file, ARCHIVE_SDMC, path, FS_OPEN_READ)))
if(R_SUCCEEDED(openLumaFile(&file, path)))
{
u64 fileSize;
@@ -273,9 +284,9 @@ static void patchCfgGetLanguage(u8 *code, u32 size, u8 languageId, u8 *CFGU_GetC
if(found)
{
*((u32 *)instr - 1) = 0xE3A00000 | languageId; // mov r0, sp => mov r0, =languageId
*(u32 *)instr = 0xE5CD0000; // bl CFGU_GetConfigInfoBlk2 => strb r0, [sp]
*((u32 *)instr + 1) = 0xE3B00000; // (1 or 2 instructions) => movs r0, 0 (result code)
*((u32 *)instr - 1) = 0xE3A00000 | languageId; //mov r0, sp => mov r0, =languageId
*(u32 *)instr = 0xE5CD0000; //bl CFGU_GetConfigInfoBlk2 => strb r0, [sp]
*((u32 *)instr + 1) = 0xE3B00000; //(1 or 2 instructions) => movs r0, 0 (result code)
//We're done
return;
@@ -298,10 +309,10 @@ static void patchCfgGetRegion(u8 *code, u32 size, u8 regionId, u32 CFGUHandleOff
cmp[2] == cfgSecureInfoGetRegionCmdPattern[2] && *((u16 *)cmdPos + 7) == 0xE59F &&
*(u32 *)(cmdPos + 20 + *((u16 *)cmdPos + 6)) == CFGUHandleOffset)
{
*((u32 *)cmdPos + 4) = 0xE3A00000 | regionId; // mov r0, =regionId
*((u32 *)cmdPos + 5) = 0xE5C40008; // strb r0, [r4, 8]
*((u32 *)cmdPos + 6) = 0xE3B00000; // movs r0, 0 (result code) ('s' not needed but nvm)
*((u32 *)cmdPos + 7) = 0xE5840004; // str r0, [r4, 4]
*((u32 *)cmdPos + 4) = 0xE3A00000 | regionId; //mov r0, =regionId
*((u32 *)cmdPos + 5) = 0xE5C40008; //strb r0, [r4, 8]
*((u32 *)cmdPos + 6) = 0xE3B00000; //movs r0, 0 (result code) ('s' not needed but nvm)
*((u32 *)cmdPos + 7) = 0xE5840004; //str r0, [r4, 4]
//The remaining, not patched, function code will do the rest for us
break;
@@ -309,153 +320,110 @@ static void patchCfgGetRegion(u8 *code, u32 size, u8 regionId, u32 CFGUHandleOff
}
}
void patchCode(u64 progId, u8 *code, u32 size)
void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
{
loadCFWInfo();
switch(progId)
if(((progId == 0x0004003000008F02LL || //USA Home Menu
progId == 0x0004003000008202LL || //JPN Home Menu
progId == 0x0004003000009802LL) //EUR Home Menu
&& progVer > 4) ||
(progId == 0x000400300000A902LL //KOR Home Menu
&& progVer > 0) ||
progId == 0x000400300000A102LL || //CHN Home Menu
progId == 0x000400300000B102LL) //TWN Home Menu
{
case 0x0004003000008F02LL: // USA Menu
case 0x0004003000008202LL: // EUR Menu
case 0x0004003000009802LL: // JPN Menu
case 0x000400300000A102LL: // CHN Menu
case 0x000400300000A902LL: // KOR Menu
case 0x000400300000B102LL: // TWN Menu
static const u8 regionFreePattern[] = {
0x0A, 0x0C, 0x00, 0x10
},
regionFreePatch[] = {
0x01, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1
};
//Patch SMDH region checks
patchMemory(code, size,
regionFreePattern,
sizeof(regionFreePattern), -31,
regionFreePatch,
sizeof(regionFreePatch), 1
);
}
else if(progId == 0x0004013000003202LL) //FRIENDS
{
static const u8 fpdVerPattern[] = {
0x42, 0xE0, 0x1E, 0xFF
};
u8 mostRecentFpdVer = 8;
u8 *off = memsearch(code, fpdVerPattern, size, sizeof(fpdVerPattern));
if(off == NULL) svcBreak(USERBREAK_ASSERT);
//Allow online access to work with old friends modules
if(off[0xA] < mostRecentFpdVer) off[0xA] = mostRecentFpdVer;
}
else if((progId == 0x0004001000021000LL || //USA MSET
progId == 0x0004001000020000LL || //JPN MSET
progId == 0x0004001000022000LL || //EUR MSET
progId == 0x0004001000026000LL || //CHN MSET
progId == 0x0004001000027000LL || //KOR MSET
progId == 0x0004001000028000LL) //TWN MSET
&& CONFIG(PATCHVERSTRING))
{
static const u16 verPattern[] = u"Ve";
static u16 *verString;
u32 verStringSize = 0,
currentNand = BOOTCFG_NAND;
u16 customVerString[19];
loadCustomVerString(customVerString, &verStringSize, currentNand);
if(verStringSize != 0) verString = customVerString;
else
{
static const u8 regionFreePattern[] = {
0x00, 0x00, 0x55, 0xE3, 0x01, 0x10, 0xA0
};
static const u8 regionFreePatch[] = {
0x01, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1
};
verStringSize = 8;
u32 currentFirm = BOOTCFG_FIRM;
//Patch SMDH region checks
patchMemory(code, size,
regionFreePattern,
sizeof(regionFreePattern), -16,
regionFreePatch,
sizeof(regionFreePatch), 1
);
static u16 *verStringsNands[] = { u" Sys",
u" Emu",
u"Emu2",
u"Emu3",
u"Emu4" },
break;
*verStringsEmuSys[] = { u"EmuS",
u"Em2S",
u"Em3S",
u"Em4S" },
*verStringsSysEmu[] = { u"SysE",
u"SyE2",
u"SyE3",
u"SyE4" };
verString = (currentFirm != 0) == (currentNand != 0) ? verStringsNands[currentNand] :
(!currentNand ? verStringsSysEmu[currentFirm - 1] : verStringsEmuSys[currentNand - 1]);
}
case 0x0004013000002C02LL: // NIM
{
static const u8 blockAutoUpdatesPattern[] = {
0x25, 0x79, 0x0B, 0x99
};
static const u8 blockAutoUpdatesPatch[] = {
0xE3, 0xA0
};
//Patch Ver. string
patchMemory(code, size,
verPattern,
sizeof(verPattern) - 2, 0,
verString,
verStringSize, 1
);
}
//Block silent auto-updates
patchMemory(code, size,
blockAutoUpdatesPattern,
sizeof(blockAutoUpdatesPattern), 0,
blockAutoUpdatesPatch,
sizeof(blockAutoUpdatesPatch), 1
);
//Apply only if the user booted with R
if((BOOTCFG_NAND != 0) != (BOOTCFG_FIRM != 0))
{
static const u8 skipEshopUpdateCheckPattern[] = {
0x30, 0xB5, 0xF1, 0xB0
};
static const u8 skipEshopUpdateCheckPatch[] = {
0x00, 0x20, 0x08, 0x60, 0x70, 0x47
};
//Skip update checks to access the EShop
patchMemory(code, size,
skipEshopUpdateCheckPattern,
sizeof(skipEshopUpdateCheckPattern), 0,
skipEshopUpdateCheckPatch,
sizeof(skipEshopUpdateCheckPatch), 1
);
}
break;
}
case 0x0004013000003202LL: // FRIENDS
{
static const u8 fpdVerPattern[] = {
0xE0, 0x1E, 0xFF, 0x2F, 0xE1, 0x01, 0x01
};
u8 mostRecentFpdVer = 7;
u8 *fpdVer = memsearch(code, fpdVerPattern, size, sizeof(fpdVerPattern));
//Allow online access to work with old friends modules
if(fpdVer != NULL && fpdVer[9] < mostRecentFpdVer) fpdVer[9] = mostRecentFpdVer;
break;
}
case 0x0004001000021000LL: // USA MSET
case 0x0004001000020000LL: // JPN MSET
case 0x0004001000022000LL: // EUR MSET
case 0x0004001000026000LL: // CHN MSET
case 0x0004001000027000LL: // KOR MSET
case 0x0004001000028000LL: // TWN MSET
{
if(CONFIG(PATCHVERSTRING))
{
static const u16 verPattern[] = u"Ver.";
static u16 *verString;
u32 verStringSize = 0;
u32 currentNand = BOOTCFG_NAND;
u16 customVerString[19];
loadCustomVerString(customVerString, &verStringSize, currentNand);
if(verStringSize != 0) verString = customVerString;
else
{
verStringSize = 8;
u32 currentFirm = BOOTCFG_FIRM;
static u16 *verStringsNands[] = { u" Sys",
u" Emu",
u"Emu2",
u"Emu3",
u"Emu4" },
*verStringsEmuSys[] = { u"EmuS",
u"Em2S",
u"Em3S",
u"Em4S" },
*verStringsSysEmu[] = { u"SysE",
u"SyE2",
u"SyE3",
u"SyE4" };
verString = (currentFirm != 0) == (currentNand != 0) ? verStringsNands[currentNand] :
(!currentNand ? verStringsSysEmu[currentFirm - 1] : verStringsEmuSys[currentNand - 1]);
}
//Patch Ver. string
patchMemory(code, size,
verPattern,
sizeof(verPattern) - 2, 0,
verString,
verStringSize, 1
);
}
break;
}
case 0x0004013000008002LL: // NS
else if(progId == 0x0004013000008002LL) //NS
{
if(progVer > 4)
{
static const u8 stopCartUpdatesPattern[] = {
0x0C, 0x18, 0xE1, 0xD8
};
static const u8 stopCartUpdatesPatch[] = {
},
stopCartUpdatesPatch[] = {
0x0B, 0x18, 0x21, 0xC8
};
@@ -466,166 +434,148 @@ void patchCode(u64 progId, u8 *code, u32 size)
stopCartUpdatesPatch,
sizeof(stopCartUpdatesPatch), 2
);
}
if(LOADERFLAG(ISN3DS))
{
u32 cpuSetting = MULTICONFIG(NEWCPU);
if(cpuSetting != 0)
{
static const u8 cfgN3dsCpuPattern[] = {
0x00, 0x40, 0xA0, 0xE1, 0x07
0x0C, 0x00, 0x94, 0x15
};
u32 *cfgN3dsCpuLoc = (u32 *)memsearch(code, cfgN3dsCpuPattern, size, sizeof(cfgN3dsCpuPattern));
u32 *off = (u32 *)memsearch(code, cfgN3dsCpuPattern, size, sizeof(cfgN3dsCpuPattern));
if(off == NULL) svcBreak(USERBREAK_ASSERT);
//Patch N3DS CPU Clock and L2 cache setting
if(cfgN3dsCpuLoc != NULL)
{
*(cfgN3dsCpuLoc + 1) = 0xE1A00000;
*(cfgN3dsCpuLoc + 8) = 0xE3A00000 | cpuSetting;
}
*(off - 4) = 0xE1A00000;
*(off + 3) = 0xE3A00000 | cpuSetting;
}
break;
}
}
case 0x0004013000001702LL: // CFG
else if(progId == 0x0004013000001702LL) //CFG
{
static const u8 secureinfoSigCheckPattern[] = {
0x06, 0x46, 0x10, 0x48
},
secureinfoSigCheckPatch[] = {
0x00, 0x26
};
//Disable SecureInfo signature check
patchMemory(code, size,
secureinfoSigCheckPattern,
sizeof(secureinfoSigCheckPattern), 0,
secureinfoSigCheckPatch,
sizeof(secureinfoSigCheckPatch), 1
);
if(secureInfoExists())
{
static const u8 secureinfoSigCheckPattern[] = {
0x06, 0x46, 0x10, 0x48
};
static const u8 secureinfoSigCheckPatch[] = {
0x00, 0x26
};
static const u16 secureinfoFilenamePattern[] = u"SecureInfo_",
secureinfoFilenamePatch[] = u"C";
//Disable SecureInfo signature check
//Use SecureInfo_C
patchMemory(code, size,
secureinfoSigCheckPattern,
sizeof(secureinfoSigCheckPattern), 0,
secureinfoSigCheckPatch,
sizeof(secureinfoSigCheckPatch), 1
secureinfoFilenamePattern,
sizeof(secureinfoFilenamePattern) - 2,
sizeof(secureinfoFilenamePattern) - 2,
secureinfoFilenamePatch,
sizeof(secureinfoFilenamePatch) - 2, 2
);
if(secureInfoExists())
{
static const u16 secureinfoFilenamePattern[] = u"SecureInfo_";
static const u16 secureinfoFilenamePatch[] = u"C";
//Use SecureInfo_C
patchMemory(code, size,
secureinfoFilenamePattern,
sizeof(secureinfoFilenamePattern) - 2,
sizeof(secureinfoFilenamePattern) - 2,
secureinfoFilenamePatch,
sizeof(secureinfoFilenamePatch) - 2, 2
);
}
break;
}
}
case 0x0004013000003702LL: // RO
else if(progId == 0x0004013000003702LL && progVer > 0) //RO
{
static const u8 sigCheckPattern[] = {
0x20, 0xA0, 0xE1, 0x8B
},
sha256ChecksPattern1[] = {
0xE1, 0x30, 0x40, 0x2D
},
sha256ChecksPattern2[] = {
0x2D, 0xE9, 0x01, 0x70
},
stub[] = {
0x00, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1 //mov r0, #0; bx lr
};
//Disable CRR0 signature (RSA2048 with SHA256) check
patchMemory(code, size,
sigCheckPattern,
sizeof(sigCheckPattern), -9,
stub,
sizeof(stub), 1
);
//Disable CRO0/CRR0 SHA256 hash checks (section hashes, and hash table)
patchMemory(code, size,
sha256ChecksPattern1,
sizeof(sha256ChecksPattern1), 1,
stub,
sizeof(stub), 1
);
patchMemory(code, size,
sha256ChecksPattern2,
sizeof(sha256ChecksPattern2), -2,
stub,
sizeof(stub), 1
);
}
else if(progId == 0x0004003000008A02LL && MULTICONFIG(DEVOPTIONS) == 1) //ErrDisp
{
static const u8 unitinfoCheckPattern1[] = {
0x00, 0xD0, 0xE5, 0xDB
},
unitinfoCheckPattern2[] = {
0x14, 0x00, 0xD0, 0xE5, 0x01
},
unitinfoCheckPatch[] = {
0x00, 0x00, 0xA0, 0xE3
};
patchMemory(code, size,
unitinfoCheckPattern1,
sizeof(unitinfoCheckPattern1), -1,
unitinfoCheckPatch,
sizeof(unitinfoCheckPatch), 1
);
patchMemory(code, size,
unitinfoCheckPattern2,
sizeof(unitinfoCheckPattern2), 0,
unitinfoCheckPatch,
sizeof(unitinfoCheckPatch), 3
);
}
else if(CONFIG(USELANGEMUANDCODE) && (u32)((progId & 0xFFFFFFF000000000LL) >> 0x24) == 0x0004000)
{
//External .code section loading
loadTitleCodeSection(progId, code, size);
//Language emulation
u8 regionId = 0xFF,
languageId = 0xFF;
loadTitleLocaleConfig(progId, &regionId, &languageId);
if(regionId != 0xFF || regionId != 0xFF)
{
static const u8 sigCheckPattern[] = {
0x30, 0x40, 0x2D, 0xE9, 0x02
};
static const u8 sha256ChecksPattern1[] = {
0x30, 0x40, 0x2D, 0xE9, 0x24
};
static const u8 sha256ChecksPattern2[] = {
0xF8, 0x4F, 0x2D, 0xE9, 0x01
};
static const u8 stub[] = {
0x00, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1 // mov r0, #0; bx lr
};
//Disable CRR0 signature (RSA2048 with SHA256) check
patchMemory(code, size,
sigCheckPattern,
sizeof(sigCheckPattern), 0,
stub,
sizeof(stub), 1
);
//Disable CRO0/CRR0 SHA256 hash checks (section hashes, and hash table)
patchMemory(code, size,
sha256ChecksPattern1,
sizeof(sha256ChecksPattern1), 0,
stub,
sizeof(stub), 1
);
patchMemory(code, size,
sha256ChecksPattern2,
sizeof(sha256ChecksPattern2), 0,
stub,
sizeof(stub), 1
);
break;
}
u32 CFGUHandleOffset;
u8 *CFGU_GetConfigInfoBlk2_endPos = getCfgOffsets(code, size, &CFGUHandleOffset);
case 0x0004003000008A02LL: // ErrDisp
{
if(MULTICONFIG(DEVOPTIONS) == 1)
if(CFGU_GetConfigInfoBlk2_endPos != NULL)
{
static const u8 unitinfoCheckPattern1[] = {
0x14, 0x00, 0xD0, 0xE5, 0xDB
};
static const u8 unitinfoCheckPattern2[] = {
0x14, 0x00, 0xD0, 0xE5, 0x01
} ;
static const u8 unitinfoCheckPatch[] = {
0x00, 0x00, 0xA0, 0xE3
} ;
patchMemory(code, size,
unitinfoCheckPattern1,
sizeof(unitinfoCheckPattern1), 0,
unitinfoCheckPatch,
sizeof(unitinfoCheckPatch), 1
);
patchMemory(code, size,
unitinfoCheckPattern2,
sizeof(unitinfoCheckPattern2), 0,
unitinfoCheckPatch,
sizeof(unitinfoCheckPatch), 3
);
if(languageId != 0xFF) patchCfgGetLanguage(code, size, languageId, CFGU_GetConfigInfoBlk2_endPos);
if(regionId != 0xFF) patchCfgGetRegion(code, size, regionId, CFGUHandleOffset);
}
break;
}
default:
if(CONFIG(USELANGEMUANDCODE))
{
if((u32)((progId & 0xFFFFFFF000000000LL) >> 0x24) == 0x0004000)
{
//External .code section loading
loadTitleCodeSection(progId, code, size);
//Language emulation
u8 regionId = 0xFF,
languageId = 0xFF;
loadTitleLocaleConfig(progId, &regionId, &languageId);
if(regionId != 0xFF || regionId != 0xFF)
{
u32 CFGUHandleOffset;
u8 *CFGU_GetConfigInfoBlk2_endPos = getCfgOffsets(code, size, &CFGUHandleOffset);
if(CFGU_GetConfigInfoBlk2_endPos != NULL)
{
if(languageId != 0xFF) patchCfgGetLanguage(code, size, languageId, CFGU_GetConfigInfoBlk2_endPos);
if(regionId != 0xFF) patchCfgGetRegion(code, size, regionId, CFGUHandleOffset);
}
}
}
}
break;
}
}

View File

@@ -2,15 +2,15 @@
#include <3ds/types.h>
#define CONFIG(a) (((info.config >> (a + 21)) & 1) != 0)
#define MULTICONFIG(a) ((info.config >> (a * 2 + 9)) & 3)
#define CONFIG(a) (((info.config >> (a + 20)) & 1) != 0)
#define MULTICONFIG(a) ((info.config >> (a * 2 + 8)) & 3)
#define BOOTCONFIG(a, b) ((info.config >> a) & b)
#define LOADERFLAG(a) ((info.flags >> (a + 4)) & 1) != 0
#define BOOTCFG_NAND BOOTCONFIG(0, 7)
#define BOOTCFG_FIRM BOOTCONFIG(3, 7)
#define BOOTCFG_A9LH BOOTCONFIG(6, 1)
#define BOOTCFG_NOFORCEFLAG BOOTCONFIG(7, 1)
#define BOOTCFG_SAFEMODE BOOTCONFIG(8, 1)
enum multiOptions
{
@@ -26,7 +26,7 @@ enum singleOptions
{
AUTOBOOTSYS = 0,
USESYSFIRM,
LOADSDFIRMSANDMODULES,
LOADEXTFIRMSANDMODULES,
USECUSTOMPATH,
USELANGEMUANDCODE,
PATCHVERSTRING,
@@ -34,4 +34,10 @@ enum singleOptions
PATCHACCESS
};
void patchCode(u64 progId, u8 *code, u32 size);
enum flags
{
ISN3DS = 0,
ISSAFEMODE
};
void patchCode(u64 progId, u16 progVer, u8 *code, u32 size);

View File

@@ -1,7 +1,7 @@
ENTRY(_start)
SECTIONS
{
. = 0x24FFFF00;
. = 0x24FFFE00;
.text.start : { *(.text.start) }
.text : { *(.text) }
.data : { *(.data) }

View File

@@ -25,11 +25,13 @@
.global flushCaches
.type flushCaches, %function
flushCaches:
@ Clean and flush data cache
@ Clean and flush both the data cache and instruction caches
@ Adpated from http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0155a/ch03s03s05.html ,
@ and https://github.com/gemarcano/libctr9_io/blob/master/src/ctr_system_ARM.c#L39 as well
@ Note: ARM's example is actually for a 8KB DCache (which is what the 3DS has)
@ Implemented in bootROM at address 0xffff0830
@ Implemented in bootROM at addresses 0xffff0830 (DCache) and 0xffff0ab4 (ICache)
mov r1, #0 @ segment counter
outer_loop:

View File

@@ -1,46 +1,48 @@
; Code by Normmatt
.arm.little
.create "build/emunand.bin", 0
.arm
nand_sd:
; Original code that still needs to be executed.
; Original code that still needs to be executed
mov r4, r0
mov r5, r1
mov r7, r2
mov r6, r3
; End.
; End
; If we're already trying to access the SD, return.
; If we're already trying to access the SD, return
ldr r2, [r0, #4]
ldr r1, [sdmmc]
cmp r2, r1
beq nand_sd_ret
beq out
str r1, [r0, #4] ; Set object to be SD
ldr r2, [r0, #8] ; Get sector to read
cmp r2, #0 ; For GW compatibility, see if we're trying to read the ncsd header (sector 0)
str r1, [r0, #4] ; Set object to be SD
ldr r2, [r0, #8] ; Get sector to read
cmp r2, #0 ; For GW compatibility, see if we're trying to read the ncsd header (sector 0)
ldr r3, [nand_offset]
add r2, r3 ; Add the offset to the NAND in the SD.
add r2, r3 ; Add the offset to the NAND in the SD
ldreq r3, [ncsd_header_offset]
addeq r2, r3 ; If we're reading the ncsd header, add the offset of that sector.
addeq r2, r3 ; If we're reading the ncsd header, add the offset of that sector
str r2, [r0, #8] ; Store sector to read
str r2, [r0, #8] ; Store sector to read
nand_sd_ret:
out:
; Restore registers.
mov r1, r5
mov r2, r7
mov r3, r6
; Return 4 bytes behind where we got called,
; due to the offset of this function being stored there.
; due to the offset of this function being stored there
mov r0, lr
add r0, #4
bx r0
.pool
sdmmc: .ascii "SDMC"
nand_offset: .ascii "NAND" ; for rednand this should be 1
ncsd_header_offset: .ascii "NCSD" ; depends on nand manufacturer + emunand type (GW/RED)
nand_offset: .ascii "NAND" ; For rednand this should be 1
ncsd_header_offset: .ascii "NCSD" ; Depends on nand manufacturer + emunand type (GW/RED)
.close

View File

@@ -20,7 +20,7 @@
; Notices displayed by works containing it.
;
; This is mainly Subv's code, big thanks to him.
; Code originally from Subv
.arm.little
@@ -35,85 +35,71 @@
; Register contents:
; r4: Pointer to a pointer to the exheader of the current NCCH
; r6: Constant 0
; SP + 0x80 - 0x7C: Pointer to the memory location where the NCCH text was loaded
; SP + 4: Pointer to the memory location where the NCCH text was loaded
; Save the value of sp
mov r0, sp
; Save the value of all registers
push {r0-r12}
; Execute the instruction we overwrote in our detour
ldr r0, [r4]
ldr r0, [r0, #(0x80 - 0x7C)] ; Load the .text address
ldr r7, [r4]
ldr r2, [r7, #0x18] ; Load the size of the .text
ldr r8, [r7, #0x200] ; Load the low title id of the current NCCH
mov r5, r0
add r11, r5, r2 ; Max bounds of the memory region
; Save the value of the register we use
push {r0-r4}
ldr r9, =0x00001002 ; Low title id of the sm module
cmp r8, r9 ; Compare the low title id to the id of the sm module
bne fs_patch ; Skip if they're not the same
ldr r1, [sp, #24] ; Load the .text address
ldr r2, [r0, #0x200] ; Load the low title id of the current NCCH
ldr r0, [r0, #0x18] ; Load the size of the .text
add r0, r1, r0 ; Max bounds of the memory region
ldr r3, =0x1002 ; Low title id of the sm module
cmp r2, r3 ; Compare the low title id to the id of the sm module
bne fs_patch ; Skip if they're not the same
ldr r2, =0xE1A01006 ; mov r1, r6
ldr r7, =0xE1A01006 ; mov r1, r6
ldr r8, =0xE1A00005 ; mov r0, r5
ldr r9, =0xE3500000 ; cmp r0, #0
ldr r10, =0xE2850004 ; add r0, r5, #4
loop:
cmp r11, r5
blo out ; Check if we didn't go past the bounds of the memory region
ldr r6, [r5]
cmp r6, r7
ldreq r6, [r5, #4]
cmpeq r6, r8
ldreq r6, [r5, #12]
cmpeq r6, r9
ldreq r6, [r5, #24]
cmpeq r6, r10
moveq r8, r5
addne r5, r5, #4
cmp r0, r1
blo die ; Check if we didn't go past the bounds of the memory region
ldr r3, [r1]
cmp r3, r2
ldreqh r3, [r1, #4]
cmpeq r3, #5
addne r1, #4
bne loop
; r8 now contains the start address of the pattern we found
; Write NOPs to the four instructions we want to patch
ldr r9, =0xE320F000 ; nop
str r9, [r8, #8] ; Patch the bl
str r9, [r8, #12] ; Patch the cmp
str r9, [r8, #16] ; Patch the ldreq
str r9, [r8, #20] ; Patch the beq
; r1 now contains the start address of the pattern we found
ldr r0, =0xE3A00001 ; mov r0, #1
str r0, [r1, #8] ; Patch the bl
b out
fs_patch: ; patch adapted from BootNTR
ldr r9, =0x00001102 ; Low title id of the fs module
cmp r8, r9 ; Compare the low title id to the id of the sm module
bne out ; Skip if they're not the same
ldr r3, =0x1102 ; Low title id of the fs module
cmp r2, r3 ; Compare the low title id to the id of the sm module
bne out ; Skip if they're not the same
ldr r7, =0x4618 ; mov r0, r3
ldr r8, =0x3481 ; add r4, #0x81
ldr r2, =0x7401 ; strb r1, [r0, #16]
ldr r3, =0x2000 ; movs r0, #0
loop_fs:
cmp r11, r5
blo out
ldrh r6, [r5]
cmp r6, r7
ldreqh r6, [r5, #2]
cmpeq r6, r8
subeq r8, r5, #8
addne r5, #2
bne loop_fs
loop_fs:
cmp r0, r1
blo die
ldrh r4, [r1]
cmp r4, r2
ldreqh r4, [r1, #2]
cmpeq r4, r3
addeq r1, #8
addne r1, #2
bne loop_fs
; r1 now contains the start address of the pattern we found
ldr r0, =0x2001 ; mov r0, #1
ldr r2, =0x4770 ; bx lr
strh r0, [r1]
strh r2, [r1, #2]
; r8 now contains the start address of the pattern we found
ldr r9, =0x2001 ; mov r0, #1
ldr r10, =0x4770 ; bx lr
strh r9, [r8]
strh r10, [r8, #2]
out:
pop {r0-r12} ; Restore the registers we used
pop {r0-r4} ; Restore the registers we used
bx lr ; Jump back to whoever called us
ldr r0, [r4] ; Execute the instruction we overwrote in our detour
bx lr ; Jump back to whoever called us
die:
b die
.pool
.close

View File

@@ -1,7 +1,10 @@
; Code originally from delebile and mid-kid
.arm.little
payload_addr equ 0x23F00000 ; Brahma payload address.
payload_maxsize equ 0x100000 ; Maximum size for the payload (maximum that CakeBrah supports).
payload_addr equ 0x23F00000 ; Brahma payload address
payload_maxsize equ 0x100000 ; Maximum size for the payload (maximum that CakeBrah supports)
sd_notmounted equ 0xC8804465 ; Error code returned when SD is not mounted
.create "build/reboot.bin", 0
.arm
@@ -25,32 +28,40 @@ payload_maxsize equ 0x100000 ; Maximum size for the payload (maximum that CakeB
cmp r0, r2
bne pxi_wait_recv
; Open file
add r0, r7, #8
adr r1, fname
mov r2, #1
ldr r6, [fopen]
orr r6, 1
blx r6
open_payload:
; Open file
add r0, r7, #8
adr r1, fname
mov r2, #1
ldr r6, [fopen]
orr r6, 1
blx r6
cmp r0, #0
beq read_payload
ldr r2, =sd_notmounted
cmp r0, r2
bne panic
adr r0, fname
adr r1, nand_mount
mov r2, #8
bl memcpy16
b open_payload
; Read file
mov r0, r7
adr r1, bytes_read
ldr r2, =payload_addr
mov r3, payload_maxsize
ldr r6, [r7]
ldr r6, [r6, #0x28]
blx r6
read_payload:
; Read file
mov r0, r7
adr r1, bytes_read
ldr r2, =payload_addr
ldr r3, =payload_maxsize
ldr r6, [r7]
ldr r6, [r6, #0x28]
blx r6
; Copy the low TID (in UTF-16) of the wanted firm to the 5th byte of the payload
add r0, r8, 0x1A
add r1, r0, #0x10
ldr r2, =payload_addr + 4
copy_TID_low:
ldrh r3, [r0], #2
strh r3, [r2], #2
cmp r0, r1
blo copy_TID_low
ldr r0, =payload_addr + 4
add r1, r8, #0x1A
mov r2, #0x10
bl memcpy16
; Set kernel state
mov r0, #0
@@ -68,28 +79,45 @@ payload_maxsize equ 0x100000 ; Maximum size for the payload (maximum that CakeB
die:
b die
memcpy16:
add r2, r0, r2
copy_loop:
ldrh r3, [r1], #2
strh r3, [r0], #2
cmp r0, r2
blo copy_loop
bx lr
panic:
mov r1, r0 ; unused register
mov r0, #0
swi 0x3C ; svcBreak(USERBREAK_PANIC)
b die
bytes_read: .word 0
fopen: .ascii "OPEN"
.pool
fname: .dcw "sdmc:/arm9loaderhax.bin"
.word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.pool
nand_mount: .dcw "nand"
.align 4
kernelcode_start:
; Disable MPU
ldr r0, =0x42078 ; alt vector select, enable itcm
ldr r0, =0x42078 ; alt vector select, enable itcm
mcr p15, 0, r0, c1, c0, 0
; Clean and flush data cache
mov r1, #0 ; segment counter
mov r1, #0 ; segment counter
outer_loop:
mov r0, #0 ; line counter
mov r0, #0 ; line counter
inner_loop:
orr r2, r1, r0 ; generate segment and line address
mcr p15, 0, r2, c7, c14, 2 ; clean and flush the line
add r0, #0x20 ; increment to next line
orr r2, r1, r0 ; generate segment and line address
mcr p15, 0, r2, c7, c14, 2 ; clean and flush the line
add r0, #0x20 ; increment to next line
cmp r0, #0x400
bne inner_loop
@@ -97,7 +125,8 @@ fname: .dcw "sdmc:/arm9loaderhax.bin"
cmp r1, #0
bne outer_loop
mcr p15, 0, r1, c7, c10, 4 ; drain write buffer
; Drain write buffer
mcr p15, 0, r1, c7, c10, 4
; Flush instruction cache
mcr p15, 0, r1, c7, c5, 0

161
source/3dsheaders.h Normal file
View File

@@ -0,0 +1,161 @@
/*
* This file is part of Luma3DS
* Copyright (C) 2016 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 of GPLv3 applies 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.
*/
/*
* Adapted from 3DBrew and https://github.com/mid-kid/CakesForeveryWan/blob/master/source/headers.h
*/
typedef struct __attribute__((packed))
{
u32 address;
u32 phyRegionSize;
u32 size;
} CodeSetInfo;
typedef struct __attribute__((packed))
{
u32 saveDataSize[2];
u32 jumpID[2];
u8 reserved[0x30];
} SystemInfo;
typedef struct __attribute__((packed))
{
char appTitle[8];
u8 reserved1[5];
u8 flag;
u8 remasterVersion[2];
CodeSetInfo textCodeSet;
u32 stackSize;
CodeSetInfo roCodeSet;
u8 reserved2[4];
CodeSetInfo dataCodeSet;
u32 bssSize;
char depends[0x180];
SystemInfo systemInfo;
} SystemControlInfo;
typedef struct __attribute__((packed))
{
SystemControlInfo systemControlInfo;
u8 aci[0x200];
u8 accessDescSig[0x100];
u8 ncchPubKey[0x100];
u8 aciLim[0x200];
} ExHeader;
typedef struct __attribute__((packed))
{
u8 sig[0x100]; //RSA-2048 signature of the NCCH header, using SHA-256
char magic[4]; //NCCH
u32 contentSize; //Media unit
u8 partitionId[8];
u8 makerCode[2];
u16 version;
u8 reserved1[4];
u8 programID[8];
u8 reserved2[0x10];
u8 logoHash[0x20]; //Logo Region SHA-256 hash
char productCode[0x10];
u8 exHeaderHash[0x20]; //Extended header SHA-256 hash
u32 exHeaderSize; //Extended header size
u32 reserved3;
u8 flags[8];
u32 plainOffset; //Media unit
u32 plainSize; //Media unit
u32 logoOffset; //Media unit
u32 logoSize; //Media unit
u32 exeFsOffset; //Media unit
u32 exeFsSize; //Media unit
u32 exeFsHashSize; //Media unit
u32 reserved4;
u32 romFsOffset; //Media unit
u32 romFsSize; //Media unit
u32 romFsHashSize; //Media unit
u32 reserved5;
u8 exeFsHash[0x20]; //ExeFS superblock SHA-256 hash
u8 romFsHash[0x20]; //RomFS superblock SHA-256 hash
} Ncch;
typedef struct __attribute__((packed))
{
Ncch ncch;
ExHeader exHeader;
} Cxi;
typedef struct __attribute__((packed))
{
char sigIssuer[0x40];
u8 eccPubKey[0x3C];
u8 version;
u8 caCrlVersion;
u8 signerCrlVersion;
u8 titleKey[0x10];
u8 reserved1;
u8 ticketId[8];
u8 consoleId[4];
u8 titleId[8];
u8 reserved2[2];
u16 ticketTitleVersion;
u8 reserved3[8];
u8 licenseType;
u8 ticketCommonKeyYIndex; //Ticket common keyY index, usually 0x1 for retail system titles.
u8 reserved4[0x2A];
u8 unk[4]; //eShop Account ID?
u8 reserved5;
u8 audit;
u8 reserved6[0x42];
u8 limits[0x40];
u8 contentIndex[0xAC];
} Ticket;
typedef struct __attribute__((packed))
{
u32 offset;
u8 *address;
u32 size;
u32 procType;
u8 hash[0x20];
} FirmSection;
typedef struct __attribute__((packed))
{
u32 magic;
u32 reserved1;
u8 *arm11Entry;
u8 *arm9Entry;
u8 reserved2[0x30];
FirmSection section[4];
} Firm;
typedef struct __attribute__((packed))
{
u8 keyX[0x10];
u8 keyY[0x10];
u8 ctr[0x10];
char size[8];
u8 reserved[8];
u8 ctlBlock[0x10];
char magic[4];
u8 reserved2[0xC];
u8 slot0x16keyX[0x10];
} Arm9Bin;

View File

@@ -29,18 +29,23 @@
#include "buttons.h"
#include "pin.h"
CfgData configData;
bool readConfig(void)
{
if(fileRead(&configData, CONFIG_PATH, sizeof(CfgData)) != sizeof(CfgData) ||
bool ret;
if(fileRead(&configData, CONFIG_FILE, sizeof(CfgData)) != sizeof(CfgData) ||
memcmp(configData.magic, "CONF", 4) != 0 ||
configData.formatVersionMajor != CONFIG_VERSIONMAJOR ||
configData.formatVersionMinor != CONFIG_VERSIONMINOR)
{
configData.config = 0;
return false;
ret = false;
}
else ret = true;
return true;
return ret;
}
void writeConfig(ConfigurationStatus needConfig, u32 configTemp)
@@ -57,14 +62,14 @@ void writeConfig(ConfigurationStatus needConfig, u32 configTemp)
}
//Merge the new options and new boot configuration
configData.config = (configData.config & 0xFFFFFE00) | (configTemp & 0x1FF);
configData.config = (configData.config & 0xFFFFFF00) | (configTemp & 0xFF);
if(!fileWrite(&configData, CONFIG_PATH, sizeof(CfgData)))
if(!fileWrite(&configData, CONFIG_FILE, sizeof(CfgData)))
error("Error writing the configuration file");
}
}
void configMenu(bool oldPinStatus, u32 oldPinMode)
void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
{
const char *multiOptionsText[] = { "Default EmuNAND: 1( ) 2( ) 3( ) 4( )",
"Screen brightness: 4( ) 3( ) 2( ) 1( )",
@@ -75,8 +80,8 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
};
const char *singleOptionsText[] = { "( ) Autoboot SysNAND",
"( ) Use SysNAND FIRM if booting with R (A9LH)",
"( ) Enable FIRMs and modules loading from SD",
"( ) Use SysNAND FIRM if booting with R",
"( ) Enable loading external FIRMs and modules",
"( ) Use custom path",
"( ) Enable region/language emu. and ext. .code",
"( ) Show NAND or user string in System Settings",
@@ -113,7 +118,8 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
"games.",
"Select the developer features.\n\n"
"\t* 'Off' disables exception handlers.\n"
"\t* 'Off' disables exception handlers\n"
"in FIRM.\n"
"\t* 'ErrDisp' displays debug info\n"
"on the 'An error has occurred' screen.\n"
"\t* 'UNITINFO' makes the console be\n"
@@ -144,8 +150,8 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
"1/2/3/4), also add A if you have\n"
"a matching payload.",
"Enable loading FIRMs and\n"
"system modules from the SD card.\n\n"
"Enable loading external FIRMs and\n"
"system modules.\n\n"
"This isn't needed in most cases.\n\n"
"Refer to the wiki for instructions.",
@@ -189,25 +195,36 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
u32 posXs[4];
u32 posY;
u32 enabled;
bool visible;
} multiOptions[] = {
{ .posXs = {19, 24, 29, 34} },
{ .posXs = {21, 26, 31, 36} },
{ .posXs = {12, 22, 31, 0} },
{ .posXs = {14, 19, 24, 29} },
{ .posXs = {17, 26, 32, 44} },
{ .posXs = {19, 30, 42, 0} }
{ .posXs = {19, 24, 29, 34}, .visible = isSdMode },
{ .posXs = {21, 26, 31, 36}, .visible = true },
{ .posXs = {12, 22, 31, 0}, .visible = true },
{ .posXs = {14, 19, 24, 29}, .visible = true },
{ .posXs = {17, 26, 32, 44}, .visible = ISN3DS },
{ .posXs = {19, 30, 42, 0}, .visible = true }
};
//Calculate the amount of the various kinds of options and pre-select the first single one
u32 multiOptionsAmount = sizeof(multiOptions) / sizeof(struct multiOption),
singleOptionsAmount = sizeof(singleOptionsText) / sizeof(char *),
totalIndexes = multiOptionsAmount + singleOptionsAmount - 1,
selectedOption = multiOptionsAmount;
struct singleOption {
u32 posY;
bool enabled;
} singleOptions[singleOptionsAmount];
bool visible;
} singleOptions[] = {
{ .visible = isSdMode },
{ .visible = isSdMode },
{ .visible = true },
{ .visible = true },
{ .visible = true },
{ .visible = true },
{ .visible = true },
{ .visible = true }
};
//Calculate the amount of the various kinds of options and pre-select the first single one
u32 multiOptionsAmount = sizeof(multiOptions) / sizeof(struct multiOption),
singleOptionsAmount = sizeof(singleOptions) / sizeof(struct singleOption),
totalIndexes = multiOptionsAmount + singleOptionsAmount - 1,
selectedOption;
//Parse the existing options
for(u32 i = 0; i < multiOptionsAmount; i++)
@@ -228,7 +245,7 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
//Display all the multiple choice options in white
for(u32 i = 0; i < multiOptionsAmount; i++)
{
if(!(i == NEWCPU && !isN3DS))
if(multiOptions[i].visible)
{
multiOptions[i].posY = endPos + SPACING_Y;
endPos = drawString(multiOptionsText[i], true, 10, multiOptions[i].posY, COLOR_WHITE);
@@ -237,15 +254,22 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
}
endPos += SPACING_Y / 2;
u32 color = COLOR_RED;
//Display all the normal options in white except for the first one
for(u32 i = 0; i < singleOptionsAmount; i++)
for(u32 i = 0, color = COLOR_RED; i < singleOptionsAmount; i++)
{
singleOptions[i].posY = endPos + SPACING_Y;
endPos = drawString(singleOptionsText[i], true, 10, singleOptions[i].posY, color);
if(singleOptions[i].enabled) drawCharacter(selected, true, 10 + SPACING_X, singleOptions[i].posY, color);
color = COLOR_WHITE;
if(singleOptions[i].visible)
{
singleOptions[i].posY = endPos + SPACING_Y;
endPos = drawString(singleOptionsText[i], true, 10, singleOptions[i].posY, color);
if(singleOptions[i].enabled) drawCharacter(selected, true, 10 + SPACING_X, singleOptions[i].posY, color);
if(color == COLOR_RED)
{
selectedOption = i + multiOptionsAmount;
color = COLOR_WHITE;
}
}
}
drawString(optionsDescription[selectedOption], false, 10, 10, COLOR_WHITE);
@@ -266,24 +290,31 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
//Remember the previously selected option
u32 oldSelectedOption = selectedOption;
switch(pressed)
while(true)
{
case BUTTON_UP:
if(!selectedOption) selectedOption = totalIndexes;
else selectedOption = (selectedOption == NEWCPU + 1 && !isN3DS) ? selectedOption - 2 : selectedOption - 1;
break;
case BUTTON_DOWN:
if(selectedOption == totalIndexes) selectedOption = 0;
else selectedOption = (selectedOption == NEWCPU - 1 && !isN3DS) ? selectedOption + 2 : selectedOption + 1;
break;
case BUTTON_LEFT:
selectedOption = 0;
break;
case BUTTON_RIGHT:
selectedOption = totalIndexes;
break;
default:
continue;
switch(pressed)
{
case BUTTON_UP:
selectedOption = !selectedOption ? totalIndexes : selectedOption - 1;
break;
case BUTTON_DOWN:
selectedOption = selectedOption == totalIndexes ? 0 : selectedOption + 1;
break;
case BUTTON_LEFT:
pressed = BUTTON_DOWN;
selectedOption = 0;
break;
case BUTTON_RIGHT:
pressed = BUTTON_UP;
selectedOption = totalIndexes;
break;
}
if(selectedOption < multiOptionsAmount)
{
if(multiOptions[selectedOption].visible) break;
}
else if(singleOptions[selectedOption - multiOptionsAmount].visible) break;
}
if(selectedOption == oldSelectedOption) continue;
@@ -346,14 +377,14 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
//Parse and write the new configuration
for(u32 i = 0; i < multiOptionsAmount; i++)
configData.config |= multiOptions[i].enabled << (i * 2 + 9);
configData.config |= multiOptions[i].enabled << (i * 2 + 8);
for(u32 i = 0; i < singleOptionsAmount; i++)
configData.config |= (singleOptions[i].enabled ? 1 : 0) << (i + 21);
configData.config |= (singleOptions[i].enabled ? 1 : 0) << (i + 20);
u32 newPinMode = MULTICONFIG(PIN);
if(newPinMode != 0) newPin(oldPinStatus && newPinMode == oldPinMode, newPinMode);
else if(oldPinStatus) fileDelete(PIN_PATH);
else if(oldPinStatus) fileDelete(PIN_FILE);
//Wait for the pressed buttons to change
while(HID_PAD & PIN_BUTTONS);

View File

@@ -24,19 +24,18 @@
#include "types.h"
#define CONFIG(a) (((configData.config >> (a + 21)) & 1) != 0)
#define MULTICONFIG(a) ((configData.config >> (a * 2 + 9)) & 3)
#define CONFIG(a) (((configData.config >> (a + 20)) & 1) != 0)
#define MULTICONFIG(a) ((configData.config >> (a * 2 + 8)) & 3)
#define BOOTCONFIG(a, b) ((configData.config >> a) & b)
#define CONFIG_PATH "/luma/config.bin"
#define CONFIG_FILE "config.bin"
#define CONFIG_VERSIONMAJOR 1
#define CONFIG_VERSIONMINOR 6
#define CONFIG_VERSIONMINOR 7
#define BOOTCFG_NAND BOOTCONFIG(0, 7)
#define BOOTCFG_FIRM BOOTCONFIG(3, 7)
#define BOOTCFG_A9LH BOOTCONFIG(6, 1)
#define BOOTCFG_NOFORCEFLAG BOOTCONFIG(7, 1)
#define BOOTCFG_SAFEMODE BOOTCONFIG(8, 1)
enum multiOptions
{
@@ -52,7 +51,7 @@ enum singleOptions
{
AUTOBOOTSYS = 0,
USESYSFIRM,
LOADSDFIRMSANDMODULES,
LOADEXTFIRMSANDMODULES,
USECUSTOMPATH,
USELANGEMUANDCODE,
PATCHVERSTRING,
@@ -60,14 +59,6 @@ enum singleOptions
PATCHACCESS
};
typedef struct __attribute__((packed))
{
char magic[4];
u16 formatVersionMajor, formatVersionMinor;
u32 config;
} CfgData;
typedef enum ConfigurationStatus
{
DONT_CONFIGURE = 0,
@@ -75,9 +66,6 @@ typedef enum ConfigurationStatus
CREATE_CONFIGURATION
} ConfigurationStatus;
extern CfgData configData;
extern bool isN3DS;
bool readConfig(void);
void writeConfig(ConfigurationStatus needConfig, u32 configTemp);
void configMenu(bool oldPinStatus, u32 oldPinMode);
void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode);

View File

@@ -24,10 +24,13 @@
* Crypto libs from http://github.com/b1l1s/ctr
* kernel9Loader code originally adapted from https://github.com/Reisyukaku/ReiNand/blob/228c378255ba693133dec6f3368e14d386f2cde7/source/crypto.c#L233
* decryptNusFirm code adapted from https://github.com/mid-kid/CakesForeveryWan/blob/master/source/firm.c
* ctrNandWrite logic adapted from https://github.com/d0k3/GodMode9/blob/master/source/nand/nand.c
*/
#include "crypto.h"
#include "memory.h"
#include "strings.h"
#include "utils.h"
#include "fatfs/sdmmc/sdmmc.h"
/****************************************************************
@@ -82,7 +85,7 @@ __asm__\
static void aes_setkey(u8 keyslot, const void *key, u32 keyType, u32 mode)
{
if(keyslot <= 0x03) return; // Ignore TWL keys for now
if(keyslot <= 0x03) return; //Ignore TWL keys for now
u32 *key32 = (u32 *)key;
*REG_AESCNT = (*REG_AESCNT & ~(AES_CNT_INPUT_ENDIAN | AES_CNT_INPUT_ORDER)) | mode;
*REG_AESKEYCNT = (*REG_AESKEYCNT >> 6 << 6) | keyslot | AES_KEYCNT_WRITE;
@@ -107,7 +110,7 @@ static void aes_setiv(const void *iv, u32 mode)
const u32 *iv32 = (const u32 *)iv;
*REG_AESCNT = (*REG_AESCNT & ~(AES_CNT_INPUT_ENDIAN | AES_CNT_INPUT_ORDER)) | mode;
// Word order for IV can't be changed in REG_AESCNT and always default to reversed
//Word order for IV can't be changed in REG_AESCNT and always default to reversed
if(mode & AES_INPUT_NORMAL)
{
REG_AESCTR[0] = iv32[3];
@@ -131,7 +134,7 @@ static void aes_advctr(void *ctr, u32 val, u32 mode)
int i;
if(mode & AES_INPUT_BE)
{
for(i = 0; i < 4; ++i) // Endian swap
for(i = 0; i < 4; ++i) //Endian swap
BSWAP32(ctr32[i]);
}
@@ -146,7 +149,7 @@ static void aes_advctr(void *ctr, u32 val, u32 mode)
if(mode & AES_INPUT_BE)
{
for(i = 0; i < 4; ++i) // Endian swap
for(i = 0; i < 4; ++i) //Endian swap
BSWAP32(ctr32[i]);
}
}
@@ -186,7 +189,7 @@ static void aes_batch(void *dst, const void *src, u32 blockCount)
while(rbc)
{
if(wbc && ((*REG_AESCNT & 0x1F) <= 0xC)) // There's space for at least 4 ints
if(wbc && ((*REG_AESCNT & 0x1F) <= 0xC)) //There's space for at least 4 ints
{
*REG_AESWRFIFO = *src32++;
*REG_AESWRFIFO = *src32++;
@@ -195,7 +198,7 @@ static void aes_batch(void *dst, const void *src, u32 blockCount)
wbc--;
}
if(rbc && ((*REG_AESCNT & (0x1F << 0x5)) >= (0x4 << 0x5))) // At least 4 ints available for read
if(rbc && ((*REG_AESCNT & (0x1F << 0x5)) >= (0x4 << 0x5))) //At least 4 ints available for read
{
*dst32++ = *REG_AESRDFIFO;
*dst32++ = *REG_AESRDFIFO;
@@ -222,24 +225,24 @@ static void aes(void *dst, const void *src, u32 blockCount, void *iv, u32 mode,
blocks = (blockCount >= 0xFFFF) ? 0xFFFF : blockCount;
// Save the last block for the next decryption CBC batch's iv
//Save the last block for the next decryption CBC batch's iv
if((mode & AES_ALL_MODES) == AES_CBC_DECRYPT_MODE)
{
memcpy(iv, src + (blocks - 1) * AES_BLOCK_SIZE, AES_BLOCK_SIZE);
aes_change_ctrmode(iv, AES_INPUT_BE | AES_INPUT_NORMAL, ivMode);
}
// Process the current batch
//Process the current batch
aes_batch(dst, src, blocks);
// Save the last block for the next encryption CBC batch's iv
//Save the last block for the next encryption CBC batch's iv
if((mode & AES_ALL_MODES) == AES_CBC_ENCRYPT_MODE)
{
memcpy(iv, dst + (blocks - 1) * AES_BLOCK_SIZE, AES_BLOCK_SIZE);
aes_change_ctrmode(iv, AES_INPUT_BE | AES_INPUT_NORMAL, ivMode);
}
// Advance counter for CTR mode
//Advance counter for CTR mode
else if((mode & AES_ALL_MODES) == AES_CTR_MODE)
aes_advctr(iv, blocks, ivMode);
@@ -256,6 +259,8 @@ static void sha_wait_idle()
static void sha(void *res, const void *src, u32 size, u32 mode)
{
backupAndRestoreShaHash(false);
sha_wait_idle();
*REG_SHA_CNT = mode | SHA_CNT_OUTPUT_ENDIAN | SHA_NORMAL_ROUND;
@@ -300,6 +305,8 @@ static u32 fatStart;
static u8 __attribute__((aligned(4))) shaHashBackup[SHA_256_HASH_SIZE];
static bool didShaHashBackup = false;
FirmwareSource firmSource;
void ctrNandInit(void)
{
u8 __attribute__((aligned(4))) cid[AES_BLOCK_SIZE];
@@ -309,7 +316,7 @@ void ctrNandInit(void)
sha(shaSum, cid, sizeof(cid), SHA_256_MODE);
memcpy(nandCtr, shaSum, sizeof(nandCtr));
if(isN3DS)
if(ISN3DS)
{
u8 __attribute__((aligned(4))) keyY0x5[AES_BLOCK_SIZE] = {0x4D, 0x80, 0x4F, 0x4E, 0x99, 0x90, 0x19, 0x46, 0x13, 0xA2, 0x04, 0xAC, 0x58, 0x44, 0x60, 0xBE};
aes_setkey(0x05, keyY0x5, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
@@ -324,14 +331,14 @@ void ctrNandInit(void)
}
}
u32 ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf)
int ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf)
{
u8 __attribute__((aligned(4))) tmpCtr[sizeof(nandCtr)];
memcpy(tmpCtr, nandCtr, sizeof(nandCtr));
aes_advctr(tmpCtr, ((sector + fatStart) * 0x200) / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL);
//Read
u32 result;
int result;
if(firmSource == FIRMWARE_SYSNAND)
result = sdmmc_nand_readsectors(sector + fatStart, sectorCount, outbuf);
else
@@ -347,6 +354,33 @@ u32 ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf)
return result;
}
int ctrNandWrite(u32 sector, u32 sectorCount, const u8 *inbuf)
{
u8 *buffer = (u8 *)0x23000000;
u32 bufferSize = 0xF00000;
u8 __attribute__((aligned(4))) tmpCtr[sizeof(nandCtr)];
memcpy(tmpCtr, nandCtr, sizeof(nandCtr));
aes_advctr(tmpCtr, ((sector + fatStart) * 0x200) / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL);
aes_use_keyslot(nandSlot);
int result = 0;
for(u32 tempSector = 0; tempSector < sectorCount && !result; tempSector += bufferSize / 0x200)
{
u32 tempCount = (bufferSize / 0x200) < (sectorCount - tempSector) ? (bufferSize / 0x200) : (sectorCount - tempSector);
memcpy(buffer, inbuf + (tempSector * 0x200), tempCount * 0x200);
//Encrypt
aes(buffer, buffer, tempCount * 0x200 / AES_BLOCK_SIZE, tmpCtr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
//Write
result = sdmmc_nand_writesectors(tempSector + sector + fatStart, tempCount, buffer);
}
return result;
}
void set6x7xKeys(void)
{
const u8 __attribute__((aligned(4))) keyX0x25[AES_BLOCK_SIZE] = {0xCE, 0xE7, 0xD8, 0xAB, 0x30, 0xC0, 0x0D, 0xAE, 0x85, 0x0E, 0xF5, 0xE3, 0x82, 0xAC, 0x5A, 0xF3};
@@ -361,47 +395,66 @@ void set6x7xKeys(void)
memset32((void *)0x01FFCD00, 0, 0x10);
}
void decryptExeFs(u8 *inbuf)
bool decryptExeFs(Cxi *cxi)
{
u8 *exeFsOffset = inbuf + *(u32 *)(inbuf + 0x1A0) * 0x200;
u32 exeFsSize = *(u32 *)(inbuf + 0x1A4) * 0x200;
u8 __attribute__((aligned(4))) ncchCtr[AES_BLOCK_SIZE] = {0};
bool isCxi;
for(u32 i = 0; i < 8; i++)
ncchCtr[7 - i] = *(inbuf + 0x108 + i);
ncchCtr[8] = 2;
if(memcmp(cxi->ncch.magic, "NCCH", 4) == 0)
{
isCxi = true;
aes_setkey(0x2C, inbuf, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
aes_use_keyslot(0x2C);
aes(inbuf - 0x200, exeFsOffset, exeFsSize / AES_BLOCK_SIZE, ncchCtr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
u8 *exeFsOffset = (u8 *)cxi + (cxi->ncch.exeFsOffset + 1) * 0x200;
u32 exeFsSize = (cxi->ncch.exeFsSize - 1) * 0x200;
u8 __attribute__((aligned(4))) ncchCtr[AES_BLOCK_SIZE] = {0};
for(u32 i = 0; i < 8; i++)
ncchCtr[7 - i] = cxi->ncch.partitionId[i];
ncchCtr[8] = 2;
aes_setkey(0x2C, cxi, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
aes_advctr(ncchCtr, 0x200 / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL);
aes_use_keyslot(0x2C);
aes(cxi, exeFsOffset, exeFsSize / AES_BLOCK_SIZE, ncchCtr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
}
else isCxi = false;
return isCxi && memcmp(cxi, "FIRM", 4) == 0;
}
void decryptNusFirm(const u8 *inbuf, u8 *outbuf, u32 ncchSize)
bool decryptNusFirm(const Ticket *ticket, Cxi *cxi, u32 ncchSize)
{
const u8 keyY0x3D[AES_BLOCK_SIZE] = {0x0C, 0x76, 0x72, 0x30, 0xF0, 0x99, 0x8F, 0x1C, 0x46, 0x82, 0x82, 0x02, 0xFA, 0xAC, 0xBE, 0x4C};
u8 __attribute__((aligned(4))) titleKey[AES_BLOCK_SIZE];
u8 __attribute__((aligned(4))) cetkIv[AES_BLOCK_SIZE] = {0};
memcpy(titleKey, inbuf + 0x1BF, sizeof(titleKey));
memcpy(cetkIv, inbuf + 0x1DC, 8);
bool isTicket;
aes_setkey(0x3D, keyY0x3D, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
aes_use_keyslot(0x3D);
aes(titleKey, titleKey, 1, cetkIv, AES_CBC_DECRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
if(memcmp(ticket->sigIssuer, "Root", 4) == 0)
{
isTicket = true;
u8 __attribute__((aligned(4))) ncchIv[AES_BLOCK_SIZE] = {0};
const u8 keyY0x3D[AES_BLOCK_SIZE] = {0x0C, 0x76, 0x72, 0x30, 0xF0, 0x99, 0x8F, 0x1C, 0x46, 0x82, 0x82, 0x02, 0xFA, 0xAC, 0xBE, 0x4C};
u8 __attribute__((aligned(4))) titleKey[AES_BLOCK_SIZE];
u8 __attribute__((aligned(4))) cetkIv[AES_BLOCK_SIZE] = {0};
memcpy(titleKey, ticket->titleKey, sizeof(titleKey));
memcpy(cetkIv, ticket->titleId, sizeof(ticket->titleId));
aes_setkey(0x16, titleKey, AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL);
aes_use_keyslot(0x16);
aes(outbuf, outbuf, ncchSize / AES_BLOCK_SIZE, ncchIv, AES_CBC_DECRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
aes_setkey(0x3D, keyY0x3D, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
aes_use_keyslot(0x3D);
aes(titleKey, titleKey, 1, cetkIv, AES_CBC_DECRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
decryptExeFs(outbuf);
u8 __attribute__((aligned(4))) ncchIv[AES_BLOCK_SIZE] = {0};
aes_setkey(0x16, titleKey, AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL);
aes_use_keyslot(0x16);
aes(cxi, cxi, ncchSize / AES_BLOCK_SIZE, ncchIv, AES_CBC_DECRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
}
else isTicket = false;
return isTicket && decryptExeFs(cxi);
}
void kernel9Loader(u8 *arm9Section)
void kernel9Loader(Arm9Bin *arm9Section)
{
//Determine the kernel9loader version
u32 k9lVersion;
switch(arm9Section[0x53])
switch(arm9Section->magic[3])
{
case 0xFF:
k9lVersion = 0;
@@ -414,10 +467,10 @@ void kernel9Loader(u8 *arm9Section)
break;
}
u32 startOfArm9Bin = *(u32 *)(arm9Section + 0x800);
bool needToDecrypt = startOfArm9Bin != 0x47704770 && startOfArm9Bin != 0xB0862000;
u32 *startOfArm9Bin = (u32 *)((u8 *)arm9Section + 0x800);
bool needToDecrypt = *startOfArm9Bin != 0x47704770 && *startOfArm9Bin != 0xB0862000;
if(!isDevUnit && (k9lVersion == 2 || (k9lVersion == 1 && needToDecrypt)))
if(!ISDEVUNIT && (k9lVersion == 2 || (k9lVersion == 1 && needToDecrypt)))
{
//Set 0x11 keyslot
const u8 __attribute__((aligned(4))) key1[AES_BLOCK_SIZE] = {0x07, 0x29, 0x44, 0x38, 0xF8, 0xC9, 0x75, 0x93, 0xAA, 0x0E, 0x4A, 0xB4, 0xAE, 0x84, 0xC1, 0xD8};
@@ -437,28 +490,24 @@ void kernel9Loader(u8 *arm9Section)
//Set keyX
u8 __attribute__((aligned(4))) keyX[AES_BLOCK_SIZE];
aes_use_keyslot(0x11);
aes(keyX, arm9Section + 0x60, 1, NULL, AES_ECB_DECRYPT_MODE, 0);
aes(keyX, arm9Section->slot0x16keyX, 1, NULL, AES_ECB_DECRYPT_MODE, 0);
aes_setkey(0x16, keyX, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL);
}
//Set keyY
u8 __attribute__((aligned(4))) keyY[AES_BLOCK_SIZE];
memcpy(keyY, arm9Section + 0x10, sizeof(keyY));
memcpy(keyY, arm9Section->keyY, sizeof(keyY));
aes_setkey(arm9BinSlot, keyY, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
//Set CTR
u8 __attribute__((aligned(4))) arm9BinCtr[AES_BLOCK_SIZE];
memcpy(arm9BinCtr, arm9Section + 0x20, sizeof(arm9BinCtr));
//Calculate the size of the ARM9 binary
u32 arm9BinSize = 0;
//http://stackoverflow.com/questions/12791077/atoi-implementation-in-c
for(u8 *tmp = arm9Section + 0x30; *tmp != 0; tmp++)
arm9BinSize = (arm9BinSize << 3) + (arm9BinSize << 1) + *tmp - '0';
memcpy(arm9BinCtr, arm9Section->ctr, sizeof(arm9BinCtr));
//Decrypt ARM9 binary
aes_use_keyslot(arm9BinSlot);
aes(arm9Section + 0x800, arm9Section + 0x800, arm9BinSize / AES_BLOCK_SIZE, arm9BinCtr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
aes(startOfArm9Bin, startOfArm9Bin, decAtoi(arm9Section->size, sizeof(arm9Section->size)) / AES_BLOCK_SIZE, arm9BinCtr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
if(*startOfArm9Bin != 0x47704770 && *startOfArm9Bin != 0xB0862000) error("Failed to decrypt the ARM9 binary.");
}
//Set >=9.6 KeyXs
@@ -482,19 +531,20 @@ void computePinHash(u8 *outbuf, const u8 *inbuf)
u8 __attribute__((aligned(4))) cid[AES_BLOCK_SIZE];
u8 __attribute__((aligned(4))) cipherText[AES_BLOCK_SIZE];
if(isA9lh && !didShaHashBackup)
{
memcpy(shaHashBackup, (void *)REG_SHA_HASH, sizeof(shaHashBackup));
didShaHashBackup = true;
}
sdmmc_get_cid(1, (u32 *)cid);
aes_use_keyslot(4); //Console-unique keyslot whose keys are set by the ARM9 bootROM
aes(cipherText, inbuf, 1, cid, AES_CBC_ENCRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
sha(outbuf, cipherText, sizeof(cipherText), SHA_256_MODE);
}
void restoreShaHashBackup(void)
void backupAndRestoreShaHash(bool isRestore)
{
if(didShaHashBackup) memcpy((void *)REG_SHA_HASH, shaHashBackup, sizeof(shaHashBackup));
if(ISA9LH)
{
if(isRestore)
{
if(didShaHashBackup) memcpy((void *)REG_SHA_HASH, shaHashBackup, sizeof(shaHashBackup));
}
else if(!didShaHashBackup) memcpy(shaHashBackup, (void *)REG_SHA_HASH, sizeof(shaHashBackup));
}
}

View File

@@ -24,6 +24,7 @@
* Crypto libs from http://github.com/b1l1s/ctr
* kernel9Loader code originally adapted from https://github.com/Reisyukaku/ReiNand/blob/228c378255ba693133dec6f3368e14d386f2cde7/source/crypto.c#L233
* decryptNusFirm code adapted from https://github.com/mid-kid/CakesForeveryWan/blob/master/source/firm.c
* ctrNandWrite logic adapted from https://github.com/d0k3/GodMode9/blob/master/source/nand/nand.c
*/
#pragma once
@@ -102,14 +103,14 @@
#define SHA_1_HASH_SIZE (160 / 8)
extern u32 emuOffset;
extern bool isN3DS, isDevUnit, isA9lh;
extern FirmwareSource firmSource;
void ctrNandInit(void);
u32 ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf);
int ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf);
int ctrNandWrite(u32 sector, u32 sectorCount, const u8 *inbuf);
void set6x7xKeys(void);
void decryptExeFs(u8 *inbuf);
void decryptNusFirm(const u8 *inbuf, u8 *outbuf, u32 ncchSize);
void kernel9Loader(u8 *arm9Section);
bool decryptExeFs(Cxi *cxi);
bool decryptNusFirm(const Ticket *ticket, Cxi *cxi, u32 ncchSize);
void kernel9Loader(Arm9Bin *arm9Section);
void computePinHash(u8 *outbuf, const u8 *inbuf);
void restoreShaHashBackup(void);
void backupAndRestoreShaHash(bool isRestore);

View File

@@ -34,27 +34,35 @@
bool loadSplash(void)
{
const char topSplashPath[] = "/luma/splash.bin",
bottomSplashPath[] = "/luma/splashbottom.bin";
const char *topSplashFile = "splash.bin",
*bottomSplashFile = "splashbottom.bin";
bool isTopSplashValid = getFileSize(topSplashPath) == SCREEN_TOP_FBSIZE,
isBottomSplashValid = getFileSize(bottomSplashPath) == SCREEN_BOTTOM_FBSIZE;
bool isTopSplashValid = getFileSize(topSplashFile) == SCREEN_TOP_FBSIZE,
isBottomSplashValid = getFileSize(bottomSplashFile) == SCREEN_BOTTOM_FBSIZE,
ret;
//Don't delay boot nor init the screens if no splash images or invalid splash images are on the SD
if(!isTopSplashValid && !isBottomSplashValid)
return false;
if(!isTopSplashValid && !isBottomSplashValid) ret = false;
else
{
initScreens();
clearScreens(true, true, true);
initScreens();
clearScreens(true, true, true);
if(isTopSplashValid) isTopSplashValid = fileRead(fbs[1].top_left, topSplashFile, SCREEN_TOP_FBSIZE) == SCREEN_TOP_FBSIZE;
if(isBottomSplashValid) isBottomSplashValid = fileRead(fbs[1].bottom, bottomSplashFile, SCREEN_BOTTOM_FBSIZE) == SCREEN_BOTTOM_FBSIZE;
if(isTopSplashValid) fileRead(fbs[1].top_left, topSplashPath, 0);
if(isBottomSplashValid) fileRead(fbs[1].bottom, bottomSplashPath, 0);
if(!isTopSplashValid && !isBottomSplashValid) ret = false;
else
{
swapFramebuffers(true);
swapFramebuffers(true);
chrono(3);
chrono(3);
ret = true;
}
}
return true;
return ret;
}
void drawCharacter(char character, bool isTopScreen, u32 posX, u32 posY, u32 color)

View File

@@ -20,11 +20,17 @@
* Notices displayed by works containing it.
*/
/*
* Code for locating the SDMMC struct by Normmatt
*/
#include "emunand.h"
#include "memory.h"
#include "fatfs/sdmmc/sdmmc.h"
#include "../build/bundled.h"
u32 emuOffset;
void locateEmuNand(u32 *emuHeader, FirmwareSource *nandType)
{
static u8 __attribute__((aligned(4))) temp[0x200];
@@ -50,7 +56,7 @@ void locateEmuNand(u32 *emuHeader, FirmwareSource *nandType)
nandOffset = ROUND_TO_4MB(nandSize + 1); //"Default" layout
break;
case 2:
nandOffset = roundedMinsizes[isN3DS ? 1 : 0]; //"Minsize" layout
nandOffset = roundedMinsizes[ISN3DS ? 1 : 0]; //"Minsize" layout
break;
default:
nandOffset = *nandType == FIRMWARE_EMUNAND ? 0 : (nandSize > 0x200000 ? 0x400000 : 0x200000); //"Legacy" layout
@@ -59,10 +65,10 @@ void locateEmuNand(u32 *emuHeader, FirmwareSource *nandType)
if(*nandType != FIRMWARE_EMUNAND) nandOffset *= ((u32)*nandType - 1);
if(fatStart >= nandOffset + roundedMinsizes[isN3DS ? 1 : 0])
if(fatStart >= nandOffset + roundedMinsizes[ISN3DS ? 1 : 0])
{
//Check for RedNAND
if(!sdmmc_sdcard_readsectors(nandOffset + 1, 1, temp) && *(u32 *)(temp + 0x100) == NCSD_MAGIC)
if(!sdmmc_sdcard_readsectors(nandOffset + 1, 1, temp) && memcmp(temp + 0x100, "NCSD", 4) == 0)
{
emuOffset = nandOffset + 1;
*emuHeader = nandOffset + 1;
@@ -70,7 +76,7 @@ void locateEmuNand(u32 *emuHeader, FirmwareSource *nandType)
}
//Check for Gateway EmuNAND
else if(i != 2 && !sdmmc_sdcard_readsectors(nandOffset + nandSize, 1, temp) && *(u32 *)(temp + 0x100) == NCSD_MAGIC)
else if(i != 2 && !sdmmc_sdcard_readsectors(nandOffset + nandSize, 1, temp) && memcmp(temp + 0x100, "NCSD", 4) == 0)
{
emuOffset = nandOffset;
*emuHeader = nandOffset + nandSize;
@@ -93,68 +99,125 @@ void locateEmuNand(u32 *emuHeader, FirmwareSource *nandType)
}
}
static inline u8 *getFreeK9Space(u8 *pos, u32 size)
static inline u32 getFreeK9Space(u8 *pos, u32 size, u8 **freeK9Space)
{
const u8 pattern[] = {0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00};
u32 ret;
//Looking for the last free space before Process9
return memsearch(pos + 0x13500, pattern, size - 0x13500, sizeof(pattern)) + 0x455;
*freeK9Space = memsearch(pos, pattern, size, sizeof(pattern));
if(*freeK9Space == NULL) ret = 1;
else
{
*freeK9Space += 0x455;
ret = 0;
}
return ret;
}
static inline u32 getSdmmc(u8 *pos, u32 size)
static inline u32 getSdmmc(u8 *pos, u32 size, u32 *sdmmc)
{
//Look for struct code
const u8 pattern[] = {0x21, 0x20, 0x18, 0x20};
u32 ret;
const u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
return *(u32 *)(off + 9) + *(u32 *)(off + 0xD);
if(off == NULL) ret = 1;
else
{
*sdmmc = *(u32 *)(off + 9) + *(u32 *)(off + 0xD);
ret = 0;
}
return ret;
}
static inline void patchNandRw(u8 *pos, u32 size, u32 branchOffset)
static inline u32 patchNandRw(u8 *pos, u32 size, u32 branchOffset)
{
//Look for read/write code
const u8 pattern[] = {0x1E, 0x00, 0xC8, 0x05};
u32 ret;
u16 *readOffset = (u16 *)memsearch(pos, pattern, size, sizeof(pattern)) - 3,
*writeOffset = (u16 *)memsearch((u8 *)(readOffset + 5), pattern, 0x100, sizeof(pattern)) - 3;
u16 *readOffset = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
*readOffset = *writeOffset = 0x4C00;
readOffset[1] = writeOffset[1] = 0x47A0;
((u32 *)writeOffset)[1] = ((u32 *)readOffset)[1] = branchOffset;
if(readOffset == NULL) ret = 1;
else
{
readOffset -= 3;
u16 *writeOffset = (u16 *)memsearch((u8 *)(readOffset + 5), pattern, 0x100, sizeof(pattern));
if(writeOffset == NULL) ret = 1;
else
{
writeOffset -= 3;
*readOffset = *writeOffset = 0x4C00;
readOffset[1] = writeOffset[1] = 0x47A0;
((u32 *)writeOffset)[1] = ((u32 *)readOffset)[1] = branchOffset;
ret = 0;
}
}
return ret;
}
static inline void patchMpu(u8 *pos, u32 size)
static inline u32 patchMpu(u8 *pos, u32 size)
{
//Look for MPU pattern
const u8 pattern[] = {0x03, 0x00, 0x24, 0x00};
u32 ret;
u32 *off = (u32 *)memsearch(pos, pattern, size, sizeof(pattern));
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
off[0] = 0x00360003;
off[6] = 0x00200603;
off[9] = 0x001C0603;
if(off == NULL) ret = 1;
else
{
off[1] = 0x0036;
off[0xC] = off[0x12] = 0x0603;
ret = 0;
}
return ret;
}
void patchEmuNand(u8 *arm9Section, u32 arm9SectionSize, u8 *process9Offset, u32 process9Size, u32 emuHeader, u32 branchAdditive)
u32 patchEmuNand(u8 *arm9Section, u32 kernel9Size, u8 *process9Offset, u32 process9Size, u32 emuHeader, u8 *kernel9Address)
{
//Copy EmuNAND code
u8 *freeK9Space = getFreeK9Space(arm9Section, arm9SectionSize);
memcpy(freeK9Space, emunand_bin, emunand_bin_size);
u32 ret = 0;
//Add the data of the found EmuNAND
u32 *posOffset = (u32 *)memsearch(freeK9Space, "NAND", emunand_bin_size, 4),
*posHeader = (u32 *)memsearch(freeK9Space, "NCSD", emunand_bin_size, 4);
*posOffset = emuOffset;
*posHeader = emuHeader;
u8 *freeK9Space;
ret += getFreeK9Space(arm9Section, kernel9Size, &freeK9Space);
//Find and add the SDMMC struct
u32 *posSdmmc = (u32 *)memsearch(freeK9Space, "SDMC", emunand_bin_size, 4);
*posSdmmc = getSdmmc(process9Offset, process9Size);
if(!ret)
{
//Copy EmuNAND code
memcpy(freeK9Space, emunand_bin, emunand_bin_size);
//Add EmuNAND hooks
u32 branchOffset = (u32)freeK9Space - branchAdditive;
patchNandRw(process9Offset, process9Size, branchOffset);
//Add the data of the found EmuNAND
u32 *posOffset = (u32 *)memsearch(freeK9Space, "NAND", emunand_bin_size, 4),
*posHeader = (u32 *)memsearch(freeK9Space, "NCSD", emunand_bin_size, 4);
*posOffset = emuOffset;
*posHeader = emuHeader;
//Set MPU
patchMpu(arm9Section, arm9SectionSize);
//Find and add the SDMMC struct
u32 *posSdmmc = (u32 *)memsearch(freeK9Space, "SDMC", emunand_bin_size, 4);
u32 sdmmc;
ret += getSdmmc(process9Offset, process9Size, &sdmmc);
if(!ret) *posSdmmc = sdmmc;
//Add EmuNAND hooks
u32 branchOffset = (u32)(freeK9Space - arm9Section + kernel9Address);
ret += patchNandRw(process9Offset, process9Size, branchOffset);
//Set MPU
ret += patchMpu(arm9Section, kernel9Size);
}
return ret;
}

View File

@@ -20,15 +20,15 @@
* Notices displayed by works containing it.
*/
/*
* Code for locating the SDMMC struct by Normmatt
*/
#pragma once
#include "types.h"
#define NCSD_MAGIC 0x4453434E
#define ROUND_TO_4MB(a) (((a) + 0x2000 - 1) & (~(0x2000 - 1)))
extern u32 emuOffset;
extern bool isN3DS;
void locateEmuNand(u32 *emuHeader, FirmwareSource *nandType);
void patchEmuNand(u8 *arm9Section, u32 arm9SectionSize, u8 *process9Offset, u32 process9Size, u32 emuHeader, u32 branchAdditive);
u32 patchEmuNand(u8 *arm9Section, u32 kernel9Size, u8 *process9Offset, u32 process9Size, u32 emuHeader, u8 *kernel9Address);

View File

@@ -45,37 +45,49 @@ void installArm9Handlers(void)
}
}
void installArm11Handlers(u32 *exceptionsPage, u32 stackAddress, u32 codeSetOffset)
u32 installArm11Handlers(u32 *exceptionsPage, u32 stackAddress, u32 codeSetOffset)
{
u32 *initFPU;
for(initFPU = exceptionsPage; initFPU < (exceptionsPage + 0x400) && (initFPU[0] != 0xE59F0008 || initFPU[1] != 0xE5900000); initFPU++);
u32 ret;
u32 *endPos = exceptionsPage + 0x400;
u32 *mcuReboot;
for(mcuReboot = exceptionsPage; mcuReboot < (exceptionsPage + 0x400) && (mcuReboot[0] != 0xE59F4104 || mcuReboot[1] != 0xE3A0A0C2); mcuReboot++);
mcuReboot--;
u32 *initFPU;
for(initFPU = exceptionsPage; *initFPU != 0xE1A0D002 && initFPU < endPos; initFPU++);
u32 *freeSpace;
for(freeSpace = initFPU; freeSpace < (exceptionsPage + 0x400) && (freeSpace[0] != 0xFFFFFFFF || freeSpace[1] != 0xFFFFFFFF); freeSpace++);
for(freeSpace = initFPU; *freeSpace != 0xFFFFFFFF && freeSpace < endPos; freeSpace++);
memcpy(freeSpace, arm11_exceptions_bin + 32, arm11_exceptions_bin_size - 32);
u32 *mcuReboot;
for(mcuReboot = exceptionsPage; *mcuReboot != 0xE3A0A0C2 && mcuReboot < endPos; mcuReboot++);
exceptionsPage[1] = MAKE_BRANCH(exceptionsPage + 1, (u8 *)freeSpace + *(u32 *)(arm11_exceptions_bin + 8) - 32); //Undefined Instruction
exceptionsPage[3] = MAKE_BRANCH(exceptionsPage + 3, (u8 *)freeSpace + *(u32 *)(arm11_exceptions_bin + 12) - 32); //Prefetch Abort
exceptionsPage[4] = MAKE_BRANCH(exceptionsPage + 4, (u8 *)freeSpace + *(u32 *)(arm11_exceptions_bin + 16) - 32); //Data Abort
exceptionsPage[7] = MAKE_BRANCH(exceptionsPage + 7, (u8 *)freeSpace + *(u32 *)(arm11_exceptions_bin + 4) - 32); //FIQ
for(u32 *pos = freeSpace; pos < (u32 *)((u8 *)freeSpace + arm11_exceptions_bin_size - 32); pos++)
if(initFPU == endPos || freeSpace == endPos || mcuReboot == endPos || *(u32 *)((u8 *)freeSpace + arm11_exceptions_bin_size - 36) != 0xFFFFFFFF) ret = 1;
else
{
switch(*pos) //Perform relocations
initFPU += 3;
mcuReboot -= 2;
memcpy(freeSpace, arm11_exceptions_bin + 32, arm11_exceptions_bin_size - 32);
exceptionsPage[1] = MAKE_BRANCH(exceptionsPage + 1, (u8 *)freeSpace + *(u32 *)(arm11_exceptions_bin + 8) - 32); //Undefined Instruction
exceptionsPage[3] = MAKE_BRANCH(exceptionsPage + 3, (u8 *)freeSpace + *(u32 *)(arm11_exceptions_bin + 12) - 32); //Prefetch Abort
exceptionsPage[4] = MAKE_BRANCH(exceptionsPage + 4, (u8 *)freeSpace + *(u32 *)(arm11_exceptions_bin + 16) - 32); //Data Abort
exceptionsPage[7] = MAKE_BRANCH(exceptionsPage + 7, (u8 *)freeSpace + *(u32 *)(arm11_exceptions_bin + 4) - 32); //FIQ
for(u32 *pos = freeSpace; pos < (u32 *)((u8 *)freeSpace + arm11_exceptions_bin_size - 32); pos++)
{
case 0xFFFF3000: *pos = stackAddress; break;
case 0xEBFFFFFE: *pos = MAKE_BRANCH_LINK(pos, initFPU); break;
case 0xEAFFFFFE: *pos = MAKE_BRANCH(pos, mcuReboot); break;
case 0xE12FFF1C: pos[1] = 0xFFFF0000 + 4 * (u32)(freeSpace - exceptionsPage) + pos[1] - 32; break; //bx r12 (mainHandler)
case 0xBEEFBEEF: *pos = codeSetOffset; break;
default: break;
switch(*pos) //Perform relocations
{
case 0xFFFF3000: *pos = stackAddress; break;
case 0xEBFFFFFE: *pos = MAKE_BRANCH_LINK(pos, initFPU); break;
case 0xEAFFFFFE: *pos = MAKE_BRANCH(pos, mcuReboot); break;
case 0xE12FFF1C: pos[1] = 0xFFFF0000 + 4 * (u32)(freeSpace - exceptionsPage) + pos[1] - 32; break; //bx r12 (mainHandler)
case 0xBEEFBEEF: *pos = codeSetOffset; break;
}
}
ret = 0;
}
return ret;
}
void detectAndProcessExceptionDumps(void)
@@ -139,13 +151,13 @@ void detectAndProcessExceptionDumps(void)
for(u32 i = 0; i < 17; i += 2)
{
posY = drawString(registerNames[i], true, 10, posY + SPACING_Y, COLOR_WHITE);
hexItoa(regs[i], hexString, 8);
hexItoa(regs[i], hexString, 8, true);
drawString(hexString, true, 10 + 7 * SPACING_X, posY, COLOR_WHITE);
if(i != 16 || dumpHeader->processor != 9)
{
drawString(registerNames[i + 1], true, 10 + 22 * SPACING_X, posY, COLOR_WHITE);
hexItoa(i == 16 ? regs[20] : regs[i + 1], hexString, 8);
hexItoa(i == 16 ? regs[20] : regs[i + 1], hexString, 8, true);
drawString(hexString, true, 10 + 29 * SPACING_X, posY, COLOR_WHITE);
}
}
@@ -160,21 +172,21 @@ void detectAndProcessExceptionDumps(void)
for(u32 line = 0; line < 19 && stackDump < additionalData; line++)
{
hexItoa(regs[13] + 8 * line, hexString, 8);
hexItoa(regs[13] + 8 * line, hexString, 8, true);
posYBottom = drawString(hexString, false, 10, posYBottom + SPACING_Y, COLOR_WHITE);
drawCharacter(':', false, 10 + 8 * SPACING_X, posYBottom, COLOR_WHITE);
for(u32 i = 0; i < 8 && stackDump < additionalData; i++, stackDump++)
{
char byteString[] = "00";
hexItoa(*stackDump, byteString, 2);
hexItoa(*stackDump, byteString, 2, false);
drawString(byteString, false, 10 + 10 * SPACING_X + 3 * i * SPACING_X, posYBottom, COLOR_WHITE);
}
}
char path[42];
char path[36];
char fileName[] = "crash_dump_00000000.dmp";
const char *pathFolder = dumpHeader->processor == 9 ? "/luma/dumps/arm9" : "/luma/dumps/arm11";
const char *pathFolder = dumpHeader->processor == 9 ? "dumps/arm9" : "dumps/arm11";
findDumpFile(pathFolder, fileName);
memcpy(path, pathFolder, strlen(pathFolder) + 1);

View File

@@ -27,21 +27,6 @@
#define MAKE_BRANCH(src,dst) (0xEA000000 | ((u32)((((u8 *)(dst) - (u8 *)(src)) >> 2) - 2) & 0xFFFFFF))
#define MAKE_BRANCH_LINK(src,dst) (0xEB000000 | ((u32)((((u8 *)(dst) - (u8 *)(src)) >> 2) - 2) & 0xFFFFFF))
typedef struct __attribute__((packed))
{
u32 magic[2];
u16 versionMinor, versionMajor;
u16 processor, core;
u32 type;
u32 totalSize;
u32 registerDumpSize;
u32 codeDumpSize;
u32 stackDumpSize;
u32 additionalDataSize;
} ExceptionDumpHeader;
void installArm9Handlers(void);
void installArm11Handlers(u32 *exceptionsPage, u32 stackAddress, u32 codeSetOffset);
u32 installArm11Handlers(u32 *exceptionsPage, u32 stackAddress, u32 codeSetOffset);
void detectAndProcessExceptionDumps(void);

View File

@@ -37,17 +37,23 @@ DSTATUS disk_initialize (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
switch(pdrv)
{
case SDCARD:
sdmmc_sdcard_init();
break;
case CTRNAND:
ctrNandInit();
break;
}
DSTATUS ret;
static u32 sdmmcInitResult = 4;
return RES_OK;
if(sdmmcInitResult == 4) sdmmcInitResult = sdmmc_sdcard_init();
if(pdrv == CTRNAND)
{
if(!(sdmmcInitResult & 1))
{
ctrNandInit();
ret = 0;
}
else ret = STA_NOINIT;
}
else ret = (!(sdmmcInitResult & 2)) ? 0 : STA_NOINIT;
return ret;
}
@@ -63,19 +69,8 @@ DRESULT disk_read (
UINT count /* Number of sectors to read */
)
{
switch(pdrv)
{
case SDCARD:
if(sdmmc_sdcard_readsectors(sector, count, (BYTE *)buff))
return RES_PARERR;
break;
case CTRNAND:
if(ctrNandRead(sector, count, (BYTE *)buff))
return RES_PARERR;
break;
}
return RES_OK;
return ((pdrv == SDCARD && !sdmmc_sdcard_readsectors(sector, count, buff)) ||
(pdrv == CTRNAND && !ctrNandRead(sector, count, buff))) ? RES_OK : RES_PARERR;
}
@@ -92,10 +87,8 @@ DRESULT disk_write (
UINT count /* Number of sectors to write */
)
{
if(pdrv == SDCARD && sdmmc_sdcard_writesectors(sector, count, (BYTE *)buff))
return RES_PARERR;
return RES_OK;
return ((pdrv == SDCARD && !sdmmc_sdcard_writesectors(sector, count, buff)) ||
(pdrv == CTRNAND && !ctrNandWrite(sector, count, buff))) ? RES_OK : RES_PARERR;
}
#endif

View File

@@ -134,7 +134,7 @@
/ This option has no effect when _LFN_UNICODE == 0. */
#define _FS_RPATH 0
#define _FS_RPATH 1
/* This option configures support of relative path.
/
/ 0: Disable relative path and remove related functions.

View File

@@ -244,7 +244,6 @@ int __attribute__((noinline)) sdmmc_nand_readsectors(u32 sector_no, u32 numsecto
return geterror(&handleNAND);
}
/*
int __attribute__((noinline)) sdmmc_nand_writesectors(u32 sector_no, u32 numsectors, const u8 *in) //experimental
{
if(handleNAND.isSDHC == 0) sector_no <<= 9;
@@ -259,7 +258,6 @@ int __attribute__((noinline)) sdmmc_nand_writesectors(u32 sector_no, u32 numsect
inittarget(&handleSD);
return geterror(&handleNAND);
}
*/
static u32 calcSDSize(u8 *csd, int type)
{
@@ -268,17 +266,17 @@ static u32 calcSDSize(u8 *csd, int type)
switch(type)
{
case 0:
{
u32 block_len = csd[9] & 0xF;
block_len = 1u << block_len;
u32 mult = (u32)((csd[4] >> 7) | ((csd[5] & 3) << 1));
mult = 1u << (mult + 2);
result = csd[8] & 3;
result = (result << 8) | csd[7];
result = (result << 2) | (csd[6] >> 6);
result = (result + 1) * mult * block_len / 512;
}
{
u32 block_len = csd[9] & 0xF;
block_len = 1u << block_len;
u32 mult = (u32)((csd[4] >> 7) | ((csd[5] & 3) << 1));
mult = 1u << (mult + 2);
result = csd[8] & 3;
result = (result << 8) | csd[7];
result = (result << 2) | (csd[6] >> 6);
result = (result + 1) * mult * block_len / 512;
break;
}
case 1:
result = csd[7] & 0x3F;
result = (result << 8) | csd[6];
@@ -472,9 +470,11 @@ void sdmmc_get_cid(bool isNand, u32 *info)
sdmmc_send_command(device, 0x10507, device->initarg << 0x10);
}
void sdmmc_sdcard_init()
u32 sdmmc_sdcard_init()
{
u32 ret = 0;
InitSD();
Nand_Init();
SD_Init();
if(Nand_Init() != 0) ret &= 1;
if(SD_Init() != 0) ret &= 2;
return ret;
}

View File

@@ -91,10 +91,10 @@ typedef struct mmcdevice {
u32 res;
} mmcdevice;
void sdmmc_sdcard_init();
u32 sdmmc_sdcard_init();
int sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, u8 *out);
int sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, const u8 *in);
int sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, u8 *out);
//int sdmmc_nand_writesectors(u32 sector_no, u32 numsectors, const u8 *in);
int sdmmc_nand_writesectors(u32 sector_no, u32 numsectors, const u8 *in);
void sdmmc_get_cid(bool isNand, u32 *info);
mmcdevice *getMMCDevice(int drive);

View File

@@ -31,299 +31,70 @@
#include "cache.h"
#include "emunand.h"
#include "crypto.h"
#include "draw.h"
#include "screen.h"
#include "buttons.h"
#include "pin.h"
#include "../build/bundled.h"
extern u16 launchedFirmTidLow[8]; //Defined in start.s
static Firm *firm = (Firm *)0x24000000;
static firmHeader *firm = (firmHeader *)0x24000000;
static const firmSectionHeader *section;
u32 emuOffset;
bool isN3DS,
isDevUnit,
isA9lh,
isFirmlaunch;
CfgData configData;
FirmwareSource firmSource;
void main(void)
u32 loadFirm(FirmwareType *firmType, FirmwareSource nandType, bool loadFromStorage, bool isSafeMode)
{
u32 configTemp,
emuHeader;
FirmwareType firmType;
FirmwareSource nandType;
ConfigurationStatus needConfig;
//Detect the console being used
isN3DS = PDN_MPCORE_CFG == 7;
//Detect dev units
isDevUnit = CFG_UNITINFO != 0;
//Mount filesystems. CTRNAND will be mounted only if/when needed
mountFs();
//Attempt to read the configuration file
needConfig = readConfig() ? MODIFY_CONFIGURATION : CREATE_CONFIGURATION;
u32 devMode = MULTICONFIG(DEVOPTIONS);
//Determine if this is a firmlaunch boot
if(launchedFirmTidLow[5] != 0)
{
isFirmlaunch = true;
if(needConfig == CREATE_CONFIGURATION) mcuReboot();
//'0' = NATIVE_FIRM, '1' = TWL_FIRM, '2' = AGB_FIRM
firmType = launchedFirmTidLow[7] == u'3' ? SAFE_FIRM : (FirmwareType)(launchedFirmTidLow[5] - u'0');
nandType = (FirmwareSource)BOOTCFG_NAND;
firmSource = (FirmwareSource)BOOTCFG_FIRM;
isA9lh = BOOTCFG_A9LH != 0;
}
else
{
isFirmlaunch = false;
firmType = NATIVE_FIRM;
//Determine if booting with A9LH
isA9lh = !PDN_SPI_CNT;
if(devMode != 0 && isA9lh) detectAndProcessExceptionDumps();
//Get pressed buttons
u32 pressed = HID_PAD;
//Save old options and begin saving the new boot configuration
configTemp = (configData.config & 0xFFFFFE00) | ((u32)isA9lh << 6);
//If it's a MCU reboot, try to force boot options
if(isA9lh && CFG_BOOTENV)
{
//Always force a sysNAND boot when quitting AGB_FIRM
if(CFG_BOOTENV == 7)
{
nandType = FIRMWARE_SYSNAND;
firmSource = CONFIG(USESYSFIRM) ? FIRMWARE_SYSNAND : (FirmwareSource)BOOTCFG_FIRM;
needConfig = DONT_CONFIGURE;
//Flag to prevent multiple boot options-forcing
configTemp |= 1 << 7;
}
/* Else, force the last used boot options unless a button is pressed
or the no-forcing flag is set */
else if(needConfig != CREATE_CONFIGURATION && !pressed && !BOOTCFG_NOFORCEFLAG)
{
nandType = (FirmwareSource)BOOTCFG_NAND;
firmSource = (FirmwareSource)BOOTCFG_FIRM;
needConfig = DONT_CONFIGURE;
}
}
if(needConfig == DONT_CONFIGURE)
{
if(devMode != 0 && isA9lh) installArm9Handlers();
}
//Boot options aren't being forced
else
{
u32 pinMode = MULTICONFIG(PIN);
bool pinExists = pinMode != 0 && verifyPin(pinMode);
//If no configuration file exists or SELECT is held, load configuration menu
bool shouldLoadConfigMenu = needConfig == CREATE_CONFIGURATION || ((pressed & BUTTON_SELECT) && !(pressed & BUTTON_L1));
if(shouldLoadConfigMenu)
{
configMenu(pinExists, pinMode);
//Update pressed buttons
pressed = HID_PAD;
devMode = MULTICONFIG(DEVOPTIONS);
}
if(devMode != 0 && isA9lh) installArm9Handlers();
if(isA9lh && !CFG_BOOTENV && pressed == SAFE_MODE)
{
nandType = FIRMWARE_SYSNAND;
firmSource = FIRMWARE_SYSNAND;
//Flag to tell loader to init SD
configTemp |= 1 << 8;
//If the PIN has been verified, wait to make it easier to press the SAFE_MODE combo
if(pinExists && !shouldLoadConfigMenu)
{
while(HID_PAD & PIN_BUTTONS);
chrono(2);
}
}
else
{
u32 splashMode = MULTICONFIG(SPLASH);
if(splashMode == 1 && loadSplash()) pressed = HID_PAD;
/* If L and R/A/Select or one of the single payload buttons are pressed,
chainload an external payload */
bool shouldLoadPayload = ((pressed & SINGLE_PAYLOAD_BUTTONS) && !(pressed & (BUTTON_L1 | BUTTON_R1 | BUTTON_A))) ||
((pressed & L_PAYLOAD_BUTTONS) && (pressed & BUTTON_L1));
if(shouldLoadPayload) loadPayload(pressed);
if(splashMode == 2) loadSplash();
//Determine if the user chose to use the SysNAND FIRM as default for a R boot
bool useSysAsDefault = isA9lh ? CONFIG(USESYSFIRM) : false;
//If R is pressed, boot the non-updated NAND with the FIRM of the opposite one
if(pressed & BUTTON_R1)
{
nandType = useSysAsDefault ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND;
firmSource = useSysAsDefault ? FIRMWARE_SYSNAND : FIRMWARE_EMUNAND;
}
/* Else, boot the NAND the user set to autoboot or the opposite one, depending on L,
with their own FIRM */
else
{
nandType = (CONFIG(AUTOBOOTSYS) != !(pressed & BUTTON_L1)) ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND;
firmSource = nandType;
}
//If we're booting EmuNAND or using EmuNAND FIRM, determine which one from the directional pad buttons, or otherwise from the config
if(nandType == FIRMWARE_EMUNAND || firmSource == FIRMWARE_EMUNAND)
{
FirmwareSource temp;
switch(pressed & EMUNAND_BUTTONS)
{
case BUTTON_UP:
temp = FIRMWARE_EMUNAND;
break;
case BUTTON_RIGHT:
temp = FIRMWARE_EMUNAND2;
break;
case BUTTON_DOWN:
temp = FIRMWARE_EMUNAND3;
break;
case BUTTON_LEFT:
temp = FIRMWARE_EMUNAND4;
break;
default:
temp = (FirmwareSource)(1 + MULTICONFIG(DEFAULTEMU));
break;
}
if(nandType == FIRMWARE_EMUNAND) nandType = temp;
else firmSource = temp;
}
}
}
}
//If we need to boot EmuNAND, make sure it exists
if(nandType != FIRMWARE_SYSNAND)
{
locateEmuNand(&emuHeader, &nandType);
if(nandType == FIRMWARE_SYSNAND) firmSource = FIRMWARE_SYSNAND;
}
//Same if we're using EmuNAND as the FIRM source
else if(firmSource != FIRMWARE_SYSNAND)
locateEmuNand(&emuHeader, &firmSource);
if(!isFirmlaunch)
{
configTemp |= (u32)nandType | ((u32)firmSource << 3);
writeConfig(needConfig, configTemp);
}
bool loadFromSd = CONFIG(LOADSDFIRMSANDMODULES);
u32 firmVersion = loadFirm(&firmType, firmSource, loadFromSd);
switch(firmType)
{
case NATIVE_FIRM:
patchNativeFirm(firmVersion, nandType, emuHeader, devMode);
break;
case SAFE_FIRM:
case NATIVE_FIRM1X2X:
if(isA9lh) patch1x2xNativeAndSafeFirm(devMode);
break;
default:
patchLegacyFirm(firmType, firmVersion, devMode);
break;
}
launchFirm(firmType, loadFromSd);
}
static inline u32 loadFirm(FirmwareType *firmType, FirmwareSource firmSource, bool loadFromSd)
{
section = firm->section;
const char *firmwareFiles[] = {
"/luma/firmware.bin",
"/luma/firmware_twl.bin",
"/luma/firmware_agb.bin",
"/luma/firmware_safe.bin"
"firmware.bin",
"firmware_twl.bin",
"firmware_agb.bin",
"firmware_safe.bin"
},
*cetkFiles[] = {
"/luma/cetk",
"/luma/cetk_twl",
"/luma/cetk_agb",
"/luma/cetk_safe"
"cetk",
"cetk_twl",
"cetk_agb",
"cetk_safe"
};
//Load FIRM from CTRNAND
u32 firmVersion = firmRead(firm, (u32)*firmType);
bool mustLoadFromSd = false;
if(firmVersion == 0xFFFFFFFF) error("Failed to get the CTRNAND FIRM.");
if(!isN3DS && *firmType == NATIVE_FIRM)
bool mustLoadFromStorage = false;
if(!ISN3DS && *firmType == NATIVE_FIRM)
{
if(firmVersion < 0x18)
{
//We can't boot < 3.x EmuNANDs
if(firmSource != FIRMWARE_SYSNAND)
if(nandType != FIRMWARE_SYSNAND)
error("An old unsupported EmuNAND has been detected.\nLuma3DS is unable to boot it.");
if(BOOTCFG_SAFEMODE != 0) error("SAFE_MODE is not supported on 1.x/2.x FIRM.");
if(isSafeMode) error("SAFE_MODE is not supported on 1.x/2.x FIRM.");
*firmType = NATIVE_FIRM1X2X;
}
//We can't boot a 3.x/4.x NATIVE_FIRM, load one from SD
else if(firmVersion < 0x25) mustLoadFromSd = true;
//We can't boot a 3.x/4.x NATIVE_FIRM, load one from SD/CTRNAND
else if(firmVersion < 0x25) mustLoadFromStorage = true;
}
if(loadFromSd || mustLoadFromSd)
if(loadFromStorage || mustLoadFromStorage)
{
u32 firmSize = fileRead(firm, *firmType == NATIVE_FIRM1X2X ? firmwareFiles[0] : firmwareFiles[(u32)*firmType], 0x400000);
u32 firmSize = fileRead(firm, *firmType == NATIVE_FIRM1X2X ? firmwareFiles[0] : firmwareFiles[(u32)*firmType], 0x400000 + sizeof(Cxi) + 0x200);
if(firmSize > 0)
{
if(firmSize <= sizeof(Cxi) + 0x200) error("The FIRM in /luma is not valid.");
if(memcmp(firm, "FIRM", 4) != 0)
{
u8 cetk[0xA50];
if(fileRead(cetk, *firmType == NATIVE_FIRM1X2X ? cetkFiles[0] : cetkFiles[(u32)*firmType], sizeof(cetk)) == sizeof(cetk))
decryptNusFirm(cetk, (u8 *)firm, firmSize);
else error("The firmware.bin in /luma is encrypted\nor corrupted.");
if(fileRead(cetk, *firmType == NATIVE_FIRM1X2X ? cetkFiles[0] : cetkFiles[(u32)*firmType], sizeof(cetk)) != sizeof(cetk) ||
!decryptNusFirm((Ticket *)(cetk + 0x140), (Cxi *)firm, firmSize))
error("The FIRM in /luma is encrypted or corrupted.");
}
//Check that the SD FIRM is right for the console from the ARM9 section address
if((section[3].offset ? section[3].address : section[2].address) != (isN3DS ? (u8 *)0x8006000 : (u8 *)0x8006800))
error("The firmware.bin in /luma is not valid for this\nconsole.");
//Check that the FIRM is right for the console from the ARM9 section address
if((firm->section[3].offset != 0 ? firm->section[3].address : firm->section[2].address) != (ISN3DS ? (u8 *)0x8006000 : (u8 *)0x8006800))
error("The FIRM in /luma is not for this console.");
firmVersion = 0xFFFFFFFF;
}
@@ -331,162 +102,229 @@ static inline u32 loadFirm(FirmwareType *firmType, FirmwareSource firmSource, bo
if(firmVersion != 0xFFFFFFFF)
{
if(mustLoadFromSd) error("An old unsupported FIRM has been detected.\nCopy a firmware.bin in /luma to boot.");
decryptExeFs((u8 *)firm);
if(mustLoadFromStorage) error("An old unsupported FIRM has been detected.\nCopy a firmware.bin in /luma to boot.");
if(!decryptExeFs((Cxi *)firm)) error("The CTRNAND FIRM is corrupted.");
}
return firmVersion;
}
static inline void patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 emuHeader, u32 devMode)
u32 patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 emuHeader, bool isA9lhInstalled, bool isSafeMode, u32 devMode)
{
u8 *arm9Section = (u8 *)firm + section[2].offset,
*arm11Section1 = (u8 *)firm + section[1].offset;
u8 *arm9Section = (u8 *)firm + firm->section[2].offset,
*arm11Section1 = (u8 *)firm + firm->section[1].offset;
if(isN3DS)
if(ISN3DS)
{
//Decrypt ARM9Bin and patch ARM9 entrypoint to skip kernel9loader
kernel9Loader(arm9Section);
kernel9Loader((Arm9Bin *)arm9Section);
firm->arm9Entry = (u8 *)0x801B01C;
}
//Sets the 7.x NCCH KeyX and the 6.x gamecard save data KeyY on >= 6.0 O3DS FIRMs, if not using A9LH or a dev unit
else if(!isA9lh && firmVersion >= 0x29 && !isDevUnit) set6x7xKeys();
else if(!ISA9LH && !ISFIRMLAUNCH && firmVersion >= 0x29 && !ISDEVUNIT) set6x7xKeys();
//Find the Process9 .code location, size and memory address
u32 process9Size,
process9MemAddr;
u8 *process9Offset = getProcess9(arm9Section + 0x15000, section[2].size - 0x15000, &process9Size, &process9MemAddr);
u8 *process9Offset = getProcess9Info(arm9Section, firm->section[2].size, &process9Size, &process9MemAddr);
//Find Kernel11 SVC table and handler, exceptions page and free space locations
//Find the Kernel11 SVC table and handler, exceptions page and free space locations
u32 baseK11VA;
u8 *freeK11Space;
u32 *arm11SvcHandler,
*arm11ExceptionsPage,
*arm11SvcTable = getKernel11Info(arm11Section1, section[1].size, &baseK11VA, &freeK11Space, &arm11SvcHandler, &arm11ExceptionsPage);
*arm11SvcTable = getKernel11Info(arm11Section1, firm->section[1].size, &baseK11VA, &freeK11Space, &arm11SvcHandler, &arm11ExceptionsPage);
u32 kernel9Size = (u32)(process9Offset - arm9Section) - sizeof(Cxi) - 0x200,
ret = 0;
//Apply signature patches
patchSignatureChecks(process9Offset, process9Size);
ret += patchSignatureChecks(process9Offset, process9Size);
//Apply EmuNAND patches
if(nandType != FIRMWARE_SYSNAND)
{
u32 branchAdditive = (u32)firm + section[2].offset - (u32)section[2].address;
patchEmuNand(arm9Section, section[2].size, process9Offset, process9Size, emuHeader, branchAdditive);
}
if(nandType != FIRMWARE_SYSNAND) ret += patchEmuNand(arm9Section, kernel9Size, process9Offset, process9Size, emuHeader, firm->section[2].address);
//Apply FIRM0/1 writes patches on sysNAND to protect A9LH
else if(isA9lh) patchFirmWrites(process9Offset, process9Size);
else if(isA9lhInstalled) ret += patchFirmWrites(process9Offset, process9Size);
//Apply firmlaunch patches
patchFirmlaunches(process9Offset, process9Size, process9MemAddr);
ret += patchFirmlaunches(process9Offset, process9Size, process9MemAddr);
//11.0 FIRM patches
if(firmVersion >= (isN3DS ? 0x21 : 0x52))
if(firmVersion >= (ISN3DS ? 0x21 : 0x52))
{
//Apply anti-anti-DG patches
patchTitleInstallMinVersionCheck(process9Offset, process9Size);
ret += patchTitleInstallMinVersionChecks(process9Offset, process9Size, firmVersion);
//Restore svcBackdoor
reimplementSvcBackdoor(arm11Section1, arm11SvcTable, baseK11VA, &freeK11Space);
ret += reimplementSvcBackdoor(arm11Section1, arm11SvcTable, baseK11VA, &freeK11Space);
}
implementSvcGetCFWInfo(arm11Section1, arm11SvcTable, baseK11VA, &freeK11Space);
ret += implementSvcGetCFWInfo(arm11Section1, arm11SvcTable, baseK11VA, &freeK11Space, isSafeMode);
//Apply UNITINFO patch
if(devMode == 2) patchUnitInfoValueSet(arm9Section, section[2].size);
if(devMode == 2) ret += patchUnitInfoValueSet(arm9Section, kernel9Size);
if(devMode != 0 && isA9lh)
if(devMode != 0 && isA9lhInstalled)
{
//ARM11 exception handlers
u32 codeSetOffset,
stackAddress = getInfoForArm11ExceptionHandlers(arm11Section1, section[1].size, &codeSetOffset);
installArm11Handlers(arm11ExceptionsPage, stackAddress, codeSetOffset);
stackAddress = getInfoForArm11ExceptionHandlers(arm11Section1, firm->section[1].size, &codeSetOffset);
ret += installArm11Handlers(arm11ExceptionsPage, stackAddress, codeSetOffset);
patchSvcBreak11(arm11Section1, arm11SvcTable);
patchKernel11Panic(arm11Section1, section[1].size);
ret += patchKernel11Panic(arm11Section1, firm->section[1].size);
//ARM9 exception handlers
patchArm9ExceptionHandlersInstall(arm9Section, section[2].size);
patchSvcBreak9(arm9Section, section[2].size, (u32)section[2].address);
patchKernel9Panic(arm9Section, section[2].size);
ret += patchArm9ExceptionHandlersInstall(arm9Section, kernel9Size);
ret += patchSvcBreak9(arm9Section, kernel9Size, (u32)firm->section[2].address);
ret += patchKernel9Panic(arm9Section, kernel9Size);
}
if(CONFIG(PATCHACCESS))
{
patchArm11SvcAccessChecks(arm11SvcHandler);
patchK11ModuleChecks(arm11Section1, section[1].size, &freeK11Space);
patchP9AccessChecks(process9Offset, process9Size);
ret += patchArm11SvcAccessChecks(arm11SvcHandler, (u32 *)(arm11Section1 + firm->section[1].size));
ret += patchK11ModuleChecks(arm11Section1, firm->section[1].size, &freeK11Space);
ret += patchP9AccessChecks(process9Offset, process9Size);
}
return ret;
}
static inline void patchLegacyFirm(FirmwareType firmType, u32 firmVersion, u32 devMode)
u32 patchTwlFirm(u32 firmVersion, u32 devMode)
{
u8 *arm9Section = (u8 *)firm + section[3].offset;
u8 *arm9Section = (u8 *)firm + firm->section[3].offset;
//On N3DS, decrypt ARM9Bin and patch ARM9 entrypoint to skip kernel9loader
if(isN3DS)
if(ISN3DS)
{
kernel9Loader(arm9Section);
kernel9Loader((Arm9Bin *)arm9Section);
firm->arm9Entry = (u8 *)0x801301C;
}
if(isN3DS || firmVersion >= (firmType == TWL_FIRM ? 0x16 : 0xB))
applyLegacyFirmPatches((u8 *)firm, firmType);
//Find the Process9 .code location, size and memory address
u32 process9Size,
process9MemAddr;
u8 *process9Offset = getProcess9Info(arm9Section, firm->section[3].size, &process9Size, &process9MemAddr);
u32 kernel9Size = (u32)(process9Offset - arm9Section) - sizeof(Cxi) - 0x200,
ret = 0;
ret += patchLgySignatureChecks(process9Offset, process9Size);
ret += patchTwlInvalidSignatureChecks(process9Offset, process9Size);
ret += patchTwlNintendoLogoChecks(process9Offset, process9Size);
ret += patchTwlWhitelistChecks(process9Offset, process9Size);
if(ISN3DS || firmVersion > 0x11) ret += patchTwlFlashcartChecks(process9Offset, process9Size, firmVersion);
else if(!ISN3DS && firmVersion == 0x11) ret += patchOldTwlFlashcartChecks(process9Offset, process9Size);
ret += patchTwlShaHashChecks(process9Offset, process9Size);
//Apply UNITINFO patch
if(devMode == 2) patchUnitInfoValueSet(arm9Section, section[3].size);
if(devMode == 2) ret += patchUnitInfoValueSet(arm9Section, kernel9Size);
return ret;
}
static inline void patch1x2xNativeAndSafeFirm(u32 devMode)
u32 patchAgbFirm(u32 devMode)
{
u8 *arm9Section = (u8 *)firm + section[2].offset;
u8 *arm9Section = (u8 *)firm + firm->section[3].offset;
//On N3DS, decrypt ARM9Bin and patch ARM9 entrypoint to skip kernel9loader
if(ISN3DS)
{
kernel9Loader((Arm9Bin *)arm9Section);
firm->arm9Entry = (u8 *)0x801301C;
}
if(isN3DS)
//Find the Process9 .code location, size and memory address
u32 process9Size,
process9MemAddr;
u8 *process9Offset = getProcess9Info(arm9Section, firm->section[3].size, &process9Size, &process9MemAddr);
u32 kernel9Size = (u32)(process9Offset - arm9Section) - sizeof(Cxi) - 0x200,
ret = 0;
ret += patchLgySignatureChecks(process9Offset, process9Size);
if(CONFIG(SHOWGBABOOT)) ret += patchAgbBootSplash(process9Offset, process9Size);
//Apply UNITINFO patch
if(devMode == 2) ret += patchUnitInfoValueSet(arm9Section, kernel9Size);
return ret;
}
u32 patch1x2xNativeAndSafeFirm(u32 devMode)
{
u8 *arm9Section = (u8 *)firm + firm->section[2].offset;
if(ISN3DS)
{
//Decrypt ARM9Bin and patch ARM9 entrypoint to skip kernel9loader
kernel9Loader(arm9Section);
kernel9Loader((Arm9Bin *)arm9Section);
firm->arm9Entry = (u8 *)0x801B01C;
patchFirmWrites(arm9Section, section[2].size);
}
else patchOldFirmWrites(arm9Section, section[2].size);
//Find the Process9 .code location, size and memory address
u32 process9Size,
process9MemAddr;
u8 *process9Offset = getProcess9Info(arm9Section, firm->section[2].size, &process9Size, &process9MemAddr);
u32 kernel9Size = (u32)(process9Offset - arm9Section) - sizeof(Cxi) - 0x200,
ret = 0;
ret += ISN3DS ? patchFirmWrites(process9Offset, process9Size) : patchOldFirmWrites(process9Offset, process9Size);
if(devMode != 0)
{
//ARM9 exception handlers
patchArm9ExceptionHandlersInstall(arm9Section, section[2].size);
patchSvcBreak9(arm9Section, section[2].size, (u32)section[2].address);
ret += patchArm9ExceptionHandlersInstall(arm9Section, kernel9Size);
ret += patchSvcBreak9(arm9Section, kernel9Size, (u32)firm->section[2].address);
}
return ret;
}
static inline void copySection0AndInjectSystemModules(FirmwareType firmType, bool loadFromSd)
static inline void copySection0AndInjectSystemModules(FirmwareType firmType, bool loadFromStorage)
{
u32 srcModuleSize,
u32 maxModuleSize = firmType == NATIVE_FIRM ? 0x80000 : 0x600000,
srcModuleSize,
dstModuleSize;
const char *extModuleSizeError = "The external FIRM modules are too large.";
for(u8 *src = (u8 *)firm + section[0].offset, *srcEnd = src + section[0].size, *dst = section[0].address;
src < srcEnd; src += srcModuleSize, dst += dstModuleSize)
for(u8 *src = (u8 *)firm + firm->section[0].offset, *srcEnd = src + firm->section[0].size, *dst = firm->section[0].address;
src < srcEnd; src += srcModuleSize, dst += dstModuleSize, maxModuleSize -= dstModuleSize)
{
srcModuleSize = *(u32 *)(src + 0x104) * 0x200;
const char *moduleName = (char *)(src + 0x200);
srcModuleSize = ((Cxi *)src)->ncch.contentSize * 0x200;
const char *moduleName = ((Cxi *)src)->exHeader.systemControlInfo.appTitle;
u32 fileSize;
bool loadedModule;
if(loadFromSd)
if(!loadFromStorage) loadedModule = false;
else
{
char fileName[30] = "/luma/sysmodules/";
const char *ext = ".cxi";
char fileName[24] = "sysmodules/";
//Read modules from files if they exist
concatenateStrings(fileName, moduleName);
concatenateStrings(fileName, ext);
concatenateStrings(fileName, ".cxi");
fileSize = fileRead(dst, fileName, 2 * srcModuleSize);
dstModuleSize = getFileSize(fileName);
if(dstModuleSize == 0) loadedModule = false;
else
{
if(dstModuleSize > maxModuleSize) error(extModuleSizeError);
if(dstModuleSize <= sizeof(Cxi) + 0x200 ||
fileRead(dst, fileName, dstModuleSize) != dstModuleSize ||
memcmp(((Cxi *)dst)->ncch.magic, "NCCH", 4) != 0 ||
memcmp(moduleName, ((Cxi *)dst)->exHeader.systemControlInfo.appTitle, sizeof(((Cxi *)dst)->exHeader.systemControlInfo.appTitle)) != 0)
error("An external FIRM module is invalid or corrupted.");
loadedModule = true;
}
}
else fileSize = 0;
if(fileSize > 0) dstModuleSize = fileSize;
else
if(!loadedModule)
{
const u8 *module;
@@ -501,29 +339,31 @@ static inline void copySection0AndInjectSystemModules(FirmwareType firmType, boo
dstModuleSize = srcModuleSize;
}
if(dstModuleSize > maxModuleSize) error(extModuleSizeError);
memcpy(dst, module, dstModuleSize);
}
}
}
static inline void launchFirm(FirmwareType firmType, bool loadFromSd)
void launchFirm(FirmwareType firmType, bool loadFromStorage)
{
//Allow module injection and/or inject 3ds_injector on new NATIVE_FIRMs and LGY FIRMs
u32 sectionNum;
if(firmType == NATIVE_FIRM || (loadFromSd && firmType != SAFE_FIRM && firmType != NATIVE_FIRM1X2X))
if(firmType == NATIVE_FIRM || (loadFromStorage && firmType != SAFE_FIRM && firmType != NATIVE_FIRM1X2X))
{
copySection0AndInjectSystemModules(firmType, loadFromSd);
copySection0AndInjectSystemModules(firmType, loadFromStorage);
sectionNum = 1;
}
else sectionNum = 0;
//Copy FIRM sections to respective memory locations
for(; sectionNum < 4 && section[sectionNum].size != 0; sectionNum++)
memcpy(section[sectionNum].address, (u8 *)firm + section[sectionNum].offset, section[sectionNum].size);
for(; sectionNum < 4 && firm->section[sectionNum].size != 0; sectionNum++)
memcpy(firm->section[sectionNum].address, (u8 *)firm + firm->section[sectionNum].offset, firm->section[sectionNum].size);
//Determine the ARM11 entry to use
vu32 *arm11;
if(isFirmlaunch) arm11 = (vu32 *)0x1FFFFFFC;
if(ISFIRMLAUNCH) arm11 = (vu32 *)0x1FFFFFFC;
else
{
deinitScreens();

View File

@@ -24,32 +24,9 @@
#include "types.h"
#define CFG_BOOTENV (*(vu32 *)0x10010000)
#define CFG_UNITINFO (*(vu8 *)0x10010010)
#define PDN_MPCORE_CFG (*(vu32 *)0x10140FFC)
#define PDN_SPI_CNT (*(vu32 *)0x101401C0)
//FIRM Header layout
typedef struct firmSectionHeader {
u32 offset;
u8 *address;
u32 size;
u32 procType;
u8 hash[0x20];
} firmSectionHeader;
typedef struct firmHeader {
u32 magic;
u32 reserved1;
u8 *arm11Entry;
u8 *arm9Entry;
u8 reserved2[0x30];
firmSectionHeader section[4];
} firmHeader;
static inline u32 loadFirm(FirmwareType *firmType, FirmwareSource firmSource, bool loadFromSd);
static inline void patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 emuHeader, u32 devMode);
static inline void patchLegacyFirm(FirmwareType firmType, u32 firmVersion, u32 devMode);
static inline void patch1x2xNativeAndSafeFirm(u32 devMode);
static inline void copySection0AndInjectSystemModules(FirmwareType firmType, bool loadFromSd);
static inline void launchFirm(FirmwareType firmType, bool loadFromSd);
u32 loadFirm(FirmwareType *firmType, FirmwareSource nandType, bool loadFromStorage, bool isSafeMode);
u32 patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 emuHeader, bool isA9lhInstalled, bool isSafeMode, u32 devMode);
u32 patchTwlFirm(u32 firmVersion, u32 devMode);
u32 patchAgbFirm(u32 devMode);
u32 patch1x2xNativeAndSafeFirm(u32 devMode);
void launchFirm(FirmwareType firmType, bool loadFromStorage);

View File

@@ -33,22 +33,45 @@
static FATFS sdFs,
nandFs;
void mountFs(void)
static bool switchToMainDir(bool isSd)
{
f_mount(&sdFs, "0:", 1);
f_mount(&nandFs, "1:", 0);
const char *mainDir = isSd ? "/luma" : "/rw/luma";
bool ret;
switch(f_chdir(mainDir))
{
case FR_OK:
ret = true;
break;
case FR_NO_PATH:
f_mkdir(mainDir);
ret = switchToMainDir(isSd);
break;
default:
ret = false;
break;
}
return ret;
}
bool mountFs(bool isSd, bool switchToCtrNand)
{
return isSd ? f_mount(&sdFs, "0:", 1) == FR_OK && switchToMainDir(true) :
f_mount(&nandFs, "1:", 1) == FR_OK && (!switchToCtrNand || (f_chdrive("1:") == FR_OK && switchToMainDir(false)));
}
u32 fileRead(void *dest, const char *path, u32 maxSize)
{
FIL file;
u32 ret = 0;
u32 ret;
if(f_open(&file, path, FA_READ) == FR_OK)
if(f_open(&file, path, FA_READ) != FR_OK) ret = 0;
else
{
u32 size = f_size(&file);
if(dest == NULL) ret = size;
else if(!(maxSize > 0 && size > maxSize))
else if(size <= maxSize)
f_read(&file, dest, size, (unsigned int *)&ret);
f_close(&file);
}
@@ -64,34 +87,38 @@ u32 getFileSize(const char *path)
bool fileWrite(const void *buffer, const char *path, u32 size)
{
FIL file;
bool ret;
FRESULT result = f_open(&file, path, FA_WRITE | FA_OPEN_ALWAYS);
if(result == FR_OK)
switch(f_open(&file, path, FA_WRITE | FA_OPEN_ALWAYS))
{
unsigned int written;
f_write(&file, buffer, size, &written);
f_truncate(&file);
f_close(&file);
case FR_OK:
{
unsigned int written;
f_write(&file, buffer, size, &written);
f_truncate(&file);
f_close(&file);
return true;
ret = (u32)written == size;
break;
}
case FR_NO_PATH:
for(u32 i = 1; path[i] != 0; i++)
if(path[i] == '/')
{
char folder[i + 1];
memcpy(folder, path, i);
folder[i] = 0;
f_mkdir(folder);
}
ret = fileWrite(buffer, path, size);
break;
default:
ret = false;
break;
}
if(result == FR_NO_PATH)
{
for(u32 i = 1; path[i] != 0; i++)
if(path[i] == '/')
{
char folder[i + 1];
memcpy(folder, path, i);
folder[i] = 0;
f_mkdir(folder);
}
return fileWrite(buffer, path, size);
}
return false;
return ret;
}
void fileDelete(const char *path)
@@ -117,35 +144,38 @@ void loadPayload(u32 pressed)
DIR dir;
FILINFO info;
char path[28] = "/luma/payloads";
char path[22] = "payloads";
FRESULT result = f_findfirst(&dir, &info, path, pattern);
f_closedir(&dir);
if(result == FR_OK && info.fname[0] != 0)
if(result == FR_OK)
{
u32 *loaderAddress = (u32 *)0x24FFFF00;
u8 *payloadAddress = (u8 *)0x24F00000;
f_closedir(&dir);
memcpy(loaderAddress, loader_bin, loader_bin_size);
concatenateStrings(path, "/");
concatenateStrings(path, info.altname);
u32 payloadSize = fileRead(payloadAddress, path, (u8 *)loaderAddress - payloadAddress);
if(payloadSize > 0)
if(info.fname[0] != 0)
{
loaderAddress[1] = payloadSize;
u32 *loaderAddress = (u32 *)0x24FFFE00;
u8 *payloadAddress = (u8 *)0x24F00000;
if(isA9lh) restoreShaHashBackup();
initScreens();
memcpy(loaderAddress, loader_bin, loader_bin_size);
flushDCacheRange(loaderAddress, loader_bin_size);
flushICacheRange(loaderAddress, loader_bin_size);
concatenateStrings(path, "/");
concatenateStrings(path, info.altname);
((void (*)())loaderAddress)();
u32 payloadSize = fileRead(payloadAddress, path, (u32)((u8 *)loaderAddress - payloadAddress));
if(payloadSize > 0)
{
loaderAddress[1] = payloadSize;
backupAndRestoreShaHash(true);
initScreens();
flushDCacheRange(loaderAddress, loader_bin_size);
flushICacheRange(loaderAddress, loader_bin_size);
((void (*)())loaderAddress)();
}
}
}
}
@@ -158,68 +188,60 @@ u32 firmRead(void *dest, u32 firmType)
{ "00000003", "20000003" }};
char path[48] = "1:/title/00040138/";
concatenateStrings(path, firmFolders[firmType][isN3DS ? 1 : 0]);
concatenateStrings(path, firmFolders[firmType][ISN3DS ? 1 : 0]);
concatenateStrings(path, "/content");
DIR dir;
FILINFO info;
f_opendir(&dir, path);
u32 firmVersion = 0xFFFFFFFF;
//Parse the target directory
while(f_readdir(&dir, &info) == FR_OK && info.fname[0] != 0)
if(f_opendir(&dir, path) == FR_OK)
{
//Not a cxi
if(info.fname[9] != 'a') continue;
FILINFO info;
//Convert the .app name to an integer
u32 tempVersion = 0;
for(char *tmp = info.altname; *tmp != '.'; tmp++)
//Parse the target directory
while(f_readdir(&dir, &info) == FR_OK && info.fname[0] != 0)
{
tempVersion <<= 4;
tempVersion += *tmp > '9' ? *tmp - 'A' + 10 : *tmp - '0';
//Not a cxi
if(info.fname[9] != 'a' || strlen(info.fname) != 12) continue;
u32 tempVersion = hexAtoi(info.altname, 8);
//Found an older cxi
if(tempVersion < firmVersion) firmVersion = tempVersion;
}
//Found an older cxi
if(tempVersion < firmVersion) firmVersion = tempVersion;
f_closedir(&dir);
if(firmVersion != 0xFFFFFFFF)
{
//Complete the string with the .app name
concatenateStrings(path, "/00000000.app");
//Convert back the .app name from integer to array
hexItoa(firmVersion, path + 35, 8, false);
if(fileRead(dest, path, 0x400000 + sizeof(Cxi) + 0x200) <= sizeof(Cxi) + 0x200) firmVersion = 0xFFFFFFFF;
}
}
f_closedir(&dir);
//Complete the string with the .app name
concatenateStrings(path, "/00000000.app");
//Convert back the .app name from integer to array
hexItoa(firmVersion, &path[35], 8);
fileRead(dest, path, 0);
return firmVersion;
}
void findDumpFile(const char *path, char *fileName)
{
DIR dir;
FILINFO info;
FRESULT result;
u32 n = 0;
while(true)
while(n <= 99999999)
{
FILINFO info;
result = f_findfirst(&dir, &info, path, fileName);
if(result != FR_OK || !info.fname[0]) break;
u32 i = 18,
tmp = ++n;
while(tmp > 0)
{
fileName[i--] = '0' + (tmp % 10);
tmp /= 10;
}
decItoa(++n, fileName + 11, 8);
}
if(result == FR_OK) f_closedir(&dir);

View File

@@ -26,9 +26,7 @@
#define PATTERN(a) a "_*.bin"
extern bool isN3DS, isA9lh;
void mountFs(void);
bool mountFs(bool isSd, bool switchToCtrNand);
u32 fileRead(void *dest, const char *path, u32 maxSize);
u32 getFileSize(const char *path);
bool fileWrite(const void *buffer, const char *path, u32 size);

261
source/main.c Normal file
View File

@@ -0,0 +1,261 @@
/*
* This file is part of Luma3DS
* Copyright (C) 2016 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 of GPLv3 applies 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.
*/
#include "config.h"
#include "emunand.h"
#include "fs.h"
#include "firm.h"
#include "utils.h"
#include "exceptions.h"
#include "draw.h"
#include "strings.h"
#include "buttons.h"
#include "pin.h"
extern CfgData configData;
extern FirmwareSource firmSource;
void main(void)
{
bool isA9lhInstalled,
isSafeMode = false;
u32 configTemp,
emuHeader;
FirmwareType firmType;
FirmwareSource nandType;
ConfigurationStatus needConfig;
//Mount SD or CTRNAND
bool isSdMode;
if(mountFs(true, false)) isSdMode = true;
else
{
firmSource = FIRMWARE_SYSNAND;
if(!mountFs(false, true)) error("Failed to mount SD and CTRNAND.");
isSdMode = false;
}
//Attempt to read the configuration file
needConfig = readConfig() ? MODIFY_CONFIGURATION : CREATE_CONFIGURATION;
//Determine if this is a firmlaunch boot
if(ISFIRMLAUNCH)
{
if(needConfig == CREATE_CONFIGURATION) mcuPowerOff();
//'0' = NATIVE_FIRM, '1' = TWL_FIRM, '2' = AGB_FIRM
firmType = launchedFirmTidLow[7] == u'3' ? SAFE_FIRM : (FirmwareType)(launchedFirmTidLow[5] - u'0');
nandType = (FirmwareSource)BOOTCFG_NAND;
firmSource = (FirmwareSource)BOOTCFG_FIRM;
isA9lhInstalled = BOOTCFG_A9LH != 0;
}
else
{
if(ISA9LH)
{
detectAndProcessExceptionDumps();
installArm9Handlers();
}
firmType = NATIVE_FIRM;
isA9lhInstalled = ISA9LH;
//Get pressed buttons
u32 pressed = HID_PAD;
//Save old options and begin saving the new boot configuration
configTemp = (configData.config & 0xFFFFFF00) | ((u32)ISA9LH << 6);
//If it's a MCU reboot, try to force boot options
if(ISA9LH && CFG_BOOTENV && needConfig != CREATE_CONFIGURATION)
{
//Always force a SysNAND boot when quitting AGB_FIRM
if(CFG_BOOTENV == 7)
{
nandType = FIRMWARE_SYSNAND;
firmSource = (BOOTCFG_NAND != 0) == (BOOTCFG_FIRM != 0) ? FIRMWARE_SYSNAND : (FirmwareSource)BOOTCFG_FIRM;
needConfig = DONT_CONFIGURE;
//Flag to prevent multiple boot options-forcing
configTemp |= 1 << 7;
}
/* Else, force the last used boot options unless a button is pressed
or the no-forcing flag is set */
else if(!pressed && !BOOTCFG_NOFORCEFLAG)
{
nandType = (FirmwareSource)BOOTCFG_NAND;
firmSource = (FirmwareSource)BOOTCFG_FIRM;
needConfig = DONT_CONFIGURE;
}
}
//Boot options aren't being forced
if(needConfig != DONT_CONFIGURE)
{
u32 pinMode = MULTICONFIG(PIN);
bool pinExists = pinMode != 0 && verifyPin(pinMode);
//If no configuration file exists or SELECT is held, load configuration menu
bool shouldLoadConfigMenu = needConfig == CREATE_CONFIGURATION || ((pressed & (BUTTON_SELECT | BUTTON_L1)) == BUTTON_SELECT);
if(shouldLoadConfigMenu)
{
configMenu(isSdMode, pinExists, pinMode);
//Update pressed buttons
pressed = HID_PAD;
}
if(ISA9LH && !CFG_BOOTENV && pressed == SAFE_MODE)
{
nandType = FIRMWARE_SYSNAND;
firmSource = FIRMWARE_SYSNAND;
isSafeMode = true;
//If the PIN has been verified, wait to make it easier to press the SAFE_MODE combo
if(pinExists && !shouldLoadConfigMenu)
{
while(HID_PAD & PIN_BUTTONS);
chrono(2);
}
}
else
{
u32 splashMode = MULTICONFIG(SPLASH);
if(splashMode == 1 && loadSplash()) pressed = HID_PAD;
/* If L and R/A/Select or one of the single payload buttons are pressed,
chainload an external payload */
bool shouldLoadPayload = ((pressed & SINGLE_PAYLOAD_BUTTONS) && !(pressed & (BUTTON_L1 | BUTTON_R1 | BUTTON_A))) ||
((pressed & L_PAYLOAD_BUTTONS) && (pressed & BUTTON_L1));
if(shouldLoadPayload) loadPayload(pressed);
if(splashMode == 2) loadSplash();
//If booting from CTRNAND, always use SysNAND
if(!isSdMode) nandType = FIRMWARE_SYSNAND;
//If R is pressed, boot the non-updated NAND with the FIRM of the opposite one
else if(pressed & BUTTON_R1)
{
if(CONFIG(USESYSFIRM))
{
nandType = FIRMWARE_EMUNAND;
firmSource = FIRMWARE_SYSNAND;
}
else
{
nandType = FIRMWARE_SYSNAND;
firmSource = FIRMWARE_EMUNAND;
}
}
/* Else, boot the NAND the user set to autoboot or the opposite one, depending on L,
with their own FIRM */
else firmSource = nandType = (CONFIG(AUTOBOOTSYS) == ((pressed & BUTTON_L1) == BUTTON_L1)) ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND;
//If we're booting EmuNAND or using EmuNAND FIRM, determine which one from the directional pad buttons, or otherwise from the config
if(nandType == FIRMWARE_EMUNAND || firmSource == FIRMWARE_EMUNAND)
{
FirmwareSource tempNand;
switch(pressed & EMUNAND_BUTTONS)
{
case BUTTON_UP:
tempNand = FIRMWARE_EMUNAND;
break;
case BUTTON_RIGHT:
tempNand = FIRMWARE_EMUNAND2;
break;
case BUTTON_DOWN:
tempNand = FIRMWARE_EMUNAND3;
break;
case BUTTON_LEFT:
tempNand = FIRMWARE_EMUNAND4;
break;
default:
tempNand = (FirmwareSource)(1 + MULTICONFIG(DEFAULTEMU));
break;
}
if(nandType == FIRMWARE_EMUNAND) nandType = tempNand;
else firmSource = tempNand;
}
}
}
}
//If we need to boot EmuNAND, make sure it exists
if(nandType != FIRMWARE_SYSNAND)
{
locateEmuNand(&emuHeader, &nandType);
if(nandType == FIRMWARE_SYSNAND) firmSource = FIRMWARE_SYSNAND;
}
//Same if we're using EmuNAND as the FIRM source
else if(firmSource != FIRMWARE_SYSNAND)
locateEmuNand(&emuHeader, &firmSource);
if(!ISFIRMLAUNCH)
{
configTemp |= (u32)nandType | ((u32)firmSource << 3);
writeConfig(needConfig, configTemp);
}
if(isSdMode && !mountFs(false, false)) error("Failed to mount CTRNAND.");
bool loadFromStorage = CONFIG(LOADEXTFIRMSANDMODULES);
u32 firmVersion = loadFirm(&firmType, firmSource, loadFromStorage, isSafeMode);
u32 devMode = MULTICONFIG(DEVOPTIONS);
u32 res;
switch(firmType)
{
case NATIVE_FIRM:
res = patchNativeFirm(firmVersion, nandType, emuHeader, isA9lhInstalled, isSafeMode, devMode);
break;
case SAFE_FIRM:
case NATIVE_FIRM1X2X:
res = isA9lhInstalled ? patch1x2xNativeAndSafeFirm(devMode) : 0;
break;
case TWL_FIRM:
res = patchTwlFirm(firmVersion, devMode);
break;
case AGB_FIRM:
res = patchAgbFirm(devMode);
break;
}
if(res != 0)
{
char patchesError[] = "Failed to apply FIRM patch(es).";
decItoa(res, patchesError + 16, 2);
error(patchesError);
}
launchFirm(firmType, loadFromStorage);
}

View File

@@ -21,277 +21,382 @@
*/
/*
* Signature patches by an unknown author
* firmlaunches patching code originally by delebile
* FIRM partition writes patches by delebile
* ARM11 modules patching code originally by Subv
* Idea for svcBreak patches from yellows8 and others on #3dsdev
*/
#include "patches.h"
#include "fs.h"
#include "memory.h"
#include "config.h"
#include "utils.h"
#include "../build/bundled.h"
u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr)
u8 *getProcess9Info(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr)
{
u8 *off = memsearch(pos, "ess9", size, 4);
u8 *temp = memsearch(pos, "NCCH", size, 4);
*process9Size = *(u32 *)(off - 0x60) * 0x200;
*process9MemAddr = *(u32 *)(off + 0xC);
if(temp == NULL) error("Failed to get Process9 data.");
//Process9 code offset (start of NCCH + ExeFS offset + ExeFS header size)
return off - 0x204 + (*(u32 *)(off - 0x64) * 0x200) + 0x200;
Cxi *off = (Cxi *)(temp - 0x100);
*process9Size = (off->ncch.exeFsSize - 1) * 0x200;
*process9MemAddr = off->exHeader.systemControlInfo.textCodeSet.address;
return (u8 *)off + (off->ncch.exeFsOffset + 1) * 0x200;
}
u32 *getKernel11Info(u8 *pos, u32 size, u32 *baseK11VA, u8 **freeK11Space, u32 **arm11SvcHandler, u32 **arm11ExceptionsPage)
{
{
const u8 pattern[] = {0x00, 0xB0, 0x9C, 0xE5};
bool res = true;
*arm11ExceptionsPage = (u32 *)memsearch(pos, pattern, size, sizeof(pattern)) - 0xB;
*arm11ExceptionsPage = (u32 *)memsearch(pos, pattern, size, sizeof(pattern));
u32 svcOffset = (-(((*arm11ExceptionsPage)[2] & 0xFFFFFF) << 2) & (0xFFFFFF << 2)) - 8; //Branch offset + 8 for prefetch
u32 pointedInstructionVA = 0xFFFF0008 - svcOffset;
*baseK11VA = pointedInstructionVA & 0xFFFF0000; //This assumes that the pointed instruction has an offset < 0x10000, iirc that's always the case
u32 *arm11SvcTable = (u32 *)(pos + *(u32 *)(pos + pointedInstructionVA - *baseK11VA + 8) - *baseK11VA); //SVC handler address
*arm11SvcHandler = arm11SvcTable;
while(*arm11SvcTable) arm11SvcTable++; //Look for SVC0 (NULL)
u32 *arm11SvcTable;
if(*arm11ExceptionsPage == NULL) res = false;
else
{
*arm11ExceptionsPage -= 0xB;
u32 svcOffset = (-(((*arm11ExceptionsPage)[2] & 0xFFFFFF) << 2) & (0xFFFFFF << 2)) - 8; //Branch offset + 8 for prefetch
u32 pointedInstructionVA = 0xFFFF0008 - svcOffset;
*baseK11VA = pointedInstructionVA & 0xFFFF0000; //This assumes that the pointed instruction has an offset < 0x10000, iirc that's always the case
arm11SvcTable = *arm11SvcHandler = (u32 *)(pos + *(u32 *)(pos + pointedInstructionVA - *baseK11VA + 8) - *baseK11VA); //SVC handler address
while(*arm11SvcTable) arm11SvcTable++; //Look for SVC0 (NULL)
}
const u8 pattern2[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
*freeK11Space = memsearch(pos, pattern2, size, sizeof(pattern2)) + 1;
*freeK11Space = memsearch(pos, pattern2, size, sizeof(pattern2));
if(*freeK11Space == NULL) res = false;
else (*freeK11Space)++;
if(!res) error("Failed to get Kernel11 data.");
return arm11SvcTable;
}
void patchSignatureChecks(u8 *pos, u32 size)
u32 patchSignatureChecks(u8 *pos, u32 size)
{
//Look for signature checks
const u8 pattern[] = {0xC0, 0x1C, 0x76, 0xE7},
pattern2[] = {0xB5, 0x22, 0x4D, 0x0C};
u32 ret;
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern)),
*off2 = (u16 *)(memsearch(pos, pattern2, size, sizeof(pattern2)) - 1);
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
u8 *temp = memsearch(pos, pattern2, size, sizeof(pattern2));
*off = off2[0] = 0x2000;
off2[1] = 0x4770;
if(off == NULL || temp == NULL) ret = 1;
else
{
u16 *off2 = (u16 *)(temp - 1);
*off = off2[0] = 0x2000;
off2[1] = 0x4770;
ret = 0;
}
return ret;
}
void patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr)
u32 patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr)
{
//Look for firmlaunch code
const u8 pattern[] = {0xE2, 0x20, 0x20, 0x90};
u8 *off = memsearch(pos, pattern, size, sizeof(pattern)) - 0x13;
//Firmlaunch function offset - offset in BLX opcode (A4-16 - ARM DDI 0100E) + 1
u32 fOpenOffset = (u32)(off + 9 - (-((*(u32 *)off & 0x00FFFFFF) << 2) & (0xFFFFFF << 2)) - pos + process9MemAddr);
//Copy firmlaunch code
memcpy(off, reboot_bin, reboot_bin_size);
//Put the fOpen offset in the right location
u32 *pos_fopen = (u32 *)memsearch(off, "OPEN", reboot_bin_size, 4);
*pos_fopen = fOpenOffset;
if(CONFIG(USECUSTOMPATH))
{
const char pathPath[] = "/luma/path.txt";
u32 pathSize = getFileSize(pathPath);
if(pathSize > 5 && pathSize < 58)
{
u8 path[pathSize];
fileRead(path, pathPath, 0);
if(path[pathSize - 1] == 0xA) pathSize--;
if(path[pathSize - 1] == 0xD) pathSize--;
if(pathSize > 5 && pathSize < 56 && path[0] == '/' && memcmp(&path[pathSize - 4], ".bin", 4) == 0)
{
u16 finalPath[pathSize + 1];
for(u32 i = 0; i < pathSize; i++)
finalPath[i] = (u16)path[i];
finalPath[pathSize] = 0;
u8 *pos_path = memsearch(off, u"sd", reboot_bin_size, 4) + 0xA;
memcpy(pos_path, finalPath, (pathSize + 1) * 2);
}
}
}
}
void patchFirmWrites(u8 *pos, u32 size)
{
//Look for FIRM writing code
u8 *off1 = memsearch(pos, "exe:", size, 4);
const u8 pattern[] = {0x00, 0x28, 0x01, 0xDA};
u16 *off2 = (u16 *)memsearch(off1 - 0x100, pattern, 0x100, sizeof(pattern));
off2[0] = 0x2000;
off2[1] = 0x46C0;
}
void patchOldFirmWrites(u8 *pos, u32 size)
{
//Look for FIRM writing code
const u8 pattern[] = {0x04, 0x1E, 0x1D, 0xDB};
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
off[0] = 0x2400;
off[1] = 0xE01D;
}
void reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space)
{
//Official implementation of svcBackdoor
const u8 svcBackdoor[40] = {0xFF, 0x10, 0xCD, 0xE3, //bic r1, sp, #0xff
0x0F, 0x1C, 0x81, 0xE3, //orr r1, r1, #0xf00
0x28, 0x10, 0x81, 0xE2, //add r1, r1, #0x28
0x00, 0x20, 0x91, 0xE5, //ldr r2, [r1]
0x00, 0x60, 0x22, 0xE9, //stmdb r2!, {sp, lr}
0x02, 0xD0, 0xA0, 0xE1, //mov sp, r2
0x30, 0xFF, 0x2F, 0xE1, //blx r0
0x03, 0x00, 0xBD, 0xE8, //pop {r0, r1}
0x00, 0xD0, 0xA0, 0xE1, //mov sp, r0
0x11, 0xFF, 0x2F, 0xE1}; //bx r1
if(!arm11SvcTable[0x7B])
{
memcpy(*freeK11Space, svcBackdoor, 40);
arm11SvcTable[0x7B] = baseK11VA + *freeK11Space - pos;
*freeK11Space += 40;
}
}
void implementSvcGetCFWInfo(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space)
{
memcpy(*freeK11Space, svcGetCFWInfo_bin, svcGetCFWInfo_bin_size);
CFWInfo *info = (CFWInfo *)memsearch(*freeK11Space, "LUMA", svcGetCFWInfo_bin_size, 4);
const char *rev = REVISION;
info->commitHash = COMMIT_HASH;
info->config = configData.config;
info->versionMajor = (u8)(rev[1] - '0');
info->versionMinor = (u8)(rev[3] - '0');
bool isRelease;
if(rev[4] == '.')
{
info->versionBuild = (u8)(rev[5] - '0');
isRelease = rev[6] == 0;
}
else isRelease = rev[4] == 0;
info->flags = isRelease ? 1 : 0;
arm11SvcTable[0x2E] = baseK11VA + *freeK11Space - pos; //Stubbed svc
*freeK11Space += svcGetCFWInfo_bin_size;
}
void patchTitleInstallMinVersionCheck(u8 *pos, u32 size)
{
const u8 pattern[] = {0x0A, 0x81, 0x42, 0x02};
u32 ret;
u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
if(off != NULL) off[4] = 0xE0;
}
void applyLegacyFirmPatches(u8 *pos, FirmwareType firmType)
{
const patchData twlPatches[] = {
{{0x1650C0, 0x165D64}, {{ 6, 0x00, 0x20, 0x4E, 0xB0, 0x70, 0xBD }}, 0},
{{0x173A0E, 0x17474A}, { .type1 = 0x2001 }, 1},
{{0x174802, 0x17553E}, { .type1 = 0x2000 }, 2},
{{0x174964, 0x1756A0}, { .type1 = 0x2000 }, 2},
{{0x174D52, 0x175A8E}, { .type1 = 0x2001 }, 2},
{{0x174D5E, 0x175A9A}, { .type1 = 0x2001 }, 2},
{{0x174D6A, 0x175AA6}, { .type1 = 0x2001 }, 2},
{{0x174E56, 0x175B92}, { .type1 = 0x2001 }, 1},
{{0x174E58, 0x175B94}, { .type1 = 0x4770 }, 1}
},
agbPatches[] = {
{{0x9D2A8, 0x9DF64}, {{ 6, 0x00, 0x20, 0x4E, 0xB0, 0x70, 0xBD }}, 0},
{{0xD7A12, 0xD8B8A}, { .type1 = 0xEF26 }, 1}
};
/* Calculate the amount of patches to apply. Only count the boot screen patch for AGB_FIRM
if the matching option was enabled (keep it as last) */
u32 numPatches = firmType == TWL_FIRM ? (sizeof(twlPatches) / sizeof(patchData)) :
(sizeof(agbPatches) / sizeof(patchData) - !CONFIG(SHOWGBABOOT));
const patchData *patches = firmType == TWL_FIRM ? twlPatches : agbPatches;
//Patch
for(u32 i = 0; i < numPatches; i++)
if(off == NULL) ret = 1;
else
{
switch(patches[i].type)
off -= 0x13;
//Firmlaunch function offset - offset in BLX opcode (A4-16 - ARM DDI 0100E) + 1
u32 fOpenOffset = (u32)(off + 9 - (-((*(u32 *)off & 0x00FFFFFF) << 2) & (0xFFFFFF << 2)) - pos + process9MemAddr);
//Copy firmlaunch code
memcpy(off, reboot_bin, reboot_bin_size);
//Put the fOpen offset in the right location
u32 *pos_fopen = (u32 *)memsearch(off, "OPEN", reboot_bin_size, 4);
*pos_fopen = fOpenOffset;
ret = 0;
if(CONFIG(USECUSTOMPATH))
{
case 0:
memcpy(pos + patches[i].offset[isN3DS ? 1 : 0], patches[i].patch.type0 + 1, patches[i].patch.type0[0]);
break;
case 2:
*(u16 *)(pos + patches[i].offset[isN3DS ? 1 : 0] + 2) = 0;
case 1:
*(u16 *)(pos + patches[i].offset[isN3DS ? 1 : 0]) = patches[i].patch.type1;
break;
const char *pathFile = "path.txt";
u32 pathSize = getFileSize(pathFile);
if(pathSize > 5 && pathSize < 58)
{
u8 path[pathSize];
fileRead(path, pathFile, pathSize);
if(path[pathSize - 1] == 0xA) pathSize--;
if(path[pathSize - 1] == 0xD) pathSize--;
if(pathSize > 5 && pathSize < 56 && path[0] == '/' && memcmp(&path[pathSize - 4], ".bin", 4) == 0)
{
u16 finalPath[pathSize];
for(u32 i = 0; i < pathSize; i++)
finalPath[i] = (u16)path[i];
u8 *pos_path = memsearch(off, u"sd", reboot_bin_size, 4) + 0xA;
memcpy(pos_path, finalPath, pathSize * 2);
}
}
}
}
return ret;
}
void patchArm9ExceptionHandlersInstall(u8 *pos, u32 size)
u32 patchFirmWrites(u8 *pos, u32 size)
{
const u8 pattern[] = {0x03, 0xA0, 0xE3, 0x18};
u32 ret;
u32 *off = (u32 *)(memsearch(pos, pattern, size, sizeof(pattern)) + 0x13);
//Look for FIRM writing code
u8 *off = memsearch(pos, "exe:", size, 4);
for(u32 r0 = 0x08000000; *off != 0xE3A01040; off++) //Until mov r1, #0x40
if(off == NULL) ret = 1;
else
{
//Discard everything that's not str rX, [r0, #imm](!)
if((*off & 0xFE5F0000) != 0xE4000000) continue;
const u8 pattern[] = {0x00, 0x28, 0x01, 0xDA};
u32 rD = (*off >> 12) & 0xF,
offset = (*off & 0xFFF) * ((((*off >> 23) & 1) == 0) ? -1 : 1);
bool writeback = ((*off >> 21) & 1) != 0,
pre = ((*off >> 24) & 1) != 0;
u16 *off2 = (u16 *)memsearch(off - 0x100, pattern, 0x100, sizeof(pattern));
u32 addr = r0 + ((pre || !writeback) ? offset : 0);
if((addr & 7) != 0 && addr != 0x08000014 && addr != 0x08000004) *off = 0xE1A00000; //nop
else *off = 0xE5800000 | (rD << 12) | (addr & 0xFFF); //Preserve IRQ and SVC handlers
if(off2 == NULL) ret = 1;
else
{
off2[0] = 0x2000;
off2[1] = 0x46C0;
if(!pre) addr += offset;
if(writeback) r0 = addr;
ret = 0;
}
}
return ret;
}
u32 patchOldFirmWrites(u8 *pos, u32 size)
{
//Look for FIRM writing code
const u8 pattern[] = {0x04, 0x1E, 0x1D, 0xDB};
u32 ret;
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
if(off == NULL) ret = 1;
else
{
off[0] = 0x2400;
off[1] = 0xE01D;
ret = 0;
}
return ret;
}
u32 reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space)
{
u32 ret = 0;
//Official implementation of svcBackdoor
const u8 svcBackdoor[] = {0xFF, 0x10, 0xCD, 0xE3, //bic r1, sp, #0xff
0x0F, 0x1C, 0x81, 0xE3, //orr r1, r1, #0xf00
0x28, 0x10, 0x81, 0xE2, //add r1, r1, #0x28
0x00, 0x20, 0x91, 0xE5, //ldr r2, [r1]
0x00, 0x60, 0x22, 0xE9, //stmdb r2!, {sp, lr}
0x02, 0xD0, 0xA0, 0xE1, //mov sp, r2
0x30, 0xFF, 0x2F, 0xE1, //blx r0
0x03, 0x00, 0xBD, 0xE8, //pop {r0, r1}
0x00, 0xD0, 0xA0, 0xE1, //mov sp, r0
0x11, 0xFF, 0x2F, 0xE1}; //bx r1
if(!arm11SvcTable[0x7B])
{
if(*(u32 *)(*freeK11Space + sizeof(svcBackdoor) - 4) != 0xFFFFFFFF) ret = 1;
else
{
memcpy(*freeK11Space, svcBackdoor, sizeof(svcBackdoor));
arm11SvcTable[0x7B] = baseK11VA + *freeK11Space - pos;
*freeK11Space += sizeof(svcBackdoor);
}
}
return ret;
}
u32 implementSvcGetCFWInfo(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space, bool isSafeMode)
{
u32 ret;
if(*(u32 *)(*freeK11Space + svcGetCFWInfo_bin_size - 4) != 0xFFFFFFFF) ret = 1;
else
{
memcpy(*freeK11Space, svcGetCFWInfo_bin, svcGetCFWInfo_bin_size);
struct CfwInfo
{
char magic[4];
u8 versionMajor;
u8 versionMinor;
u8 versionBuild;
u8 flags;
u32 commitHash;
u32 config;
} __attribute__((packed)) *info = (struct CfwInfo *)memsearch(*freeK11Space, "LUMA", svcGetCFWInfo_bin_size, 4);
const char *rev = REVISION;
info->commitHash = COMMIT_HASH;
info->config = configData.config;
info->versionMajor = (u8)(rev[1] - '0');
info->versionMinor = (u8)(rev[3] - '0');
bool isRelease;
if(rev[4] == '.')
{
info->versionBuild = (u8)(rev[5] - '0');
isRelease = rev[6] == 0;
}
else isRelease = rev[4] == 0;
if(isRelease) info->flags = 1;
if(ISN3DS) info->flags |= 1 << 4;
if(isSafeMode) info->flags |= 1 << 5;
arm11SvcTable[0x2E] = baseK11VA + *freeK11Space - pos; //Stubbed svc
*freeK11Space += svcGetCFWInfo_bin_size;
ret = 0;
}
return ret;
}
u32 patchTitleInstallMinVersionChecks(u8 *pos, u32 size, u32 firmVersion)
{
const u8 pattern[] = {0xFF, 0x00, 0x00, 0x02};
u32 ret;
u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
if(off == NULL) ret = firmVersion == 0xFFFFFFFF ? 0 : 1;
else
{
off++;
memset32(off, 0, 8); //Zero out the first TitleID in the list
ret = 0;
}
return ret;
}
u32 patchArm9ExceptionHandlersInstall(u8 *pos, u32 size)
{
const u8 pattern[] = {0x80, 0xE5, 0x40, 0x1C};
u32 ret;
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
if(temp == NULL) ret = 1;
else
{
u32 *off = (u32 *)(temp - 0xA);
for(u32 r0 = 0x08000000; *off != 0xE3A01040; off++) //Until mov r1, #0x40
{
//Discard everything that's not str rX, [r0, #imm](!)
if((*off & 0xFE5F0000) == 0xE4000000)
{
u32 rD = (*off >> 12) & 0xF,
offset = (*off & 0xFFF) * ((((*off >> 23) & 1) == 0) ? -1 : 1);
bool writeback = ((*off >> 21) & 1) != 0,
pre = ((*off >> 24) & 1) != 0;
u32 addr = r0 + ((pre || !writeback) ? offset : 0);
if((addr & 7) != 0 && addr != 0x08000014 && addr != 0x08000004) *off = 0xE1A00000; //nop
else *off = 0xE5800000 | (rD << 12) | (addr & 0xFFF); //Preserve IRQ and SVC handlers
if(!pre) addr += offset;
if(writeback) r0 = addr;
}
}
ret = 0;
}
return ret;
}
u32 getInfoForArm11ExceptionHandlers(u8 *pos, u32 size, u32 *codeSetOffset)
{
const u8 pattern[] = {0xE3, 0xDC, 0x05, 0xC0}, //Get TitleID from CodeSet
pattern2[] = {0xE1, 0x0F, 0x00, 0xBD}; //Call exception dispatcher
const u8 pattern[] = {0x1B, 0x50, 0xA0, 0xE3}, //Get TitleID from CodeSet
pattern2[] = {0xE8, 0x13, 0x00, 0x02}; //Call exception dispatcher
bool ret = true;
u32 *loadCodeSet = (u32 *)(memsearch(pos, pattern, size, sizeof(pattern)) - 0xB);
u32 *loadCodeSet = (u32 *)memsearch(pos, pattern, size, sizeof(pattern));
*codeSetOffset = *loadCodeSet & 0xFFF;
if(loadCodeSet == NULL) ret = false;
else
{
loadCodeSet -= 2;
*codeSetOffset = *loadCodeSet & 0xFFF;
}
return *(u32 *)(memsearch(pos, pattern2, size, sizeof(pattern2)) + 0xD);
u8 *temp = memsearch(pos, pattern2, size, sizeof(pattern2));
u32 stackAddress;
if(temp == NULL) ret = false;
else stackAddress = *(u32 *)(temp + 9);
if(!ret) error("Failed to get ARM11 exception handlers data.");
return stackAddress;
}
void patchSvcBreak9(u8 *pos, u32 size, u32 kernel9Address)
u32 patchSvcBreak9(u8 *pos, u32 size, u32 kernel9Address)
{
/* Stub svcBreak with "bkpt 65535" so we can debug the panic.
Thanks @yellows8 and others for mentioning this idea on #3dsdev */
//Stub svcBreak with "bkpt 65535" so we can debug the panic
//Look for the svc handler
const u8 pattern[] = {0x00, 0xE0, 0x4F, 0xE1}; //mrs lr, spsr
u32 ret;
u32 *arm9SvcTable = (u32 *)memsearch(pos, pattern, size, sizeof(pattern));
while(*arm9SvcTable) arm9SvcTable++; //Look for SVC0 (NULL)
u32 *addr = (u32 *)(pos + arm9SvcTable[0x3C] - kernel9Address);
*addr = 0xE12FFF7F;
if(arm9SvcTable == NULL) ret = 1;
else
{
while(*arm9SvcTable != 0) arm9SvcTable++; //Look for SVC0 (NULL)
u32 *addr = (u32 *)(pos + arm9SvcTable[0x3C] - kernel9Address);
*addr = 0xE12FFF7F;
ret = 0;
}
return ret;
}
void patchSvcBreak11(u8 *pos, u32 *arm11SvcTable)
@@ -301,68 +406,294 @@ void patchSvcBreak11(u8 *pos, u32 *arm11SvcTable)
*addr = 0xE12FFF7F;
}
void patchKernel9Panic(u8 *pos, u32 size)
u32 patchKernel9Panic(u8 *pos, u32 size)
{
const u8 pattern[] = {0xFF, 0xEA, 0x04, 0xD0};
u32 ret;
u32 *off = (u32 *)(memsearch(pos, pattern, size, sizeof(pattern)) - 0x12);
*off = 0xE12FFF7E;
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
if(temp == NULL) ret = 1;
else
{
u32 *off = (u32 *)(temp - 0x12);
*off = 0xE12FFF7E;
ret = 0;
}
return ret;
}
void patchKernel11Panic(u8 *pos, u32 size)
u32 patchKernel11Panic(u8 *pos, u32 size)
{
const u8 pattern[] = {0x02, 0x0B, 0x44, 0xE2};
u32 ret;
u32 *off = (u32 *)memsearch(pos, pattern, size, sizeof(pattern));
*off = 0xE12FFF7E;
if(off == NULL) ret = 1;
else
{
*off = 0xE12FFF7E;
ret = 0;
}
return ret;
}
void patchP9AccessChecks(u8 *pos, u32 size)
u32 patchP9AccessChecks(u8 *pos, u32 size)
{
const u8 pattern[] = {0xE0, 0x00, 0x40, 0x39};
const u8 pattern[] = {0x00, 0x08, 0x49, 0x68};
u32 ret;
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern)) - 7;
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
off[0] = 0x2001; //mov r0, #1
off[1] = 0x4770; //bx lr
if(temp == NULL) ret = 1;
else
{
u16 *off = (u16 *)(temp - 3);
off[0] = 0x2001; //mov r0, #1
off[1] = 0x4770; //bx lr
ret = 0;
}
return ret;
}
void patchArm11SvcAccessChecks(u32 *arm11SvcHandler)
u32 patchArm11SvcAccessChecks(u32 *arm11SvcHandler, u32 *endPos)
{
while(*arm11SvcHandler != 0xE11A0E1B) arm11SvcHandler++; //TST R10, R11,LSL LR
*arm11SvcHandler = 0xE3B0A001; //MOVS R10, #1
u32 ret;
while(*arm11SvcHandler != 0xE11A0E1B && arm11SvcHandler < endPos) arm11SvcHandler++; //TST R10, R11,LSL LR
if(arm11SvcHandler == endPos) ret = 1;
else
{
*arm11SvcHandler = 0xE3B0A001; //MOVS R10, #1
ret = 0;
}
return ret;
}
void patchK11ModuleChecks(u8 *pos, u32 size, u8 **freeK11Space)
u32 patchK11ModuleChecks(u8 *pos, u32 size, u8 **freeK11Space)
{
/* We have to detour a function in the ARM11 kernel because builtin modules
are compressed in memory and are only decompressed at runtime */
//Check that we have enough free space
if(*(u32 *)(*freeK11Space + k11modules_bin_size - 4) == 0xFFFFFFFF)
{
//Inject our code into the free space
memcpy(*freeK11Space, k11modules_bin, k11modules_bin_size);
u32 ret;
//Check that we have enough free space
if(*(u32 *)(*freeK11Space + k11modules_bin_size - 4) != 0xFFFFFFFF) ret = 0;
else
{
//Look for the code that decompresses the .code section of the builtin modules
const u8 pattern[] = {0xE5, 0x48, 0x00, 0x9D};
u32 *off = (u32 *)(memsearch(pos, pattern, size, sizeof(pattern)) - 0xB);
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
//Inject a jump (BL) instruction to our code at the offset we found
*off = 0xEB000000 | (((((u32)*freeK11Space) - ((u32)off + 8)) >> 2) & 0xFFFFFF);
if(temp == NULL) ret = 1;
else
{
//Inject our code into the free space
memcpy(*freeK11Space, k11modules_bin, k11modules_bin_size);
*freeK11Space += k11modules_bin_size;
u32 *off = (u32 *)(temp - 0xB);
//Inject a jump (BL) instruction to our code at the offset we found
*off = 0xEB000000 | (((((u32)*freeK11Space) - ((u32)off + 8)) >> 2) & 0xFFFFFF);
*freeK11Space += k11modules_bin_size;
ret = 0;
}
}
return ret;
}
void patchUnitInfoValueSet(u8 *pos, u32 size)
u32 patchUnitInfoValueSet(u8 *pos, u32 size)
{
//Look for UNITINFO value being set during kernel sync
const u8 pattern[] = {0x01, 0x10, 0xA0, 0x13};
u32 ret;
u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
off[0] = isDevUnit ? 0 : 1;
off[3] = 0xE3;
if(off == NULL) ret = 1;
else
{
off[0] = ISDEVUNIT ? 0 : 1;
off[3] = 0xE3;
ret = 0;
}
return ret;
}
u32 patchLgySignatureChecks(u8 *pos, u32 size)
{
const u8 pattern[] = {0x47, 0xC1, 0x17, 0x49};
u32 ret;
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
if(temp == NULL) ret = 1;
else
{
u16 *off = (u16 *)(temp + 1);
off[0] = 0x2000;
off[1] = 0xB04E;
off[2] = 0xBD70;
ret = 0;
}
return ret;
}
u32 patchTwlInvalidSignatureChecks(u8 *pos, u32 size)
{
const u8 pattern[] = {0x20, 0xF6, 0xE7, 0x7F};
u32 ret;
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
if(temp == NULL) ret = 1;
else
{
u16 *off = (u16 *)(temp - 1);
*off = 0x2001; //mov r0, #1
ret = 0;
}
return ret;
}
u32 patchTwlNintendoLogoChecks(u8 *pos, u32 size)
{
const u8 pattern[] = {0xC0, 0x30, 0x06, 0xF0};
u32 ret;
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
if(off == NULL) ret = 1;
else
{
off[1] = 0x2000;
off[2] = 0;
ret = 0;
}
return ret;
}
u32 patchTwlWhitelistChecks(u8 *pos, u32 size)
{
const u8 pattern[] = {0x22, 0x00, 0x20, 0x30};
u32 ret;
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
if(off == NULL) ret = 1;
else
{
off[2] = 0x2000;
off[3] = 0;
ret = 0;
}
return ret;
}
u32 patchTwlFlashcartChecks(u8 *pos, u32 size, u32 firmVersion)
{
const u8 pattern[] = {0x25, 0x20, 0x00, 0x0E};
u32 ret;
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
if(temp == NULL)
{
if(firmVersion == 0xFFFFFFFF) ret = patchOldTwlFlashcartChecks(pos, size);
else ret = 1;
}
else
{
u16 *off = (u16 *)(temp + 3);
off[0] = off[6] = off[0xC] = 0x2001; //mov r0, #1
off[1] = off[7] = off[0xD] = 0; //nop
ret = 0;
}
return ret;
}
u32 patchOldTwlFlashcartChecks(u8 *pos, u32 size)
{
const u8 pattern[] = {0x06, 0xF0, 0xA0, 0xFD};
u32 ret;
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
if(off == NULL) ret = 1;
else
{
off[0] = off[6] = 0x2001; //mov r0, #1
off[1] = off[7] = 0; //nop
ret = 0;
}
return ret;
}
u32 patchTwlShaHashChecks(u8 *pos, u32 size)
{
const u8 pattern[] = {0x10, 0xB5, 0x14, 0x22};
u32 ret;
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
if(off == NULL) ret = 1;
else
{
off[0] = 0x2001; //mov r0, #1
off[1] = 0x4770;
ret = 0;
}
return ret;
}
u32 patchAgbBootSplash(u8 *pos, u32 size)
{
const u8 pattern[] = {0x00, 0x00, 0x01, 0xEF};
u32 ret;
u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
if(off == NULL) ret = 1;
else
{
off[2] = 0x26;
ret = 0;
}
return ret;
}

View File

@@ -21,55 +21,43 @@
*/
/*
* Signature patches by an unknown author
* firmlaunches patching code originally by delebile
* FIRM partition writes patches by delebile
* ARM11 modules patching code originally by Subv
* Idea for svcBreak patches from yellows8 and others on #3dsdev
*/
#pragma once
#include "types.h"
typedef struct patchData {
u32 offset[2];
union {
u8 type0[8];
u16 type1;
} patch;
u32 type;
} patchData;
extern CfgData configData;
typedef struct __attribute__((packed))
{
char magic[4];
u8 versionMajor;
u8 versionMinor;
u8 versionBuild;
u8 flags;
u32 commitHash;
u32 config;
} CFWInfo;
extern bool isDevUnit;
u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr);
u8 *getProcess9Info(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr);
u32 *getKernel11Info(u8 *pos, u32 size, u32 *baseK11VA, u8 **freeK11Space, u32 **arm11SvcHandler, u32 **arm11ExceptionsPage);
void patchSignatureChecks(u8 *pos, u32 size);
void patchTitleInstallMinVersionCheck(u8 *pos, u32 size);
void patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr);
void patchFirmWrites(u8 *pos, u32 size);
void patchOldFirmWrites(u8 *pos, u32 size);
void reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space);
void implementSvcGetCFWInfo(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space);
void applyLegacyFirmPatches(u8 *pos, FirmwareType firmType);
void patchArm9ExceptionHandlersInstall(u8 *pos, u32 size);
u32 patchSignatureChecks(u8 *pos, u32 size);
u32 patchTitleInstallMinVersionChecks(u8 *pos, u32 size, u32 firmVersion);
u32 patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr);
u32 patchFirmWrites(u8 *pos, u32 size);
u32 patchOldFirmWrites(u8 *pos, u32 size);
u32 reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space);
u32 implementSvcGetCFWInfo(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space, bool isSafeMode);
u32 patchArm9ExceptionHandlersInstall(u8 *pos, u32 size);
u32 getInfoForArm11ExceptionHandlers(u8 *pos, u32 size, u32 *codeSetOffset);
void patchSvcBreak9(u8 *pos, u32 size, u32 kernel9Address);
u32 patchSvcBreak9(u8 *pos, u32 size, u32 kernel9Address);
void patchSvcBreak11(u8 *pos, u32 *arm11SvcTable);
void patchKernel9Panic(u8 *pos, u32 size);
void patchKernel11Panic(u8 *pos, u32 size);
void patchP9AccessChecks(u8 *pos, u32 size);
void patchArm11SvcAccessChecks(u32 *arm11SvcHandler);
void patchK11ModuleChecks(u8 *pos, u32 size, u8 **freeK11Space);
void patchUnitInfoValueSet(u8 *pos, u32 size);
u32 patchKernel9Panic(u8 *pos, u32 size);
u32 patchKernel11Panic(u8 *pos, u32 size);
u32 patchP9AccessChecks(u8 *pos, u32 size);
u32 patchArm11SvcAccessChecks(u32 *arm11SvcHandler, u32 *endPos);
u32 patchK11ModuleChecks(u8 *pos, u32 size, u8 **freeK11Space);
u32 patchUnitInfoValueSet(u8 *pos, u32 size);
u32 patchLgySignatureChecks(u8 *pos, u32 size);
u32 patchTwlInvalidSignatureChecks(u8 *pos, u32 size);
u32 patchTwlNintendoLogoChecks(u8 *pos, u32 size);
u32 patchTwlWhitelistChecks(u8 *pos, u32 size);
u32 patchTwlFlashcartChecks(u8 *pos, u32 size, u32 firmVersion);
u32 patchOldTwlFlashcartChecks(u8 *pos, u32 size);
u32 patchTwlShaHashChecks(u8 *pos, u32 size);
u32 patchAgbBootSplash(u8 *pos, u32 size);

View File

@@ -50,7 +50,7 @@ void newPin(bool allowSkipping, u32 pinMode)
u8 length = 4 + 2 * (pinMode - 1);
char *title = allowSkipping ? "Press START to skip or enter a new PIN" : "Enter a new PIN to proceed";
const char *title = allowSkipping ? "Press START to skip or enter a new PIN" : "Enter a new PIN to proceed";
drawString(title, true, 10, 10, COLOR_TITLE);
drawString("PIN ( digits): ", true, 10, 10 + 2 * SPACING_Y, COLOR_WHITE);
drawCharacter('0' + length, true, 10 + 5 * SPACING_X, 10 + 2 * SPACING_Y, COLOR_WHITE);
@@ -100,7 +100,7 @@ void newPin(bool allowSkipping, u32 pinMode)
computePinHash(tmp, enteredPassword);
memcpy(pin.hash, tmp, sizeof(tmp));
if(!fileWrite(&pin, PIN_PATH, sizeof(PinData)))
if(!fileWrite(&pin, PIN_FILE, sizeof(PinData)))
error("Error writing the PIN file");
}
@@ -108,7 +108,7 @@ bool verifyPin(u32 pinMode)
{
PinData pin;
if(fileRead(&pin, PIN_PATH, sizeof(PinData)) != sizeof(PinData) ||
if(fileRead(&pin, PIN_FILE, sizeof(PinData)) != sizeof(PinData) ||
memcmp(pin.magic, "PINF", 4) != 0 ||
pin.formatVersionMajor != PIN_VERSIONMAJOR ||
pin.formatVersionMinor != PIN_VERSIONMINOR)
@@ -132,16 +132,19 @@ bool verifyPin(u32 pinMode)
u8 cnt = 0;
u32 charDrawPos = 16 * SPACING_X;
const char messagePath[] = "/luma/pinmessage.txt";
const char *messageFile = "pinmessage.txt";
u32 messageSize = getFileSize(messagePath);
u32 messageSize = getFileSize(messageFile);
if(messageSize > 0 && messageSize <= 800)
{
char message[messageSize + 1];
fileRead(message, messagePath, 0);
message[messageSize] = 0;
drawString(message, false, 10, 10, COLOR_WHITE);
if(fileRead(message, messageFile, messageSize) == messageSize)
{
message[messageSize] = 0;
drawString(message, false, 10, 10, COLOR_WHITE);
}
}
while(!unlock)

View File

@@ -28,18 +28,9 @@
#include "types.h"
#define PIN_PATH "/luma/pin.bin"
#define PIN_FILE "pin.bin"
#define PIN_VERSIONMAJOR 1
#define PIN_VERSIONMINOR 3
typedef struct __attribute__((packed))
{
char magic[4];
u16 formatVersionMajor, formatVersionMinor;
u8 lengthHash[32];
u8 hash[32];
} PinData;
void newPin(bool allowSkipping, u32 pinMode);
bool verifyPin(u32 pinMode);

View File

@@ -41,14 +41,11 @@
#include "cache.h"
#include "i2c.h"
vu32 *const arm11Entry = (vu32 *)BRAHMA_ARM11_ENTRY;
vu32 *arm11Entry = (vu32 *)BRAHMA_ARM11_ENTRY;
static const u32 brightness[4] = {0x5F, 0x4C, 0x39, 0x26};
void __attribute__((naked)) arm11Stub(void)
{
//Disable interrupts
__asm(".word 0xF10C01C0");
//Wait for the entry to be set
while(*arm11Entry == ARM11_STUB_ADDRESS);
@@ -62,7 +59,6 @@ static void invokeArm11Function(void (*func)())
if(!hasCopiedStub)
{
memcpy((void *)ARM11_STUB_ADDRESS, arm11Stub, 0x30);
flushDCacheRange((void *)ARM11_STUB_ADDRESS, 0x30);
hasCopiedStub = true;
}
@@ -86,7 +82,7 @@ void deinitScreens(void)
WAIT_FOR_ARM9();
}
if(PDN_GPU_CNT != 1) invokeArm11Function(ARM11);
if(ARESCREENSINITED) invokeArm11Function(ARM11);
}
void updateBrightness(u32 brightnessIndex)
@@ -167,16 +163,6 @@ void clearScreens(bool clearTop, bool clearBottom, bool clearAlternate)
while(!((!clearTopTmp || (REGs_PSC0[3] & 2)) && (!clearBottomTmp || (REGs_PSC1[3] & 2))));
if(fbTmp->top_right != fbTmp->top_left && clearTopTmp)
{
REGs_PSC0[0] = (u32)fbTmp->top_right >> 3; //Start address
REGs_PSC0[1] = (u32)(fbTmp->top_right + SCREEN_TOP_FBSIZE) >> 3; //End address
REGs_PSC0[2] = 0; //Fill value
REGs_PSC0[3] = (2 << 8) | 1; //32-bit pattern; start
while(!(REGs_PSC0[3] & 2));
}
WAIT_FOR_ARM9();
}
@@ -195,7 +181,7 @@ void initScreens(void)
__asm(".word 0xF10C01C0");
u32 brightnessLevel = brightness[MULTICONFIG(BRIGHTNESS)];
*(vu32 *)0x10141200 = 0x1007F;
*(vu32 *)0x10202014 = 0x00000001;
*(vu32 *)0x1020200C &= 0xFFFEFFFE;
@@ -302,7 +288,7 @@ void initScreens(void)
if(needToSetup)
{
if(PDN_GPU_CNT == 1)
if(!ARESCREENSINITED)
{
flushDCacheRange(&configData, sizeof(CfgData));
invokeArm11Function(initSequence);

View File

@@ -31,7 +31,9 @@
#define PDN_GPU_CNT (*(vu8 *)0x10141200)
#define ARM11_STUB_ADDRESS (0x25000000 - 0x30) //It's currently only 0x28 bytes large. We're putting 0x30 just to be sure here
#define ARESCREENSINITED (PDN_GPU_CNT != 1)
#define ARM11_STUB_ADDRESS 0x1FFFFFC8
#define WAIT_FOR_ARM9() *arm11Entry = 0; while(!*arm11Entry); ((void (*)())*arm11Entry)();
#define SCREEN_TOP_WIDTH 400
@@ -44,7 +46,9 @@ static volatile struct fb {
u8 *top_left;
u8 *top_right;
u8 *bottom;
} *const fbs = (volatile struct fb *)0x23FFFE00;
} __attribute__((packed)) *const fbs = (volatile struct fb *)0x23FFFE00;
extern CfgData configData;
void deinitScreens(void);
void swapFramebuffers(bool isAlternate);

View File

@@ -28,17 +28,17 @@ _start:
.global launchedFirmTidLow
launchedFirmTidLow:
.hword 0, 0, 0, 0, 0, 0, 0, 0
.hword 0, 0, 0, 0, 0, 0, 0, 0
start:
@ Change the stack pointer
mov sp, #0x27000000
@ Disable interrupts
mrs r0, cpsr
orr r0, #0x1C0
msr cpsr_cx, r0
@ Change the stack pointer
mov sp, #0x27000000
@ Disable caches / MPU
mrc p15, 0, r0, c1, c0, 0 @ read control register
bic r0, #(1<<12) @ - instruction cache disable

View File

@@ -27,7 +27,7 @@ u32 strlen(const char *string)
{
char *stringEnd = (char *)string;
while(*stringEnd) stringEnd++;
while(*stringEnd != 0) stringEnd++;
return stringEnd - string;
}
@@ -40,16 +40,47 @@ void concatenateStrings(char *destination, const char *source)
memcpy(&destination[j], source, i + 1);
}
void hexItoa(u32 number, char *out, u32 digits)
void hexItoa(u32 number, char *out, u32 digits, bool fillString)
{
const char hexDigits[] = "0123456789ABCDEF";
u32 i = 0;
u32 i;
while(number > 0)
for(i = 0; number > 0; i++)
{
out[digits - 1 - i++] = hexDigits[number & 0xF];
out[digits - 1 - i] = hexDigits[number & 0xF];
number >>= 4;
}
while(i < digits) out[digits - 1 - i++] = '0';
if(fillString) while(i < digits) out[digits - 1 - i++] = '0';
}
void decItoa(u32 number, char *out, u32 digits)
{
for(u32 i = 0; number > 0; i++)
{
out[digits - 1 - i] = '0' + number % 10;
number /= 10;
}
}
u32 hexAtoi(const char *in, u32 digits)
{
u32 res = 0;
char *tmp = (char *)in;
for(u32 i = 0; i < digits && *tmp != 0; tmp++, i++)
res = (*tmp > '9' ? *tmp - 'A' + 10 : *tmp - '0') + (res << 4);
return res;
}
u32 decAtoi(const char *in, u32 digits)
{
u32 res = 0;
char *tmp = (char *)in;
for(u32 i = 0; i < digits && *tmp != 0; tmp++, i++)
res = *tmp - '0' + res * 10;
return res;
}

View File

@@ -26,4 +26,7 @@
u32 strlen(const char *string);
void concatenateStrings(char *destination, const char *source);
void hexItoa(u32 number, char *out, u32 digits);
void hexItoa(u32 number, char *out, u32 digits, bool fillString);
void decItoa(u32 number, char *out, u32 digits);
u32 hexAtoi(const char *in, u32 digits);
u32 decAtoi(const char *in, u32 digits);

View File

@@ -36,9 +36,52 @@ typedef volatile u16 vu16;
typedef volatile u32 vu32;
typedef volatile u64 vu64;
//Used by multiple files
#include "3dsheaders.h"
#define BRAHMA_ARM11_ENTRY 0x1FFFFFF8
#define CFG_BOOTENV (*(vu32 *)0x10010000)
#define CFG_UNITINFO (*(vu8 *)0x10010010)
#define PDN_MPCORE_CFG (*(vu32 *)0x10140FFC)
#define PDN_SPI_CNT (*(vu32 *)0x101401C0)
#define ISN3DS (PDN_MPCORE_CFG == 7)
#define ISDEVUNIT (CFG_UNITINFO != 0)
#define ISA9LH (!PDN_SPI_CNT)
#define ISFIRMLAUNCH (launchedFirmTidLow[5] != 0)
typedef struct __attribute__((packed))
{
char magic[4];
u16 formatVersionMajor, formatVersionMinor;
u32 config;
} CfgData;
typedef struct __attribute__((packed))
{
char magic[4];
u16 formatVersionMajor, formatVersionMinor;
u8 lengthHash[32];
u8 hash[32];
} PinData;
typedef struct __attribute__((packed))
{
u32 magic[2];
u16 versionMinor, versionMajor;
u16 processor, core;
u32 type;
u32 totalSize;
u32 registerDumpSize;
u32 codeDumpSize;
u32 stackDumpSize;
u32 additionalDataSize;
} ExceptionDumpHeader;
typedef enum FirmwareSource
{
FIRMWARE_SYSNAND = 0,
@@ -55,4 +98,6 @@ typedef enum FirmwareType
AGB_FIRM,
SAFE_FIRM,
NATIVE_FIRM1X2X
} FirmwareType;
} FirmwareType;
extern u16 launchedFirmTidLow[8]; //Defined in start.s

View File

@@ -54,24 +54,13 @@ u32 waitInput(void)
return key;
}
void mcuReboot(void)
{
if(!isFirmlaunch && PDN_GPU_CNT != 1) clearScreens(true, true, false);
//Ensure that all memory transfers have completed and that the data cache has been flushed
flushEntireDCache();
i2cWriteRegister(I2C_DEV_MCU, 0x20, 1 << 1);
while(true);
}
void mcuPowerOff(void)
{
if(!isFirmlaunch && PDN_GPU_CNT != 1) clearScreens(true, true, false);
if(!ISFIRMLAUNCH && ARESCREENSINITED) clearScreens(true, true, false);
//Ensure that all memory transfers have completed and that the data cache has been flushed
flushEntireDCache();
i2cWriteRegister(I2C_DEV_MCU, 0x20, 1 << 0);
while(true);
}
@@ -112,14 +101,16 @@ void chrono(u32 seconds)
void error(const char *message)
{
if(isFirmlaunch) mcuReboot();
if(!ISFIRMLAUNCH)
{
initScreens();
initScreens();
drawString("An error has occurred:", true, 10, 10, COLOR_RED);
u32 posY = drawString(message, true, 10, 30, COLOR_WHITE);
drawString("Press any button to shutdown", true, 10, posY + 2 * SPACING_Y, COLOR_WHITE);
drawString("An error has occurred:", true, 10, 10, COLOR_RED);
u32 posY = drawString(message, true, 10, 30, COLOR_WHITE);
drawString("Press any button to shutdown", true, 10, posY + 2 * SPACING_Y, COLOR_WHITE);
waitInput();
}
waitInput();
mcuPowerOff();
}

View File

@@ -28,10 +28,7 @@
#define REG_TIMER_CNT(i) *(vu16 *)(0x10003002 + 4 * i)
#define REG_TIMER_VAL(i) *(vu16 *)(0x10003000 + 4 * i)
extern bool isFirmlaunch;
u32 waitInput(void);
void mcuReboot(void);
void mcuPowerOff(void);
void chrono(u32 seconds);
void error(const char *message);