Compare commits

...

117 Commits
v6.3 ... v6.6

Author SHA1 Message Date
Aurora
0b16d88756 Minor stuff 2016-11-12 14:17:42 +01:00
TuxSH
796cb31ed7 Update exceptions.c 2016-11-12 13:18:24 +01:00
Aurora
d7fd2f26c1 Revert "Add support for installing retail 0x3D[0] key-encrypted CIAs on dev units"
This reverts commit 5adb8749de.
2016-11-12 02:52:54 +01:00
TuxSH
5adb8749de Add support for installing retail 0x3D[0] key-encrypted CIAs on dev units
When "UNITINFO" is enabled
2016-11-12 02:14:35 +01:00
Aurora
3474faa4a2 Added support for dev units sysupdater FIRM (untested but should work) 2016-11-12 01:50:43 +01:00
Aurora
acd9c04ff6 Minor stuff 2016-11-11 18:31:38 +01:00
Aurora
833c9406b0 Make it possible to use the reboot patch with the payload on CTRNAND and no SD 2016-11-11 16:31:17 +01:00
Aurora
2f1253e27f Merge pull request #276 from SciresM/patch-1
Add devkit keys.
2016-11-11 15:56:57 +01:00
SciresM
53b847e31c Add devkit keys. 2016-11-11 06:55:29 -08:00
Aurora
7efa33dd7f It seems some games check just for the language 2016-11-11 04:04:12 +01:00
Aurora
4011970a57 Fix derp 2016-11-11 02:16:33 +01:00
TuxSH
a2cfa2be16 Fix same handling bug on arm9 2016-11-11 00:30:49 +01:00
Aurora
c4b691d688 Add another check 2016-11-10 22:41:51 +01:00
Aurora
72a7a8eee5 Minor stuff 2016-11-10 13:17:58 +01:00
Aurora
52d352385f Move dev common key patch to UNITINFO since it breaks SD retail encrypted CIAs 2016-11-10 13:10:02 +01:00
Aurora
c1f85650bd Minor stuff 2016-11-09 22:52:29 +01:00
Aurora
b830909504 Optimize function 2016-11-09 17:01:56 +01:00
Aurora
4ad6b1c220 Attempt to increase the compatibility of language emulation with some rare games, add checks for the functions 2016-11-09 16:33:54 +01:00
Aurora
429488a4ba Fixed New 3DS CPU patch overriding the CPU mode of N3DS exclusives/enhanced titles 2016-11-08 17:52:02 +01:00
Aurora
40c6cc11a5 Minor stuff 2016-11-06 14:52:10 +01:00
Aurora
594881c6ce Do things properly 2016-11-06 14:45:45 +01:00
Aurora
1cc64a0fbc Merge branch 'master' of https://github.com/AuroraWright/Luma3DS 2016-11-06 01:10:18 +01:00
Aurora
f492318e3c The cart update pattern is only present once on NS up to 8.0 2016-11-06 01:09:44 +01:00
TuxSH
4b06bb7795 Fix svcBreak handling bug 2016-11-06 00:05:12 +01:00
Aurora
f7e570383a Add dev unit check 2016-11-05 17:46:47 +01:00
Aurora
896a088199 Fix comment 2016-11-04 22:31:43 +01:00
Aurora
f3322bd003 Fix config derp, remove 0key encryption/nand ncch encryption/CIA dev common key checks on retail consoles 2016-11-04 22:28:33 +01:00
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
Aurora
b3e3a2937a Fix CakeBrah error displaying the .dat file 2016-09-26 21:42:27 +02:00
Aurora
d010038228 Minor config descriptions changes 2016-09-26 16:00:10 +02:00
Aurora
c28eada93e Minor makefile stuff 2016-09-26 14:23:35 +02:00
Aurora
9d84a92b1f Minor stuff 2016-09-26 13:53:58 +02:00
Aurora
7884be106d Fix closing the directory object if the directory does not exist in findDumpFile 2016-09-26 13:24:37 +02:00
Aurora
5fe7c7e7e1 Up the maximum custom path size to 55 characters from 37 2016-09-26 13:03:39 +02:00
Aurora
f244b95aad Replace CakesROP's default top screen image 2016-09-25 18:06:38 +02:00
Aurora
3b5a5759b8 Forgot this 2016-09-25 15:22:07 +02:00
Aurora
528e7ee33b Git reset CakeBrah on clean for good measure 2016-09-25 15:14:07 +02:00
Aurora
c1f55735fc Reintroduce the CakeBrah patch to avoid having the Luma3DS.dat file on *hax/menuhax 2016-09-25 15:10:44 +02:00
56 changed files with 2753 additions and 1713 deletions

View File

@@ -34,8 +34,8 @@ objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \ $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
$(call rwildcard, $(dir_source), *.s *.c))) $(call rwildcard, $(dir_source), *.s *.c)))
bundled = $(dir_build)/reboot.bin.o $(dir_build)/emunand.bin.o $(dir_build)/svcGetCFWInfo.bin.o $(dir_build)/injector.bin.o \ bundled = $(dir_build)/reboot.bin.o $(dir_build)/emunand.bin.o $(dir_build)/svcGetCFWInfo.bin.o $(dir_build)/k11modules.bin.o \
$(dir_build)/loader.bin.o $(dir_build)/k11modules.bin.o $(dir_build)/arm9_exceptions.bin.o $(dir_build)/arm11_exceptions.bin.o $(dir_build)/injector.bin.o $(dir_build)/loader.bin.o $(dir_build)/arm9_exceptions.bin.o $(dir_build)/arm11_exceptions.bin.o
define bin2o define bin2o
bin2s $< | $(AS) -o $(@) bin2s $< | $(AS) -o $(@)
@@ -58,7 +58,7 @@ haxloader: a9lh
.PHONY: clean .PHONY: clean
clean: clean:
@$(MAKE) $(FLAGS) -C $(dir_haxloader) clean @$(MAKE) -C $(dir_haxloader) clean
@$(MAKE) -C $(dir_loader) clean @$(MAKE) -C $(dir_loader) clean
@$(MAKE) -C $(dir_arm9_exceptions) clean @$(MAKE) -C $(dir_arm9_exceptions) clean
@$(MAKE) -C $(dir_arm11_exceptions) clean @$(MAKE) -C $(dir_arm11_exceptions) clean

View File

@@ -83,7 +83,8 @@ _commonHandler:
cps #0x13 @ switch to supervisor mode cps #0x13 @ switch to supervisor mode
cmp r10, #0 cmp r10, #0
addne sp, #0x28 addne sp, #0x28
ldr r2, [sp, #0x1c] @ implementation details of the official svc handler ldmfd sp, {r8-r11}^ @ implementation details of the official svc handler
ldr r2, [sp, #0x1c]
ldr r4, [sp, #0x18] ldr r4, [sp, #0x18]
msr cpsr_c, r3 @ restore processor mode msr cpsr_c, r3 @ restore processor mode
tst r2, #0x20 tst r2, #0x20

View File

@@ -22,8 +22,6 @@
#include "handlers.h" #include "handlers.h"
#define FINAL_BUFFER 0xE5000000 //0x25000000
#define REG_DUMP_SIZE 4 * 23 #define REG_DUMP_SIZE 4 * 23
#define CODE_DUMP_SIZE 48 #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]; u32 registerDump[REG_DUMP_SIZE / 4];
u8 codeDump[CODE_DUMP_SIZE]; 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); while(*(vu32 *)final == 0xDEADC0DE && *((vu32 *)final + 1) == 0xDEADCAFE);
@@ -81,7 +80,7 @@ void __attribute__((noreturn)) mainHandler(u32 *regs, u32 type, u32 cpuId)
dumpHeader.codeDumpSize = copyMemory(codeDump, instr, dumpHeader.codeDumpSize, ((cpsr & 0x20) != 0) ? 2 : 4); dumpHeader.codeDumpSize = copyMemory(codeDump, instr, dumpHeader.codeDumpSize, ((cpsr & 0x20) != 0) ? 2 : 4);
//Copy register dump and code dump //Copy register dump and code dump
final = (u8 *)(FINAL_BUFFER + sizeof(ExceptionDumpHeader)); final = (u8 *)(finalBuffer + sizeof(ExceptionDumpHeader));
final += copyMemory(final, registerDump, dumpHeader.registerDumpSize, 1); final += copyMemory(final, registerDump, dumpHeader.registerDumpSize, 1);
final += copyMemory(final, codeDump, dumpHeader.codeDumpSize, 1); final += copyMemory(final, codeDump, dumpHeader.codeDumpSize, 1);
@@ -89,7 +88,7 @@ void __attribute__((noreturn)) mainHandler(u32 *regs, u32 type, u32 cpuId)
dumpHeader.stackDumpSize = copyMemory(final, (const void *)registerDump[13], 0x1000 - (registerDump[13] & 0xFFF), 1); dumpHeader.stackDumpSize = copyMemory(final, (const void *)registerDump[13], 0x1000 - (registerDump[13] & 0xFFF), 1);
final += dumpHeader.stackDumpSize; final += dumpHeader.stackDumpSize;
if(!cannotAccessVA((u8 *)0xFFFF9004)) if(!cannotAccessVA((void *)0xFFFF9004))
{ {
vu64 *additionalData = (vu64 *)final; vu64 *additionalData = (vu64 *)final;
dumpHeader.additionalDataSize = 16; dumpHeader.additionalDataSize = 16;
@@ -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; dumpHeader.totalSize = sizeof(ExceptionDumpHeader) + dumpHeader.registerDumpSize + dumpHeader.codeDumpSize + dumpHeader.stackDumpSize + dumpHeader.additionalDataSize;
//Copy header (actually optimized by the compiler) //Copy header (actually optimized by the compiler)
*(ExceptionDumpHeader *)FINAL_BUFFER = dumpHeader; *(ExceptionDumpHeader *)finalBuffer = dumpHeader;
cleanInvalidateDCacheAndDMB(); cleanInvalidateDCacheAndDMB();
mcuReboot(); //Also contains DCache-cleaning code mcuReboot(); //Also contains DCache-cleaning code

View File

@@ -62,6 +62,7 @@ _commonHandler:
bic r5, r3, #0xf bic r5, r3, #0xf
orr r5, #0x3 orr r5, #0x3
msr cpsr_c, r5 @ switch to supervisor mode msr cpsr_c, r5 @ switch to supervisor mode
ldmfd sp, {r8-r11}^
ldr r2, [sp, #0x1c] @ implementation details of the official svc handler ldr r2, [sp, #0x1c] @ implementation details of the official svc handler
ldr r4, [sp, #0x18] ldr r4, [sp, #0x18]
msr cpsr_c, r3 @ restore processor mode msr cpsr_c, r3 @ restore processor mode

View File

@@ -24,7 +24,7 @@
__author__ = "TuxSH" __author__ = "TuxSH"
__copyright__ = "Copyright (c) 2016 TuxSH" __copyright__ = "Copyright (c) 2016 TuxSH"
__license__ = "GPLv3" __license__ = "GPLv3"
__version__ = "v1.0" __version__ = "v1.2"
""" """
Parses Luma3DS exception dumps Parses Luma3DS exception dumps
@@ -33,6 +33,9 @@ Parses Luma3DS exception dumps
import argparse import argparse
from struct import unpack_from from struct import unpack_from
import os
import subprocess
# Source of hexdump: https://gist.github.com/ImmortalPC/c340564823f283fe530b # Source of hexdump: https://gist.github.com/ImmortalPC/c340564823f283fe530b
# Credits for hexdump go to the original authors # Credits for hexdump go to the original authors
# Slightly edited by TuxSH # Slightly edited by TuxSH
@@ -76,7 +79,7 @@ def hexdump(addr, src, length=16, sep='.' ):
text += chr(c) text += chr(c)
else: else:
text += sep 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) return '\n'.join(result)
@@ -104,8 +107,9 @@ if __name__ == "__main__":
raise SystemExit("Incompatible format version, please use the appropriate parser.") raise SystemExit("Incompatible format version, please use the appropriate parser.")
registers = unpack_from("<{0}I".format(nbRegisters), data, 40) registers = unpack_from("<{0}I".format(nbRegisters), data, 40)
codeDump = data[40 + 4 * nbRegisters : 40 + 4 * nbRegisters + codeDumpSize] codeOffset = 40 + 4 * nbRegisters
stackOffset = 40 + 4 * nbRegisters + codeDumpSize codeDump = data[codeOffset : codeOffset + codeDumpSize]
stackOffset = codeOffset + codeDumpSize
stackDump = data[stackOffset : stackOffset + stackDumpSize] stackDump = data[stackOffset : stackOffset + stackDumpSize]
addtionalDataOffset = stackOffset + stackDumpSize addtionalDataOffset = stackOffset + stackDumpSize
additionalData = data[addtionalDataOffset : addtionalDataOffset + additionalDataSize] additionalData = data[addtionalDataOffset : addtionalDataOffset + additionalDataSize]
@@ -139,9 +143,27 @@ if __name__ == "__main__":
print(makeRegisterLine(registerNames[i], registers[i], registerNames[i+1], registers[i+1])) 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]))) 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("\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("\nStack dump:\n")
print(hexdump(registers[13], stackDump)) print(hexdump(registers[13], stackDump))

View File

@@ -12,6 +12,7 @@ LD := arm-none-eabi-ld
OC := arm-none-eabi-objcopy OC := arm-none-eabi-objcopy
dir_source := source dir_source := source
dir_diffs := diffs
dir_cakebrah := CakeBrah dir_cakebrah := CakeBrah
dir_cakehax := CakeHax dir_cakehax := CakeHax
dir_cakesrop := CakesROP dir_cakesrop := CakesROP
@@ -22,34 +23,37 @@ ASFLAGS := -mcpu=arm946e-s
CFLAGS := -Wall -Wextra -MMD -MP -marm $(ASFLAGS) -fno-builtin -fshort-wchar -std=c11 -Wno-main -O2 -flto -ffast-math CFLAGS := -Wall -Wextra -MMD -MP -marm $(ASFLAGS) -fno-builtin -fshort-wchar -std=c11 -Wno-main -O2 -flto -ffast-math
LDFLAGS := -nostartfiles LDFLAGS := -nostartfiles
FLAGS := name=$(name).dat dir_out=$(abspath $(dir_out)) ICON=$(abspath icon.png) APP_DESCRIPTION="Noob-friendly 3DS CFW." APP_AUTHOR="Aurora Wright/TuxSH" --no-print-directory FLAGS := name=$(name).dat dir_out=$(abspath $(dir_out)) ICON=$(abspath icon.png) APP_DESCRIPTION="Noob-friendly 3DS CFW." APP_AUTHOR="Aurora Wright/TuxSH" --no-print-directory
ROPFLAGS := DATNAME=$(name).dat DISPNAME=$(name) ROPFLAGS := DATNAME=$(name).dat DISPNAME=$(name) GRAPHICS=../graphics/
objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \ objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \ $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
$(call rwildcard, $(dir_source), *.s *.c))) $(call rwildcard, $(dir_source), *.s *.c)))
.PHONY: all .PHONY: all
all: $(dir_out)/$(name).dat $(dir_out)/hax/3ds/$(name) $(dir_out)/menuhax/boot.3dsx $(dir_out)/mset/$(name).nds all: $(dir_out)/hax/3ds/$(name) $(dir_out)/menuhax/boot.3dsx $(dir_out)/mset-spider/$(name).dat $(dir_out)/mset/$(name).nds
.PHONY: clean .PHONY: clean
clean: clean:
@cd $(dir_cakebrah); git reset --hard
@$(MAKE) $(FLAGS) -C $(dir_cakebrah) clean @$(MAKE) $(FLAGS) -C $(dir_cakebrah) clean
@$(MAKE) $(FLAGS) -C $(dir_cakehax) clean @$(MAKE) $(FLAGS) -C $(dir_cakehax) clean
@$(MAKE) $(ROPFLAGS) -C $(dir_cakesrop) clean @$(MAKE) $(ROPFLAGS) -C $(dir_cakesrop) clean
@rm -rf $(dir_build) @rm -rf $(dir_build)
$(dir_out)/$(name).dat: $(dir_build)/main.bin $(dir_out)/mset-spider/$(name).dat: $(dir_build)/main.bin
@mkdir -p "$(@D)"
@$(MAKE) $(FLAGS) -C $(dir_cakehax) launcher @$(MAKE) $(FLAGS) -C $(dir_cakehax) launcher
@dd if=$(dir_build)/main.bin of=$@ bs=512 seek=144 @dd if=$(dir_build)/main.bin of=$(dir_out)/$(name).dat bs=512 seek=144
@mv $(dir_out)/$(name).dat $@
$(dir_out)/hax/3ds/$(name): $(dir_out)/hax/3ds/$(name):
@mkdir -p "$@" @mkdir -p "$@"
@$(MAKE) $(FLAGS) -C $(dir_cakebrah) @cd $(dir_cakebrah); patch -p1 < ../$(dir_diffs)/1.diff; $(MAKE) $(FLAGS); git reset --hard
@mv $(dir_out)/$(name).3dsx $(dir_out)/$(name).smdh $@ @mv $(dir_out)/$(name).3dsx $(dir_out)/$(name).smdh $@
$(dir_out)/menuhax/boot.3dsx: $(dir_out)/hax/3ds/$(name) $(dir_out)/menuhax/boot.3dsx:
@mkdir -p "$(@D)" @mkdir -p "$(@D)"
@cd $(dir_cakebrah); patch -p1 < ../menuhax.diff; $(MAKE) $(FLAGS); git reset --hard @cd $(dir_cakebrah); patch -p1 < ../$(dir_diffs)/1.diff; patch -p1 < ../$(dir_diffs)/2.diff; $(MAKE) $(FLAGS); git reset --hard
@mv $(dir_out)/$(name).3dsx $@ @mv $(dir_out)/$(name).3dsx $@
@rm $(dir_out)/$(name).smdh @rm $(dir_out)/$(name).smdh

123
haxloader/diffs/1.diff Normal file
View File

@@ -0,0 +1,123 @@
diff -uNr a/include/brahma.h b/include/brahma.h
--- a/include/brahma.h 2016-09-26 16:05:36.363067000 +0200
+++ b/include/brahma.h 2016-09-26 21:35:14.800519000 +0200
@@ -4,7 +4,7 @@
u32 brahma_init (void);
u32 brahma_exit (void);
-s32 load_arm9_payload_offset (char *filename, u32 offset, u32 max_psize);
+s32 load_arm9_payload_offset (void);
s32 load_arm9_payload_from_mem (u8* data, u32 dsize);
void redirect_codeflow (u32 *dst_addr, u32 *src_addr);
s32 map_arm9_payload (void);
@@ -13,8 +13,6 @@
s32 get_exploit_data (struct exploit_data *data);
s32 firm_reboot ();
-#define load_arm9_payload(filename) load_arm9_payload_offset(filename, 0, 0)
-
#define BRAHMA_NETWORK_PORT 80
#define ARM_JUMPOUT 0xE51FF004 // LDR PC, [PC, -#04]
diff -uNr a/source/brahma.c b/source/brahma.c
--- a/source/brahma.c 2016-09-26 16:05:36.363067000 +0200
+++ b/source/brahma.c 2016-09-26 21:37:58.660516468 +0200
@@ -179,39 +179,54 @@
return g_ext_arm9_loaded;
}
-/* reads ARM9 payload from a given path.
- filename: full path of payload
- offset: offset of the payload in the file
- max_psize: the maximum size of the payload that should be loaded (if 0, ARM9_MAX_PAYLOAD_SIZE. Should be smaller than ARM9_MAX_PAYLOAD_SIZE)
+/* reads Luma payload
returns: 0 on failure, 1 on success */
-s32 load_arm9_payload_offset (char *filename, u32 offset, u32 max_psize) {
+s32 load_arm9_payload_offset (void) {
s32 result = 0;
u32 fsize = 0;
u32 psize = 0;
+ bool use_default = true;
+ FILE *f;
- if (max_psize == 0 || max_psize > ARM9_PAYLOAD_MAX_SIZE)
- max_psize = ARM9_PAYLOAD_MAX_SIZE;
+ FILE *p = fopen("/luma/path.txt", "r");
- if (!filename)
- return result;
+ if (p) {
+ fseek(p , 0, SEEK_END);
+ psize = ftell(p);
+ if (psize > 5 && psize < 58) {
+ char path[psize + 1];
+
+ fseek(p, 0, SEEK_SET);
+ u32 bytes_read = fread(path, 1, psize, p);
+
+ if (bytes_read == psize) {
+ if (path[psize - 1] == 0xA) psize--;
+ if (path[psize - 1] == 0xD) psize--;
+ if (psize > 5 && psize < 56 && path[0] == '/' && memcmp(&path[psize - 4], ".bin", 4)) {
+ path[psize] = 0;
+ f = fopen(path, "rb");
+ if (f) use_default = false;
+ }
+ }
+ }
+ fclose(p);
+ }
+
+ if (use_default) f = fopen("/arm9loaderhax.bin", "rb");
- FILE *f = fopen(filename, "rb");
if (f) {
- fseek(f , 0, SEEK_END);
+ fseek(f, 0, SEEK_END);
fsize = ftell(f);
- if (offset < fsize) {
- psize = fsize - offset;
- if (psize > max_psize)
- psize = max_psize;
-
- g_ext_arm9_size = psize;
-
- fseek(f, offset, SEEK_SET);
- if (psize >= 8) {
- u32 bytes_read = fread(g_ext_arm9_buf, 1, psize, f);
- result = (g_ext_arm9_loaded = (bytes_read == psize));
- }
+ if (fsize > ARM9_PAYLOAD_MAX_SIZE)
+ fsize = ARM9_PAYLOAD_MAX_SIZE;
+
+ g_ext_arm9_size = fsize;
+
+ fseek(f, 0, SEEK_SET);
+ if (fsize >= 8) {
+ u32 bytes_read = fread(g_ext_arm9_buf, 1, fsize, f);
+ result = (g_ext_arm9_loaded = (bytes_read == fsize));
}
fclose(f);
}
diff -uNr a/source/main.c b/source/main.c
--- a/source/main.c 2016-09-26 16:05:36.363067000 +0200
+++ b/source/main.c 2016-09-26 21:40:35.202513018 +0200
@@ -10,7 +10,7 @@
int main (void) {
if (brahma_init()) {
- if (load_arm9_payload_offset("/" LAUNCHER_PATH, 0x12000, 0x10000) != 1)
+ if (load_arm9_payload_offset() != 1)
goto error;
firm_reboot();
brahma_exit();
@@ -22,7 +22,7 @@
error:
gfxInitDefault();
consoleInit(GFX_BOTTOM, NULL);
- printf("An error occurred while loading the payload.\nMake sure your launcher is located at:\n/" LAUNCHER_PATH);
+ printf("An error occurred while loading the payload.");
wait_any_key();
gfxExit();

View File

@@ -7,5 +7,5 @@ diff -uNr a/source/main.c b/source/main.c
int main (void) { int main (void) {
+ svcSleepThread(2500 * 1000000ULL); + svcSleepThread(2500 * 1000000ULL);
if (brahma_init()) { if (brahma_init()) {
if (load_arm9_payload_offset("/" LAUNCHER_PATH, 0x12000, 0x10000) != 1) if (load_arm9_payload_offset() != 1)
goto error; goto error;

View File

@@ -0,0 +1,11 @@
-W3
# disable alpha and set opaque bit for all pixels
-gT!
# use lz77 compression
-gzl
# 16 bit bitmap
-gB16
-gb

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

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

View File

@@ -283,8 +283,8 @@ static u32 calcSDSize(u8 *csd, int type)
result = (result << 8) | csd[7]; result = (result << 8) | csd[7];
result = (result << 2) | (csd[6] >> 6); result = (result << 2) | (csd[6] >> 6);
result = (result + 1) * mult * block_len / 512; result = (result + 1) * mult * block_len / 512;
}
break; break;
}
case 1: case 1:
result = csd[7] & 0x3F; result = csd[7] & 0x3F;
result = (result << 8) | csd[6]; 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(); InitSD();
//Nand_Init(); //Nand_Init();
SD_Init(); return SD_Init() == 0;
} }

View File

@@ -91,7 +91,7 @@ typedef struct mmcdevice {
u32 res; u32 res;
} mmcdevice; } mmcdevice;
void sdmmc_sdcard_init(); bool sdmmc_sdcard_init();
int sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, u8 *out); 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_sdcard_writesectors(u32 sector_no, u32 numsectors, const u8 *in);
//int sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, u8 *out); //int sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, u8 *out);

View File

@@ -37,11 +37,11 @@ void main(void)
payload; payload;
bool foundPayload = false; 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); u32 pathSize = f_size(&pathFile);
if(pathSize > 5 && pathSize < 40) if(pathSize > 5 && pathSize < 58)
{ {
char path[pathSize + 1]; char path[pathSize + 1];
unsigned int read; unsigned int read;
@@ -49,7 +49,7 @@ void main(void)
if(path[pathSize - 1] == 0xA) pathSize--; if(path[pathSize - 1] == 0xA) pathSize--;
if(path[pathSize - 1] == 0xD) pathSize--; if(path[pathSize - 1] == 0xD) pathSize--;
if(pathSize > 5 && pathSize < 38 && path[0] == '/' && memcmp(&path[pathSize - 4], ".bin", 4) == 0) if(pathSize > 5 && pathSize < 56 && path[0] == '/' && memcmp(&path[pathSize - 4], ".bin", 4) == 0)
{ {
path[pathSize] = 0; path[pathSize] = 0;
foundPayload = f_open(&payload, path, FA_READ) == FR_OK; foundPayload = f_open(&payload, path, FA_READ) == FR_OK;
@@ -74,12 +74,15 @@ void main(void)
f_read(&payload, payloadAddress, payloadSize, &read); f_read(&payload, payloadAddress, payloadSize, &read);
f_close(&payload); f_close(&payload);
if((u32)read == payloadSize)
{
flushDCacheRange(loaderAddress, loader_bin_size); flushDCacheRange(loaderAddress, loader_bin_size);
flushICacheRange(loaderAddress, loader_bin_size); flushICacheRange(loaderAddress, loader_bin_size);
((void (*)())loaderAddress)(); ((void (*)())loaderAddress)();
} }
} }
}
//Ensure that all memory transfers have completed and that the data cache has been flushed //Ensure that all memory transfers have completed and that the data cache has been flushed
flushEntireDCache(); flushEntireDCache();

View File

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

View File

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

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); lzss_decompress((u8 *)shared->text_addr + size);
} }
u16 progver = g_exheader.codesetinfo.flags.remasterversion[0] | (g_exheader.codesetinfo.flags.remasterversion[1] << 8);
// patch // patch
patchCode(progid, (u8 *)shared->text_addr, shared->total_size << 12); patchCode(progid, progver, (u8 *)shared->text_addr, shared->total_size << 12);
return 0; return 0;
} }

View File

@@ -7,9 +7,11 @@
static CFWInfo info; static CFWInfo info;
static void patchMemory(u8 *start, u32 size, const void *pattern, u32 patSize, int offset, const void *replace, u32 repSize, u32 count) static u32 patchMemory(u8 *start, u32 size, const void *pattern, u32 patSize, int offset, const void *replace, u32 repSize, u32 count)
{ {
for(u32 i = 0; i < count; i++) u32 i;
for(i = 0; i < count; i++)
{ {
u8 *found = memsearch(start, pattern, size, patSize); u8 *found = memsearch(start, pattern, size, patSize);
@@ -24,9 +26,11 @@ static void patchMemory(u8 *start, u32 size, const void *pattern, u32 patSize, i
size -= at + patSize; size -= at + patSize;
start = found + patSize; start = found + patSize;
} }
return 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}, FS_Path filePath = {PATH_ASCII, strnlen(path, 255) + 1, path},
archivePath = {PATH_EMPTY, 1, (u8 *)""}; archivePath = {PATH_EMPTY, 1, (u8 *)""};
@@ -34,7 +38,16 @@ static int fileOpen(IFile *file, FS_ArchiveID archiveId, const char *path, int f
return IFile_Open(file, archiveId, archivePath, filePath, flags); 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; static bool infoLoaded = false;
@@ -43,14 +56,14 @@ static void loadCFWInfo(void)
svcGetCFWInfo(&info); svcGetCFWInfo(&info);
IFile file; 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); IFile_Close(&file);
infoLoaded = true; infoLoaded = true;
} }
} }
static bool secureInfoExists(void) static inline bool secureInfoExists(void)
{ {
static bool exists = false; static bool exists = false;
@@ -67,7 +80,7 @@ static bool secureInfoExists(void)
return exists; return exists;
} }
static void loadCustomVerString(u16 *out, u32 *verStringSize, u32 currentNand) static inline void loadCustomVerString(u16 *out, u32 *verStringSize, u32 currentNand)
{ {
static const char *paths[] = { "/luma/customversion_sys.txt", static const char *paths[] = { "/luma/customversion_sys.txt",
"/luma/customversion_emu.txt", "/luma/customversion_emu.txt",
@@ -77,7 +90,7 @@ static void loadCustomVerString(u16 *out, u32 *verStringSize, u32 currentNand)
IFile file; IFile file;
if(R_SUCCEEDED(fileOpen(&file, ARCHIVE_SDMC, paths[currentNand], FS_OPEN_READ))) if(R_SUCCEEDED(openLumaFile(&file, paths[currentNand])))
{ {
u64 fileSize; u64 fileSize;
@@ -125,7 +138,7 @@ static void loadCustomVerString(u16 *out, u32 *verStringSize, u32 currentNand)
} }
} }
static void loadTitleCodeSection(u64 progId, u8 *code, u32 size) static inline u32 loadTitleCodeSection(u64 progId, u8 *code, u32 size)
{ {
/* Here we look for "/luma/code_sections/[u64 titleID in hex, uppercase].bin" /* Here we look for "/luma/code_sections/[u64 titleID in hex, uppercase].bin"
If it exists it should be a decompressed binary code file */ If it exists it should be a decompressed binary code file */
@@ -134,22 +147,27 @@ static void loadTitleCodeSection(u64 progId, u8 *code, u32 size)
progIdToStr(path + 35, progId); progIdToStr(path + 35, progId);
IFile file; IFile file;
u32 ret = 0;
if(R_SUCCEEDED(fileOpen(&file, ARCHIVE_SDMC, path, FS_OPEN_READ))) if(R_SUCCEEDED(openLumaFile(&file, path)))
{ {
u64 fileSize; u64 fileSize;
if(R_SUCCEEDED(IFile_GetSize(&file, &fileSize)) && fileSize <= size) if(R_FAILED(IFile_GetSize(&file, &fileSize)) || fileSize > size) ret = 1;
else
{ {
u64 total; u64 total;
IFile_Read(&file, &total, code, fileSize);
if(R_FAILED(IFile_Read(&file, &total, code, fileSize)) || total != fileSize) ret = 1;
} }
IFile_Close(&file); IFile_Close(&file);
} }
return ret;
} }
static void loadTitleLocaleConfig(u64 progId, u8 *regionId, u8 *languageId) static inline u32 loadTitleLocaleConfig(u64 progId, u8 *regionId, u8 *languageId)
{ {
/* Here we look for "/luma/locales/[u64 titleID in hex, uppercase].txt" /* Here we look for "/luma/locales/[u64 titleID in hex, uppercase].txt"
If it exists it should contain, for example, "EUR IT" */ If it exists it should contain, for example, "EUR IT" */
@@ -158,19 +176,25 @@ static void loadTitleLocaleConfig(u64 progId, u8 *regionId, u8 *languageId)
progIdToStr(path + 29, progId); progIdToStr(path + 29, progId);
IFile file; IFile file;
u32 ret = 0;
if(R_SUCCEEDED(fileOpen(&file, ARCHIVE_SDMC, path, FS_OPEN_READ))) if(R_SUCCEEDED(openLumaFile(&file, path)))
{ {
u64 fileSize; u64 fileSize;
if(R_SUCCEEDED(IFile_GetSize(&file, &fileSize)) && fileSize > 5 && fileSize < 9) if(R_FAILED(IFile_GetSize(&file, &fileSize)) || fileSize < 6 || fileSize > 8) ret = 1;
else
{ {
char buf[fileSize]; char buf[fileSize];
u64 total; u64 total;
if(R_SUCCEEDED(IFile_Read(&file, &total, buf, fileSize))) if(R_FAILED(IFile_Read(&file, &total, buf, fileSize))) ret = 1;
else
{ {
for(u32 i = 0; i < 7; i++) u32 i,
j;
for(i = 0; i < 7; i++)
{ {
static const char *regions[] = {"JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"}; static const char *regions[] = {"JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"};
@@ -181,24 +205,28 @@ static void loadTitleLocaleConfig(u64 progId, u8 *regionId, u8 *languageId)
} }
} }
for(u32 i = 0; i < 12; i++) for(j = 0; j < 12; j++)
{ {
static const char *languages[] = {"JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"}; static const char *languages[] = {"JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"};
if(memcmp(buf + 4, languages[i], 2) == 0) if(memcmp(buf + 4, languages[j], 2) == 0)
{ {
*languageId = (u8)i; *languageId = (u8)j;
break; break;
} }
} }
if(i == 7 || j == 12) ret = 1;
} }
} }
IFile_Close(&file); IFile_Close(&file);
} }
return ret;
} }
static u8 *getCfgOffsets(u8 *code, u32 size, u32 *CFGUHandleOffset) static inline u8 *getCfgOffsets(u8 *code, u32 size, u32 *CFGUHandleOffset)
{ {
/* HANS: /* HANS:
Look for error code which is known to be stored near cfg:u handle Look for error code which is known to be stored near cfg:u handle
@@ -217,6 +245,8 @@ static u8 *getCfgOffsets(u8 *code, u32 size, u32 *CFGUHandleOffset)
} }
} }
if(n > 0)
{
for(u8 *CFGU_GetConfigInfoBlk2_endPos = code; CFGU_GetConfigInfoBlk2_endPos < code + size - 8; CFGU_GetConfigInfoBlk2_endPos += 4) for(u8 *CFGU_GetConfigInfoBlk2_endPos = code; CFGU_GetConfigInfoBlk2_endPos < code + size - 8; CFGU_GetConfigInfoBlk2_endPos += 4)
{ {
static const u32 CFGU_GetConfigInfoBlk2_endPattern[] = {0xE8BD8010, 0x00010082}; static const u32 CFGU_GetConfigInfoBlk2_endPattern[] = {0xE8BD8010, 0x00010082};
@@ -234,18 +264,21 @@ static u8 *getCfgOffsets(u8 *code, u32 size, u32 *CFGUHandleOffset)
CFGU_GetConfigInfoBlk2_endPos += 4; CFGU_GetConfigInfoBlk2_endPos += 4;
} }
} }
}
return NULL; return NULL;
} }
static void patchCfgGetLanguage(u8 *code, u32 size, u8 languageId, u8 *CFGU_GetConfigInfoBlk2_endPos) static inline u32 patchCfgGetLanguage(u8 *code, u32 size, u8 languageId, u8 *CFGU_GetConfigInfoBlk2_endPos)
{ {
u8 *CFGU_GetConfigInfoBlk2_startPos; //Let's find STMFD SP (there might be a NOP before, but nevermind) u8 *CFGU_GetConfigInfoBlk2_startPos; //Let's find STMFD SP (there might be a NOP before, but nevermind)
for(CFGU_GetConfigInfoBlk2_startPos = CFGU_GetConfigInfoBlk2_endPos - 4; for(CFGU_GetConfigInfoBlk2_startPos = CFGU_GetConfigInfoBlk2_endPos - 4;
CFGU_GetConfigInfoBlk2_startPos >= code && *((u16 *)CFGU_GetConfigInfoBlk2_startPos + 1) != 0xE92D; CFGU_GetConfigInfoBlk2_startPos >= code && *((u16 *)CFGU_GetConfigInfoBlk2_startPos + 1) != 0xE92D;
CFGU_GetConfigInfoBlk2_startPos -= 2); CFGU_GetConfigInfoBlk2_startPos -= 4);
if(CFGU_GetConfigInfoBlk2_startPos >= code)
{
for(u8 *languageBlkIdPos = code; languageBlkIdPos < code + size; languageBlkIdPos += 4) for(u8 *languageBlkIdPos = code; languageBlkIdPos < code + size; languageBlkIdPos += 4)
{ {
if(*(u32 *)languageBlkIdPos == 0xA0002) if(*(u32 *)languageBlkIdPos == 0xA0002)
@@ -273,149 +306,114 @@ static void patchCfgGetLanguage(u8 *code, u32 size, u8 languageId, u8 *CFGU_GetC
if(found) if(found)
{ {
*((u32 *)instr - 1) = 0xE3A00000 | languageId; // mov r0, sp => mov r0, =languageId *((u32 *)instr - 1) = 0xE3A00000 | languageId; //mov r0, sp => mov r0, =languageId
*(u32 *)instr = 0xE5CD0000; // bl CFGU_GetConfigInfoBlk2 => strb r0, [sp] *(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) = 0xE3B00000; //(1 or 2 instructions) => movs r0, 0 (result code)
//We're done //We're done
return 0;
}
}
}
}
}
}
return 1;
}
static inline void patchCfgGetRegion(u8 *code, u32 size, u8 regionId, u32 CFGUHandleOffset)
{
for(u8 *cmdPos = code; cmdPos < code + size - 28; cmdPos += 4)
{
static const u32 cfgSecureInfoGetRegionCmdPattern[] = {0xEE1D0F70, 0xE3A00802};
u32 *cmp = (u32 *)cmdPos;
if(*cmp == cfgSecureInfoGetRegionCmdPattern[1])
{
for(u32 i = 1; i < 3; i++)
if((*(cmp - i) & 0xFFFF0FFF) == cfgSecureInfoGetRegionCmdPattern[0] && *((u16 *)cmdPos + 5) == 0xE59F &&
*(u32 *)(cmdPos + 16 + *((u16 *)cmdPos + 4)) == CFGUHandleOffset)
{
cmp[3] = 0xE3A00000 | regionId; //mov r0, =regionId
cmp[4] = 0xE5C40008; //strb r0, [r4, #8]
cmp[5] = 0xE3A00000; //mov r0, #0 (result code)
cmp[6] = 0xE5840004; //str r0, [r4, #4]
//The remaining, not patched, function code will do the rest for us
return; return;
} }
} }
} }
}
}
} }
static void patchCfgGetRegion(u8 *code, u32 size, u8 regionId, u32 CFGUHandleOffset) void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
{
for(u8 *cmdPos = code; cmdPos < code + size - 28; cmdPos += 4)
{
static const u32 cfgSecureInfoGetRegionCmdPattern[] = {0xEE1D4F70, 0xE3A00802, 0xE5A40080};
u32 *cmp = (u32 *)cmdPos;
if(cmp[0] == cfgSecureInfoGetRegionCmdPattern[0] && cmp[1] == cfgSecureInfoGetRegionCmdPattern[1] &&
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]
//The remaining, not patched, function code will do the rest for us
break;
}
}
}
void patchCode(u64 progId, u8 *code, u32 size)
{ {
loadCFWInfo(); loadCFWInfo();
u32 res = 0;
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 static const u8 pattern[] = {
case 0x0004003000008202LL: // EUR Menu 0x0A, 0x0C, 0x00, 0x10
case 0x0004003000009802LL: // JPN Menu },
case 0x000400300000A102LL: // CHN Menu patch[] = {
case 0x000400300000A902LL: // KOR Menu
case 0x000400300000B102LL: // TWN Menu
{
static const u8 regionFreePattern[] = {
0x00, 0x00, 0x55, 0xE3, 0x01, 0x10, 0xA0
};
static const u8 regionFreePatch[] = {
0x01, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1 0x01, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1
}; };
//Patch SMDH region checks //Patch SMDH region checks
patchMemory(code, size, if(!patchMemory(code, size,
regionFreePattern, pattern,
sizeof(regionFreePattern), -16, sizeof(pattern), -31,
regionFreePatch, patch,
sizeof(regionFreePatch), 1 sizeof(patch), 1
); )) res++;
break;
} }
case 0x0004013000002C02LL: // NIM else if(progId == 0x0004013000003202LL) //FRIENDS
{ {
static const u8 blockAutoUpdatesPattern[] = { static const u8 pattern[] = {
0x25, 0x79, 0x0B, 0x99 0x42, 0xE0, 0x1E, 0xFF
};
static const u8 blockAutoUpdatesPatch[] = {
0xE3, 0xA0
}; };
//Block silent auto-updates u8 mostRecentFpdVer = 8;
patchMemory(code, size,
blockAutoUpdatesPattern,
sizeof(blockAutoUpdatesPattern), 0,
blockAutoUpdatesPatch,
sizeof(blockAutoUpdatesPatch), 1
);
//Apply only if the user booted with R u8 *off = memsearch(code, pattern, size, sizeof(pattern));
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 if(off == NULL) res++;
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 //Allow online access to work with old friends modules
if(fpdVer != NULL && fpdVer[9] < mostRecentFpdVer) fpdVer[9] = mostRecentFpdVer; else if(off[0xA] < mostRecentFpdVer) off[0xA] = mostRecentFpdVer;
break;
} }
case 0x0004001000021000LL: // USA MSET else if((progId == 0x0004001000021000LL || //USA MSET
case 0x0004001000020000LL: // JPN MSET progId == 0x0004001000020000LL || //JPN MSET
case 0x0004001000022000LL: // EUR MSET progId == 0x0004001000022000LL || //EUR MSET
case 0x0004001000026000LL: // CHN MSET progId == 0x0004001000026000LL || //CHN MSET
case 0x0004001000027000LL: // KOR MSET progId == 0x0004001000027000LL || //KOR MSET
case 0x0004001000028000LL: // TWN MSET progId == 0x0004001000028000LL) //TWN MSET
&& CONFIG(PATCHVERSTRING))
{ {
if(CONFIG(PATCHVERSTRING)) static const u16 pattern[] = u"Ve";
{ static u16 *patch;
static const u16 verPattern[] = u"Ver."; u32 patchSize = 0,
static u16 *verString; currentNand = BOOTCFG_NAND;
u32 verStringSize = 0;
u32 currentNand = BOOTCFG_NAND;
u16 customVerString[19]; u16 customVerString[19];
loadCustomVerString(customVerString, &verStringSize, currentNand); loadCustomVerString(customVerString, &patchSize, currentNand);
if(verStringSize != 0) verString = customVerString; if(patchSize != 0) patch = customVerString;
else else
{ {
verStringSize = 8; patchSize = 8;
u32 currentFirm = BOOTCFG_FIRM; u32 currentFirm = BOOTCFG_FIRM;
static u16 *verStringsNands[] = { u" Sys", static u16 *verStringsNands[] = { u" Sys",
@@ -434,198 +432,188 @@ void patchCode(u64 progId, u8 *code, u32 size)
u"SyE3", u"SyE3",
u"SyE4" }; u"SyE4" };
verString = (currentFirm != 0) == (currentNand != 0) ? verStringsNands[currentNand] : patch = (currentFirm != 0) == (currentNand != 0) ? verStringsNands[currentNand] :
(!currentNand ? verStringsSysEmu[currentFirm - 1] : verStringsEmuSys[currentNand - 1]); (!currentNand ? verStringsSysEmu[currentFirm - 1] : verStringsEmuSys[currentNand - 1]);
} }
//Patch Ver. string //Patch Ver. string
patchMemory(code, size, if(!patchMemory(code, size,
verPattern, pattern,
sizeof(verPattern) - 2, 0, sizeof(pattern) - 2, 0,
verString, patch,
verStringSize, 1 patchSize, 1
); )) res++;
} }
break; else if(progId == 0x0004013000008002LL) //NS
}
case 0x0004013000008002LL: // NS
{ {
static const u8 stopCartUpdatesPattern[] = { if(progVer > 4)
{
static const u8 pattern[] = {
0x0C, 0x18, 0xE1, 0xD8 0x0C, 0x18, 0xE1, 0xD8
}; },
static const u8 stopCartUpdatesPatch[] = { patch[] = {
0x0B, 0x18, 0x21, 0xC8 0x0B, 0x18, 0x21, 0xC8
}; };
//Disable updates from foreign carts (makes carts region-free) //Disable updates from foreign carts (makes carts region-free)
patchMemory(code, size, u32 ret = patchMemory(code, size,
stopCartUpdatesPattern, pattern,
sizeof(stopCartUpdatesPattern), 0, sizeof(pattern), 0,
stopCartUpdatesPatch, patch,
sizeof(stopCartUpdatesPatch), 2 sizeof(patch), 2
); );
if(ret == 0 || (ret == 1 && progVer > 0xB)) res++;
}
if(LOADERFLAG(ISN3DS))
{
u32 cpuSetting = MULTICONFIG(NEWCPU); u32 cpuSetting = MULTICONFIG(NEWCPU);
if(cpuSetting != 0) if(cpuSetting != 0)
{ {
static const u8 cfgN3dsCpuPattern[] = { static const u8 pattern[] = {
0x00, 0x40, 0xA0, 0xE1, 0x07 0x0C, 0x00, 0x94, 0x15
}; };
u32 *cfgN3dsCpuLoc = (u32 *)memsearch(code, cfgN3dsCpuPattern, size, sizeof(cfgN3dsCpuPattern)); u32 *off = (u32 *)memsearch(code, pattern, size, sizeof(pattern));
if(off == NULL) res++;
else
{
//Patch N3DS CPU Clock and L2 cache setting //Patch N3DS CPU Clock and L2 cache setting
if(cfgN3dsCpuLoc != NULL) *(off - 4) = *(off - 3);
{ *(off - 3) = *(off - 1);
*(cfgN3dsCpuLoc + 1) = 0xE1A00000; memcpy(off - 1, off, 16);
*(cfgN3dsCpuLoc + 8) = 0xE3A00000 | cpuSetting; *(off + 3) = 0xE3800000 | cpuSetting;
}
}
} }
} }
break; else if(progId == 0x0004013000001702LL) //CFG
}
case 0x0004013000001702LL: // CFG
{ {
static const u8 secureinfoSigCheckPattern[] = { static const u8 pattern[] = {
0x06, 0x46, 0x10, 0x48 0x06, 0x46, 0x10, 0x48
}; },
static const u8 secureinfoSigCheckPatch[] = { patch[] = {
0x00, 0x26 0x00, 0x26
}; };
//Disable SecureInfo signature check //Disable SecureInfo signature check
patchMemory(code, size, if(!patchMemory(code, size,
secureinfoSigCheckPattern, pattern,
sizeof(secureinfoSigCheckPattern), 0, sizeof(pattern), 0,
secureinfoSigCheckPatch, patch,
sizeof(secureinfoSigCheckPatch), 1 sizeof(patch), 1
); )) res++;
if(secureInfoExists()) if(secureInfoExists())
{ {
static const u16 secureinfoFilenamePattern[] = u"SecureInfo_"; static const u16 pattern[] = u"Sec",
static const u16 secureinfoFilenamePatch[] = u"C"; patch[] = u"C";
//Use SecureInfo_C //Use SecureInfo_C
patchMemory(code, size, if(patchMemory(code, size,
secureinfoFilenamePattern, pattern,
sizeof(secureinfoFilenamePattern) - 2, sizeof(pattern) - 2, 22,
sizeof(secureinfoFilenamePattern) - 2, patch,
secureinfoFilenamePatch, sizeof(patch) - 2, 2
sizeof(secureinfoFilenamePatch) - 2, 2 ) != 2) res++;
); }
} }
break; else if(progId == 0x0004013000003702LL && progVer > 0) //RO
}
case 0x0004013000003702LL: // RO
{ {
static const u8 sigCheckPattern[] = { static const u8 pattern[] = {
0x30, 0x40, 0x2D, 0xE9, 0x02 0x20, 0xA0, 0xE1, 0x8B
}; },
static const u8 sha256ChecksPattern1[] = { pattern2[] = {
0x30, 0x40, 0x2D, 0xE9, 0x24 0xE1, 0x30, 0x40, 0x2D
}; },
static const u8 sha256ChecksPattern2[] = { pattern3[] = {
0xF8, 0x4F, 0x2D, 0xE9, 0x01 0x2D, 0xE9, 0x01, 0x70
}; },
patch[] = {
static const u8 stub[] = { 0x00, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1 //mov r0, #0; bx lr
0x00, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1 // mov r0, #0; bx lr
}; };
//Disable CRR0 signature (RSA2048 with SHA256) check //Disable CRR0 signature (RSA2048 with SHA256) check
patchMemory(code, size, if(!patchMemory(code, size,
sigCheckPattern, pattern,
sizeof(sigCheckPattern), 0, sizeof(pattern), -9,
stub, patch,
sizeof(stub), 1 sizeof(patch), 1
); )) res++;
//Disable CRO0/CRR0 SHA256 hash checks (section hashes, and hash table) //Disable CRO0/CRR0 SHA256 hash checks (section hashes, and hash table)
patchMemory(code, size, if(!patchMemory(code, size,
sha256ChecksPattern1, pattern2,
sizeof(sha256ChecksPattern1), 0, sizeof(pattern2), 1,
stub, patch,
sizeof(stub), 1 sizeof(patch), 1
); )) res++;
patchMemory(code, size, if(!patchMemory(code, size,
sha256ChecksPattern2, pattern3,
sizeof(sha256ChecksPattern2), 0, sizeof(pattern3), -2,
stub, patch,
sizeof(stub), 1 sizeof(patch), 1
); )) res++;
break;
} }
case 0x0004003000008A02LL: // ErrDisp else if(progId == 0x0004003000008A02LL && MULTICONFIG(DEVOPTIONS) == 1) //ErrDisp
{ {
if(MULTICONFIG(DEVOPTIONS) == 1) static const u8 pattern[] = {
{ 0x00, 0xD0, 0xE5, 0xDB
static const u8 unitinfoCheckPattern1[] = { },
0x14, 0x00, 0xD0, 0xE5, 0xDB pattern2[] = {
0x14, 0x00, 0xD0, 0xE5, 0x01
},
patch[] = {
0x00, 0x00, 0xA0, 0xE3
}; };
static const u8 unitinfoCheckPattern2[] = { //Patch UNITINFO checks to make ErrDisp more verbose
0x14, 0x00, 0xD0, 0xE5, 0x01 if(!patchMemory(code, size,
} ; pattern,
sizeof(pattern), -1,
patch,
sizeof(patch), 1
)) res++;
static const u8 unitinfoCheckPatch[] = { if(patchMemory(code, size,
0x00, 0x00, 0xA0, 0xE3 pattern2,
} ; sizeof(pattern2), 0,
patch,
patchMemory(code, size, sizeof(patch), 3
unitinfoCheckPattern1, ) != 3) res++;
sizeof(unitinfoCheckPattern1), 0,
unitinfoCheckPatch,
sizeof(unitinfoCheckPatch), 1
);
patchMemory(code, size,
unitinfoCheckPattern2,
sizeof(unitinfoCheckPattern2), 0,
unitinfoCheckPatch,
sizeof(unitinfoCheckPatch), 3
);
} }
break; else if(CONFIG(USELANGEMUANDCODE) && (u32)((progId & 0xFFFFFFF000000000LL) >> 0x24) == 0x0004000)
}
default:
if(CONFIG(USELANGEMUANDCODE))
{
if((u32)((progId & 0xFFFFFFF000000000LL) >> 0x24) == 0x0004000)
{ {
//External .code section loading //External .code section loading
loadTitleCodeSection(progId, code, size); res += loadTitleCodeSection(progId, code, size);
//Language emulation //Language emulation
u8 regionId = 0xFF, u8 regionId = 0xFF,
languageId = 0xFF; languageId;
loadTitleLocaleConfig(progId, &regionId, &languageId); res += loadTitleLocaleConfig(progId, &regionId, &languageId);
if(regionId != 0xFF || regionId != 0xFF) if(!res && regionId != 0xFF)
{ {
u32 CFGUHandleOffset; u32 CFGUHandleOffset;
u8 *CFGU_GetConfigInfoBlk2_endPos = getCfgOffsets(code, size, &CFGUHandleOffset); u8 *CFGU_GetConfigInfoBlk2_endPos = getCfgOffsets(code, size, &CFGUHandleOffset);
if(CFGU_GetConfigInfoBlk2_endPos != NULL) if(CFGU_GetConfigInfoBlk2_endPos == NULL) res++;
else
{ {
if(languageId != 0xFF) patchCfgGetLanguage(code, size, languageId, CFGU_GetConfigInfoBlk2_endPos); res += patchCfgGetLanguage(code, size, languageId, CFGU_GetConfigInfoBlk2_endPos);
if(regionId != 0xFF) patchCfgGetRegion(code, size, regionId, CFGUHandleOffset); patchCfgGetRegion(code, size, regionId, CFGUHandleOffset);
}
} }
} }
} }
break; if(res != 0) svcBreak(USERBREAK_ASSERT);
}
} }

View File

@@ -2,15 +2,15 @@
#include <3ds/types.h> #include <3ds/types.h>
#define CONFIG(a) (((info.config >> (a + 21)) & 1) != 0) #define CONFIG(a) (((info.config >> (a + 20)) & 1) != 0)
#define MULTICONFIG(a) ((info.config >> (a * 2 + 9)) & 3) #define MULTICONFIG(a) ((info.config >> (a * 2 + 8)) & 3)
#define BOOTCONFIG(a, b) ((info.config >> a) & b) #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_NAND BOOTCONFIG(0, 7)
#define BOOTCFG_FIRM BOOTCONFIG(3, 7) #define BOOTCFG_FIRM BOOTCONFIG(3, 7)
#define BOOTCFG_A9LH BOOTCONFIG(6, 1) #define BOOTCFG_A9LH BOOTCONFIG(6, 1)
#define BOOTCFG_NOFORCEFLAG BOOTCONFIG(7, 1) #define BOOTCFG_NOFORCEFLAG BOOTCONFIG(7, 1)
#define BOOTCFG_SAFEMODE BOOTCONFIG(8, 1)
enum multiOptions enum multiOptions
{ {
@@ -26,7 +26,7 @@ enum singleOptions
{ {
AUTOBOOTSYS = 0, AUTOBOOTSYS = 0,
USESYSFIRM, USESYSFIRM,
LOADSDFIRMSANDMODULES, LOADEXTFIRMSANDMODULES,
USECUSTOMPATH, USECUSTOMPATH,
USELANGEMUANDCODE, USELANGEMUANDCODE,
PATCHVERSTRING, PATCHVERSTRING,
@@ -34,4 +34,10 @@ enum singleOptions
PATCHACCESS 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) ENTRY(_start)
SECTIONS SECTIONS
{ {
. = 0x24FFFF00; . = 0x24FFFE00;
.text.start : { *(.text.start) } .text.start : { *(.text.start) }
.text : { *(.text) } .text : { *(.text) }
.data : { *(.data) } .data : { *(.data) }

View File

@@ -25,11 +25,13 @@
.global flushCaches .global flushCaches
.type flushCaches, %function .type flushCaches, %function
flushCaches: 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 , @ 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 @ 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) @ 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 mov r1, #0 @ segment counter
outer_loop: outer_loop:

View File

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

View File

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

View File

@@ -1,7 +1,9 @@
; Code originally from delebile and mid-kid
.arm.little .arm.little
payload_addr equ 0x23F00000 ; Brahma payload address. payload_addr equ 0x23F00000 ; Brahma payload address
payload_maxsize equ 0x100000 ; Maximum size for the payload (maximum that CakeBrah supports). payload_maxsize equ 0x100000 ; Maximum size for the payload (maximum that CakeBrah supports)
.create "build/reboot.bin", 0 .create "build/reboot.bin", 0
.arm .arm
@@ -25,6 +27,9 @@ payload_maxsize equ 0x100000 ; Maximum size for the payload (maximum that CakeB
cmp r0, r2 cmp r0, r2
bne pxi_wait_recv bne pxi_wait_recv
mov r4, #2
open_payload:
; Open file ; Open file
add r0, r7, #8 add r0, r7, #8
adr r1, fname adr r1, fname
@@ -32,25 +37,31 @@ payload_maxsize equ 0x100000 ; Maximum size for the payload (maximum that CakeB
ldr r6, [fopen] ldr r6, [fopen]
orr r6, 1 orr r6, 1
blx r6 blx r6
cmp r0, #0
beq read_payload
subs r4, r4, #1
beq panic
adr r0, fname
adr r1, nand_mount
mov r2, #8
bl memcpy16
b open_payload
read_payload:
; Read file ; Read file
mov r0, r7 mov r0, r7
adr r1, bytes_read adr r1, bytes_read
ldr r2, =payload_addr ldr r2, =payload_addr
mov r3, payload_maxsize ldr r3, =payload_maxsize
ldr r6, [r7] ldr r6, [r7]
ldr r6, [r6, #0x28] ldr r6, [r6, #0x28]
blx r6 blx r6
; Copy the low TID (in UTF-16) of the wanted firm to the 5th byte of the payload ; Copy the low TID (in UTF-16) of the wanted firm to the 5th byte of the payload
add r0, r8, 0x1A ldr r0, =payload_addr + 4
add r1, r0, #0x10 add r1, r8, #0x1A
ldr r2, =payload_addr + 4 mov r2, #0x10
copy_TID_low: bl memcpy16
ldrh r3, [r0], #2
strh r3, [r2], #2
cmp r0, r1
blo copy_TID_low
; Set kernel state ; Set kernel state
mov r0, #0 mov r0, #0
@@ -68,11 +79,28 @@ payload_maxsize equ 0x100000 ; Maximum size for the payload (maximum that CakeB
die: die:
b 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 bytes_read: .word 0
fopen: .ascii "OPEN" fopen: .ascii "OPEN"
.pool .pool
fname: .dcw "sdmc:/arm9loaderhax.bin" fname: .dcw "sdmc:/arm9loaderhax.bin"
.word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 .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 .align 4
kernelcode_start: kernelcode_start:
@@ -97,7 +125,8 @@ fname: .dcw "sdmc:/arm9loaderhax.bin"
cmp r1, #0 cmp r1, #0
bne outer_loop 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 ; Flush instruction cache
mcr p15, 0, r1, c7, c5, 0 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 "buttons.h"
#include "pin.h" #include "pin.h"
CfgData configData;
bool readConfig(void) 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 || memcmp(configData.magic, "CONF", 4) != 0 ||
configData.formatVersionMajor != CONFIG_VERSIONMAJOR || configData.formatVersionMajor != CONFIG_VERSIONMAJOR ||
configData.formatVersionMinor != CONFIG_VERSIONMINOR) configData.formatVersionMinor != CONFIG_VERSIONMINOR)
{ {
configData.config = 0; configData.config = 0;
return false; ret = false;
} }
else ret = true;
return true; return ret;
} }
void writeConfig(ConfigurationStatus needConfig, u32 configTemp) void writeConfig(ConfigurationStatus needConfig, u32 configTemp)
@@ -57,14 +62,14 @@ void writeConfig(ConfigurationStatus needConfig, u32 configTemp)
} }
//Merge the new options and new boot configuration //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"); 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( )", const char *multiOptionsText[] = { "Default EmuNAND: 1( ) 2( ) 3( ) 4( )",
"Screen brightness: 4( ) 3( ) 2( ) 1( )", "Screen brightness: 4( ) 3( ) 2( ) 1( )",
@@ -75,8 +80,8 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
}; };
const char *singleOptionsText[] = { "( ) Autoboot SysNAND", const char *singleOptionsText[] = { "( ) Autoboot SysNAND",
"( ) Use SysNAND FIRM if booting with R (A9LH)", "( ) Use SysNAND FIRM if booting with R",
"( ) Enable FIRMs and modules loading from SD", "( ) Enable loading external FIRMs and modules",
"( ) Use custom path", "( ) Use custom path",
"( ) Enable region/language emu. and ext. .code", "( ) Enable region/language emu. and ext. .code",
"( ) Show NAND or user string in System Settings", "( ) Show NAND or user string in System Settings",
@@ -92,7 +97,7 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
"Enable splash screen support.\n\n" "Enable splash screen support.\n\n"
"\t* 'Before payloads' displays it\n" "\t* 'Before payloads' displays it\n"
"before booting payloads.\n" "before booting payloads\n"
"(intended for splashes that display\n" "(intended for splashes that display\n"
"button hints).\n\n" "button hints).\n\n"
"\t* 'After payloads' displays it\n" "\t* 'After payloads' displays it\n"
@@ -103,22 +108,26 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
"Luma3DS boots.\n\n" "Luma3DS boots.\n\n"
"4, 6 or 8 digits can be selected.\n\n" "4, 6 or 8 digits can be selected.\n\n"
"The ABXY buttons and the directional\n" "The ABXY buttons and the directional\n"
"pad buttons can be used as keys.", "pad buttons can be used as keys.\n\n"
"A message can also be displayed\n"
"(refer to the wiki for instructions).",
"Select the New 3DS CPU mode.\n\n" "Select the New 3DS CPU mode.\n\n"
"It will be always enabled.\n\n" "This won't apply to\n"
"New 3DS exclusive/enhanced games.\n\n"
"'Clock+L2' can cause issues with some\n" "'Clock+L2' can cause issues with some\n"
"games.", "games.",
"Select the developer features.\n\n" "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" "\t* 'ErrDisp' displays debug info\n"
"on the 'An error has occurred' screen.\n" "on the 'An error has occurred' screen.\n"
"\t* 'UNITINFO' makes the console be\n" "\t* 'UNITINFO' makes the console be\n"
"always detected as a development unit\n" "always detected as a development unit\n"
"(which breaks online features and\n" "(which breaks online features and\n"
"allows booting some developer\n" "allows booting some developer\n"
"software).\n\n" "software and installing dev CIAs).\n\n"
"Only change this if you know what you\n" "Only change this if you know what you\n"
"are doing!", "are doing!",
@@ -142,9 +151,10 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
"1/2/3/4), also add A if you have\n" "1/2/3/4), also add A if you have\n"
"a matching payload.", "a matching payload.",
"Enable loading FIRMs and\n" "Enable loading external FIRMs and\n"
"system modules from the SD card.\n\n" "system modules.\n\n"
"This isn't needed in most cases.", "This isn't needed in most cases.\n\n"
"Refer to the wiki for instructions.",
"Use a custom path for the\n" "Use a custom path for the\n"
"Luma3DS payload.\n\n" "Luma3DS payload.\n\n"
@@ -158,12 +168,13 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
"out-of-region games work.\n\n" "out-of-region games work.\n\n"
"Refer to the wiki for instructions.", "Refer to the wiki for instructions.",
"Enable showing the current NAND:\n\n" "Enable showing the current NAND/FIRM:\n\n"
"\t* Sys = SysNAND\n" "\t* Sys = SysNAND\n"
"\t* Emu = EmuNAND 1\n" "\t* Emu = EmuNAND 1\n"
"\t* EmuX = EmuNAND X\n" "\t* EmuX = EmuNAND X\n"
"\t* SysE = SysNAND with EmuNAND 1 FIRM\n" "\t* SysE = SysNAND with EmuNAND 1 FIRM\n"
"\t* SyEX = SysNAND with EmuNAND X FIRM\n" "\t* SyEX = SysNAND with EmuNAND X FIRM\n"
"\t* EmuS = EmuNAND 1 with SysNAND FIRM\n"
"\t* EmXS = EmuNAND X with SysNAND FIRM\n\n" "\t* EmXS = EmuNAND X with SysNAND FIRM\n\n"
"or an user-defined custom string in\n" "or an user-defined custom string in\n"
"System Settings.\n\n" "System Settings.\n\n"
@@ -185,25 +196,36 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
u32 posXs[4]; u32 posXs[4];
u32 posY; u32 posY;
u32 enabled; u32 enabled;
bool visible;
} multiOptions[] = { } multiOptions[] = {
{ .posXs = {19, 24, 29, 34} }, { .posXs = {19, 24, 29, 34}, .visible = isSdMode },
{ .posXs = {21, 26, 31, 36} }, { .posXs = {21, 26, 31, 36}, .visible = true },
{ .posXs = {12, 22, 31, 0} }, { .posXs = {12, 22, 31, 0}, .visible = true },
{ .posXs = {14, 19, 24, 29} }, { .posXs = {14, 19, 24, 29}, .visible = true },
{ .posXs = {17, 26, 32, 44} }, { .posXs = {17, 26, 32, 44}, .visible = ISN3DS },
{ .posXs = {19, 30, 42, 0} } { .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 { struct singleOption {
u32 posY; u32 posY;
bool enabled; 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 //Parse the existing options
for(u32 i = 0; i < multiOptionsAmount; i++) for(u32 i = 0; i < multiOptionsAmount; i++)
@@ -224,7 +246,7 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
//Display all the multiple choice options in white //Display all the multiple choice options in white
for(u32 i = 0; i < multiOptionsAmount; i++) for(u32 i = 0; i < multiOptionsAmount; i++)
{ {
if(!(i == NEWCPU && !isN3DS)) if(multiOptions[i].visible)
{ {
multiOptions[i].posY = endPos + SPACING_Y; multiOptions[i].posY = endPos + SPACING_Y;
endPos = drawString(multiOptionsText[i], true, 10, multiOptions[i].posY, COLOR_WHITE); endPos = drawString(multiOptionsText[i], true, 10, multiOptions[i].posY, COLOR_WHITE);
@@ -233,16 +255,23 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
} }
endPos += SPACING_Y / 2; endPos += SPACING_Y / 2;
u32 color = COLOR_RED;
//Display all the normal options in white except for the first one //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++)
{
if(singleOptions[i].visible)
{ {
singleOptions[i].posY = endPos + SPACING_Y; singleOptions[i].posY = endPos + SPACING_Y;
endPos = drawString(singleOptionsText[i], true, 10, singleOptions[i].posY, color); 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(singleOptions[i].enabled) drawCharacter(selected, true, 10 + SPACING_X, singleOptions[i].posY, color);
if(color == COLOR_RED)
{
selectedOption = i + multiOptionsAmount;
color = COLOR_WHITE; color = COLOR_WHITE;
} }
}
}
drawString(optionsDescription[selectedOption], false, 10, 10, COLOR_WHITE); drawString(optionsDescription[selectedOption], false, 10, 10, COLOR_WHITE);
@@ -262,24 +291,31 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
//Remember the previously selected option //Remember the previously selected option
u32 oldSelectedOption = selectedOption; u32 oldSelectedOption = selectedOption;
while(true)
{
switch(pressed) switch(pressed)
{ {
case BUTTON_UP: case BUTTON_UP:
if(!selectedOption) selectedOption = totalIndexes; selectedOption = !selectedOption ? totalIndexes : selectedOption - 1;
else selectedOption = (selectedOption == NEWCPU + 1 && !isN3DS) ? selectedOption - 2 : selectedOption - 1;
break; break;
case BUTTON_DOWN: case BUTTON_DOWN:
if(selectedOption == totalIndexes) selectedOption = 0; selectedOption = selectedOption == totalIndexes ? 0 : selectedOption + 1;
else selectedOption = (selectedOption == NEWCPU - 1 && !isN3DS) ? selectedOption + 2 : selectedOption + 1;
break; break;
case BUTTON_LEFT: case BUTTON_LEFT:
pressed = BUTTON_DOWN;
selectedOption = 0; selectedOption = 0;
break; break;
case BUTTON_RIGHT: case BUTTON_RIGHT:
pressed = BUTTON_UP;
selectedOption = totalIndexes; selectedOption = totalIndexes;
break; break;
default: }
continue;
if(selectedOption < multiOptionsAmount)
{
if(multiOptions[selectedOption].visible) break;
}
else if(singleOptions[selectedOption - multiOptionsAmount].visible) break;
} }
if(selectedOption == oldSelectedOption) continue; if(selectedOption == oldSelectedOption) continue;
@@ -338,18 +374,18 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
} }
//Preserve the last-used boot options (first 9 bits) //Preserve the last-used boot options (first 9 bits)
configData.config &= 0x1FF; configData.config &= 0xFF;
//Parse and write the new configuration //Parse and write the new configuration
for(u32 i = 0; i < multiOptionsAmount; i++) 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++) 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); u32 newPinMode = MULTICONFIG(PIN);
if(newPinMode != 0) newPin(oldPinStatus && newPinMode == oldPinMode, newPinMode); 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 //Wait for the pressed buttons to change
while(HID_PAD & PIN_BUTTONS); while(HID_PAD & PIN_BUTTONS);

View File

@@ -24,19 +24,18 @@
#include "types.h" #include "types.h"
#define CONFIG(a) (((configData.config >> (a + 21)) & 1) != 0) #define CONFIG(a) (((configData.config >> (a + 20)) & 1) != 0)
#define MULTICONFIG(a) ((configData.config >> (a * 2 + 9)) & 3) #define MULTICONFIG(a) ((configData.config >> (a * 2 + 8)) & 3)
#define BOOTCONFIG(a, b) ((configData.config >> a) & b) #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_VERSIONMAJOR 1
#define CONFIG_VERSIONMINOR 6 #define CONFIG_VERSIONMINOR 7
#define BOOTCFG_NAND BOOTCONFIG(0, 7) #define BOOTCFG_NAND BOOTCONFIG(0, 7)
#define BOOTCFG_FIRM BOOTCONFIG(3, 7) #define BOOTCFG_FIRM BOOTCONFIG(3, 7)
#define BOOTCFG_A9LH BOOTCONFIG(6, 1) #define BOOTCFG_A9LH BOOTCONFIG(6, 1)
#define BOOTCFG_NOFORCEFLAG BOOTCONFIG(7, 1) #define BOOTCFG_NOFORCEFLAG BOOTCONFIG(7, 1)
#define BOOTCFG_SAFEMODE BOOTCONFIG(8, 1)
enum multiOptions enum multiOptions
{ {
@@ -52,7 +51,7 @@ enum singleOptions
{ {
AUTOBOOTSYS = 0, AUTOBOOTSYS = 0,
USESYSFIRM, USESYSFIRM,
LOADSDFIRMSANDMODULES, LOADEXTFIRMSANDMODULES,
USECUSTOMPATH, USECUSTOMPATH,
USELANGEMUANDCODE, USELANGEMUANDCODE,
PATCHVERSTRING, PATCHVERSTRING,
@@ -60,14 +59,6 @@ enum singleOptions
PATCHACCESS PATCHACCESS
}; };
typedef struct __attribute__((packed))
{
char magic[4];
u16 formatVersionMajor, formatVersionMinor;
u32 config;
} CfgData;
typedef enum ConfigurationStatus typedef enum ConfigurationStatus
{ {
DONT_CONFIGURE = 0, DONT_CONFIGURE = 0,
@@ -75,9 +66,6 @@ typedef enum ConfigurationStatus
CREATE_CONFIGURATION CREATE_CONFIGURATION
} ConfigurationStatus; } ConfigurationStatus;
extern CfgData configData;
extern bool isN3DS;
bool readConfig(void); bool readConfig(void);
void writeConfig(ConfigurationStatus needConfig, u32 configTemp); 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 * Crypto libs from http://github.com/b1l1s/ctr
* kernel9Loader code originally adapted from https://github.com/Reisyukaku/ReiNand/blob/228c378255ba693133dec6f3368e14d386f2cde7/source/crypto.c#L233 * 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 * 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 "crypto.h"
#include "memory.h" #include "memory.h"
#include "strings.h"
#include "utils.h"
#include "fatfs/sdmmc/sdmmc.h" #include "fatfs/sdmmc/sdmmc.h"
/**************************************************************** /****************************************************************
@@ -82,7 +85,7 @@ __asm__\
static void aes_setkey(u8 keyslot, const void *key, u32 keyType, u32 mode) 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; u32 *key32 = (u32 *)key;
*REG_AESCNT = (*REG_AESCNT & ~(AES_CNT_INPUT_ENDIAN | AES_CNT_INPUT_ORDER)) | mode; *REG_AESCNT = (*REG_AESCNT & ~(AES_CNT_INPUT_ENDIAN | AES_CNT_INPUT_ORDER)) | mode;
*REG_AESKEYCNT = (*REG_AESKEYCNT >> 6 << 6) | keyslot | AES_KEYCNT_WRITE; *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; const u32 *iv32 = (const u32 *)iv;
*REG_AESCNT = (*REG_AESCNT & ~(AES_CNT_INPUT_ENDIAN | AES_CNT_INPUT_ORDER)) | mode; *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) if(mode & AES_INPUT_NORMAL)
{ {
REG_AESCTR[0] = iv32[3]; REG_AESCTR[0] = iv32[3];
@@ -131,7 +134,7 @@ static void aes_advctr(void *ctr, u32 val, u32 mode)
int i; int i;
if(mode & AES_INPUT_BE) if(mode & AES_INPUT_BE)
{ {
for(i = 0; i < 4; ++i) // Endian swap for(i = 0; i < 4; ++i) //Endian swap
BSWAP32(ctr32[i]); BSWAP32(ctr32[i]);
} }
@@ -146,7 +149,7 @@ static void aes_advctr(void *ctr, u32 val, u32 mode)
if(mode & AES_INPUT_BE) if(mode & AES_INPUT_BE)
{ {
for(i = 0; i < 4; ++i) // Endian swap for(i = 0; i < 4; ++i) //Endian swap
BSWAP32(ctr32[i]); BSWAP32(ctr32[i]);
} }
} }
@@ -186,7 +189,7 @@ static void aes_batch(void *dst, const void *src, u32 blockCount)
while(rbc) 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++;
*REG_AESWRFIFO = *src32++; *REG_AESWRFIFO = *src32++;
@@ -195,7 +198,7 @@ static void aes_batch(void *dst, const void *src, u32 blockCount)
wbc--; 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;
*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; 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) if((mode & AES_ALL_MODES) == AES_CBC_DECRYPT_MODE)
{ {
memcpy(iv, src + (blocks - 1) * AES_BLOCK_SIZE, AES_BLOCK_SIZE); memcpy(iv, src + (blocks - 1) * AES_BLOCK_SIZE, AES_BLOCK_SIZE);
aes_change_ctrmode(iv, AES_INPUT_BE | AES_INPUT_NORMAL, ivMode); aes_change_ctrmode(iv, AES_INPUT_BE | AES_INPUT_NORMAL, ivMode);
} }
// Process the current batch //Process the current batch
aes_batch(dst, src, blocks); 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) if((mode & AES_ALL_MODES) == AES_CBC_ENCRYPT_MODE)
{ {
memcpy(iv, dst + (blocks - 1) * AES_BLOCK_SIZE, AES_BLOCK_SIZE); memcpy(iv, dst + (blocks - 1) * AES_BLOCK_SIZE, AES_BLOCK_SIZE);
aes_change_ctrmode(iv, AES_INPUT_BE | AES_INPUT_NORMAL, ivMode); 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) else if((mode & AES_ALL_MODES) == AES_CTR_MODE)
aes_advctr(iv, blocks, ivMode); 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) static void sha(void *res, const void *src, u32 size, u32 mode)
{ {
backupAndRestoreShaHash(false);
sha_wait_idle(); sha_wait_idle();
*REG_SHA_CNT = mode | SHA_CNT_OUTPUT_ENDIAN | SHA_NORMAL_ROUND; *REG_SHA_CNT = mode | SHA_CNT_OUTPUT_ENDIAN | SHA_NORMAL_ROUND;
@@ -294,24 +299,26 @@ static void sha(void *res, const void *src, u32 size, u32 mode)
/*****************************************************************/ /*****************************************************************/
static u8 __attribute__((aligned(4))) nandCtr[AES_BLOCK_SIZE]; __attribute__((aligned(4))) static u8 nandCtr[AES_BLOCK_SIZE],
shaHashBackup[SHA_256_HASH_SIZE];
static u8 nandSlot; static u8 nandSlot;
static u32 fatStart; static u32 fatStart;
static u8 __attribute__((aligned(4))) shaHashBackup[SHA_256_HASH_SIZE];
static bool didShaHashBackup = false; static bool didShaHashBackup = false;
FirmwareSource firmSource;
void ctrNandInit(void) void ctrNandInit(void)
{ {
u8 __attribute__((aligned(4))) cid[AES_BLOCK_SIZE]; __attribute__((aligned(4))) u8 cid[AES_BLOCK_SIZE],
u8 __attribute__((aligned(4))) shaSum[SHA_256_HASH_SIZE]; shaSum[SHA_256_HASH_SIZE];
sdmmc_get_cid(1, (u32 *)cid); sdmmc_get_cid(1, (u32 *)cid);
sha(shaSum, cid, sizeof(cid), SHA_256_MODE); sha(shaSum, cid, sizeof(cid), SHA_256_MODE);
memcpy(nandCtr, shaSum, sizeof(nandCtr)); 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}; __attribute__((aligned(4))) u8 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); aes_setkey(0x05, keyY0x5, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
nandSlot = 0x05; nandSlot = 0x05;
@@ -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)]; __attribute__((aligned(4))) u8 tmpCtr[sizeof(nandCtr)];
memcpy(tmpCtr, nandCtr, sizeof(nandCtr)); memcpy(tmpCtr, nandCtr, sizeof(nandCtr));
aes_advctr(tmpCtr, ((sector + fatStart) * 0x200) / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL); aes_advctr(tmpCtr, ((sector + fatStart) * 0x200) / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL);
//Read //Read
u32 result; int result;
if(firmSource == FIRMWARE_SYSNAND) if(firmSource == FIRMWARE_SYSNAND)
result = sdmmc_nand_readsectors(sector + fatStart, sectorCount, outbuf); result = sdmmc_nand_readsectors(sector + fatStart, sectorCount, outbuf);
else else
@@ -347,64 +354,113 @@ u32 ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf)
return result; return result;
} }
int ctrNandWrite(u32 sector, u32 sectorCount, const u8 *inbuf)
{
u8 *buffer = (u8 *)0x23000000;
u32 bufferSize = 0xF00000;
__attribute__((aligned(4))) u8 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) void set6x7xKeys(void)
{ {
if(!isDevUnit) __attribute__((aligned(4))) const u8 keyX0x25s[2][AES_BLOCK_SIZE] = {
{ {0xCE, 0xE7, 0xD8, 0xAB, 0x30, 0xC0, 0x0D, 0xAE, 0x85, 0x0E, 0xF5, 0xE3, 0x82, 0xAC, 0x5A, 0xF3},
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}; {0x81, 0x90, 0x7A, 0x4B, 0x6F, 0x1B, 0x47, 0x32, 0x3A, 0x67, 0x79, 0x74, 0xCE, 0x4A, 0xD7, 0x1B}
const u8 __attribute__((aligned(4))) keyY0x2F[AES_BLOCK_SIZE] = {0xC3, 0x69, 0xBA, 0xA2, 0x1E, 0x18, 0x8A, 0x88, 0xA9, 0xAA, 0x94, 0xE5, 0x50, 0x6A, 0x9F, 0x16}; },
keyY0x2Fs[2][AES_BLOCK_SIZE] = {
{0xC3, 0x69, 0xBA, 0xA2, 0x1E, 0x18, 0x8A, 0x88, 0xA9, 0xAA, 0x94, 0xE5, 0x50, 0x6A, 0x9F, 0x16},
{0x73, 0x25, 0xC4, 0xEB, 0x14, 0x3A, 0x0D, 0x5F, 0x5D, 0xB6, 0xE5, 0xC5, 0x7A, 0x21, 0x95, 0xAC}
};
aes_setkey(0x25, keyX0x25, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL); aes_setkey(0x25, keyX0x25s[ISDEVUNIT ? 1 : 0], AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL);
aes_setkey(0x2F, keyY0x2F, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL); aes_setkey(0x2F, keyY0x2Fs[ISDEVUNIT ? 1 : 0], AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
/* [3dbrew] The first 0x10-bytes are checked by the v6.0/v7.0 NATIVE_FIRM keyinit function, /* [3dbrew] The first 0x10-bytes are checked by the v6.0/v7.0 NATIVE_FIRM keyinit function,
when non-zero it clears this block and continues to do the key generation. when non-zero it clears this block and continues to do the key generation.
Otherwise when this block was already all-zero, it immediately returns. */ Otherwise when this block was already all-zero, it immediately returns. */
memset32((void *)0x01FFCD00, 0, 0x10); memset32((void *)0x01FFCD00, 0, 0x10);
}
} }
void decryptExeFs(u8 *inbuf) bool decryptExeFs(Cxi *cxi)
{ {
u8 *exeFsOffset = inbuf + *(u32 *)(inbuf + 0x1A0) * 0x200; bool isCxi;
u32 exeFsSize = *(u32 *)(inbuf + 0x1A4) * 0x200;
u8 __attribute__((aligned(4))) ncchCtr[AES_BLOCK_SIZE] = {0}; if(memcmp(cxi->ncch.magic, "NCCH", 4) == 0)
{
isCxi = true;
u8 *exeFsOffset = (u8 *)cxi + (cxi->ncch.exeFsOffset + 1) * 0x200;
u32 exeFsSize = (cxi->ncch.exeFsSize - 1) * 0x200;
__attribute__((aligned(4))) u8 ncchCtr[AES_BLOCK_SIZE] = {0};
for(u32 i = 0; i < 8; i++) for(u32 i = 0; i < 8; i++)
ncchCtr[7 - i] = *(inbuf + 0x108 + i); ncchCtr[7 - i] = cxi->ncch.partitionId[i];
ncchCtr[8] = 2; ncchCtr[8] = 2;
aes_setkey(0x2C, inbuf, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL); 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_use_keyslot(0x2C);
aes(inbuf - 0x200, exeFsOffset, exeFsSize / AES_BLOCK_SIZE, ncchCtr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL); 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}; bool isTicket;
u8 __attribute__((aligned(4))) cetkIv[AES_BLOCK_SIZE] = {0};
u8 __attribute__((aligned(4))) titleKey[AES_BLOCK_SIZE]; if(memcmp(ticket->sigIssuer, "Root", 4) == 0)
memcpy(titleKey, inbuf + 0x1BF, sizeof(titleKey)); {
memcpy(cetkIv, inbuf + 0x1DC, 8); isTicket = true;
__attribute__((aligned(4))) const u8 keyY0x3D[AES_BLOCK_SIZE] = {0x0C, 0x76, 0x72, 0x30, 0xF0, 0x99, 0x8F, 0x1C, 0x46, 0x82, 0x82, 0x02, 0xFA, 0xAC, 0xBE, 0x4C};
__attribute__((aligned(4))) u8 titleKey[AES_BLOCK_SIZE],
cetkIv[AES_BLOCK_SIZE] = {0};
memcpy(titleKey, ticket->titleKey, sizeof(titleKey));
memcpy(cetkIv, ticket->titleId, sizeof(ticket->titleId));
aes_setkey(0x3D, keyY0x3D, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL); aes_setkey(0x3D, keyY0x3D, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
aes_use_keyslot(0x3D); aes_use_keyslot(0x3D);
aes(titleKey, titleKey, 1, cetkIv, AES_CBC_DECRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL); aes(titleKey, titleKey, 1, cetkIv, AES_CBC_DECRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
u8 __attribute__((aligned(4))) ncchIv[AES_BLOCK_SIZE] = {0}; __attribute__((aligned(4))) u8 ncchIv[AES_BLOCK_SIZE] = {0};
aes_setkey(0x16, titleKey, AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL); aes_setkey(0x16, titleKey, AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL);
aes_use_keyslot(0x16); aes_use_keyslot(0x16);
aes(outbuf, outbuf, ncchSize / AES_BLOCK_SIZE, ncchIv, AES_CBC_DECRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL); aes(cxi, cxi, ncchSize / AES_BLOCK_SIZE, ncchIv, AES_CBC_DECRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
}
else isTicket = false;
decryptExeFs(outbuf); return isTicket && decryptExeFs(cxi);
} }
void kernel9Loader(u8 *arm9Section) void kernel9Loader(Arm9Bin *arm9Section)
{ {
//Determine the kernel9loader version //Determine the kernel9loader version
u32 k9lVersion; u32 k9lVersion;
switch(arm9Section[0x53]) switch(arm9Section->magic[3])
{ {
case 0xFF: case 0xFF:
k9lVersion = 0; k9lVersion = 0;
@@ -417,15 +473,22 @@ void kernel9Loader(u8 *arm9Section)
break; break;
} }
u32 startOfArm9Bin = *(u32 *)(arm9Section + 0x800); u32 *startOfArm9Bin = (u32 *)((u8 *)arm9Section + 0x800);
bool needToDecrypt = startOfArm9Bin != 0x47704770 && startOfArm9Bin != 0xB0862000; bool needToDecrypt = *startOfArm9Bin != 0x47704770 && *startOfArm9Bin != 0xB0862000;
if(!isDevUnit && (k9lVersion == 2 || (k9lVersion == 1 && needToDecrypt))) if(k9lVersion == 2 || (k9lVersion == 1 && needToDecrypt))
{ {
//Set 0x11 keyslot //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}; __attribute__((aligned(4))) const u8 key1s[2][AES_BLOCK_SIZE] = {
const u8 __attribute__((aligned(4))) key2[AES_BLOCK_SIZE] = {0x42, 0x3F, 0x81, 0x7A, 0x23, 0x52, 0x58, 0x31, 0x6E, 0x75, 0x8E, 0x3A, 0x39, 0x43, 0x2E, 0xD0}; {0x07, 0x29, 0x44, 0x38, 0xF8, 0xC9, 0x75, 0x93, 0xAA, 0x0E, 0x4A, 0xB4, 0xAE, 0x84, 0xC1, 0xD8},
aes_setkey(0x11, k9lVersion == 2 ? key2 : key1, AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL); {0xA2, 0xF4, 0x00, 0x3C, 0x7A, 0x95, 0x10, 0x25, 0xDF, 0x4E, 0x9E, 0x74, 0xE3, 0x0C, 0x92, 0x99}
},
key2s[2][AES_BLOCK_SIZE] = {
{0x42, 0x3F, 0x81, 0x7A, 0x23, 0x52, 0x58, 0x31, 0x6E, 0x75, 0x8E, 0x3A, 0x39, 0x43, 0x2E, 0xD0},
{0xFF, 0x77, 0xA0, 0x9A, 0x99, 0x81, 0xE9, 0x48, 0xEC, 0x51, 0xC9, 0x32, 0x5D, 0x14, 0xEC, 0x25}
};
aes_setkey(0x11, k9lVersion == 2 ? key2s[ISDEVUNIT ? 1 : 0] : key1s[ISDEVUNIT ? 1 : 0], AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL);
} }
if(needToDecrypt) if(needToDecrypt)
@@ -438,37 +501,33 @@ void kernel9Loader(u8 *arm9Section)
arm9BinSlot = 0x16; arm9BinSlot = 0x16;
//Set keyX //Set keyX
u8 __attribute__((aligned(4))) keyX[AES_BLOCK_SIZE]; __attribute__((aligned(4))) u8 keyX[AES_BLOCK_SIZE];
aes_use_keyslot(0x11); 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); aes_setkey(0x16, keyX, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL);
} }
//Set keyY //Set keyY
u8 __attribute__((aligned(4))) keyY[AES_BLOCK_SIZE]; __attribute__((aligned(4))) u8 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); aes_setkey(arm9BinSlot, keyY, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
//Set CTR //Set CTR
u8 __attribute__((aligned(4))) arm9BinCtr[AES_BLOCK_SIZE]; __attribute__((aligned(4))) u8 arm9BinCtr[AES_BLOCK_SIZE];
memcpy(arm9BinCtr, arm9Section + 0x20, sizeof(arm9BinCtr)); memcpy(arm9BinCtr, arm9Section->ctr, 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';
//Decrypt ARM9 binary //Decrypt ARM9 binary
aes_use_keyslot(arm9BinSlot); 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 //Set >=9.6 KeyXs
if(k9lVersion == 2) if(k9lVersion == 2)
{ {
u8 __attribute__((aligned(4))) keyData[AES_BLOCK_SIZE] = {0xDD, 0xDA, 0xA4, 0xC6, 0x2C, 0xC4, 0x50, 0xE9, 0xDA, 0xB6, 0x9B, 0x0D, 0x9D, 0x2A, 0x21, 0x98}; __attribute__((aligned(4))) u8 keyData[AES_BLOCK_SIZE] = {0xDD, 0xDA, 0xA4, 0xC6, 0x2C, 0xC4, 0x50, 0xE9, 0xDA, 0xB6, 0x9B, 0x0D, 0x9D, 0x2A, 0x21, 0x98},
u8 __attribute__((aligned(4))) decKey[sizeof(keyData)]; decKey[sizeof(keyData)];
//Set keys 0x19..0x1F keyXs //Set keys 0x19..0x1F keyXs
aes_use_keyslot(0x11); aes_use_keyslot(0x11);
@@ -482,22 +541,23 @@ void kernel9Loader(u8 *arm9Section)
void computePinHash(u8 *outbuf, const u8 *inbuf) void computePinHash(u8 *outbuf, const u8 *inbuf)
{ {
u8 __attribute__((aligned(4))) cid[AES_BLOCK_SIZE]; __attribute__((aligned(4))) u8 cid[AES_BLOCK_SIZE],
u8 __attribute__((aligned(4))) cipherText[AES_BLOCK_SIZE]; cipherText[AES_BLOCK_SIZE];
if(!didShaHashBackup)
{
memcpy(shaHashBackup, (void *)REG_SHA_HASH, sizeof(shaHashBackup));
didShaHashBackup = true;
}
sdmmc_get_cid(1, (u32 *)cid); sdmmc_get_cid(1, (u32 *)cid);
aes_use_keyslot(4); //Console-unique keyslot whose keys are set by the ARM9 bootROM aes_use_keyslot(0x04); //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); aes(cipherText, inbuf, 1, cid, AES_CBC_ENCRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
sha(outbuf, cipherText, sizeof(cipherText), SHA_256_MODE); sha(outbuf, cipherText, sizeof(cipherText), SHA_256_MODE);
} }
void restoreShaHashBackup(void) void backupAndRestoreShaHash(bool isRestore)
{ {
if(ISA9LH)
{
if(isRestore)
{
if(didShaHashBackup) memcpy((void *)REG_SHA_HASH, shaHashBackup, sizeof(shaHashBackup)); 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 * Crypto libs from http://github.com/b1l1s/ctr
* kernel9Loader code originally adapted from https://github.com/Reisyukaku/ReiNand/blob/228c378255ba693133dec6f3368e14d386f2cde7/source/crypto.c#L233 * 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 * 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 #pragma once
@@ -102,14 +103,14 @@
#define SHA_1_HASH_SIZE (160 / 8) #define SHA_1_HASH_SIZE (160 / 8)
extern u32 emuOffset; extern u32 emuOffset;
extern bool isN3DS, isDevUnit;
extern FirmwareSource firmSource; extern FirmwareSource firmSource;
void ctrNandInit(void); 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 set6x7xKeys(void);
void decryptExeFs(u8 *inbuf); bool decryptExeFs(Cxi *cxi);
void decryptNusFirm(const u8 *inbuf, u8 *outbuf, u32 ncchSize); bool decryptNusFirm(const Ticket *ticket, Cxi *cxi, u32 ncchSize);
void kernel9Loader(u8 *arm9Section); void kernel9Loader(Arm9Bin *arm9Section);
void computePinHash(u8 *outbuf, const u8 *inbuf); void computePinHash(u8 *outbuf, const u8 *inbuf);
void restoreShaHashBackup(void); void backupAndRestoreShaHash(bool isRestore);

View File

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

View File

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

View File

@@ -20,15 +20,15 @@
* Notices displayed by works containing it. * Notices displayed by works containing it.
*/ */
/*
* Code for locating the SDMMC struct by Normmatt
*/
#pragma once #pragma once
#include "types.h" #include "types.h"
#define NCSD_MAGIC 0x4453434E
#define ROUND_TO_4MB(a) (((a) + 0x2000 - 1) & (~(0x2000 - 1))) #define ROUND_TO_4MB(a) (((a) + 0x2000 - 1) & (~(0x2000 - 1)))
extern u32 emuOffset;
extern bool isN3DS;
void locateEmuNand(u32 *emuHeader, FirmwareSource *nandType); 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,17 +45,25 @@ void installArm9Handlers(void)
} }
} }
void installArm11Handlers(u32 *exceptionsPage, u32 stackAddress, u32 codeSetOffset) u32 installArm11Handlers(u32 *exceptionsPage, u32 stackAddress, u32 codeSetOffset)
{ {
u32 *initFPU; u32 ret;
for(initFPU = exceptionsPage; initFPU < (exceptionsPage + 0x400) && (initFPU[0] != 0xE59F0008 || initFPU[1] != 0xE5900000); initFPU++); u32 *endPos = exceptionsPage + 0x400;
u32 *mcuReboot; u32 *initFPU;
for(mcuReboot = exceptionsPage; mcuReboot < (exceptionsPage + 0x400) && (mcuReboot[0] != 0xE59F4104 || mcuReboot[1] != 0xE3A0A0C2); mcuReboot++); for(initFPU = exceptionsPage; initFPU < endPos && *initFPU != 0xE1A0D002; initFPU++);
mcuReboot--;
u32 *freeSpace; u32 *freeSpace;
for(freeSpace = initFPU; freeSpace < (exceptionsPage + 0x400) && (freeSpace[0] != 0xFFFFFFFF || freeSpace[1] != 0xFFFFFFFF); freeSpace++); for(freeSpace = initFPU; freeSpace < endPos && *freeSpace != 0xFFFFFFFF; freeSpace++);
u32 *mcuReboot;
for(mcuReboot = exceptionsPage; mcuReboot < endPos && *mcuReboot != 0xE3A0A0C2; mcuReboot++);
if(initFPU == endPos || freeSpace == endPos || mcuReboot == endPos || *(u32 *)((u8 *)freeSpace + arm11_exceptions_bin_size - 36) != 0xFFFFFFFF) ret = 1;
else
{
initFPU += 3;
mcuReboot -= 2;
memcpy(freeSpace, arm11_exceptions_bin + 32, arm11_exceptions_bin_size - 32); memcpy(freeSpace, arm11_exceptions_bin + 32, arm11_exceptions_bin_size - 32);
@@ -73,9 +81,13 @@ void installArm11Handlers(u32 *exceptionsPage, u32 stackAddress, u32 codeSetOffs
case 0xEAFFFFFE: *pos = MAKE_BRANCH(pos, mcuReboot); 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 0xE12FFF1C: pos[1] = 0xFFFF0000 + 4 * (u32)(freeSpace - exceptionsPage) + pos[1] - 32; break; //bx r12 (mainHandler)
case 0xBEEFBEEF: *pos = codeSetOffset; break; case 0xBEEFBEEF: *pos = codeSetOffset; break;
default: break;
} }
} }
ret = 0;
}
return ret;
} }
void detectAndProcessExceptionDumps(void) void detectAndProcessExceptionDumps(void)
@@ -139,13 +151,13 @@ void detectAndProcessExceptionDumps(void)
for(u32 i = 0; i < 17; i += 2) for(u32 i = 0; i < 17; i += 2)
{ {
posY = drawString(registerNames[i], true, 10, posY + SPACING_Y, COLOR_WHITE); 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); drawString(hexString, true, 10 + 7 * SPACING_X, posY, COLOR_WHITE);
if(i != 16 || dumpHeader->processor != 9) if(i != 16 || dumpHeader->processor != 9)
{ {
drawString(registerNames[i + 1], true, 10 + 22 * SPACING_X, posY, COLOR_WHITE); 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); 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++) 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); posYBottom = drawString(hexString, false, 10, posYBottom + SPACING_Y, COLOR_WHITE);
drawCharacter(':', false, 10 + 8 * SPACING_X, posYBottom, COLOR_WHITE); drawCharacter(':', false, 10 + 8 * SPACING_X, posYBottom, COLOR_WHITE);
for(u32 i = 0; i < 8 && stackDump < additionalData; i++, stackDump++) for(u32 i = 0; i < 8 && stackDump < additionalData; i++, stackDump++)
{ {
char byteString[] = "00"; 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); 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"; 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); findDumpFile(pathFolder, fileName);
memcpy(path, pathFolder, strlen(pathFolder) + 1); 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(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)) #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 installArm9Handlers(void);
void installArm11Handlers(u32 *exceptionsPage, u32 stackAddress, u32 codeSetOffset); u32 installArm11Handlers(u32 *exceptionsPage, u32 stackAddress, u32 codeSetOffset);
void detectAndProcessExceptionDumps(void); void detectAndProcessExceptionDumps(void);

View File

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

View File

@@ -134,7 +134,7 @@
/ This option has no effect when _LFN_UNICODE == 0. */ / This option has no effect when _LFN_UNICODE == 0. */
#define _FS_RPATH 0 #define _FS_RPATH 1
/* This option configures support of relative path. /* This option configures support of relative path.
/ /
/ 0: Disable relative path and remove related functions. / 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); return geterror(&handleNAND);
} }
/*
int __attribute__((noinline)) sdmmc_nand_writesectors(u32 sector_no, u32 numsectors, const u8 *in) //experimental int __attribute__((noinline)) sdmmc_nand_writesectors(u32 sector_no, u32 numsectors, const u8 *in) //experimental
{ {
if(handleNAND.isSDHC == 0) sector_no <<= 9; if(handleNAND.isSDHC == 0) sector_no <<= 9;
@@ -259,7 +258,6 @@ int __attribute__((noinline)) sdmmc_nand_writesectors(u32 sector_no, u32 numsect
inittarget(&handleSD); inittarget(&handleSD);
return geterror(&handleNAND); return geterror(&handleNAND);
} }
*/
static u32 calcSDSize(u8 *csd, int type) static u32 calcSDSize(u8 *csd, int type)
{ {
@@ -277,8 +275,8 @@ static u32 calcSDSize(u8 *csd, int type)
result = (result << 8) | csd[7]; result = (result << 8) | csd[7];
result = (result << 2) | (csd[6] >> 6); result = (result << 2) | (csd[6] >> 6);
result = (result + 1) * mult * block_len / 512; result = (result + 1) * mult * block_len / 512;
}
break; break;
}
case 1: case 1:
result = csd[7] & 0x3F; result = csd[7] & 0x3F;
result = (result << 8) | csd[6]; 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); sdmmc_send_command(device, 0x10507, device->initarg << 0x10);
} }
void sdmmc_sdcard_init() u32 sdmmc_sdcard_init()
{ {
u32 ret = 0;
InitSD(); InitSD();
Nand_Init(); if(Nand_Init() != 0) ret &= 1;
SD_Init(); if(SD_Init() != 0) ret &= 2;
return ret;
} }

View File

@@ -91,10 +91,10 @@ typedef struct mmcdevice {
u32 res; u32 res;
} mmcdevice; } mmcdevice;
void sdmmc_sdcard_init(); u32 sdmmc_sdcard_init();
int sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, u8 *out); 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_sdcard_writesectors(u32 sector_no, u32 numsectors, const u8 *in);
int sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, u8 *out); 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); void sdmmc_get_cid(bool isNand, u32 *info);
mmcdevice *getMMCDevice(int drive); mmcdevice *getMMCDevice(int drive);

View File

@@ -31,299 +31,70 @@
#include "cache.h" #include "cache.h"
#include "emunand.h" #include "emunand.h"
#include "crypto.h" #include "crypto.h"
#include "draw.h"
#include "screen.h" #include "screen.h"
#include "buttons.h"
#include "pin.h"
#include "../build/bundled.h" #include "../build/bundled.h"
extern u16 launchedFirmTidLow[8]; //Defined in start.s static Firm *firm = (Firm *)0x24000000;
static firmHeader *firm = (firmHeader *)0x24000000; u32 loadFirm(FirmwareType *firmType, FirmwareSource nandType, bool loadFromStorage, bool isSafeMode)
static const firmSectionHeader *section;
u32 emuOffset;
bool isN3DS,
isDevUnit,
isFirmlaunch;
CfgData configData;
FirmwareSource firmSource;
void main(void)
{ {
bool isA9lh;
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, isA9lh, 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[] = { const char *firmwareFiles[] = {
"/luma/firmware.bin", "firmware.bin",
"/luma/firmware_twl.bin", "firmware_twl.bin",
"/luma/firmware_agb.bin", "firmware_agb.bin",
"/luma/firmware_safe.bin" "firmware_safe.bin"
}, },
*cetkFiles[] = { *cetkFiles[] = {
"/luma/cetk", "cetk",
"/luma/cetk_twl", "cetk_twl",
"/luma/cetk_agb", "cetk_agb",
"/luma/cetk_safe" "cetk_safe"
}; };
//Load FIRM from CTRNAND //Load FIRM from CTRNAND
u32 firmVersion = firmRead(firm, (u32)*firmType); 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 && !ISDEVUNIT)
{ {
if(firmVersion < 0x18) if(firmVersion < 0x18)
{ {
//We can't boot < 3.x EmuNANDs //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."); 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; *firmType = NATIVE_FIRM1X2X;
} }
//We can't boot a 3.x/4.x NATIVE_FIRM, load one from SD //We can't boot a 3.x/4.x NATIVE_FIRM, load one from SD/CTRNAND
else if(firmVersion < 0x25) mustLoadFromSd = true; 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 > 0)
{ {
if(firmSize <= sizeof(Cxi) + 0x200) error("The FIRM in /luma is not valid.");
if(memcmp(firm, "FIRM", 4) != 0) if(memcmp(firm, "FIRM", 4) != 0)
{ {
u8 cetk[0xA50]; u8 cetk[0xA50];
if(fileRead(cetk, *firmType == NATIVE_FIRM1X2X ? cetkFiles[0] : cetkFiles[(u32)*firmType], sizeof(cetk)) == sizeof(cetk)) if(fileRead(cetk, *firmType == NATIVE_FIRM1X2X ? cetkFiles[0] : cetkFiles[(u32)*firmType], sizeof(cetk)) != sizeof(cetk) ||
decryptNusFirm(cetk, (u8 *)firm, firmSize); !decryptNusFirm((Ticket *)(cetk + 0x140), (Cxi *)firm, firmSize))
else error("The firmware.bin in /luma is encrypted\nor corrupted."); error("The FIRM in /luma is encrypted or corrupted.");
} }
//Check that the SD FIRM is right for the console from the ARM9 section address //Check that the 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)) if((firm->section[3].offset != 0 ? firm->section[3].address : firm->section[2].address) != (ISN3DS ? (u8 *)0x8006000 : (u8 *)0x8006800))
error("The firmware.bin in /luma is not valid for this\nconsole."); error("The FIRM in /luma is not for this console.");
firmVersion = 0xFFFFFFFF; firmVersion = 0xFFFFFFFF;
} }
@@ -331,162 +102,241 @@ static inline u32 loadFirm(FirmwareType *firmType, FirmwareSource firmSource, bo
if(firmVersion != 0xFFFFFFFF) if(firmVersion != 0xFFFFFFFF)
{ {
if(mustLoadFromSd) error("An old unsupported FIRM has been detected.\nCopy a firmware.bin in /luma to boot."); if(mustLoadFromStorage) error("An old unsupported FIRM has been detected.\nCopy a firmware.bin in /luma to boot.");
decryptExeFs((u8 *)firm); if(!decryptExeFs((Cxi *)firm)) error("The CTRNAND FIRM is corrupted.");
if(ISDEVUNIT) firmVersion = 0xFFFFFFFF;
} }
return firmVersion; return firmVersion;
} }
static inline void patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 emuHeader, bool isA9lh, u32 devMode) u32 patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 emuHeader, bool isA9lhInstalled, bool isSafeMode, u32 devMode)
{ {
u8 *arm9Section = (u8 *)firm + section[2].offset, u8 *arm9Section = (u8 *)firm + firm->section[2].offset,
*arm11Section1 = (u8 *)firm + section[1].offset; *arm11Section1 = (u8 *)firm + firm->section[1].offset;
if(isN3DS) if(ISN3DS)
{ {
//Decrypt ARM9Bin and patch ARM9 entrypoint to skip kernel9loader //Decrypt ARM9Bin and patch ARM9 entrypoint to skip kernel9loader
kernel9Loader(arm9Section); kernel9Loader((Arm9Bin *)arm9Section);
firm->arm9Entry = (u8 *)0x801B01C; 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 //Sets the 7.x NCCH KeyX and the 6.x gamecard save data KeyY on >= 6.0 O3DS FIRMs, if not using A9LH
else if(!isA9lh && firmVersion >= 0x29) set6x7xKeys(); else if(!ISA9LH && !ISFIRMLAUNCH && firmVersion >= 0x29) set6x7xKeys();
//Find the Process9 .code location, size and memory address //Find the Process9 .code location, size and memory address
u32 process9Size, u32 process9Size,
process9MemAddr; 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; u32 baseK11VA;
u8 *freeK11Space; u8 *freeK11Space;
u32 *arm11SvcHandler, u32 *arm11SvcHandler,
*arm11ExceptionsPage, *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 //Apply signature patches
patchSignatureChecks(process9Offset, process9Size); ret += patchSignatureChecks(process9Offset, process9Size);
//Apply EmuNAND patches //Apply EmuNAND patches
if(nandType != FIRMWARE_SYSNAND) if(nandType != FIRMWARE_SYSNAND) ret += patchEmuNand(arm9Section, kernel9Size, process9Offset, process9Size, emuHeader, firm->section[2].address);
{
u32 branchAdditive = (u32)firm + section[2].offset - (u32)section[2].address;
patchEmuNand(arm9Section, section[2].size, process9Offset, process9Size, emuHeader, branchAdditive);
}
//Apply FIRM0/1 writes patches on sysNAND to protect A9LH //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 //Apply firmlaunch patches
patchFirmlaunches(process9Offset, process9Size, process9MemAddr); ret += patchFirmlaunches(process9Offset, process9Size, process9MemAddr);
//11.0 FIRM patches //Apply dev unit check patches related to NCCH encryption
if(firmVersion >= (isN3DS ? 0x21 : 0x52)) if(!ISDEVUNIT)
{ {
//Apply anti-anti-DG patches ret += patchZeroKeyNcchEncryptionCheck(process9Offset, process9Size);
patchTitleInstallMinVersionCheck(process9Offset, process9Size); ret += patchNandNcchEncryptionCheck(process9Offset, process9Size);
//Restore svcBackdoor
reimplementSvcBackdoor(arm11Section1, arm11SvcTable, baseK11VA, &freeK11Space);
} }
implementSvcGetCFWInfo(arm11Section1, arm11SvcTable, baseK11VA, &freeK11Space); //11.0 FIRM patches
if(firmVersion >= (ISN3DS ? 0x21 : 0x52))
{
//Apply anti-anti-DG patches
ret += patchTitleInstallMinVersionChecks(process9Offset, process9Size, firmVersion);
//Apply UNITINFO patch //Restore svcBackdoor
if(devMode == 2) patchUnitInfoValueSet(arm9Section, section[2].size); ret += reimplementSvcBackdoor(arm11Section1, arm11SvcTable, baseK11VA, &freeK11Space);
}
if(devMode != 0 && isA9lh) ret += implementSvcGetCFWInfo(arm11Section1, arm11SvcTable, baseK11VA, &freeK11Space, isSafeMode);
//Apply UNITINFO patches
if(devMode == 2)
{
ret += patchUnitInfoValueSet(arm9Section, kernel9Size);
if(!ISDEVUNIT) ret += patchCheckForDevCommonKey(process9Offset, process9Size);
}
if(devMode != 0 && isA9lhInstalled)
{ {
//ARM11 exception handlers //ARM11 exception handlers
u32 codeSetOffset, u32 codeSetOffset,
stackAddress = getInfoForArm11ExceptionHandlers(arm11Section1, section[1].size, &codeSetOffset); stackAddress = getInfoForArm11ExceptionHandlers(arm11Section1, firm->section[1].size, &codeSetOffset);
installArm11Handlers(arm11ExceptionsPage, stackAddress, codeSetOffset); ret += installArm11Handlers(arm11ExceptionsPage, stackAddress, codeSetOffset);
patchSvcBreak11(arm11Section1, arm11SvcTable); patchSvcBreak11(arm11Section1, arm11SvcTable);
patchKernel11Panic(arm11Section1, section[1].size); ret += patchKernel11Panic(arm11Section1, firm->section[1].size);
//ARM9 exception handlers //ARM9 exception handlers
patchArm9ExceptionHandlersInstall(arm9Section, section[2].size); ret += patchArm9ExceptionHandlersInstall(arm9Section, kernel9Size);
patchSvcBreak9(arm9Section, section[2].size, (u32)section[2].address); ret += patchSvcBreak9(arm9Section, kernel9Size, (u32)firm->section[2].address);
patchKernel9Panic(arm9Section, section[2].size); ret += patchKernel9Panic(arm9Section, kernel9Size);
} }
if(CONFIG(PATCHACCESS)) if(CONFIG(PATCHACCESS))
{ {
patchArm11SvcAccessChecks(arm11SvcHandler); ret += patchArm11SvcAccessChecks(arm11SvcHandler, (u32 *)(arm11Section1 + firm->section[1].size));
patchK11ModuleChecks(arm11Section1, section[1].size, &freeK11Space); ret += patchK11ModuleChecks(arm11Section1, firm->section[1].size, &freeK11Space);
patchP9AccessChecks(process9Offset, process9Size); 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 //On N3DS, decrypt ARM9Bin and patch ARM9 entrypoint to skip kernel9loader
if(isN3DS) if(ISN3DS)
{ {
kernel9Loader(arm9Section); kernel9Loader((Arm9Bin *)arm9Section);
firm->arm9Entry = (u8 *)0x801301C; firm->arm9Entry = (u8 *)0x801301C;
} }
if(isN3DS || firmVersion >= (firmType == TWL_FIRM ? 0x16 : 0xB)) //Find the Process9 .code location, size and memory address
applyLegacyFirmPatches((u8 *)firm, firmType); 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 //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;
if(isN3DS) //On N3DS, decrypt ARM9Bin and patch ARM9 entrypoint to skip kernel9loader
if(ISN3DS)
{
kernel9Loader((Arm9Bin *)arm9Section);
firm->arm9Entry = (u8 *)0x801301C;
}
//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 //Decrypt ARM9Bin and patch ARM9 entrypoint to skip kernel9loader
kernel9Loader(arm9Section); kernel9Loader((Arm9Bin *)arm9Section);
firm->arm9Entry = (u8 *)0x801B01C; 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) if(devMode != 0)
{ {
//ARM9 exception handlers //ARM9 exception handlers
patchArm9ExceptionHandlersInstall(arm9Section, section[2].size); ret += patchArm9ExceptionHandlersInstall(arm9Section, kernel9Size);
patchSvcBreak9(arm9Section, section[2].size, (u32)section[2].address); 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; 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; 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) src < srcEnd; src += srcModuleSize, dst += dstModuleSize, maxModuleSize -= dstModuleSize)
{ {
srcModuleSize = *(u32 *)(src + 0x104) * 0x200; srcModuleSize = ((Cxi *)src)->ncch.contentSize * 0x200;
const char *moduleName = (char *)(src + 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/"; char fileName[24] = "sysmodules/";
const char *ext = ".cxi";
//Read modules from files if they exist //Read modules from files if they exist
concatenateStrings(fileName, moduleName); concatenateStrings(fileName, moduleName);
concatenateStrings(fileName, ext); concatenateStrings(fileName, ".cxi");
fileSize = fileRead(dst, fileName, 2 * srcModuleSize); dstModuleSize = getFileSize(fileName);
}
else fileSize = 0;
if(fileSize > 0) dstModuleSize = fileSize; if(dstModuleSize == 0) loadedModule = false;
else 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;
}
}
if(!loadedModule)
{ {
const u8 *module; const u8 *module;
@@ -501,29 +351,31 @@ static inline void copySection0AndInjectSystemModules(FirmwareType firmType, boo
dstModuleSize = srcModuleSize; dstModuleSize = srcModuleSize;
} }
if(dstModuleSize > maxModuleSize) error(extModuleSizeError);
memcpy(dst, module, dstModuleSize); 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 //Allow module injection and/or inject 3ds_injector on new NATIVE_FIRMs and LGY FIRMs
u32 sectionNum; 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; sectionNum = 1;
} }
else sectionNum = 0; else sectionNum = 0;
//Copy FIRM sections to respective memory locations //Copy FIRM sections to respective memory locations
for(; sectionNum < 4 && section[sectionNum].size != 0; sectionNum++) for(; sectionNum < 4 && firm->section[sectionNum].size != 0; sectionNum++)
memcpy(section[sectionNum].address, (u8 *)firm + section[sectionNum].offset, section[sectionNum].size); memcpy(firm->section[sectionNum].address, (u8 *)firm + firm->section[sectionNum].offset, firm->section[sectionNum].size);
//Determine the ARM11 entry to use //Determine the ARM11 entry to use
vu32 *arm11; vu32 *arm11;
if(isFirmlaunch) arm11 = (vu32 *)0x1FFFFFFC; if(ISFIRMLAUNCH) arm11 = (vu32 *)0x1FFFFFFC;
else else
{ {
deinitScreens(); deinitScreens();

View File

@@ -24,32 +24,9 @@
#include "types.h" #include "types.h"
#define CFG_BOOTENV (*(vu32 *)0x10010000) u32 loadFirm(FirmwareType *firmType, FirmwareSource nandType, bool loadFromStorage, bool isSafeMode);
#define CFG_UNITINFO (*(vu8 *)0x10010010) u32 patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 emuHeader, bool isA9lhInstalled, bool isSafeMode, u32 devMode);
#define PDN_MPCORE_CFG (*(vu32 *)0x10140FFC) u32 patchTwlFirm(u32 firmVersion, u32 devMode);
#define PDN_SPI_CNT (*(vu32 *)0x101401C0) u32 patchAgbFirm(u32 devMode);
u32 patch1x2xNativeAndSafeFirm(u32 devMode);
//FIRM Header layout void launchFirm(FirmwareType firmType, bool loadFromStorage);
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, bool isA9lh, 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);

View File

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

View File

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

272
source/main.c Normal file
View File

@@ -0,0 +1,272 @@
/*
* 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();
switch(launchedFirmTidLow[7])
{
case u'2':
firmType = (FirmwareType)(launchedFirmTidLow[5] - u'0');
break;
case u'3':
firmType = SAFE_FIRM;
break;
case u'1':
firmType = SYSUPDATER_FIRM;
break;
}
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 TWL_FIRM:
res = patchTwlFirm(firmVersion, devMode);
break;
case AGB_FIRM:
res = patchAgbFirm(devMode);
break;
case SAFE_FIRM:
case SYSUPDATER_FIRM:
case NATIVE_FIRM1X2X:
res = isA9lhInstalled ? patch1x2xNativeAndSafeFirm(devMode) : 0;
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,65 +21,102 @@
*/ */
/* /*
* 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 * ARM11 modules patching code originally by Subv
* Idea for svcBreak patches from yellows8 and others on #3dsdev
*/ */
#include "patches.h" #include "patches.h"
#include "fs.h" #include "fs.h"
#include "memory.h" #include "memory.h"
#include "config.h" #include "config.h"
#include "utils.h"
#include "../build/bundled.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; if(temp == NULL) error("Failed to get Process9 data.");
*process9MemAddr = *(u32 *)(off + 0xC);
//Process9 code offset (start of NCCH + ExeFS offset + ExeFS header size) Cxi *off = (Cxi *)(temp - 0x100);
return off - 0x204 + (*(u32 *)(off - 0x64) * 0x200) + 0x200;
*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) u32 *getKernel11Info(u8 *pos, u32 size, u32 *baseK11VA, u8 **freeK11Space, u32 **arm11SvcHandler, u32 **arm11ExceptionsPage)
{ {
const u8 pattern[] = {0x00, 0xB0, 0x9C, 0xE5}; 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 *arm11SvcTable;
if(*arm11ExceptionsPage == NULL) res = false;
else
{
*arm11ExceptionsPage -= 0xB;
u32 svcOffset = (-(((*arm11ExceptionsPage)[2] & 0xFFFFFF) << 2) & (0xFFFFFF << 2)) - 8; //Branch offset + 8 for prefetch u32 svcOffset = (-(((*arm11ExceptionsPage)[2] & 0xFFFFFF) << 2) & (0xFFFFFF << 2)) - 8; //Branch offset + 8 for prefetch
u32 pointedInstructionVA = 0xFFFF0008 - svcOffset; u32 pointedInstructionVA = 0xFFFF0008 - svcOffset;
*baseK11VA = pointedInstructionVA & 0xFFFF0000; //This assumes that the pointed instruction has an offset < 0x10000, iirc that's always the case *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 arm11SvcTable = *arm11SvcHandler = (u32 *)(pos + *(u32 *)(pos + pointedInstructionVA - *baseK11VA + 8) - *baseK11VA); //SVC handler address
*arm11SvcHandler = arm11SvcTable;
while(*arm11SvcTable) arm11SvcTable++; //Look for SVC0 (NULL) while(*arm11SvcTable) arm11SvcTable++; //Look for SVC0 (NULL)
}
const u8 pattern2[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; 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; return arm11SvcTable;
} }
void patchSignatureChecks(u8 *pos, u32 size) u32 patchSignatureChecks(u8 *pos, u32 size)
{ {
//Look for signature checks //Look for signature checks
const u8 pattern[] = {0xC0, 0x1C, 0x76, 0xE7}, const u8 pattern[] = {0xC0, 0x1C, 0x76, 0xE7},
pattern2[] = {0xB5, 0x22, 0x4D, 0x0C}; pattern2[] = {0xB5, 0x22, 0x4D, 0x0C};
u32 ret;
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern)), u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
*off2 = (u16 *)(memsearch(pos, pattern2, size, sizeof(pattern2)) - 1); u8 *temp = memsearch(pos, pattern2, size, sizeof(pattern2));
if(off == NULL || temp == NULL) ret = 1;
else
{
u16 *off2 = (u16 *)(temp - 1);
*off = off2[0] = 0x2000; *off = off2[0] = 0x2000;
off2[1] = 0x4770; 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 //Look for firmlaunch code
const u8 pattern[] = {0xE2, 0x20, 0x20, 0x90}; const u8 pattern[] = {0xE2, 0x20, 0x20, 0x90};
u32 ret;
u8 *off = memsearch(pos, pattern, size, sizeof(pattern)) - 0x13; u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
if(off == NULL) ret = 1;
else
{
off -= 0x13;
//Firmlaunch function offset - offset in BLX opcode (A4-16 - ARM DDI 0100E) + 1 //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); u32 fOpenOffset = (u32)(off + 9 - (-((*(u32 *)off & 0x00FFFFFF) << 2) & (0xFFFFFF << 2)) - pos + process9MemAddr);
@@ -91,60 +128,168 @@ void patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr)
u32 *pos_fopen = (u32 *)memsearch(off, "OPEN", reboot_bin_size, 4); u32 *pos_fopen = (u32 *)memsearch(off, "OPEN", reboot_bin_size, 4);
*pos_fopen = fOpenOffset; *pos_fopen = fOpenOffset;
ret = 0;
if(CONFIG(USECUSTOMPATH)) if(CONFIG(USECUSTOMPATH))
{ {
const char pathPath[] = "/luma/path.txt"; const char *pathFile = "path.txt";
u32 pathSize = getFileSize(pathPath); u32 pathSize = getFileSize(pathFile);
if(pathSize > 5 && pathSize < 40) if(pathSize > 5 && pathSize < 58)
{ {
u8 path[pathSize]; u8 path[pathSize];
fileRead(path, pathPath, 0); fileRead(path, pathFile, pathSize);
if(path[pathSize - 1] == 0xA) pathSize--; if(path[pathSize - 1] == 0xA) pathSize--;
if(path[pathSize - 1] == 0xD) pathSize--; if(path[pathSize - 1] == 0xD) pathSize--;
if(pathSize > 5 && pathSize < 38 && path[0] == '/' && memcmp(&path[pathSize - 4], ".bin", 4) == 0) if(pathSize > 5 && pathSize < 56 && path[0] == '/' && memcmp(&path[pathSize - 4], ".bin", 4) == 0)
{ {
u16 finalPath[pathSize + 1]; u16 finalPath[pathSize];
for(u32 i = 0; i < pathSize; i++) for(u32 i = 0; i < pathSize; i++)
finalPath[i] = (u16)path[i]; finalPath[i] = (u16)path[i];
finalPath[pathSize] = 0;
u8 *pos_path = memsearch(off, u"sd", reboot_bin_size, 4) + 0xA; u8 *pos_path = memsearch(off, u"sd", reboot_bin_size, 4) + 0xA;
memcpy(pos_path, finalPath, (pathSize + 1) * 2); memcpy(pos_path, finalPath, pathSize * 2);
} }
} }
} }
}
return ret;
} }
void patchFirmWrites(u8 *pos, u32 size) u32 patchFirmWrites(u8 *pos, u32 size)
{ {
u32 ret;
//Look for FIRM writing code //Look for FIRM writing code
u8 *off1 = memsearch(pos, "exe:", size, 4); u8 *off = memsearch(pos, "exe:", size, 4);
if(off == NULL) ret = 1;
else
{
const u8 pattern[] = {0x00, 0x28, 0x01, 0xDA}; const u8 pattern[] = {0x00, 0x28, 0x01, 0xDA};
u16 *off2 = (u16 *)memsearch(off1 - 0x100, pattern, 0x100, sizeof(pattern)); u16 *off2 = (u16 *)memsearch(off - 0x100, pattern, 0x100, sizeof(pattern));
if(off2 == NULL) ret = 1;
else
{
off2[0] = 0x2000; off2[0] = 0x2000;
off2[1] = 0x46C0; off2[1] = 0x46C0;
ret = 0;
}
}
return ret;
} }
void patchOldFirmWrites(u8 *pos, u32 size) u32 patchOldFirmWrites(u8 *pos, u32 size)
{ {
//Look for FIRM writing code //Look for FIRM writing code
const u8 pattern[] = {0x04, 0x1E, 0x1D, 0xDB}; const u8 pattern[] = {0x04, 0x1E, 0x1D, 0xDB};
u32 ret;
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern)); u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
if(off == NULL) ret = 1;
else
{
off[0] = 0x2400; off[0] = 0x2400;
off[1] = 0xE01D; off[1] = 0xE01D;
ret = 0;
}
return ret;
} }
void reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space) 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 patchZeroKeyNcchEncryptionCheck(u8 *pos, u32 size)
{
const u8 pattern[] = {0x28, 0x2A, 0xD0, 0x08};
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 patchNandNcchEncryptionCheck(u8 *pos, u32 size)
{
const u8 pattern[] = {0x07, 0xD1, 0x28, 0x7A};
u32 ret;
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
if(off == NULL) ret = 1;
else
{
off--;
*off = 0x2001; //mov r0, #1
ret = 0;
}
return ret;
}
u32 patchCheckForDevCommonKey(u8 *pos, u32 size)
{
const u8 pattern[] = {0x03, 0x7C, 0x28, 0x00};
u32 ret;
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
if(off == NULL) ret = 1;
else
{
*off = 0x2301; //mov r3, #1
ret = 0;
}
return ret;
}
u32 reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space)
{
u32 ret = 0;
//Official implementation of svcBackdoor //Official implementation of svcBackdoor
const u8 svcBackdoor[40] = {0xFF, 0x10, 0xCD, 0xE3, //bic r1, sp, #0xff const u8 svcBackdoor[] = {0xFF, 0x10, 0xCD, 0xE3, //bic r1, sp, #0xff
0x0F, 0x1C, 0x81, 0xE3, //orr r1, r1, #0xf00 0x0F, 0x1C, 0x81, 0xE3, //orr r1, r1, #0xf00
0x28, 0x10, 0x81, 0xE2, //add r1, r1, #0x28 0x28, 0x10, 0x81, 0xE2, //add r1, r1, #0x28
0x00, 0x20, 0x91, 0xE5, //ldr r2, [r1] 0x00, 0x20, 0x91, 0xE5, //ldr r2, [r1]
@@ -157,18 +302,41 @@ void reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **fre
if(!arm11SvcTable[0x7B]) if(!arm11SvcTable[0x7B])
{ {
memcpy(*freeK11Space, svcBackdoor, 40); if(*(u32 *)(*freeK11Space + sizeof(svcBackdoor) - 4) != 0xFFFFFFFF) ret = 1;
else
{
memcpy(*freeK11Space, svcBackdoor, sizeof(svcBackdoor));
arm11SvcTable[0x7B] = baseK11VA + *freeK11Space - pos; arm11SvcTable[0x7B] = baseK11VA + *freeK11Space - pos;
*freeK11Space += 40; *freeK11Space += sizeof(svcBackdoor);
} }
}
return ret;
} }
void implementSvcGetCFWInfo(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space) 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); memcpy(*freeK11Space, svcGetCFWInfo_bin, svcGetCFWInfo_bin_size);
CFWInfo *info = (CFWInfo *)memsearch(*freeK11Space, "LUMA", svcGetCFWInfo_bin_size, 4); 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; const char *rev = REVISION;
@@ -186,73 +354,38 @@ void implementSvcGetCFWInfo(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **fre
} }
else isRelease = rev[4] == 0; else isRelease = rev[4] == 0;
info->flags = isRelease ? 1 : 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 arm11SvcTable[0x2E] = baseK11VA + *freeK11Space - pos; //Stubbed svc
*freeK11Space += svcGetCFWInfo_bin_size; *freeK11Space += svcGetCFWInfo_bin_size;
}
void patchTitleInstallMinVersionCheck(u8 *pos, u32 size) ret = 0;
{
const u8 pattern[] = {0x0A, 0x81, 0x42, 0x02};
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++)
{
switch(patches[i].type)
{
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;
}
} }
return ret;
} }
void patchArm9ExceptionHandlersInstall(u8 *pos, u32 size) u32 patchArm9ExceptionHandlersInstall(u8 *pos, u32 size)
{ {
const u8 pattern[] = {0x03, 0xA0, 0xE3, 0x18}; const u8 pattern[] = {0x80, 0xE5, 0x40, 0x1C};
u32 ret;
u32 *off = (u32 *)(memsearch(pos, pattern, size, sizeof(pattern)) + 0x13); 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 for(u32 r0 = 0x08000000; *off != 0xE3A01040; off++) //Until mov r1, #0x40
{ {
//Discard everything that's not str rX, [r0, #imm](!) //Discard everything that's not str rX, [r0, #imm](!)
if((*off & 0xFE5F0000) != 0xE4000000) continue; if((*off & 0xFE5F0000) == 0xE4000000)
{
u32 rD = (*off >> 12) & 0xF, u32 rD = (*off >> 12) & 0xF,
offset = (*off & 0xFFF) * ((((*off >> 23) & 1) == 0) ? -1 : 1); offset = (*off & 0xFFF) * ((((*off >> 23) & 1) == 0) ? -1 : 1);
bool writeback = ((*off >> 21) & 1) != 0, bool writeback = ((*off >> 21) & 1) != 0,
@@ -265,33 +398,63 @@ void patchArm9ExceptionHandlersInstall(u8 *pos, u32 size)
if(!pre) addr += offset; if(!pre) addr += offset;
if(writeback) r0 = addr; if(writeback) r0 = addr;
} }
}
ret = 0;
}
return ret;
} }
u32 getInfoForArm11ExceptionHandlers(u8 *pos, u32 size, u32 *codeSetOffset) u32 getInfoForArm11ExceptionHandlers(u8 *pos, u32 size, u32 *codeSetOffset)
{ {
const u8 pattern[] = {0xE3, 0xDC, 0x05, 0xC0}, //Get TitleID from CodeSet const u8 pattern[] = {0x1B, 0x50, 0xA0, 0xE3}, //Get TitleID from CodeSet
pattern2[] = {0xE1, 0x0F, 0x00, 0xBD}; //Call exception dispatcher 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));
if(loadCodeSet == NULL) ret = false;
else
{
loadCodeSet -= 2;
*codeSetOffset = *loadCodeSet & 0xFFF; *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. //Stub svcBreak with "bkpt 65535" so we can debug the panic
Thanks @yellows8 and others for mentioning this idea on #3dsdev */
//Look for the svc handler //Look for the svc handler
const u8 pattern[] = {0x00, 0xE0, 0x4F, 0xE1}; //mrs lr, spsr const u8 pattern[] = {0x00, 0xE0, 0x4F, 0xE1}; //mrs lr, spsr
u32 ret;
u32 *arm9SvcTable = (u32 *)memsearch(pos, pattern, size, sizeof(pattern)); u32 *arm9SvcTable = (u32 *)memsearch(pos, pattern, size, sizeof(pattern));
while(*arm9SvcTable) arm9SvcTable++; //Look for SVC0 (NULL)
if(arm9SvcTable == NULL) ret = 1;
else
{
while(*arm9SvcTable != 0) arm9SvcTable++; //Look for SVC0 (NULL)
u32 *addr = (u32 *)(pos + arm9SvcTable[0x3C] - kernel9Address); u32 *addr = (u32 *)(pos + arm9SvcTable[0x3C] - kernel9Address);
*addr = 0xE12FFF7F; *addr = 0xE12FFF7F;
ret = 0;
}
return ret;
} }
void patchSvcBreak11(u8 *pos, u32 *arm11SvcTable) void patchSvcBreak11(u8 *pos, u32 *arm11SvcTable)
@@ -301,68 +464,294 @@ void patchSvcBreak11(u8 *pos, u32 *arm11SvcTable)
*addr = 0xE12FFF7F; *addr = 0xE12FFF7F;
} }
void patchKernel9Panic(u8 *pos, u32 size) u32 patchKernel9Panic(u8 *pos, u32 size)
{ {
const u8 pattern[] = {0xFF, 0xEA, 0x04, 0xD0}; const u8 pattern[] = {0xFF, 0xEA, 0x04, 0xD0};
u32 ret;
u32 *off = (u32 *)(memsearch(pos, pattern, size, sizeof(pattern)) - 0x12); u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
if(temp == NULL) ret = 1;
else
{
u32 *off = (u32 *)(temp - 0x12);
*off = 0xE12FFF7E; *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}; const u8 pattern[] = {0x02, 0x0B, 0x44, 0xE2};
u32 ret;
u32 *off = (u32 *)memsearch(pos, pattern, size, sizeof(pattern)); u32 *off = (u32 *)memsearch(pos, pattern, size, sizeof(pattern));
if(off == NULL) ret = 1;
else
{
*off = 0xE12FFF7E; *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));
if(temp == NULL) ret = 1;
else
{
u16 *off = (u16 *)(temp - 3);
off[0] = 0x2001; //mov r0, #1 off[0] = 0x2001; //mov r0, #1
off[1] = 0x4770; //bx lr 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 u32 ret;
while(*arm11SvcHandler != 0xE11A0E1B && arm11SvcHandler < endPos) arm11SvcHandler++; //TST R10, R11,LSL LR
if(arm11SvcHandler == endPos) ret = 1;
else
{
*arm11SvcHandler = 0xE3B0A001; //MOVS R10, #1 *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 /* We have to detour a function in the ARM11 kernel because builtin modules
are compressed in memory and are only decompressed at runtime */ are compressed in memory and are only decompressed at runtime */
u32 ret;
//Check that we have enough free space //Check that we have enough free space
if(*(u32 *)(*freeK11Space + k11modules_bin_size - 4) == 0xFFFFFFFF) 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};
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
if(temp == NULL) ret = 1;
else
{ {
//Inject our code into the free space //Inject our code into the free space
memcpy(*freeK11Space, k11modules_bin, k11modules_bin_size); memcpy(*freeK11Space, k11modules_bin, k11modules_bin_size);
//Look for the code that decompresses the .code section of the builtin modules u32 *off = (u32 *)(temp - 0xB);
const u8 pattern[] = {0xE5, 0x48, 0x00, 0x9D};
u32 *off = (u32 *)(memsearch(pos, pattern, size, sizeof(pattern)) - 0xB);
//Inject a jump (BL) instruction to our code at the offset we found //Inject a jump (BL) instruction to our code at the offset we found
*off = 0xEB000000 | (((((u32)*freeK11Space) - ((u32)off + 8)) >> 2) & 0xFFFFFF); *off = 0xEB000000 | (((((u32)*freeK11Space) - ((u32)off + 8)) >> 2) & 0xFFFFFF);
*freeK11Space += k11modules_bin_size; *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 //Look for UNITINFO value being set during kernel sync
const u8 pattern[] = {0x01, 0x10, 0xA0, 0x13}; const u8 pattern[] = {0x01, 0x10, 0xA0, 0x13};
u32 ret;
u8 *off = memsearch(pos, pattern, size, sizeof(pattern)); u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
off[0] = isDevUnit ? 0 : 1; if(off == NULL) ret = 1;
else
{
off[0] = ISDEVUNIT ? 0 : 1;
off[3] = 0xE3; 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,46 @@
*/ */
/* /*
* 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 * ARM11 modules patching code originally by Subv
* Idea for svcBreak patches from yellows8 and others on #3dsdev
*/ */
#pragma once #pragma once
#include "types.h" #include "types.h"
typedef struct patchData { extern CfgData configData;
u32 offset[2];
union {
u8 type0[8];
u16 type1;
} patch;
u32 type;
} patchData;
typedef struct __attribute__((packed)) u8 *getProcess9Info(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr);
{
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);
u32 *getKernel11Info(u8 *pos, u32 size, u32 *baseK11VA, u8 **freeK11Space, u32 **arm11SvcHandler, u32 **arm11ExceptionsPage); u32 *getKernel11Info(u8 *pos, u32 size, u32 *baseK11VA, u8 **freeK11Space, u32 **arm11SvcHandler, u32 **arm11ExceptionsPage);
void patchSignatureChecks(u8 *pos, u32 size); u32 patchSignatureChecks(u8 *pos, u32 size);
void patchTitleInstallMinVersionCheck(u8 *pos, u32 size); u32 patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr);
void patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr); u32 patchFirmWrites(u8 *pos, u32 size);
void patchFirmWrites(u8 *pos, u32 size); u32 patchOldFirmWrites(u8 *pos, u32 size);
void patchOldFirmWrites(u8 *pos, u32 size); u32 patchTitleInstallMinVersionChecks(u8 *pos, u32 size, u32 firmVersion);
void reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space); u32 patchZeroKeyNcchEncryptionCheck(u8 *pos, u32 size);
void implementSvcGetCFWInfo(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space); u32 patchNandNcchEncryptionCheck(u8 *pos, u32 size);
void applyLegacyFirmPatches(u8 *pos, FirmwareType firmType); u32 patchCheckForDevCommonKey(u8 *pos, u32 size);
void patchArm9ExceptionHandlersInstall(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); 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 patchSvcBreak11(u8 *pos, u32 *arm11SvcTable);
void patchKernel9Panic(u8 *pos, u32 size); u32 patchKernel9Panic(u8 *pos, u32 size);
void patchKernel11Panic(u8 *pos, u32 size); u32 patchKernel11Panic(u8 *pos, u32 size);
void patchP9AccessChecks(u8 *pos, u32 size); u32 patchP9AccessChecks(u8 *pos, u32 size);
void patchArm11SvcAccessChecks(u32 *arm11SvcHandler); u32 patchArm11SvcAccessChecks(u32 *arm11SvcHandler, u32 *endPos);
void patchK11ModuleChecks(u8 *pos, u32 size, u8 **freeK11Space); u32 patchK11ModuleChecks(u8 *pos, u32 size, u8 **freeK11Space);
void patchUnitInfoValueSet(u8 *pos, u32 size); 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,13 +50,13 @@ void newPin(bool allowSkipping, u32 pinMode)
u8 length = 4 + 2 * (pinMode - 1); 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(title, true, 10, 10, COLOR_TITLE);
drawString("PIN ( digits): ", true, 10, 10 + 2 * SPACING_Y, COLOR_WHITE); 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); drawCharacter('0' + length, true, 10 + 5 * SPACING_X, 10 + 2 * SPACING_Y, COLOR_WHITE);
//Pad to AES block length with zeroes //Pad to AES block length with zeroes
u8 __attribute__((aligned(4))) enteredPassword[AES_BLOCK_SIZE] = {0}; __attribute__((aligned(4))) u8 enteredPassword[AES_BLOCK_SIZE] = {0};
u8 cnt = 0; u8 cnt = 0;
u32 charDrawPos = 16 * SPACING_X; u32 charDrawPos = 16 * SPACING_X;
@@ -90,8 +90,8 @@ void newPin(bool allowSkipping, u32 pinMode)
pin.formatVersionMajor = PIN_VERSIONMAJOR; pin.formatVersionMajor = PIN_VERSIONMAJOR;
pin.formatVersionMinor = PIN_VERSIONMINOR; pin.formatVersionMinor = PIN_VERSIONMINOR;
u8 __attribute__((aligned(4))) tmp[SHA_256_HASH_SIZE]; __attribute__((aligned(4))) u8 tmp[SHA_256_HASH_SIZE],
u8 __attribute__((aligned(4))) lengthBlock[AES_BLOCK_SIZE] = {0}; lengthBlock[AES_BLOCK_SIZE] = {0};
lengthBlock[0] = length; lengthBlock[0] = length;
computePinHash(tmp, lengthBlock); computePinHash(tmp, lengthBlock);
@@ -100,7 +100,7 @@ void newPin(bool allowSkipping, u32 pinMode)
computePinHash(tmp, enteredPassword); computePinHash(tmp, enteredPassword);
memcpy(pin.hash, tmp, sizeof(tmp)); 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"); error("Error writing the PIN file");
} }
@@ -108,14 +108,14 @@ bool verifyPin(u32 pinMode)
{ {
PinData pin; 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 || memcmp(pin.magic, "PINF", 4) != 0 ||
pin.formatVersionMajor != PIN_VERSIONMAJOR || pin.formatVersionMajor != PIN_VERSIONMAJOR ||
pin.formatVersionMinor != PIN_VERSIONMINOR) pin.formatVersionMinor != PIN_VERSIONMINOR)
return false; return false;
u8 __attribute__((aligned(4))) tmp[SHA_256_HASH_SIZE]; __attribute__((aligned(4))) u8 tmp[SHA_256_HASH_SIZE],
u8 __attribute__((aligned(4))) lengthBlock[AES_BLOCK_SIZE] = {0}; lengthBlock[AES_BLOCK_SIZE] = {0};
lengthBlock[0] = 4 + 2 * (pinMode - 1); lengthBlock[0] = 4 + 2 * (pinMode - 1);
computePinHash(tmp, lengthBlock); computePinHash(tmp, lengthBlock);
@@ -126,23 +126,26 @@ bool verifyPin(u32 pinMode)
initScreens(); initScreens();
//Pad to AES block length with zeroes //Pad to AES block length with zeroes
u8 __attribute__((aligned(4))) enteredPassword[AES_BLOCK_SIZE] = {0}; __attribute__((aligned(4))) u8 enteredPassword[AES_BLOCK_SIZE] = {0};
bool unlock = false; bool unlock = false;
u8 cnt = 0; u8 cnt = 0;
u32 charDrawPos = 16 * SPACING_X; 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) if(messageSize > 0 && messageSize <= 800)
{ {
char message[messageSize + 1]; char message[messageSize + 1];
fileRead(message, messagePath, 0);
if(fileRead(message, messageFile, messageSize) == messageSize)
{
message[messageSize] = 0; message[messageSize] = 0;
drawString(message, false, 10, 10, COLOR_WHITE); drawString(message, false, 10, 10, COLOR_WHITE);
} }
}
while(!unlock) while(!unlock)
{ {

View File

@@ -28,18 +28,9 @@
#include "types.h" #include "types.h"
#define PIN_PATH "/luma/pin.bin" #define PIN_FILE "pin.bin"
#define PIN_VERSIONMAJOR 1 #define PIN_VERSIONMAJOR 1
#define PIN_VERSIONMINOR 3 #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); void newPin(bool allowSkipping, u32 pinMode);
bool verifyPin(u32 pinMode); bool verifyPin(u32 pinMode);

View File

@@ -41,14 +41,11 @@
#include "cache.h" #include "cache.h"
#include "i2c.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}; static const u32 brightness[4] = {0x5F, 0x4C, 0x39, 0x26};
void __attribute__((naked)) arm11Stub(void) void __attribute__((naked)) arm11Stub(void)
{ {
//Disable interrupts
__asm(".word 0xF10C01C0");
//Wait for the entry to be set //Wait for the entry to be set
while(*arm11Entry == ARM11_STUB_ADDRESS); while(*arm11Entry == ARM11_STUB_ADDRESS);
@@ -62,7 +59,6 @@ static void invokeArm11Function(void (*func)())
if(!hasCopiedStub) if(!hasCopiedStub)
{ {
memcpy((void *)ARM11_STUB_ADDRESS, arm11Stub, 0x30); memcpy((void *)ARM11_STUB_ADDRESS, arm11Stub, 0x30);
flushDCacheRange((void *)ARM11_STUB_ADDRESS, 0x30);
hasCopiedStub = true; hasCopiedStub = true;
} }
@@ -86,7 +82,7 @@ void deinitScreens(void)
WAIT_FOR_ARM9(); WAIT_FOR_ARM9();
} }
if(PDN_GPU_CNT != 1) invokeArm11Function(ARM11); if(ARESCREENSINITED) invokeArm11Function(ARM11);
} }
void updateBrightness(u32 brightnessIndex) 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)))); 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(); WAIT_FOR_ARM9();
} }
@@ -302,7 +288,7 @@ void initScreens(void)
if(needToSetup) if(needToSetup)
{ {
if(PDN_GPU_CNT == 1) if(!ARESCREENSINITED)
{ {
flushDCacheRange(&configData, sizeof(CfgData)); flushDCacheRange(&configData, sizeof(CfgData));
invokeArm11Function(initSequence); invokeArm11Function(initSequence);

View File

@@ -31,7 +31,9 @@
#define PDN_GPU_CNT (*(vu8 *)0x10141200) #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 WAIT_FOR_ARM9() *arm11Entry = 0; while(!*arm11Entry); ((void (*)())*arm11Entry)();
#define SCREEN_TOP_WIDTH 400 #define SCREEN_TOP_WIDTH 400
@@ -44,7 +46,9 @@ static volatile struct fb {
u8 *top_left; u8 *top_left;
u8 *top_right; u8 *top_right;
u8 *bottom; u8 *bottom;
} *const fbs = (volatile struct fb *)0x23FFFE00; } __attribute__((packed)) *const fbs = (volatile struct fb *)0x23FFFE00;
extern CfgData configData;
void deinitScreens(void); void deinitScreens(void);
void swapFramebuffers(bool isAlternate); void swapFramebuffers(bool isAlternate);

View File

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

View File

@@ -27,7 +27,7 @@ u32 strlen(const char *string)
{ {
char *stringEnd = (char *)string; char *stringEnd = (char *)string;
while(*stringEnd) stringEnd++; while(*stringEnd != 0) stringEnd++;
return stringEnd - string; return stringEnd - string;
} }
@@ -40,16 +40,47 @@ void concatenateStrings(char *destination, const char *source)
memcpy(&destination[j], source, i + 1); 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"; 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; 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); u32 strlen(const char *string);
void concatenateStrings(char *destination, const char *source); 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 u32 vu32;
typedef volatile u64 vu64; typedef volatile u64 vu64;
//Used by multiple files #include "3dsheaders.h"
#define BRAHMA_ARM11_ENTRY 0x1FFFFFF8 #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 typedef enum FirmwareSource
{ {
FIRMWARE_SYSNAND = 0, FIRMWARE_SYSNAND = 0,
@@ -54,5 +97,8 @@ typedef enum FirmwareType
TWL_FIRM, TWL_FIRM,
AGB_FIRM, AGB_FIRM,
SAFE_FIRM, SAFE_FIRM,
SYSUPDATER_FIRM,
NATIVE_FIRM1X2X NATIVE_FIRM1X2X
} FirmwareType; } FirmwareType;
extern u16 launchedFirmTidLow[8]; //Defined in start.s

View File

@@ -54,20 +54,9 @@ u32 waitInput(void)
return key; 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) 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 //Ensure that all memory transfers have completed and that the data cache has been flushed
flushEntireDCache(); flushEntireDCache();
@@ -112,8 +101,8 @@ void chrono(u32 seconds)
void error(const char *message) void error(const char *message)
{ {
if(isFirmlaunch) mcuReboot(); if(!ISFIRMLAUNCH)
{
initScreens(); initScreens();
drawString("An error has occurred:", true, 10, 10, COLOR_RED); drawString("An error has occurred:", true, 10, 10, COLOR_RED);
@@ -121,5 +110,7 @@ void error(const char *message)
drawString("Press any button to shutdown", true, 10, posY + 2 * SPACING_Y, COLOR_WHITE); drawString("Press any button to shutdown", true, 10, posY + 2 * SPACING_Y, COLOR_WHITE);
waitInput(); waitInput();
}
mcuPowerOff(); mcuPowerOff();
} }

View File

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