Compare commits
90 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cef947d67d | ||
|
|
b6640d118d | ||
|
|
273d0b2ce9 | ||
|
|
9b724d776e | ||
|
|
3eaa706ccf | ||
|
|
b4c854dfe8 | ||
|
|
9cdadbe834 | ||
|
|
b3f38a8764 | ||
|
|
5d868284c6 | ||
|
|
035751980d | ||
|
|
6b80bc08d5 | ||
|
|
2089959d1b | ||
|
|
67b00ec28d | ||
|
|
127ae6b945 | ||
|
|
4f53b3ce35 | ||
|
|
c3092b482a | ||
|
|
080219f88d | ||
|
|
3fd783cd01 | ||
|
|
211cd964d7 | ||
|
|
04f42f0be4 | ||
|
|
e00ef893d0 | ||
|
|
858efa604e | ||
|
|
b63b17c54f | ||
|
|
f0e111c20e | ||
|
|
7339f57138 | ||
|
|
1e39c999f9 | ||
|
|
62f7a06192 | ||
|
|
a0531b7930 | ||
|
|
0619d04939 | ||
|
|
cd76476d26 | ||
|
|
53b6c17e33 | ||
|
|
3b7b66b272 | ||
|
|
a795a45c34 | ||
|
|
b58cbd228c | ||
|
|
fc994285f9 | ||
|
|
d5e74b91c7 | ||
|
|
c5eb2e1070 | ||
|
|
d613cb057e | ||
|
|
121792bebe | ||
|
|
e07c230106 | ||
|
|
d5ce3044c8 | ||
|
|
98ff43b4d2 | ||
|
|
1704fbcd62 | ||
|
|
014ac1cf72 | ||
|
|
b499c7ee75 | ||
|
|
2e069e326c | ||
|
|
e47d42da22 | ||
|
|
615e5dfaa7 | ||
|
|
fde2c371ef | ||
|
|
a0b4e7fd5d | ||
|
|
45c36bbcae | ||
|
|
66c041ad93 | ||
|
|
32d5c52b5f | ||
|
|
9cf5b01633 | ||
|
|
d4cf22d370 | ||
|
|
973640f023 | ||
|
|
248ea82f76 | ||
|
|
a868079a93 | ||
|
|
d270d5b9ca | ||
|
|
e9692a438b | ||
|
|
06ea123dbd | ||
|
|
85141d5eda | ||
|
|
fa13b8fbd0 | ||
|
|
5b4712644a | ||
|
|
aa422914bd | ||
|
|
22c453e297 | ||
|
|
e5f40cec5a | ||
|
|
50b24bf6c2 | ||
|
|
b575ee9e28 | ||
|
|
37030621ac | ||
|
|
f005da4d12 | ||
|
|
6295559d9c | ||
|
|
f36ff303d9 | ||
|
|
1a62e91c01 | ||
|
|
7f314dfe11 | ||
|
|
0caf9f4214 | ||
|
|
1ad600c81a | ||
|
|
bd6c7b7fdb | ||
|
|
5f93724845 | ||
|
|
ad60eac6ef | ||
|
|
b3e3a2937a | ||
|
|
d010038228 | ||
|
|
c28eada93e | ||
|
|
9d84a92b1f | ||
|
|
7884be106d | ||
|
|
5fe7c7e7e1 | ||
|
|
f244b95aad | ||
|
|
3b5a5759b8 | ||
|
|
528e7ee33b | ||
|
|
c1f55735fc |
6
Makefile
6
Makefile
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
@@ -80,8 +79,8 @@ void __attribute__((noreturn)) mainHandler(u32 *regs, u32 type, u32 cpuId)
|
|||||||
u8 *instr = (u8 *)pc + ((cpsr & 0x20) ? 2 : 4) - dumpHeader.codeDumpSize; //Doesn't work well on 32-bit Thumb instructions, but it isn't much of a problem
|
u8 *instr = (u8 *)pc + ((cpsr & 0x20) ? 2 : 4) - dumpHeader.codeDumpSize; //Doesn't work well on 32-bit Thumb instructions, but it isn't much of a problem
|
||||||
dumpHeader.codeDumpSize = copyMemory(codeDump, instr, dumpHeader.codeDumpSize, ((cpsr & 0x20) != 0) ? 2 : 4);
|
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,13 +88,13 @@ void __attribute__((noreturn)) mainHandler(u32 *regs, u32 type, u32 cpuId)
|
|||||||
dumpHeader.stackDumpSize = copyMemory(final, (const void *)registerDump[13], 0x1000 - (registerDump[13] & 0xFFF), 1);
|
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;
|
||||||
vu8 *currentKCodeSet = *(vu8 **)(*(vu8 **)0xFFFF9004 + CODESET_OFFSET); //currentKProcess + CodeSet
|
vu8 *currentKCodeSet = *(vu8 **)(*(vu8 **)0xFFFF9004 + CODESET_OFFSET); //currentKProcess + CodeSet
|
||||||
|
|
||||||
additionalData[0] = *(vu64 *)(currentKCodeSet + 0x50); //Process name
|
additionalData[0] = *(vu64 *)(currentKCodeSet + 0x50); //Process name
|
||||||
additionalData[1] = *(vu64 *)(currentKCodeSet + 0x5C); //Title ID
|
additionalData[1] = *(vu64 *)(currentKCodeSet + 0x5C); //Title ID
|
||||||
}
|
}
|
||||||
else dumpHeader.additionalDataSize = 0;
|
else dumpHeader.additionalDataSize = 0;
|
||||||
@@ -103,7 +102,7 @@ void __attribute__((noreturn)) mainHandler(u32 *regs, u32 type, u32 cpuId)
|
|||||||
dumpHeader.totalSize = sizeof(ExceptionDumpHeader) + dumpHeader.registerDumpSize + dumpHeader.codeDumpSize + dumpHeader.stackDumpSize + dumpHeader.additionalDataSize;
|
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
|
||||||
|
|||||||
@@ -22,9 +22,9 @@
|
|||||||
# Notices displayed by works containing it.
|
# Notices displayed by works containing it.
|
||||||
|
|
||||||
__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,14 +79,14 @@ 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)
|
||||||
|
|
||||||
|
|
||||||
def makeRegisterLine(A, rA, B, rB):
|
def makeRegisterLine(A, rA, B, rB):
|
||||||
return "{0:<15}{1:<20}{2:<15}{3:<20}".format(A, "{0:08x}".format(rA), B, "{0:08x}".format(rB))
|
return "{0:<15}{1:<20}{2:<15}{3:<20}".format(A, "{0:08x}".format(rA), B, "{0:08x}".format(rB))
|
||||||
|
|
||||||
handledExceptionNames = ("FIQ", "undefined instruction", "prefetch abort", "data abort")
|
handledExceptionNames = ("FIQ", "undefined instruction", "prefetch abort", "data abort")
|
||||||
registerNames = tuple("r{0}".format(i) for i in range(13)) + ("sp", "lr", "pc", "cpsr") + ("dfsr", "ifsr", "far") + ("fpexc", "fpinst", "fpinst2")
|
registerNames = tuple("r{0}".format(i) for i in range(13)) + ("sp", "lr", "pc", "cpsr") + ("dfsr", "ifsr", "far") + ("fpexc", "fpinst", "fpinst2")
|
||||||
svcBreakReasons = ("(svcBreak: panic)", "(svcBreak: assertion failed)", "(svcBreak: user-related)")
|
svcBreakReasons = ("(svcBreak: panic)", "(svcBreak: assertion failed)", "(svcBreak: user-related)")
|
||||||
@@ -96,23 +99,24 @@ if __name__ == "__main__":
|
|||||||
with open(args.filename, "rb") as f: data = f.read()
|
with open(args.filename, "rb") as f: data = f.read()
|
||||||
if unpack_from("<2I", data) != (0xdeadc0de, 0xdeadcafe):
|
if unpack_from("<2I", data) != (0xdeadc0de, 0xdeadcafe):
|
||||||
raise SystemExit("Invalid file format")
|
raise SystemExit("Invalid file format")
|
||||||
|
|
||||||
version, processor, exceptionType, _, nbRegisters, codeDumpSize, stackDumpSize, additionalDataSize = unpack_from("<8I", data, 8)
|
version, processor, exceptionType, _, nbRegisters, codeDumpSize, stackDumpSize, additionalDataSize = unpack_from("<8I", data, 8)
|
||||||
nbRegisters //= 4
|
nbRegisters //= 4
|
||||||
|
|
||||||
if version < (1 << 16) | 2:
|
if version < (1 << 16) | 2:
|
||||||
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]
|
||||||
|
|
||||||
if processor == 9: print("Processor: ARM9")
|
if processor == 9: print("Processor: ARM9")
|
||||||
else: print("Processor: ARM11 (core {0})".format(processor >> 16))
|
else: print("Processor: ARM11 (core {0})".format(processor >> 16))
|
||||||
|
|
||||||
typeDetailsStr = ""
|
typeDetailsStr = ""
|
||||||
if exceptionType == 2:
|
if exceptionType == 2:
|
||||||
if (registers[16] & 0x20) == 0 and codeDumpSize >= 4:
|
if (registers[16] & 0x20) == 0 and codeDumpSize >= 4:
|
||||||
@@ -125,23 +129,41 @@ if __name__ == "__main__":
|
|||||||
instr = unpack_from("<I", codeDump[-4:])[0]
|
instr = unpack_from("<I", codeDump[-4:])[0]
|
||||||
if instr == 0xdf3c:
|
if instr == 0xdf3c:
|
||||||
typeDetailsStr = " " + (svcBreakReasons[registers[0]] if registers[0] < 3 else "(svcBreak)")
|
typeDetailsStr = " " + (svcBreakReasons[registers[0]] if registers[0] < 3 else "(svcBreak)")
|
||||||
|
|
||||||
elif processor != 9 and (registers[20] & 0x80000000) != 0:
|
elif processor != 9 and (registers[20] & 0x80000000) != 0:
|
||||||
typeDetailsStr = " (VFP exception)"
|
typeDetailsStr = " (VFP exception)"
|
||||||
|
|
||||||
print("Exception type: {0}{1}".format("unknown" if exceptionType >= len(handledExceptionNames) else handledExceptionNames[exceptionType], typeDetailsStr))
|
print("Exception type: {0}{1}".format("unknown" if exceptionType >= len(handledExceptionNames) else handledExceptionNames[exceptionType], typeDetailsStr))
|
||||||
if additionalDataSize != 0:
|
if additionalDataSize != 0:
|
||||||
print("Current process: {0} ({1:016x})".format(additionalData[:8].decode("ascii"), unpack_from("<Q", additionalData, 8)[0]))
|
print("Current process: {0} ({1:016x})".format(additionalData[:8].decode("ascii"), unpack_from("<Q", additionalData, 8)[0]))
|
||||||
|
|
||||||
print("\nRegister dump:\n")
|
print("\nRegister dump:\n")
|
||||||
for i in range(0, nbRegisters - (nbRegisters % 2), 2):
|
for i in range(0, nbRegisters - (nbRegisters % 2), 2):
|
||||||
if i == 16: print("")
|
if i == 16: print("")
|
||||||
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))
|
||||||
|
|
||||||
@@ -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
123
haxloader/diffs/1.diff
Normal 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();
|
||||||
@@ -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;
|
||||||
11
haxloader/graphics/drunkenlogo.grit
Normal file
11
haxloader/graphics/drunkenlogo.grit
Normal 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
|
||||||
BIN
haxloader/graphics/drunkenlogo.png
Normal file
BIN
haxloader/graphics/drunkenlogo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
@@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -274,17 +274,17 @@ static u32 calcSDSize(u8 *csd, int type)
|
|||||||
switch(type)
|
switch(type)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
{
|
{
|
||||||
u32 block_len = csd[9] & 0xF;
|
u32 block_len = csd[9] & 0xF;
|
||||||
block_len = 1u << block_len;
|
block_len = 1u << block_len;
|
||||||
u32 mult = (u32)((csd[4] >> 7) | ((csd[5] & 3) << 1));
|
u32 mult = (u32)((csd[4] >> 7) | ((csd[5] & 3) << 1));
|
||||||
mult = 1u << (mult + 2);
|
mult = 1u << (mult + 2);
|
||||||
result = csd[8] & 3;
|
result = csd[8] & 3;
|
||||||
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;
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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,10 +74,13 @@ void main(void)
|
|||||||
f_read(&payload, payloadAddress, payloadSize, &read);
|
f_read(&payload, payloadAddress, payloadSize, &read);
|
||||||
f_close(&payload);
|
f_close(&payload);
|
||||||
|
|
||||||
flushDCacheRange(loaderAddress, loader_bin_size);
|
if((u32)read == payloadSize)
|
||||||
flushICacheRange(loaderAddress, loader_bin_size);
|
{
|
||||||
|
flushDCacheRange(loaderAddress, loader_bin_size);
|
||||||
|
flushICacheRange(loaderAddress, loader_bin_size);
|
||||||
|
|
||||||
((void (*)())loaderAddress)();
|
((void (*)())loaderAddress)();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -63,4 +63,4 @@ Result IFile_Read(IFile *file, u64 *total, void *buffer, u32 len)
|
|||||||
|
|
||||||
*total = cur;
|
*total = cur;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ static void patchMemory(u8 *start, u32 size, const void *pattern, u32 patSize, i
|
|||||||
{
|
{
|
||||||
u8 *found = memsearch(start, pattern, size, patSize);
|
u8 *found = memsearch(start, pattern, size, patSize);
|
||||||
|
|
||||||
if(found == NULL) break;
|
if(found == NULL) svcBreak(USERBREAK_ASSERT);
|
||||||
|
|
||||||
memcpy(found + offset, replace, repSize);
|
memcpy(found + offset, replace, repSize);
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ static void patchMemory(u8 *start, u32 size, const void *pattern, u32 patSize, i
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int fileOpen(IFile *file, FS_ArchiveID archiveId, const char *path, int flags)
|
static Result fileOpen(IFile *file, FS_ArchiveID archiveId, const char *path, int flags)
|
||||||
{
|
{
|
||||||
FS_Path filePath = {PATH_ASCII, strnlen(path, 255) + 1, path},
|
FS_Path filePath = {PATH_ASCII, strnlen(path, 255) + 1, path},
|
||||||
archivePath = {PATH_EMPTY, 1, (u8 *)""};
|
archivePath = {PATH_EMPTY, 1, (u8 *)""};
|
||||||
@@ -34,7 +34,16 @@ static int fileOpen(IFile *file, FS_ArchiveID archiveId, const char *path, int f
|
|||||||
return IFile_Open(file, archiveId, archivePath, filePath, flags);
|
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,7 +52,7 @@ 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;
|
||||||
@@ -77,7 +86,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;
|
||||||
|
|
||||||
@@ -135,7 +144,7 @@ static void loadTitleCodeSection(u64 progId, u8 *code, u32 size)
|
|||||||
|
|
||||||
IFile file;
|
IFile file;
|
||||||
|
|
||||||
if(R_SUCCEEDED(fileOpen(&file, ARCHIVE_SDMC, path, FS_OPEN_READ)))
|
if(R_SUCCEEDED(openLumaFile(&file, path)))
|
||||||
{
|
{
|
||||||
u64 fileSize;
|
u64 fileSize;
|
||||||
|
|
||||||
@@ -143,6 +152,8 @@ static void loadTitleCodeSection(u64 progId, u8 *code, u32 size)
|
|||||||
{
|
{
|
||||||
u64 total;
|
u64 total;
|
||||||
IFile_Read(&file, &total, code, fileSize);
|
IFile_Read(&file, &total, code, fileSize);
|
||||||
|
|
||||||
|
if(total != fileSize) svcBreak(USERBREAK_ASSERT);
|
||||||
}
|
}
|
||||||
|
|
||||||
IFile_Close(&file);
|
IFile_Close(&file);
|
||||||
@@ -159,7 +170,7 @@ static void loadTitleLocaleConfig(u64 progId, u8 *regionId, u8 *languageId)
|
|||||||
|
|
||||||
IFile file;
|
IFile file;
|
||||||
|
|
||||||
if(R_SUCCEEDED(fileOpen(&file, ARCHIVE_SDMC, path, FS_OPEN_READ)))
|
if(R_SUCCEEDED(openLumaFile(&file, path)))
|
||||||
{
|
{
|
||||||
u64 fileSize;
|
u64 fileSize;
|
||||||
|
|
||||||
@@ -273,9 +284,9 @@ 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;
|
return;
|
||||||
@@ -298,10 +309,10 @@ static void patchCfgGetRegion(u8 *code, u32 size, u8 regionId, u32 CFGUHandleOff
|
|||||||
cmp[2] == cfgSecureInfoGetRegionCmdPattern[2] && *((u16 *)cmdPos + 7) == 0xE59F &&
|
cmp[2] == cfgSecureInfoGetRegionCmdPattern[2] && *((u16 *)cmdPos + 7) == 0xE59F &&
|
||||||
*(u32 *)(cmdPos + 20 + *((u16 *)cmdPos + 6)) == CFGUHandleOffset)
|
*(u32 *)(cmdPos + 20 + *((u16 *)cmdPos + 6)) == CFGUHandleOffset)
|
||||||
{
|
{
|
||||||
*((u32 *)cmdPos + 4) = 0xE3A00000 | regionId; // mov r0, =regionId
|
*((u32 *)cmdPos + 4) = 0xE3A00000 | regionId; //mov r0, =regionId
|
||||||
*((u32 *)cmdPos + 5) = 0xE5C40008; // strb r0, [r4, 8]
|
*((u32 *)cmdPos + 5) = 0xE5C40008; //strb r0, [r4, 8]
|
||||||
*((u32 *)cmdPos + 6) = 0xE3B00000; // movs r0, 0 (result code) ('s' not needed but nvm)
|
*((u32 *)cmdPos + 6) = 0xE3B00000; //movs r0, 0 (result code) ('s' not needed but nvm)
|
||||||
*((u32 *)cmdPos + 7) = 0xE5840004; // str r0, [r4, 4]
|
*((u32 *)cmdPos + 7) = 0xE5840004; //str r0, [r4, 4]
|
||||||
|
|
||||||
//The remaining, not patched, function code will do the rest for us
|
//The remaining, not patched, function code will do the rest for us
|
||||||
break;
|
break;
|
||||||
@@ -309,153 +320,110 @@ static void patchCfgGetRegion(u8 *code, u32 size, u8 regionId, u32 CFGUHandleOff
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void patchCode(u64 progId, u8 *code, u32 size)
|
void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
|
||||||
{
|
{
|
||||||
loadCFWInfo();
|
loadCFWInfo();
|
||||||
|
|
||||||
switch(progId)
|
if(((progId == 0x0004003000008F02LL || //USA Home Menu
|
||||||
|
progId == 0x0004003000008202LL || //JPN Home Menu
|
||||||
|
progId == 0x0004003000009802LL) //EUR Home Menu
|
||||||
|
&& progVer > 4) ||
|
||||||
|
(progId == 0x000400300000A902LL //KOR Home Menu
|
||||||
|
&& progVer > 0) ||
|
||||||
|
progId == 0x000400300000A102LL || //CHN Home Menu
|
||||||
|
progId == 0x000400300000B102LL) //TWN Home Menu
|
||||||
{
|
{
|
||||||
case 0x0004003000008F02LL: // USA Menu
|
static const u8 regionFreePattern[] = {
|
||||||
case 0x0004003000008202LL: // EUR Menu
|
0x0A, 0x0C, 0x00, 0x10
|
||||||
case 0x0004003000009802LL: // JPN Menu
|
},
|
||||||
case 0x000400300000A102LL: // CHN Menu
|
regionFreePatch[] = {
|
||||||
case 0x000400300000A902LL: // KOR Menu
|
0x01, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1
|
||||||
case 0x000400300000B102LL: // TWN Menu
|
};
|
||||||
|
|
||||||
|
//Patch SMDH region checks
|
||||||
|
patchMemory(code, size,
|
||||||
|
regionFreePattern,
|
||||||
|
sizeof(regionFreePattern), -31,
|
||||||
|
regionFreePatch,
|
||||||
|
sizeof(regionFreePatch), 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(progId == 0x0004013000003202LL) //FRIENDS
|
||||||
|
{
|
||||||
|
static const u8 fpdVerPattern[] = {
|
||||||
|
0x42, 0xE0, 0x1E, 0xFF
|
||||||
|
};
|
||||||
|
|
||||||
|
u8 mostRecentFpdVer = 8;
|
||||||
|
|
||||||
|
u8 *off = memsearch(code, fpdVerPattern, size, sizeof(fpdVerPattern));
|
||||||
|
|
||||||
|
if(off == NULL) svcBreak(USERBREAK_ASSERT);
|
||||||
|
|
||||||
|
//Allow online access to work with old friends modules
|
||||||
|
if(off[0xA] < mostRecentFpdVer) off[0xA] = mostRecentFpdVer;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if((progId == 0x0004001000021000LL || //USA MSET
|
||||||
|
progId == 0x0004001000020000LL || //JPN MSET
|
||||||
|
progId == 0x0004001000022000LL || //EUR MSET
|
||||||
|
progId == 0x0004001000026000LL || //CHN MSET
|
||||||
|
progId == 0x0004001000027000LL || //KOR MSET
|
||||||
|
progId == 0x0004001000028000LL) //TWN MSET
|
||||||
|
&& CONFIG(PATCHVERSTRING))
|
||||||
|
{
|
||||||
|
static const u16 verPattern[] = u"Ve";
|
||||||
|
static u16 *verString;
|
||||||
|
u32 verStringSize = 0,
|
||||||
|
currentNand = BOOTCFG_NAND;
|
||||||
|
|
||||||
|
u16 customVerString[19];
|
||||||
|
loadCustomVerString(customVerString, &verStringSize, currentNand);
|
||||||
|
|
||||||
|
if(verStringSize != 0) verString = customVerString;
|
||||||
|
else
|
||||||
{
|
{
|
||||||
static const u8 regionFreePattern[] = {
|
verStringSize = 8;
|
||||||
0x00, 0x00, 0x55, 0xE3, 0x01, 0x10, 0xA0
|
u32 currentFirm = BOOTCFG_FIRM;
|
||||||
};
|
|
||||||
static const u8 regionFreePatch[] = {
|
|
||||||
0x01, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1
|
|
||||||
};
|
|
||||||
|
|
||||||
//Patch SMDH region checks
|
static u16 *verStringsNands[] = { u" Sys",
|
||||||
patchMemory(code, size,
|
u" Emu",
|
||||||
regionFreePattern,
|
u"Emu2",
|
||||||
sizeof(regionFreePattern), -16,
|
u"Emu3",
|
||||||
regionFreePatch,
|
u"Emu4" },
|
||||||
sizeof(regionFreePatch), 1
|
|
||||||
);
|
|
||||||
|
|
||||||
break;
|
*verStringsEmuSys[] = { u"EmuS",
|
||||||
|
u"Em2S",
|
||||||
|
u"Em3S",
|
||||||
|
u"Em4S" },
|
||||||
|
|
||||||
|
*verStringsSysEmu[] = { u"SysE",
|
||||||
|
u"SyE2",
|
||||||
|
u"SyE3",
|
||||||
|
u"SyE4" };
|
||||||
|
|
||||||
|
verString = (currentFirm != 0) == (currentNand != 0) ? verStringsNands[currentNand] :
|
||||||
|
(!currentNand ? verStringsSysEmu[currentFirm - 1] : verStringsEmuSys[currentNand - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x0004013000002C02LL: // NIM
|
//Patch Ver. string
|
||||||
{
|
patchMemory(code, size,
|
||||||
static const u8 blockAutoUpdatesPattern[] = {
|
verPattern,
|
||||||
0x25, 0x79, 0x0B, 0x99
|
sizeof(verPattern) - 2, 0,
|
||||||
};
|
verString,
|
||||||
static const u8 blockAutoUpdatesPatch[] = {
|
verStringSize, 1
|
||||||
0xE3, 0xA0
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
//Block silent auto-updates
|
else if(progId == 0x0004013000008002LL) //NS
|
||||||
patchMemory(code, size,
|
{
|
||||||
blockAutoUpdatesPattern,
|
if(progVer > 4)
|
||||||
sizeof(blockAutoUpdatesPattern), 0,
|
|
||||||
blockAutoUpdatesPatch,
|
|
||||||
sizeof(blockAutoUpdatesPatch), 1
|
|
||||||
);
|
|
||||||
|
|
||||||
//Apply only if the user booted with R
|
|
||||||
if((BOOTCFG_NAND != 0) != (BOOTCFG_FIRM != 0))
|
|
||||||
{
|
|
||||||
static const u8 skipEshopUpdateCheckPattern[] = {
|
|
||||||
0x30, 0xB5, 0xF1, 0xB0
|
|
||||||
};
|
|
||||||
static const u8 skipEshopUpdateCheckPatch[] = {
|
|
||||||
0x00, 0x20, 0x08, 0x60, 0x70, 0x47
|
|
||||||
};
|
|
||||||
|
|
||||||
//Skip update checks to access the EShop
|
|
||||||
patchMemory(code, size,
|
|
||||||
skipEshopUpdateCheckPattern,
|
|
||||||
sizeof(skipEshopUpdateCheckPattern), 0,
|
|
||||||
skipEshopUpdateCheckPatch,
|
|
||||||
sizeof(skipEshopUpdateCheckPatch), 1
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 0x0004013000003202LL: // FRIENDS
|
|
||||||
{
|
|
||||||
static const u8 fpdVerPattern[] = {
|
|
||||||
0xE0, 0x1E, 0xFF, 0x2F, 0xE1, 0x01, 0x01
|
|
||||||
};
|
|
||||||
|
|
||||||
u8 mostRecentFpdVer = 7;
|
|
||||||
|
|
||||||
u8 *fpdVer = memsearch(code, fpdVerPattern, size, sizeof(fpdVerPattern));
|
|
||||||
|
|
||||||
//Allow online access to work with old friends modules
|
|
||||||
if(fpdVer != NULL && fpdVer[9] < mostRecentFpdVer) fpdVer[9] = mostRecentFpdVer;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 0x0004001000021000LL: // USA MSET
|
|
||||||
case 0x0004001000020000LL: // JPN MSET
|
|
||||||
case 0x0004001000022000LL: // EUR MSET
|
|
||||||
case 0x0004001000026000LL: // CHN MSET
|
|
||||||
case 0x0004001000027000LL: // KOR MSET
|
|
||||||
case 0x0004001000028000LL: // TWN MSET
|
|
||||||
{
|
|
||||||
if(CONFIG(PATCHVERSTRING))
|
|
||||||
{
|
|
||||||
static const u16 verPattern[] = u"Ver.";
|
|
||||||
static u16 *verString;
|
|
||||||
u32 verStringSize = 0;
|
|
||||||
u32 currentNand = BOOTCFG_NAND;
|
|
||||||
|
|
||||||
u16 customVerString[19];
|
|
||||||
loadCustomVerString(customVerString, &verStringSize, currentNand);
|
|
||||||
|
|
||||||
if(verStringSize != 0) verString = customVerString;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
verStringSize = 8;
|
|
||||||
u32 currentFirm = BOOTCFG_FIRM;
|
|
||||||
|
|
||||||
static u16 *verStringsNands[] = { u" Sys",
|
|
||||||
u" Emu",
|
|
||||||
u"Emu2",
|
|
||||||
u"Emu3",
|
|
||||||
u"Emu4" },
|
|
||||||
|
|
||||||
*verStringsEmuSys[] = { u"EmuS",
|
|
||||||
u"Em2S",
|
|
||||||
u"Em3S",
|
|
||||||
u"Em4S" },
|
|
||||||
|
|
||||||
*verStringsSysEmu[] = { u"SysE",
|
|
||||||
u"SyE2",
|
|
||||||
u"SyE3",
|
|
||||||
u"SyE4" };
|
|
||||||
|
|
||||||
verString = (currentFirm != 0) == (currentNand != 0) ? verStringsNands[currentNand] :
|
|
||||||
(!currentNand ? verStringsSysEmu[currentFirm - 1] : verStringsEmuSys[currentNand - 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Patch Ver. string
|
|
||||||
patchMemory(code, size,
|
|
||||||
verPattern,
|
|
||||||
sizeof(verPattern) - 2, 0,
|
|
||||||
verString,
|
|
||||||
verStringSize, 1
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 0x0004013000008002LL: // NS
|
|
||||||
{
|
{
|
||||||
static const u8 stopCartUpdatesPattern[] = {
|
static const u8 stopCartUpdatesPattern[] = {
|
||||||
0x0C, 0x18, 0xE1, 0xD8
|
0x0C, 0x18, 0xE1, 0xD8
|
||||||
};
|
},
|
||||||
static const u8 stopCartUpdatesPatch[] = {
|
stopCartUpdatesPatch[] = {
|
||||||
0x0B, 0x18, 0x21, 0xC8
|
0x0B, 0x18, 0x21, 0xC8
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -466,166 +434,148 @@ void patchCode(u64 progId, u8 *code, u32 size)
|
|||||||
stopCartUpdatesPatch,
|
stopCartUpdatesPatch,
|
||||||
sizeof(stopCartUpdatesPatch), 2
|
sizeof(stopCartUpdatesPatch), 2
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(LOADERFLAG(ISN3DS))
|
||||||
|
{
|
||||||
u32 cpuSetting = MULTICONFIG(NEWCPU);
|
u32 cpuSetting = MULTICONFIG(NEWCPU);
|
||||||
|
|
||||||
if(cpuSetting != 0)
|
if(cpuSetting != 0)
|
||||||
{
|
{
|
||||||
static const u8 cfgN3dsCpuPattern[] = {
|
static const u8 cfgN3dsCpuPattern[] = {
|
||||||
0x00, 0x40, 0xA0, 0xE1, 0x07
|
0x0C, 0x00, 0x94, 0x15
|
||||||
};
|
};
|
||||||
|
|
||||||
u32 *cfgN3dsCpuLoc = (u32 *)memsearch(code, cfgN3dsCpuPattern, size, sizeof(cfgN3dsCpuPattern));
|
u32 *off = (u32 *)memsearch(code, cfgN3dsCpuPattern, size, sizeof(cfgN3dsCpuPattern));
|
||||||
|
|
||||||
|
if(off == NULL) svcBreak(USERBREAK_ASSERT);
|
||||||
|
|
||||||
//Patch N3DS CPU Clock and L2 cache setting
|
//Patch N3DS CPU Clock and L2 cache setting
|
||||||
if(cfgN3dsCpuLoc != NULL)
|
*(off - 4) = 0xE1A00000;
|
||||||
{
|
*(off + 3) = 0xE3A00000 | cpuSetting;
|
||||||
*(cfgN3dsCpuLoc + 1) = 0xE1A00000;
|
|
||||||
*(cfgN3dsCpuLoc + 8) = 0xE3A00000 | cpuSetting;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case 0x0004013000001702LL: // CFG
|
else if(progId == 0x0004013000001702LL) //CFG
|
||||||
|
{
|
||||||
|
static const u8 secureinfoSigCheckPattern[] = {
|
||||||
|
0x06, 0x46, 0x10, 0x48
|
||||||
|
},
|
||||||
|
secureinfoSigCheckPatch[] = {
|
||||||
|
0x00, 0x26
|
||||||
|
};
|
||||||
|
|
||||||
|
//Disable SecureInfo signature check
|
||||||
|
patchMemory(code, size,
|
||||||
|
secureinfoSigCheckPattern,
|
||||||
|
sizeof(secureinfoSigCheckPattern), 0,
|
||||||
|
secureinfoSigCheckPatch,
|
||||||
|
sizeof(secureinfoSigCheckPatch), 1
|
||||||
|
);
|
||||||
|
|
||||||
|
if(secureInfoExists())
|
||||||
{
|
{
|
||||||
static const u8 secureinfoSigCheckPattern[] = {
|
static const u16 secureinfoFilenamePattern[] = u"SecureInfo_",
|
||||||
0x06, 0x46, 0x10, 0x48
|
secureinfoFilenamePatch[] = u"C";
|
||||||
};
|
|
||||||
static const u8 secureinfoSigCheckPatch[] = {
|
|
||||||
0x00, 0x26
|
|
||||||
};
|
|
||||||
|
|
||||||
//Disable SecureInfo signature check
|
//Use SecureInfo_C
|
||||||
patchMemory(code, size,
|
patchMemory(code, size,
|
||||||
secureinfoSigCheckPattern,
|
secureinfoFilenamePattern,
|
||||||
sizeof(secureinfoSigCheckPattern), 0,
|
sizeof(secureinfoFilenamePattern) - 2,
|
||||||
secureinfoSigCheckPatch,
|
sizeof(secureinfoFilenamePattern) - 2,
|
||||||
sizeof(secureinfoSigCheckPatch), 1
|
secureinfoFilenamePatch,
|
||||||
|
sizeof(secureinfoFilenamePatch) - 2, 2
|
||||||
);
|
);
|
||||||
|
|
||||||
if(secureInfoExists())
|
|
||||||
{
|
|
||||||
static const u16 secureinfoFilenamePattern[] = u"SecureInfo_";
|
|
||||||
static const u16 secureinfoFilenamePatch[] = u"C";
|
|
||||||
|
|
||||||
//Use SecureInfo_C
|
|
||||||
patchMemory(code, size,
|
|
||||||
secureinfoFilenamePattern,
|
|
||||||
sizeof(secureinfoFilenamePattern) - 2,
|
|
||||||
sizeof(secureinfoFilenamePattern) - 2,
|
|
||||||
secureinfoFilenamePatch,
|
|
||||||
sizeof(secureinfoFilenamePatch) - 2, 2
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case 0x0004013000003702LL: // RO
|
else if(progId == 0x0004013000003702LL && progVer > 0) //RO
|
||||||
|
{
|
||||||
|
static const u8 sigCheckPattern[] = {
|
||||||
|
0x20, 0xA0, 0xE1, 0x8B
|
||||||
|
},
|
||||||
|
sha256ChecksPattern1[] = {
|
||||||
|
0xE1, 0x30, 0x40, 0x2D
|
||||||
|
},
|
||||||
|
sha256ChecksPattern2[] = {
|
||||||
|
0x2D, 0xE9, 0x01, 0x70
|
||||||
|
},
|
||||||
|
stub[] = {
|
||||||
|
0x00, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1 //mov r0, #0; bx lr
|
||||||
|
};
|
||||||
|
|
||||||
|
//Disable CRR0 signature (RSA2048 with SHA256) check
|
||||||
|
patchMemory(code, size,
|
||||||
|
sigCheckPattern,
|
||||||
|
sizeof(sigCheckPattern), -9,
|
||||||
|
stub,
|
||||||
|
sizeof(stub), 1
|
||||||
|
);
|
||||||
|
|
||||||
|
//Disable CRO0/CRR0 SHA256 hash checks (section hashes, and hash table)
|
||||||
|
patchMemory(code, size,
|
||||||
|
sha256ChecksPattern1,
|
||||||
|
sizeof(sha256ChecksPattern1), 1,
|
||||||
|
stub,
|
||||||
|
sizeof(stub), 1
|
||||||
|
);
|
||||||
|
|
||||||
|
patchMemory(code, size,
|
||||||
|
sha256ChecksPattern2,
|
||||||
|
sizeof(sha256ChecksPattern2), -2,
|
||||||
|
stub,
|
||||||
|
sizeof(stub), 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(progId == 0x0004003000008A02LL && MULTICONFIG(DEVOPTIONS) == 1) //ErrDisp
|
||||||
|
{
|
||||||
|
static const u8 unitinfoCheckPattern1[] = {
|
||||||
|
0x00, 0xD0, 0xE5, 0xDB
|
||||||
|
},
|
||||||
|
unitinfoCheckPattern2[] = {
|
||||||
|
0x14, 0x00, 0xD0, 0xE5, 0x01
|
||||||
|
},
|
||||||
|
unitinfoCheckPatch[] = {
|
||||||
|
0x00, 0x00, 0xA0, 0xE3
|
||||||
|
};
|
||||||
|
|
||||||
|
patchMemory(code, size,
|
||||||
|
unitinfoCheckPattern1,
|
||||||
|
sizeof(unitinfoCheckPattern1), -1,
|
||||||
|
unitinfoCheckPatch,
|
||||||
|
sizeof(unitinfoCheckPatch), 1
|
||||||
|
);
|
||||||
|
|
||||||
|
patchMemory(code, size,
|
||||||
|
unitinfoCheckPattern2,
|
||||||
|
sizeof(unitinfoCheckPattern2), 0,
|
||||||
|
unitinfoCheckPatch,
|
||||||
|
sizeof(unitinfoCheckPatch), 3
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(CONFIG(USELANGEMUANDCODE) && (u32)((progId & 0xFFFFFFF000000000LL) >> 0x24) == 0x0004000)
|
||||||
|
{
|
||||||
|
//External .code section loading
|
||||||
|
loadTitleCodeSection(progId, code, size);
|
||||||
|
|
||||||
|
//Language emulation
|
||||||
|
u8 regionId = 0xFF,
|
||||||
|
languageId = 0xFF;
|
||||||
|
loadTitleLocaleConfig(progId, ®ionId, &languageId);
|
||||||
|
|
||||||
|
if(regionId != 0xFF || regionId != 0xFF)
|
||||||
{
|
{
|
||||||
static const u8 sigCheckPattern[] = {
|
u32 CFGUHandleOffset;
|
||||||
0x30, 0x40, 0x2D, 0xE9, 0x02
|
u8 *CFGU_GetConfigInfoBlk2_endPos = getCfgOffsets(code, size, &CFGUHandleOffset);
|
||||||
};
|
|
||||||
static const u8 sha256ChecksPattern1[] = {
|
|
||||||
0x30, 0x40, 0x2D, 0xE9, 0x24
|
|
||||||
};
|
|
||||||
static const u8 sha256ChecksPattern2[] = {
|
|
||||||
0xF8, 0x4F, 0x2D, 0xE9, 0x01
|
|
||||||
};
|
|
||||||
|
|
||||||
static const u8 stub[] = {
|
|
||||||
0x00, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1 // mov r0, #0; bx lr
|
|
||||||
};
|
|
||||||
|
|
||||||
//Disable CRR0 signature (RSA2048 with SHA256) check
|
|
||||||
patchMemory(code, size,
|
|
||||||
sigCheckPattern,
|
|
||||||
sizeof(sigCheckPattern), 0,
|
|
||||||
stub,
|
|
||||||
sizeof(stub), 1
|
|
||||||
);
|
|
||||||
|
|
||||||
//Disable CRO0/CRR0 SHA256 hash checks (section hashes, and hash table)
|
|
||||||
patchMemory(code, size,
|
|
||||||
sha256ChecksPattern1,
|
|
||||||
sizeof(sha256ChecksPattern1), 0,
|
|
||||||
stub,
|
|
||||||
sizeof(stub), 1
|
|
||||||
);
|
|
||||||
|
|
||||||
patchMemory(code, size,
|
|
||||||
sha256ChecksPattern2,
|
|
||||||
sizeof(sha256ChecksPattern2), 0,
|
|
||||||
stub,
|
|
||||||
sizeof(stub), 1
|
|
||||||
);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 0x0004003000008A02LL: // ErrDisp
|
if(CFGU_GetConfigInfoBlk2_endPos != NULL)
|
||||||
{
|
|
||||||
if(MULTICONFIG(DEVOPTIONS) == 1)
|
|
||||||
{
|
{
|
||||||
static const u8 unitinfoCheckPattern1[] = {
|
if(languageId != 0xFF) patchCfgGetLanguage(code, size, languageId, CFGU_GetConfigInfoBlk2_endPos);
|
||||||
0x14, 0x00, 0xD0, 0xE5, 0xDB
|
if(regionId != 0xFF) patchCfgGetRegion(code, size, regionId, CFGUHandleOffset);
|
||||||
};
|
|
||||||
|
|
||||||
static const u8 unitinfoCheckPattern2[] = {
|
|
||||||
0x14, 0x00, 0xD0, 0xE5, 0x01
|
|
||||||
} ;
|
|
||||||
|
|
||||||
static const u8 unitinfoCheckPatch[] = {
|
|
||||||
0x00, 0x00, 0xA0, 0xE3
|
|
||||||
} ;
|
|
||||||
|
|
||||||
patchMemory(code, size,
|
|
||||||
unitinfoCheckPattern1,
|
|
||||||
sizeof(unitinfoCheckPattern1), 0,
|
|
||||||
unitinfoCheckPatch,
|
|
||||||
sizeof(unitinfoCheckPatch), 1
|
|
||||||
);
|
|
||||||
|
|
||||||
patchMemory(code, size,
|
|
||||||
unitinfoCheckPattern2,
|
|
||||||
sizeof(unitinfoCheckPattern2), 0,
|
|
||||||
unitinfoCheckPatch,
|
|
||||||
sizeof(unitinfoCheckPatch), 3
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
|
||||||
if(CONFIG(USELANGEMUANDCODE))
|
|
||||||
{
|
|
||||||
if((u32)((progId & 0xFFFFFFF000000000LL) >> 0x24) == 0x0004000)
|
|
||||||
{
|
|
||||||
//External .code section loading
|
|
||||||
loadTitleCodeSection(progId, code, size);
|
|
||||||
|
|
||||||
//Language emulation
|
|
||||||
u8 regionId = 0xFF,
|
|
||||||
languageId = 0xFF;
|
|
||||||
loadTitleLocaleConfig(progId, ®ionId, &languageId);
|
|
||||||
|
|
||||||
if(regionId != 0xFF || regionId != 0xFF)
|
|
||||||
{
|
|
||||||
u32 CFGUHandleOffset;
|
|
||||||
u8 *CFGU_GetConfigInfoBlk2_endPos = getCfgOffsets(code, size, &CFGUHandleOffset);
|
|
||||||
|
|
||||||
if(CFGU_GetConfigInfoBlk2_endPos != NULL)
|
|
||||||
{
|
|
||||||
if(languageId != 0xFF) patchCfgGetLanguage(code, size, languageId, CFGU_GetConfigInfoBlk2_endPos);
|
|
||||||
if(regionId != 0xFF) patchCfgGetRegion(code, size, regionId, CFGUHandleOffset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
@@ -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) }
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
bne fs_patch ; Skip if they're not the same
|
ldr r0, [r0, #0x18] ; Load the size of the .text
|
||||||
|
add r0, r1, r0 ; Max bounds of the memory region
|
||||||
|
|
||||||
|
ldr r3, =0x1002 ; Low title id of the sm module
|
||||||
|
cmp r2, r3 ; Compare the low title id to the id of the sm module
|
||||||
|
bne fs_patch ; Skip if they're not the same
|
||||||
|
|
||||||
|
ldr r2, =0xE1A01006 ; mov r1, r6
|
||||||
|
|
||||||
ldr r7, =0xE1A01006 ; mov r1, r6
|
|
||||||
ldr r8, =0xE1A00005 ; mov r0, r5
|
|
||||||
ldr r9, =0xE3500000 ; cmp r0, #0
|
|
||||||
ldr r10, =0xE2850004 ; add r0, r5, #4
|
|
||||||
|
|
||||||
loop:
|
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
|
||||||
|
|
||||||
|
; r1 now contains the start address of the pattern we found
|
||||||
|
ldr r0, =0x2001 ; mov r0, #1
|
||||||
|
ldr r2, =0x4770 ; bx lr
|
||||||
|
strh r0, [r1]
|
||||||
|
strh r2, [r1, #2]
|
||||||
|
|
||||||
; r8 now contains the start address of the pattern we found
|
|
||||||
ldr r9, =0x2001 ; mov r0, #1
|
|
||||||
ldr r10, =0x4770 ; bx lr
|
|
||||||
strh r9, [r8]
|
|
||||||
strh r10, [r8, #2]
|
|
||||||
|
|
||||||
out:
|
out:
|
||||||
pop {r0-r12} ; Restore the registers we used
|
pop {r0-r4} ; Restore the registers we used
|
||||||
|
bx lr ; Jump back to whoever called us
|
||||||
|
|
||||||
ldr r0, [r4] ; Execute the instruction we overwrote in our detour
|
die:
|
||||||
|
b die
|
||||||
bx lr ; Jump back to whoever called us
|
|
||||||
|
|
||||||
.pool
|
.pool
|
||||||
.close
|
.close
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
|
; 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)
|
||||||
|
sd_notmounted equ 0xC8804465 ; Error code returned when SD is not mounted
|
||||||
|
|
||||||
.create "build/reboot.bin", 0
|
.create "build/reboot.bin", 0
|
||||||
.arm
|
.arm
|
||||||
@@ -25,32 +28,40 @@ 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
|
||||||
|
|
||||||
; Open file
|
open_payload:
|
||||||
add r0, r7, #8
|
; Open file
|
||||||
adr r1, fname
|
add r0, r7, #8
|
||||||
mov r2, #1
|
adr r1, fname
|
||||||
ldr r6, [fopen]
|
mov r2, #1
|
||||||
orr r6, 1
|
ldr r6, [fopen]
|
||||||
blx r6
|
orr r6, 1
|
||||||
|
blx r6
|
||||||
|
cmp r0, #0
|
||||||
|
beq read_payload
|
||||||
|
ldr r2, =sd_notmounted
|
||||||
|
cmp r0, r2
|
||||||
|
bne panic
|
||||||
|
adr r0, fname
|
||||||
|
adr r1, nand_mount
|
||||||
|
mov r2, #8
|
||||||
|
bl memcpy16
|
||||||
|
b open_payload
|
||||||
|
|
||||||
; Read file
|
read_payload:
|
||||||
mov r0, r7
|
; Read file
|
||||||
adr r1, bytes_read
|
mov r0, r7
|
||||||
ldr r2, =payload_addr
|
adr r1, bytes_read
|
||||||
mov r3, payload_maxsize
|
ldr r2, =payload_addr
|
||||||
ldr r6, [r7]
|
ldr r3, =payload_maxsize
|
||||||
ldr r6, [r6, #0x28]
|
ldr r6, [r7]
|
||||||
blx r6
|
ldr r6, [r6, #0x28]
|
||||||
|
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,28 +79,45 @@ 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:
|
||||||
|
|
||||||
; Disable MPU
|
; Disable MPU
|
||||||
ldr r0, =0x42078 ; alt vector select, enable itcm
|
ldr r0, =0x42078 ; alt vector select, enable itcm
|
||||||
mcr p15, 0, r0, c1, c0, 0
|
mcr p15, 0, r0, c1, c0, 0
|
||||||
|
|
||||||
; Clean and flush data cache
|
; Clean and flush data cache
|
||||||
mov r1, #0 ; segment counter
|
mov r1, #0 ; segment counter
|
||||||
outer_loop:
|
outer_loop:
|
||||||
mov r0, #0 ; line counter
|
mov r0, #0 ; line counter
|
||||||
|
|
||||||
inner_loop:
|
inner_loop:
|
||||||
orr r2, r1, r0 ; generate segment and line address
|
orr r2, r1, r0 ; generate segment and line address
|
||||||
mcr p15, 0, r2, c7, c14, 2 ; clean and flush the line
|
mcr p15, 0, r2, c7, c14, 2 ; clean and flush the line
|
||||||
add r0, #0x20 ; increment to next line
|
add r0, #0x20 ; increment to next line
|
||||||
cmp r0, #0x400
|
cmp r0, #0x400
|
||||||
bne inner_loop
|
bne inner_loop
|
||||||
|
|
||||||
@@ -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
161
source/3dsheaders.h
Normal 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;
|
||||||
145
source/config.c
145
source/config.c
@@ -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,7 +108,9 @@ 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"
|
"It will be always enabled.\n\n"
|
||||||
@@ -111,7 +118,8 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
|
|||||||
"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"
|
||||||
@@ -142,9 +150,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 +167,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 +195,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 +245,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,15 +254,22 @@ 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++)
|
||||||
{
|
{
|
||||||
singleOptions[i].posY = endPos + SPACING_Y;
|
if(singleOptions[i].visible)
|
||||||
endPos = drawString(singleOptionsText[i], true, 10, singleOptions[i].posY, color);
|
{
|
||||||
if(singleOptions[i].enabled) drawCharacter(selected, true, 10 + SPACING_X, singleOptions[i].posY, color);
|
singleOptions[i].posY = endPos + SPACING_Y;
|
||||||
color = COLOR_WHITE;
|
endPos = drawString(singleOptionsText[i], true, 10, singleOptions[i].posY, color);
|
||||||
|
if(singleOptions[i].enabled) drawCharacter(selected, true, 10 + SPACING_X, singleOptions[i].posY, color);
|
||||||
|
|
||||||
|
if(color == COLOR_RED)
|
||||||
|
{
|
||||||
|
selectedOption = i + multiOptionsAmount;
|
||||||
|
color = COLOR_WHITE;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
drawString(optionsDescription[selectedOption], false, 10, 10, COLOR_WHITE);
|
drawString(optionsDescription[selectedOption], false, 10, 10, COLOR_WHITE);
|
||||||
@@ -262,24 +290,31 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
|
|||||||
//Remember the previously selected option
|
//Remember the previously selected option
|
||||||
u32 oldSelectedOption = selectedOption;
|
u32 oldSelectedOption = selectedOption;
|
||||||
|
|
||||||
switch(pressed)
|
while(true)
|
||||||
{
|
{
|
||||||
case BUTTON_UP:
|
switch(pressed)
|
||||||
if(!selectedOption) selectedOption = totalIndexes;
|
{
|
||||||
else selectedOption = (selectedOption == NEWCPU + 1 && !isN3DS) ? selectedOption - 2 : selectedOption - 1;
|
case BUTTON_UP:
|
||||||
break;
|
selectedOption = !selectedOption ? totalIndexes : selectedOption - 1;
|
||||||
case BUTTON_DOWN:
|
break;
|
||||||
if(selectedOption == totalIndexes) selectedOption = 0;
|
case BUTTON_DOWN:
|
||||||
else selectedOption = (selectedOption == NEWCPU - 1 && !isN3DS) ? selectedOption + 2 : selectedOption + 1;
|
selectedOption = selectedOption == totalIndexes ? 0 : selectedOption + 1;
|
||||||
break;
|
break;
|
||||||
case BUTTON_LEFT:
|
case BUTTON_LEFT:
|
||||||
selectedOption = 0;
|
pressed = BUTTON_DOWN;
|
||||||
break;
|
selectedOption = 0;
|
||||||
case BUTTON_RIGHT:
|
break;
|
||||||
selectedOption = totalIndexes;
|
case BUTTON_RIGHT:
|
||||||
break;
|
pressed = BUTTON_UP;
|
||||||
default:
|
selectedOption = totalIndexes;
|
||||||
continue;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(selectedOption < multiOptionsAmount)
|
||||||
|
{
|
||||||
|
if(multiOptions[selectedOption].visible) break;
|
||||||
|
}
|
||||||
|
else if(singleOptions[selectedOption - multiOptionsAmount].visible) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(selectedOption == oldSelectedOption) continue;
|
if(selectedOption == oldSelectedOption) continue;
|
||||||
@@ -342,14 +377,14 @@ void configMenu(bool oldPinStatus, u32 oldPinMode)
|
|||||||
|
|
||||||
//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);
|
||||||
|
|||||||
@@ -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);
|
||||||
199
source/crypto.c
199
source/crypto.c
@@ -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;
|
||||||
|
|
||||||
@@ -300,6 +305,8 @@ static u32 fatStart;
|
|||||||
static u8 __attribute__((aligned(4))) shaHashBackup[SHA_256_HASH_SIZE];
|
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];
|
u8 __attribute__((aligned(4))) cid[AES_BLOCK_SIZE];
|
||||||
@@ -309,7 +316,7 @@ void ctrNandInit(void)
|
|||||||
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};
|
u8 __attribute__((aligned(4))) keyY0x5[AES_BLOCK_SIZE] = {0x4D, 0x80, 0x4F, 0x4E, 0x99, 0x90, 0x19, 0x46, 0x13, 0xA2, 0x04, 0xAC, 0x58, 0x44, 0x60, 0xBE};
|
||||||
aes_setkey(0x05, keyY0x5, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
aes_setkey(0x05, keyY0x5, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
@@ -324,14 +331,14 @@ void ctrNandInit(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf)
|
int ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf)
|
||||||
{
|
{
|
||||||
u8 __attribute__((aligned(4))) tmpCtr[sizeof(nandCtr)];
|
u8 __attribute__((aligned(4))) 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,107 @@ 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;
|
||||||
|
|
||||||
|
u8 __attribute__((aligned(4))) tmpCtr[sizeof(nandCtr)];
|
||||||
|
memcpy(tmpCtr, nandCtr, sizeof(nandCtr));
|
||||||
|
aes_advctr(tmpCtr, ((sector + fatStart) * 0x200) / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
|
aes_use_keyslot(nandSlot);
|
||||||
|
|
||||||
|
int result = 0;
|
||||||
|
for(u32 tempSector = 0; tempSector < sectorCount && !result; tempSector += bufferSize / 0x200)
|
||||||
|
{
|
||||||
|
u32 tempCount = (bufferSize / 0x200) < (sectorCount - tempSector) ? (bufferSize / 0x200) : (sectorCount - tempSector);
|
||||||
|
|
||||||
|
memcpy(buffer, inbuf + (tempSector * 0x200), tempCount * 0x200);
|
||||||
|
|
||||||
|
//Encrypt
|
||||||
|
aes(buffer, buffer, tempCount * 0x200 / AES_BLOCK_SIZE, tmpCtr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
|
|
||||||
|
//Write
|
||||||
|
result = sdmmc_nand_writesectors(tempSector + sector + fatStart, tempCount, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void set6x7xKeys(void)
|
void set6x7xKeys(void)
|
||||||
{
|
{
|
||||||
if(!isDevUnit)
|
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};
|
||||||
|
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};
|
||||||
|
|
||||||
|
aes_setkey(0x25, keyX0x25, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
|
aes_setkey(0x2F, keyY0x2F, 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,
|
||||||
|
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. */
|
||||||
|
memset32((void *)0x01FFCD00, 0, 0x10);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool decryptExeFs(Cxi *cxi)
|
||||||
|
{
|
||||||
|
bool isCxi;
|
||||||
|
|
||||||
|
if(memcmp(cxi->ncch.magic, "NCCH", 4) == 0)
|
||||||
{
|
{
|
||||||
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};
|
isCxi = true;
|
||||||
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};
|
|
||||||
|
|
||||||
aes_setkey(0x25, keyX0x25, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL);
|
u8 *exeFsOffset = (u8 *)cxi + (cxi->ncch.exeFsOffset + 1) * 0x200;
|
||||||
aes_setkey(0x2F, keyY0x2F, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
u32 exeFsSize = (cxi->ncch.exeFsSize - 1) * 0x200;
|
||||||
|
u8 __attribute__((aligned(4))) ncchCtr[AES_BLOCK_SIZE] = {0};
|
||||||
|
|
||||||
/* [3dbrew] The first 0x10-bytes are checked by the v6.0/v7.0 NATIVE_FIRM keyinit function,
|
for(u32 i = 0; i < 8; i++)
|
||||||
when non-zero it clears this block and continues to do the key generation.
|
ncchCtr[7 - i] = cxi->ncch.partitionId[i];
|
||||||
Otherwise when this block was already all-zero, it immediately returns. */
|
ncchCtr[8] = 2;
|
||||||
memset32((void *)0x01FFCD00, 0, 0x10);
|
|
||||||
|
aes_setkey(0x2C, cxi, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
|
aes_advctr(ncchCtr, 0x200 / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
|
aes_use_keyslot(0x2C);
|
||||||
|
aes(cxi, exeFsOffset, exeFsSize / AES_BLOCK_SIZE, ncchCtr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
}
|
}
|
||||||
|
else isCxi = false;
|
||||||
|
|
||||||
|
return isCxi && memcmp(cxi, "FIRM", 4) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void decryptExeFs(u8 *inbuf)
|
bool decryptNusFirm(const Ticket *ticket, Cxi *cxi, u32 ncchSize)
|
||||||
{
|
{
|
||||||
u8 *exeFsOffset = inbuf + *(u32 *)(inbuf + 0x1A0) * 0x200;
|
bool isTicket;
|
||||||
u32 exeFsSize = *(u32 *)(inbuf + 0x1A4) * 0x200;
|
|
||||||
u8 __attribute__((aligned(4))) ncchCtr[AES_BLOCK_SIZE] = {0};
|
|
||||||
|
|
||||||
for(u32 i = 0; i < 8; i++)
|
if(memcmp(ticket->sigIssuer, "Root", 4) == 0)
|
||||||
ncchCtr[7 - i] = *(inbuf + 0x108 + i);
|
{
|
||||||
ncchCtr[8] = 2;
|
isTicket = true;
|
||||||
|
|
||||||
aes_setkey(0x2C, inbuf, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
const u8 keyY0x3D[AES_BLOCK_SIZE] = {0x0C, 0x76, 0x72, 0x30, 0xF0, 0x99, 0x8F, 0x1C, 0x46, 0x82, 0x82, 0x02, 0xFA, 0xAC, 0xBE, 0x4C};
|
||||||
aes_use_keyslot(0x2C);
|
u8 __attribute__((aligned(4))) titleKey[AES_BLOCK_SIZE];
|
||||||
aes(inbuf - 0x200, exeFsOffset, exeFsSize / AES_BLOCK_SIZE, ncchCtr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
u8 __attribute__((aligned(4))) 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_use_keyslot(0x3D);
|
||||||
|
aes(titleKey, titleKey, 1, cetkIv, AES_CBC_DECRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
|
|
||||||
|
u8 __attribute__((aligned(4))) ncchIv[AES_BLOCK_SIZE] = {0};
|
||||||
|
|
||||||
|
aes_setkey(0x16, titleKey, AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
|
aes_use_keyslot(0x16);
|
||||||
|
aes(cxi, cxi, ncchSize / AES_BLOCK_SIZE, ncchIv, AES_CBC_DECRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
|
}
|
||||||
|
else isTicket = false;
|
||||||
|
|
||||||
|
return isTicket && decryptExeFs(cxi);
|
||||||
}
|
}
|
||||||
|
|
||||||
void decryptNusFirm(const u8 *inbuf, u8 *outbuf, u32 ncchSize)
|
void kernel9Loader(Arm9Bin *arm9Section)
|
||||||
{
|
|
||||||
const u8 keyY0x3D[AES_BLOCK_SIZE] = {0x0C, 0x76, 0x72, 0x30, 0xF0, 0x99, 0x8F, 0x1C, 0x46, 0x82, 0x82, 0x02, 0xFA, 0xAC, 0xBE, 0x4C};
|
|
||||||
u8 __attribute__((aligned(4))) cetkIv[AES_BLOCK_SIZE] = {0};
|
|
||||||
u8 __attribute__((aligned(4))) titleKey[AES_BLOCK_SIZE];
|
|
||||||
memcpy(titleKey, inbuf + 0x1BF, sizeof(titleKey));
|
|
||||||
memcpy(cetkIv, inbuf + 0x1DC, 8);
|
|
||||||
|
|
||||||
aes_setkey(0x3D, keyY0x3D, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
|
||||||
aes_use_keyslot(0x3D);
|
|
||||||
aes(titleKey, titleKey, 1, cetkIv, AES_CBC_DECRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
|
||||||
|
|
||||||
u8 __attribute__((aligned(4))) ncchIv[AES_BLOCK_SIZE] = {0};
|
|
||||||
|
|
||||||
aes_setkey(0x16, titleKey, AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL);
|
|
||||||
aes_use_keyslot(0x16);
|
|
||||||
aes(outbuf, outbuf, ncchSize / AES_BLOCK_SIZE, ncchIv, AES_CBC_DECRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
|
||||||
|
|
||||||
decryptExeFs(outbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
void kernel9Loader(u8 *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,10 +467,10 @@ 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(!ISDEVUNIT && (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};
|
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};
|
||||||
@@ -440,28 +490,24 @@ void kernel9Loader(u8 *arm9Section)
|
|||||||
//Set keyX
|
//Set keyX
|
||||||
u8 __attribute__((aligned(4))) keyX[AES_BLOCK_SIZE];
|
u8 __attribute__((aligned(4))) 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];
|
u8 __attribute__((aligned(4))) keyY[AES_BLOCK_SIZE];
|
||||||
memcpy(keyY, arm9Section + 0x10, sizeof(keyY));
|
memcpy(keyY, arm9Section->keyY, sizeof(keyY));
|
||||||
aes_setkey(arm9BinSlot, keyY, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
aes_setkey(arm9BinSlot, keyY, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
|
|
||||||
//Set CTR
|
//Set CTR
|
||||||
u8 __attribute__((aligned(4))) arm9BinCtr[AES_BLOCK_SIZE];
|
u8 __attribute__((aligned(4))) 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
|
||||||
@@ -485,19 +531,20 @@ void computePinHash(u8 *outbuf, const u8 *inbuf)
|
|||||||
u8 __attribute__((aligned(4))) cid[AES_BLOCK_SIZE];
|
u8 __attribute__((aligned(4))) cid[AES_BLOCK_SIZE];
|
||||||
u8 __attribute__((aligned(4))) cipherText[AES_BLOCK_SIZE];
|
u8 __attribute__((aligned(4))) 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(4); //Console-unique keyslot whose keys are set by the ARM9 bootROM
|
||||||
aes(cipherText, inbuf, 1, cid, AES_CBC_ENCRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
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(didShaHashBackup) memcpy((void *)REG_SHA_HASH, shaHashBackup, sizeof(shaHashBackup));
|
if(ISA9LH)
|
||||||
|
{
|
||||||
|
if(isRestore)
|
||||||
|
{
|
||||||
|
if(didShaHashBackup) memcpy((void *)REG_SHA_HASH, shaHashBackup, sizeof(shaHashBackup));
|
||||||
|
}
|
||||||
|
else if(!didShaHashBackup) memcpy(shaHashBackup, (void *)REG_SHA_HASH, sizeof(shaHashBackup));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
@@ -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();
|
||||||
|
clearScreens(true, true, true);
|
||||||
|
|
||||||
initScreens();
|
if(isTopSplashValid) isTopSplashValid = fileRead(fbs[1].top_left, topSplashFile, SCREEN_TOP_FBSIZE) == SCREEN_TOP_FBSIZE;
|
||||||
clearScreens(true, true, true);
|
if(isBottomSplashValid) isBottomSplashValid = fileRead(fbs[1].bottom, bottomSplashFile, SCREEN_BOTTOM_FBSIZE) == SCREEN_BOTTOM_FBSIZE;
|
||||||
|
|
||||||
if(isTopSplashValid) fileRead(fbs[1].top_left, topSplashPath, 0);
|
if(!isTopSplashValid && !isBottomSplashValid) ret = false;
|
||||||
if(isBottomSplashValid) fileRead(fbs[1].bottom, bottomSplashPath, 0);
|
else
|
||||||
|
{
|
||||||
|
swapFramebuffers(true);
|
||||||
|
|
||||||
swapFramebuffers(true);
|
chrono(3);
|
||||||
|
|
||||||
chrono(3);
|
ret = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawCharacter(char character, bool isTopScreen, u32 posX, u32 posY, u32 color)
|
void drawCharacter(char character, bool isTopScreen, u32 posX, u32 posY, u32 color)
|
||||||
|
|||||||
135
source/emunand.c
135
source/emunand.c
@@ -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,68 +99,125 @@ void locateEmuNand(u32 *emuHeader, FirmwareSource *nandType)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u8 *getFreeK9Space(u8 *pos, u32 size)
|
static inline u32 getFreeK9Space(u8 *pos, u32 size, u8 **freeK9Space)
|
||||||
{
|
{
|
||||||
const u8 pattern[] = {0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00};
|
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;
|
|
||||||
|
|
||||||
*readOffset = *writeOffset = 0x4C00;
|
if(readOffset == NULL) ret = 1;
|
||||||
readOffset[1] = writeOffset[1] = 0x47A0;
|
else
|
||||||
((u32 *)writeOffset)[1] = ((u32 *)readOffset)[1] = branchOffset;
|
{
|
||||||
|
readOffset -= 3;
|
||||||
|
|
||||||
|
u16 *writeOffset = (u16 *)memsearch((u8 *)(readOffset + 5), pattern, 0x100, sizeof(pattern));
|
||||||
|
|
||||||
|
if(writeOffset == NULL) ret = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writeOffset -= 3;
|
||||||
|
*readOffset = *writeOffset = 0x4C00;
|
||||||
|
readOffset[1] = writeOffset[1] = 0x47A0;
|
||||||
|
((u32 *)writeOffset)[1] = ((u32 *)readOffset)[1] = branchOffset;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void patchMpu(u8 *pos, u32 size)
|
static inline u32 patchMpu(u8 *pos, u32 size)
|
||||||
{
|
{
|
||||||
//Look for MPU pattern
|
//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)
|
||||||
{
|
{
|
||||||
//Copy EmuNAND code
|
u32 ret = 0;
|
||||||
u8 *freeK9Space = getFreeK9Space(arm9Section, arm9SectionSize);
|
|
||||||
memcpy(freeK9Space, emunand_bin, emunand_bin_size);
|
|
||||||
|
|
||||||
//Add the data of the found EmuNAND
|
u8 *freeK9Space;
|
||||||
u32 *posOffset = (u32 *)memsearch(freeK9Space, "NAND", emunand_bin_size, 4),
|
ret += getFreeK9Space(arm9Section, kernel9Size, &freeK9Space);
|
||||||
*posHeader = (u32 *)memsearch(freeK9Space, "NCSD", emunand_bin_size, 4);
|
|
||||||
*posOffset = emuOffset;
|
|
||||||
*posHeader = emuHeader;
|
|
||||||
|
|
||||||
//Find and add the SDMMC struct
|
if(!ret)
|
||||||
u32 *posSdmmc = (u32 *)memsearch(freeK9Space, "SDMC", emunand_bin_size, 4);
|
{
|
||||||
*posSdmmc = getSdmmc(process9Offset, process9Size);
|
//Copy EmuNAND code
|
||||||
|
memcpy(freeK9Space, emunand_bin, emunand_bin_size);
|
||||||
|
|
||||||
//Add EmuNAND hooks
|
//Add the data of the found EmuNAND
|
||||||
u32 branchOffset = (u32)freeK9Space - branchAdditive;
|
u32 *posOffset = (u32 *)memsearch(freeK9Space, "NAND", emunand_bin_size, 4),
|
||||||
patchNandRw(process9Offset, process9Size, branchOffset);
|
*posHeader = (u32 *)memsearch(freeK9Space, "NCSD", emunand_bin_size, 4);
|
||||||
|
*posOffset = emuOffset;
|
||||||
|
*posHeader = emuHeader;
|
||||||
|
|
||||||
//Set MPU
|
//Find and add the SDMMC struct
|
||||||
patchMpu(arm9Section, arm9SectionSize);
|
u32 *posSdmmc = (u32 *)memsearch(freeK9Space, "SDMC", emunand_bin_size, 4);
|
||||||
|
u32 sdmmc;
|
||||||
|
ret += getSdmmc(process9Offset, process9Size, &sdmmc);
|
||||||
|
if(!ret) *posSdmmc = sdmmc;
|
||||||
|
|
||||||
|
//Add EmuNAND hooks
|
||||||
|
u32 branchOffset = (u32)(freeK9Space - arm9Section + kernel9Address);
|
||||||
|
ret += patchNandRw(process9Offset, process9Size, branchOffset);
|
||||||
|
|
||||||
|
//Set MPU
|
||||||
|
ret += patchMpu(arm9Section, kernel9Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
@@ -45,37 +45,49 @@ void installArm9Handlers(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void installArm11Handlers(u32 *exceptionsPage, u32 stackAddress, u32 codeSetOffset)
|
u32 installArm11Handlers(u32 *exceptionsPage, u32 stackAddress, u32 codeSetOffset)
|
||||||
{
|
{
|
||||||
u32 *initFPU;
|
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 != 0xE1A0D002 && initFPU < endPos; initFPU++);
|
||||||
mcuReboot--;
|
|
||||||
|
|
||||||
u32 *freeSpace;
|
u32 *freeSpace;
|
||||||
for(freeSpace = initFPU; freeSpace < (exceptionsPage + 0x400) && (freeSpace[0] != 0xFFFFFFFF || freeSpace[1] != 0xFFFFFFFF); freeSpace++);
|
for(freeSpace = initFPU; *freeSpace != 0xFFFFFFFF && freeSpace < endPos; freeSpace++);
|
||||||
|
|
||||||
memcpy(freeSpace, arm11_exceptions_bin + 32, arm11_exceptions_bin_size - 32);
|
u32 *mcuReboot;
|
||||||
|
for(mcuReboot = exceptionsPage; *mcuReboot != 0xE3A0A0C2 && mcuReboot < endPos; mcuReboot++);
|
||||||
|
|
||||||
exceptionsPage[1] = MAKE_BRANCH(exceptionsPage + 1, (u8 *)freeSpace + *(u32 *)(arm11_exceptions_bin + 8) - 32); //Undefined Instruction
|
if(initFPU == endPos || freeSpace == endPos || mcuReboot == endPos || *(u32 *)((u8 *)freeSpace + arm11_exceptions_bin_size - 36) != 0xFFFFFFFF) ret = 1;
|
||||||
exceptionsPage[3] = MAKE_BRANCH(exceptionsPage + 3, (u8 *)freeSpace + *(u32 *)(arm11_exceptions_bin + 12) - 32); //Prefetch Abort
|
else
|
||||||
exceptionsPage[4] = MAKE_BRANCH(exceptionsPage + 4, (u8 *)freeSpace + *(u32 *)(arm11_exceptions_bin + 16) - 32); //Data Abort
|
|
||||||
exceptionsPage[7] = MAKE_BRANCH(exceptionsPage + 7, (u8 *)freeSpace + *(u32 *)(arm11_exceptions_bin + 4) - 32); //FIQ
|
|
||||||
|
|
||||||
for(u32 *pos = freeSpace; pos < (u32 *)((u8 *)freeSpace + arm11_exceptions_bin_size - 32); pos++)
|
|
||||||
{
|
{
|
||||||
switch(*pos) //Perform relocations
|
initFPU += 3;
|
||||||
|
mcuReboot -= 2;
|
||||||
|
|
||||||
|
memcpy(freeSpace, arm11_exceptions_bin + 32, arm11_exceptions_bin_size - 32);
|
||||||
|
|
||||||
|
exceptionsPage[1] = MAKE_BRANCH(exceptionsPage + 1, (u8 *)freeSpace + *(u32 *)(arm11_exceptions_bin + 8) - 32); //Undefined Instruction
|
||||||
|
exceptionsPage[3] = MAKE_BRANCH(exceptionsPage + 3, (u8 *)freeSpace + *(u32 *)(arm11_exceptions_bin + 12) - 32); //Prefetch Abort
|
||||||
|
exceptionsPage[4] = MAKE_BRANCH(exceptionsPage + 4, (u8 *)freeSpace + *(u32 *)(arm11_exceptions_bin + 16) - 32); //Data Abort
|
||||||
|
exceptionsPage[7] = MAKE_BRANCH(exceptionsPage + 7, (u8 *)freeSpace + *(u32 *)(arm11_exceptions_bin + 4) - 32); //FIQ
|
||||||
|
|
||||||
|
for(u32 *pos = freeSpace; pos < (u32 *)((u8 *)freeSpace + arm11_exceptions_bin_size - 32); pos++)
|
||||||
{
|
{
|
||||||
case 0xFFFF3000: *pos = stackAddress; break;
|
switch(*pos) //Perform relocations
|
||||||
case 0xEBFFFFFE: *pos = MAKE_BRANCH_LINK(pos, initFPU); break;
|
{
|
||||||
case 0xEAFFFFFE: *pos = MAKE_BRANCH(pos, mcuReboot); break;
|
case 0xFFFF3000: *pos = stackAddress; break;
|
||||||
case 0xE12FFF1C: pos[1] = 0xFFFF0000 + 4 * (u32)(freeSpace - exceptionsPage) + pos[1] - 32; break; //bx r12 (mainHandler)
|
case 0xEBFFFFFE: *pos = MAKE_BRANCH_LINK(pos, initFPU); break;
|
||||||
case 0xBEEFBEEF: *pos = codeSetOffset; break;
|
case 0xEAFFFFFE: *pos = MAKE_BRANCH(pos, mcuReboot); break;
|
||||||
default: break;
|
case 0xE12FFF1C: pos[1] = 0xFFFF0000 + 4 * (u32)(freeSpace - exceptionsPage) + pos[1] - 32; break; //bx r12 (mainHandler)
|
||||||
|
case 0xBEEFBEEF: *pos = codeSetOffset; break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void detectAndProcessExceptionDumps(void)
|
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);
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
@@ -268,17 +266,17 @@ static u32 calcSDSize(u8 *csd, int type)
|
|||||||
switch(type)
|
switch(type)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
{
|
{
|
||||||
u32 block_len = csd[9] & 0xF;
|
u32 block_len = csd[9] & 0xF;
|
||||||
block_len = 1u << block_len;
|
block_len = 1u << block_len;
|
||||||
u32 mult = (u32)((csd[4] >> 7) | ((csd[5] & 3) << 1));
|
u32 mult = (u32)((csd[4] >> 7) | ((csd[5] & 3) << 1));
|
||||||
mult = 1u << (mult + 2);
|
mult = 1u << (mult + 2);
|
||||||
result = csd[8] & 3;
|
result = csd[8] & 3;
|
||||||
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;
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
498
source/firm.c
498
source/firm.c
@@ -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)
|
||||||
{
|
{
|
||||||
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,229 @@ 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.");
|
||||||
}
|
}
|
||||||
|
|
||||||
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 or a dev unit
|
||||||
else if(!isA9lh && firmVersion >= 0x29) set6x7xKeys();
|
else if(!ISA9LH && !ISFIRMLAUNCH && firmVersion >= 0x29 && !ISDEVUNIT) 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
|
//11.0 FIRM patches
|
||||||
if(firmVersion >= (isN3DS ? 0x21 : 0x52))
|
if(firmVersion >= (ISN3DS ? 0x21 : 0x52))
|
||||||
{
|
{
|
||||||
//Apply anti-anti-DG patches
|
//Apply anti-anti-DG patches
|
||||||
patchTitleInstallMinVersionCheck(process9Offset, process9Size);
|
ret += patchTitleInstallMinVersionChecks(process9Offset, process9Size, firmVersion);
|
||||||
|
|
||||||
//Restore svcBackdoor
|
//Restore svcBackdoor
|
||||||
reimplementSvcBackdoor(arm11Section1, arm11SvcTable, baseK11VA, &freeK11Space);
|
ret += reimplementSvcBackdoor(arm11Section1, arm11SvcTable, baseK11VA, &freeK11Space);
|
||||||
}
|
}
|
||||||
|
|
||||||
implementSvcGetCFWInfo(arm11Section1, arm11SvcTable, baseK11VA, &freeK11Space);
|
ret += implementSvcGetCFWInfo(arm11Section1, arm11SvcTable, baseK11VA, &freeK11Space, isSafeMode);
|
||||||
|
|
||||||
//Apply UNITINFO patch
|
//Apply UNITINFO patch
|
||||||
if(devMode == 2) patchUnitInfoValueSet(arm9Section, section[2].size);
|
if(devMode == 2) ret += patchUnitInfoValueSet(arm9Section, kernel9Size);
|
||||||
|
|
||||||
if(devMode != 0 && isA9lh)
|
if(devMode != 0 && isA9lhInstalled)
|
||||||
{
|
{
|
||||||
//ARM11 exception handlers
|
//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;
|
||||||
|
|
||||||
|
//On N3DS, decrypt ARM9Bin and patch ARM9 entrypoint to skip kernel9loader
|
||||||
|
if(ISN3DS)
|
||||||
|
{
|
||||||
|
kernel9Loader((Arm9Bin *)arm9Section);
|
||||||
|
firm->arm9Entry = (u8 *)0x801301C;
|
||||||
|
}
|
||||||
|
|
||||||
if(isN3DS)
|
//Find the Process9 .code location, size and memory address
|
||||||
|
u32 process9Size,
|
||||||
|
process9MemAddr;
|
||||||
|
u8 *process9Offset = getProcess9Info(arm9Section, firm->section[3].size, &process9Size, &process9MemAddr);
|
||||||
|
|
||||||
|
u32 kernel9Size = (u32)(process9Offset - arm9Section) - sizeof(Cxi) - 0x200,
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
ret += patchLgySignatureChecks(process9Offset, process9Size);
|
||||||
|
if(CONFIG(SHOWGBABOOT)) ret += patchAgbBootSplash(process9Offset, process9Size);
|
||||||
|
|
||||||
|
//Apply UNITINFO patch
|
||||||
|
if(devMode == 2) ret += patchUnitInfoValueSet(arm9Section, kernel9Size);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 patch1x2xNativeAndSafeFirm(u32 devMode)
|
||||||
|
{
|
||||||
|
u8 *arm9Section = (u8 *)firm + firm->section[2].offset;
|
||||||
|
|
||||||
|
if(ISN3DS)
|
||||||
{
|
{
|
||||||
//Decrypt ARM9Bin and patch ARM9 entrypoint to skip kernel9loader
|
//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);
|
||||||
|
|
||||||
|
if(dstModuleSize == 0) loadedModule = false;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(dstModuleSize > maxModuleSize) error(extModuleSizeError);
|
||||||
|
|
||||||
|
if(dstModuleSize <= sizeof(Cxi) + 0x200 ||
|
||||||
|
fileRead(dst, fileName, dstModuleSize) != dstModuleSize ||
|
||||||
|
memcmp(((Cxi *)dst)->ncch.magic, "NCCH", 4) != 0 ||
|
||||||
|
memcmp(moduleName, ((Cxi *)dst)->exHeader.systemControlInfo.appTitle, sizeof(((Cxi *)dst)->exHeader.systemControlInfo.appTitle)) != 0)
|
||||||
|
error("An external FIRM module is invalid or corrupted.");
|
||||||
|
|
||||||
|
loadedModule = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else fileSize = 0;
|
|
||||||
|
|
||||||
if(fileSize > 0) dstModuleSize = fileSize;
|
if(!loadedModule)
|
||||||
else
|
|
||||||
{
|
{
|
||||||
const u8 *module;
|
const u8 *module;
|
||||||
|
|
||||||
@@ -501,29 +339,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();
|
||||||
|
|||||||
@@ -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);
|
|
||||||
197
source/fs.c
197
source/fs.c
@@ -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,34 +87,38 @@ 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)
|
|
||||||
{
|
{
|
||||||
unsigned int written;
|
case FR_OK:
|
||||||
f_write(&file, buffer, size, &written);
|
{
|
||||||
f_truncate(&file);
|
unsigned int written;
|
||||||
f_close(&file);
|
f_write(&file, buffer, size, &written);
|
||||||
|
f_truncate(&file);
|
||||||
|
f_close(&file);
|
||||||
|
|
||||||
return true;
|
ret = (u32)written == size;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FR_NO_PATH:
|
||||||
|
for(u32 i = 1; path[i] != 0; i++)
|
||||||
|
if(path[i] == '/')
|
||||||
|
{
|
||||||
|
char folder[i + 1];
|
||||||
|
memcpy(folder, path, i);
|
||||||
|
folder[i] = 0;
|
||||||
|
f_mkdir(folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = fileWrite(buffer, path, size);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = false;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(result == FR_NO_PATH)
|
return ret;
|
||||||
{
|
|
||||||
for(u32 i = 1; path[i] != 0; i++)
|
|
||||||
if(path[i] == '/')
|
|
||||||
{
|
|
||||||
char folder[i + 1];
|
|
||||||
memcpy(folder, path, i);
|
|
||||||
folder[i] = 0;
|
|
||||||
f_mkdir(folder);
|
|
||||||
}
|
|
||||||
|
|
||||||
return fileWrite(buffer, path, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void fileDelete(const char *path)
|
void fileDelete(const char *path)
|
||||||
@@ -117,35 +144,38 @@ 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);
|
||||||
|
|
||||||
f_closedir(&dir);
|
if(result == FR_OK)
|
||||||
|
|
||||||
if(result == FR_OK && info.fname[0] != 0)
|
|
||||||
{
|
{
|
||||||
u32 *loaderAddress = (u32 *)0x24FFFF00;
|
f_closedir(&dir);
|
||||||
u8 *payloadAddress = (u8 *)0x24F00000;
|
|
||||||
|
|
||||||
memcpy(loaderAddress, loader_bin, loader_bin_size);
|
if(info.fname[0] != 0)
|
||||||
|
|
||||||
concatenateStrings(path, "/");
|
|
||||||
concatenateStrings(path, info.altname);
|
|
||||||
|
|
||||||
u32 payloadSize = fileRead(payloadAddress, path, (u8 *)loaderAddress - payloadAddress);
|
|
||||||
|
|
||||||
if(payloadSize > 0)
|
|
||||||
{
|
{
|
||||||
loaderAddress[1] = payloadSize;
|
u32 *loaderAddress = (u32 *)0x24FFFE00;
|
||||||
|
u8 *payloadAddress = (u8 *)0x24F00000;
|
||||||
|
|
||||||
restoreShaHashBackup();
|
memcpy(loaderAddress, loader_bin, loader_bin_size);
|
||||||
initScreens();
|
|
||||||
|
|
||||||
flushDCacheRange(loaderAddress, loader_bin_size);
|
concatenateStrings(path, "/");
|
||||||
flushICacheRange(loaderAddress, loader_bin_size);
|
concatenateStrings(path, info.altname);
|
||||||
|
|
||||||
((void (*)())loaderAddress)();
|
u32 payloadSize = fileRead(payloadAddress, path, (u32)((u8 *)loaderAddress - payloadAddress));
|
||||||
|
|
||||||
|
if(payloadSize > 0)
|
||||||
|
{
|
||||||
|
loaderAddress[1] = payloadSize;
|
||||||
|
|
||||||
|
backupAndRestoreShaHash(true);
|
||||||
|
initScreens();
|
||||||
|
|
||||||
|
flushDCacheRange(loaderAddress, loader_bin_size);
|
||||||
|
flushICacheRange(loaderAddress, loader_bin_size);
|
||||||
|
|
||||||
|
((void (*)())loaderAddress)();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -158,64 +188,61 @@ u32 firmRead(void *dest, u32 firmType)
|
|||||||
{ "00000003", "20000003" }};
|
{ "00000003", "20000003" }};
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
//Parse the target directory
|
if(f_opendir(&dir, path) == FR_OK)
|
||||||
while(f_readdir(&dir, &info) == FR_OK && info.fname[0] != 0)
|
|
||||||
{
|
{
|
||||||
//Not a cxi
|
FILINFO info;
|
||||||
if(info.fname[9] != 'a') continue;
|
|
||||||
|
|
||||||
//Convert the .app name to an integer
|
//Parse the target directory
|
||||||
u32 tempVersion = 0;
|
while(f_readdir(&dir, &info) == FR_OK && info.fname[0] != 0)
|
||||||
for(char *tmp = info.altname; *tmp != '.'; tmp++)
|
|
||||||
{
|
{
|
||||||
tempVersion <<= 4;
|
//Not a cxi
|
||||||
tempVersion += *tmp > '9' ? *tmp - 'A' + 10 : *tmp - '0';
|
if(info.fname[9] != 'a' || strlen(info.fname) != 12) continue;
|
||||||
|
|
||||||
|
u32 tempVersion = hexAtoi(info.altname, 8);
|
||||||
|
|
||||||
|
//Found an older cxi
|
||||||
|
if(tempVersion < firmVersion) firmVersion = tempVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Found an older cxi
|
f_closedir(&dir);
|
||||||
if(tempVersion < firmVersion) firmVersion = tempVersion;
|
|
||||||
|
if(firmVersion != 0xFFFFFFFF)
|
||||||
|
{
|
||||||
|
//Complete the string with the .app name
|
||||||
|
concatenateStrings(path, "/00000000.app");
|
||||||
|
|
||||||
|
//Convert back the .app name from integer to array
|
||||||
|
hexItoa(firmVersion, path + 35, 8, false);
|
||||||
|
|
||||||
|
if(fileRead(dest, path, 0x400000 + sizeof(Cxi) + 0x200) <= sizeof(Cxi) + 0x200) firmVersion = 0xFFFFFFFF;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
f_closedir(&dir);
|
|
||||||
|
|
||||||
//Complete the string with the .app name
|
|
||||||
concatenateStrings(path, "/00000000.app");
|
|
||||||
|
|
||||||
//Convert back the .app name from integer to array
|
|
||||||
hexItoa(firmVersion, &path[35], 8);
|
|
||||||
|
|
||||||
fileRead(dest, path, 0);
|
|
||||||
|
|
||||||
return firmVersion;
|
return firmVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
|
|||||||
261
source/main.c
Normal file
261
source/main.c
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of Luma3DS
|
||||||
|
* Copyright (C) 2016 Aurora Wright, TuxSH
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||||
|
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||||
|
* Notices displayed by works containing it.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "emunand.h"
|
||||||
|
#include "fs.h"
|
||||||
|
#include "firm.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "exceptions.h"
|
||||||
|
#include "draw.h"
|
||||||
|
#include "strings.h"
|
||||||
|
#include "buttons.h"
|
||||||
|
#include "pin.h"
|
||||||
|
|
||||||
|
extern CfgData configData;
|
||||||
|
extern FirmwareSource firmSource;
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
bool isA9lhInstalled,
|
||||||
|
isSafeMode = false;
|
||||||
|
u32 configTemp,
|
||||||
|
emuHeader;
|
||||||
|
FirmwareType firmType;
|
||||||
|
FirmwareSource nandType;
|
||||||
|
ConfigurationStatus needConfig;
|
||||||
|
|
||||||
|
//Mount SD or CTRNAND
|
||||||
|
bool isSdMode;
|
||||||
|
if(mountFs(true, false)) isSdMode = true;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
firmSource = FIRMWARE_SYSNAND;
|
||||||
|
if(!mountFs(false, true)) error("Failed to mount SD and CTRNAND.");
|
||||||
|
isSdMode = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Attempt to read the configuration file
|
||||||
|
needConfig = readConfig() ? MODIFY_CONFIGURATION : CREATE_CONFIGURATION;
|
||||||
|
|
||||||
|
//Determine if this is a firmlaunch boot
|
||||||
|
if(ISFIRMLAUNCH)
|
||||||
|
{
|
||||||
|
if(needConfig == CREATE_CONFIGURATION) mcuPowerOff();
|
||||||
|
|
||||||
|
//'0' = NATIVE_FIRM, '1' = TWL_FIRM, '2' = AGB_FIRM
|
||||||
|
firmType = launchedFirmTidLow[7] == u'3' ? SAFE_FIRM : (FirmwareType)(launchedFirmTidLow[5] - u'0');
|
||||||
|
|
||||||
|
nandType = (FirmwareSource)BOOTCFG_NAND;
|
||||||
|
firmSource = (FirmwareSource)BOOTCFG_FIRM;
|
||||||
|
isA9lhInstalled = BOOTCFG_A9LH != 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(ISA9LH)
|
||||||
|
{
|
||||||
|
detectAndProcessExceptionDumps();
|
||||||
|
installArm9Handlers();
|
||||||
|
}
|
||||||
|
|
||||||
|
firmType = NATIVE_FIRM;
|
||||||
|
isA9lhInstalled = ISA9LH;
|
||||||
|
|
||||||
|
//Get pressed buttons
|
||||||
|
u32 pressed = HID_PAD;
|
||||||
|
|
||||||
|
//Save old options and begin saving the new boot configuration
|
||||||
|
configTemp = (configData.config & 0xFFFFFF00) | ((u32)ISA9LH << 6);
|
||||||
|
|
||||||
|
//If it's a MCU reboot, try to force boot options
|
||||||
|
if(ISA9LH && CFG_BOOTENV && needConfig != CREATE_CONFIGURATION)
|
||||||
|
{
|
||||||
|
//Always force a SysNAND boot when quitting AGB_FIRM
|
||||||
|
if(CFG_BOOTENV == 7)
|
||||||
|
{
|
||||||
|
nandType = FIRMWARE_SYSNAND;
|
||||||
|
firmSource = (BOOTCFG_NAND != 0) == (BOOTCFG_FIRM != 0) ? FIRMWARE_SYSNAND : (FirmwareSource)BOOTCFG_FIRM;
|
||||||
|
needConfig = DONT_CONFIGURE;
|
||||||
|
|
||||||
|
//Flag to prevent multiple boot options-forcing
|
||||||
|
configTemp |= 1 << 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Else, force the last used boot options unless a button is pressed
|
||||||
|
or the no-forcing flag is set */
|
||||||
|
else if(!pressed && !BOOTCFG_NOFORCEFLAG)
|
||||||
|
{
|
||||||
|
nandType = (FirmwareSource)BOOTCFG_NAND;
|
||||||
|
firmSource = (FirmwareSource)BOOTCFG_FIRM;
|
||||||
|
needConfig = DONT_CONFIGURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Boot options aren't being forced
|
||||||
|
if(needConfig != DONT_CONFIGURE)
|
||||||
|
{
|
||||||
|
u32 pinMode = MULTICONFIG(PIN);
|
||||||
|
bool pinExists = pinMode != 0 && verifyPin(pinMode);
|
||||||
|
|
||||||
|
//If no configuration file exists or SELECT is held, load configuration menu
|
||||||
|
bool shouldLoadConfigMenu = needConfig == CREATE_CONFIGURATION || ((pressed & (BUTTON_SELECT | BUTTON_L1)) == BUTTON_SELECT);
|
||||||
|
|
||||||
|
if(shouldLoadConfigMenu)
|
||||||
|
{
|
||||||
|
configMenu(isSdMode, pinExists, pinMode);
|
||||||
|
|
||||||
|
//Update pressed buttons
|
||||||
|
pressed = HID_PAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ISA9LH && !CFG_BOOTENV && pressed == SAFE_MODE)
|
||||||
|
{
|
||||||
|
nandType = FIRMWARE_SYSNAND;
|
||||||
|
firmSource = FIRMWARE_SYSNAND;
|
||||||
|
|
||||||
|
isSafeMode = true;
|
||||||
|
|
||||||
|
//If the PIN has been verified, wait to make it easier to press the SAFE_MODE combo
|
||||||
|
if(pinExists && !shouldLoadConfigMenu)
|
||||||
|
{
|
||||||
|
while(HID_PAD & PIN_BUTTONS);
|
||||||
|
chrono(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
u32 splashMode = MULTICONFIG(SPLASH);
|
||||||
|
|
||||||
|
if(splashMode == 1 && loadSplash()) pressed = HID_PAD;
|
||||||
|
|
||||||
|
/* If L and R/A/Select or one of the single payload buttons are pressed,
|
||||||
|
chainload an external payload */
|
||||||
|
bool shouldLoadPayload = ((pressed & SINGLE_PAYLOAD_BUTTONS) && !(pressed & (BUTTON_L1 | BUTTON_R1 | BUTTON_A))) ||
|
||||||
|
((pressed & L_PAYLOAD_BUTTONS) && (pressed & BUTTON_L1));
|
||||||
|
|
||||||
|
if(shouldLoadPayload) loadPayload(pressed);
|
||||||
|
|
||||||
|
if(splashMode == 2) loadSplash();
|
||||||
|
|
||||||
|
//If booting from CTRNAND, always use SysNAND
|
||||||
|
if(!isSdMode) nandType = FIRMWARE_SYSNAND;
|
||||||
|
|
||||||
|
//If R is pressed, boot the non-updated NAND with the FIRM of the opposite one
|
||||||
|
else if(pressed & BUTTON_R1)
|
||||||
|
{
|
||||||
|
if(CONFIG(USESYSFIRM))
|
||||||
|
{
|
||||||
|
nandType = FIRMWARE_EMUNAND;
|
||||||
|
firmSource = FIRMWARE_SYSNAND;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nandType = FIRMWARE_SYSNAND;
|
||||||
|
firmSource = FIRMWARE_EMUNAND;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Else, boot the NAND the user set to autoboot or the opposite one, depending on L,
|
||||||
|
with their own FIRM */
|
||||||
|
else firmSource = nandType = (CONFIG(AUTOBOOTSYS) == ((pressed & BUTTON_L1) == BUTTON_L1)) ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND;
|
||||||
|
|
||||||
|
//If we're booting EmuNAND or using EmuNAND FIRM, determine which one from the directional pad buttons, or otherwise from the config
|
||||||
|
if(nandType == FIRMWARE_EMUNAND || firmSource == FIRMWARE_EMUNAND)
|
||||||
|
{
|
||||||
|
FirmwareSource tempNand;
|
||||||
|
switch(pressed & EMUNAND_BUTTONS)
|
||||||
|
{
|
||||||
|
case BUTTON_UP:
|
||||||
|
tempNand = FIRMWARE_EMUNAND;
|
||||||
|
break;
|
||||||
|
case BUTTON_RIGHT:
|
||||||
|
tempNand = FIRMWARE_EMUNAND2;
|
||||||
|
break;
|
||||||
|
case BUTTON_DOWN:
|
||||||
|
tempNand = FIRMWARE_EMUNAND3;
|
||||||
|
break;
|
||||||
|
case BUTTON_LEFT:
|
||||||
|
tempNand = FIRMWARE_EMUNAND4;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
tempNand = (FirmwareSource)(1 + MULTICONFIG(DEFAULTEMU));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(nandType == FIRMWARE_EMUNAND) nandType = tempNand;
|
||||||
|
else firmSource = tempNand;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//If we need to boot EmuNAND, make sure it exists
|
||||||
|
if(nandType != FIRMWARE_SYSNAND)
|
||||||
|
{
|
||||||
|
locateEmuNand(&emuHeader, &nandType);
|
||||||
|
if(nandType == FIRMWARE_SYSNAND) firmSource = FIRMWARE_SYSNAND;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Same if we're using EmuNAND as the FIRM source
|
||||||
|
else if(firmSource != FIRMWARE_SYSNAND)
|
||||||
|
locateEmuNand(&emuHeader, &firmSource);
|
||||||
|
|
||||||
|
if(!ISFIRMLAUNCH)
|
||||||
|
{
|
||||||
|
configTemp |= (u32)nandType | ((u32)firmSource << 3);
|
||||||
|
writeConfig(needConfig, configTemp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isSdMode && !mountFs(false, false)) error("Failed to mount CTRNAND.");
|
||||||
|
|
||||||
|
bool loadFromStorage = CONFIG(LOADEXTFIRMSANDMODULES);
|
||||||
|
u32 firmVersion = loadFirm(&firmType, firmSource, loadFromStorage, isSafeMode);
|
||||||
|
|
||||||
|
u32 devMode = MULTICONFIG(DEVOPTIONS);
|
||||||
|
|
||||||
|
u32 res;
|
||||||
|
switch(firmType)
|
||||||
|
{
|
||||||
|
case NATIVE_FIRM:
|
||||||
|
res = patchNativeFirm(firmVersion, nandType, emuHeader, isA9lhInstalled, isSafeMode, devMode);
|
||||||
|
break;
|
||||||
|
case SAFE_FIRM:
|
||||||
|
case NATIVE_FIRM1X2X:
|
||||||
|
res = isA9lhInstalled ? patch1x2xNativeAndSafeFirm(devMode) : 0;
|
||||||
|
break;
|
||||||
|
case TWL_FIRM:
|
||||||
|
res = patchTwlFirm(firmVersion, devMode);
|
||||||
|
break;
|
||||||
|
case AGB_FIRM:
|
||||||
|
res = patchAgbFirm(devMode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(res != 0)
|
||||||
|
{
|
||||||
|
char patchesError[] = "Failed to apply FIRM patch(es).";
|
||||||
|
decItoa(res, patchesError + 16, 2);
|
||||||
|
error(patchesError);
|
||||||
|
}
|
||||||
|
|
||||||
|
launchFirm(firmType, loadFromStorage);
|
||||||
|
}
|
||||||
787
source/patches.c
787
source/patches.c
@@ -21,277 +21,382 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
* Signature patches by an unknown author
|
||||||
|
* firmlaunches patching code originally by delebile
|
||||||
|
* FIRM partition writes patches by delebile
|
||||||
* ARM11 modules patching code originally by Subv
|
* 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 svcOffset = (-(((*arm11ExceptionsPage)[2] & 0xFFFFFF) << 2) & (0xFFFFFF << 2)) - 8; //Branch offset + 8 for prefetch
|
u32 *arm11SvcTable;
|
||||||
u32 pointedInstructionVA = 0xFFFF0008 - svcOffset;
|
|
||||||
*baseK11VA = pointedInstructionVA & 0xFFFF0000; //This assumes that the pointed instruction has an offset < 0x10000, iirc that's always the case
|
if(*arm11ExceptionsPage == NULL) res = false;
|
||||||
u32 *arm11SvcTable = (u32 *)(pos + *(u32 *)(pos + pointedInstructionVA - *baseK11VA + 8) - *baseK11VA); //SVC handler address
|
else
|
||||||
*arm11SvcHandler = arm11SvcTable;
|
{
|
||||||
while(*arm11SvcTable) arm11SvcTable++; //Look for SVC0 (NULL)
|
*arm11ExceptionsPage -= 0xB;
|
||||||
|
u32 svcOffset = (-(((*arm11ExceptionsPage)[2] & 0xFFFFFF) << 2) & (0xFFFFFF << 2)) - 8; //Branch offset + 8 for prefetch
|
||||||
|
u32 pointedInstructionVA = 0xFFFF0008 - svcOffset;
|
||||||
|
*baseK11VA = pointedInstructionVA & 0xFFFF0000; //This assumes that the pointed instruction has an offset < 0x10000, iirc that's always the case
|
||||||
|
arm11SvcTable = *arm11SvcHandler = (u32 *)(pos + *(u32 *)(pos + pointedInstructionVA - *baseK11VA + 8) - *baseK11VA); //SVC handler address
|
||||||
|
while(*arm11SvcTable) arm11SvcTable++; //Look for SVC0 (NULL)
|
||||||
|
}
|
||||||
|
|
||||||
const u8 pattern2[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
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));
|
||||||
|
|
||||||
*off = off2[0] = 0x2000;
|
if(off == NULL || temp == NULL) ret = 1;
|
||||||
off2[1] = 0x4770;
|
else
|
||||||
|
{
|
||||||
|
u16 *off2 = (u16 *)(temp - 1);
|
||||||
|
|
||||||
|
*off = off2[0] = 0x2000;
|
||||||
|
off2[1] = 0x4770;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr)
|
u32 patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr)
|
||||||
{
|
{
|
||||||
//Look for firmlaunch code
|
//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;
|
|
||||||
|
|
||||||
//Firmlaunch function offset - offset in BLX opcode (A4-16 - ARM DDI 0100E) + 1
|
|
||||||
u32 fOpenOffset = (u32)(off + 9 - (-((*(u32 *)off & 0x00FFFFFF) << 2) & (0xFFFFFF << 2)) - pos + process9MemAddr);
|
|
||||||
|
|
||||||
//Copy firmlaunch code
|
|
||||||
memcpy(off, reboot_bin, reboot_bin_size);
|
|
||||||
|
|
||||||
//Put the fOpen offset in the right location
|
|
||||||
u32 *pos_fopen = (u32 *)memsearch(off, "OPEN", reboot_bin_size, 4);
|
|
||||||
*pos_fopen = fOpenOffset;
|
|
||||||
|
|
||||||
if(CONFIG(USECUSTOMPATH))
|
|
||||||
{
|
|
||||||
const char pathPath[] = "/luma/path.txt";
|
|
||||||
|
|
||||||
u32 pathSize = getFileSize(pathPath);
|
|
||||||
|
|
||||||
if(pathSize > 5 && pathSize < 40)
|
|
||||||
{
|
|
||||||
u8 path[pathSize];
|
|
||||||
fileRead(path, pathPath, 0);
|
|
||||||
if(path[pathSize - 1] == 0xA) pathSize--;
|
|
||||||
if(path[pathSize - 1] == 0xD) pathSize--;
|
|
||||||
|
|
||||||
if(pathSize > 5 && pathSize < 38 && path[0] == '/' && memcmp(&path[pathSize - 4], ".bin", 4) == 0)
|
|
||||||
{
|
|
||||||
u16 finalPath[pathSize + 1];
|
|
||||||
for(u32 i = 0; i < pathSize; i++)
|
|
||||||
finalPath[i] = (u16)path[i];
|
|
||||||
finalPath[pathSize] = 0;
|
|
||||||
|
|
||||||
u8 *pos_path = memsearch(off, u"sd", reboot_bin_size, 4) + 0xA;
|
|
||||||
memcpy(pos_path, finalPath, (pathSize + 1) * 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void patchFirmWrites(u8 *pos, u32 size)
|
|
||||||
{
|
|
||||||
//Look for FIRM writing code
|
|
||||||
u8 *off1 = memsearch(pos, "exe:", size, 4);
|
|
||||||
const u8 pattern[] = {0x00, 0x28, 0x01, 0xDA};
|
|
||||||
|
|
||||||
u16 *off2 = (u16 *)memsearch(off1 - 0x100, pattern, 0x100, sizeof(pattern));
|
|
||||||
|
|
||||||
off2[0] = 0x2000;
|
|
||||||
off2[1] = 0x46C0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void patchOldFirmWrites(u8 *pos, u32 size)
|
|
||||||
{
|
|
||||||
//Look for FIRM writing code
|
|
||||||
const u8 pattern[] = {0x04, 0x1E, 0x1D, 0xDB};
|
|
||||||
|
|
||||||
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
|
||||||
|
|
||||||
off[0] = 0x2400;
|
|
||||||
off[1] = 0xE01D;
|
|
||||||
}
|
|
||||||
|
|
||||||
void reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space)
|
|
||||||
{
|
|
||||||
//Official implementation of svcBackdoor
|
|
||||||
const u8 svcBackdoor[40] = {0xFF, 0x10, 0xCD, 0xE3, //bic r1, sp, #0xff
|
|
||||||
0x0F, 0x1C, 0x81, 0xE3, //orr r1, r1, #0xf00
|
|
||||||
0x28, 0x10, 0x81, 0xE2, //add r1, r1, #0x28
|
|
||||||
0x00, 0x20, 0x91, 0xE5, //ldr r2, [r1]
|
|
||||||
0x00, 0x60, 0x22, 0xE9, //stmdb r2!, {sp, lr}
|
|
||||||
0x02, 0xD0, 0xA0, 0xE1, //mov sp, r2
|
|
||||||
0x30, 0xFF, 0x2F, 0xE1, //blx r0
|
|
||||||
0x03, 0x00, 0xBD, 0xE8, //pop {r0, r1}
|
|
||||||
0x00, 0xD0, 0xA0, 0xE1, //mov sp, r0
|
|
||||||
0x11, 0xFF, 0x2F, 0xE1}; //bx r1
|
|
||||||
|
|
||||||
if(!arm11SvcTable[0x7B])
|
|
||||||
{
|
|
||||||
memcpy(*freeK11Space, svcBackdoor, 40);
|
|
||||||
|
|
||||||
arm11SvcTable[0x7B] = baseK11VA + *freeK11Space - pos;
|
|
||||||
*freeK11Space += 40;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void implementSvcGetCFWInfo(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space)
|
|
||||||
{
|
|
||||||
memcpy(*freeK11Space, svcGetCFWInfo_bin, svcGetCFWInfo_bin_size);
|
|
||||||
|
|
||||||
CFWInfo *info = (CFWInfo *)memsearch(*freeK11Space, "LUMA", svcGetCFWInfo_bin_size, 4);
|
|
||||||
|
|
||||||
const char *rev = REVISION;
|
|
||||||
|
|
||||||
info->commitHash = COMMIT_HASH;
|
|
||||||
info->config = configData.config;
|
|
||||||
info->versionMajor = (u8)(rev[1] - '0');
|
|
||||||
info->versionMinor = (u8)(rev[3] - '0');
|
|
||||||
|
|
||||||
bool isRelease;
|
|
||||||
|
|
||||||
if(rev[4] == '.')
|
|
||||||
{
|
|
||||||
info->versionBuild = (u8)(rev[5] - '0');
|
|
||||||
isRelease = rev[6] == 0;
|
|
||||||
}
|
|
||||||
else isRelease = rev[4] == 0;
|
|
||||||
|
|
||||||
info->flags = isRelease ? 1 : 0;
|
|
||||||
|
|
||||||
arm11SvcTable[0x2E] = baseK11VA + *freeK11Space - pos; //Stubbed svc
|
|
||||||
*freeK11Space += svcGetCFWInfo_bin_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
void patchTitleInstallMinVersionCheck(u8 *pos, u32 size)
|
|
||||||
{
|
|
||||||
const u8 pattern[] = {0x0A, 0x81, 0x42, 0x02};
|
|
||||||
|
|
||||||
u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
if(off != NULL) off[4] = 0xE0;
|
if(off == NULL) ret = 1;
|
||||||
}
|
else
|
||||||
|
|
||||||
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)
|
off -= 0x13;
|
||||||
|
|
||||||
|
//Firmlaunch function offset - offset in BLX opcode (A4-16 - ARM DDI 0100E) + 1
|
||||||
|
u32 fOpenOffset = (u32)(off + 9 - (-((*(u32 *)off & 0x00FFFFFF) << 2) & (0xFFFFFF << 2)) - pos + process9MemAddr);
|
||||||
|
|
||||||
|
//Copy firmlaunch code
|
||||||
|
memcpy(off, reboot_bin, reboot_bin_size);
|
||||||
|
|
||||||
|
//Put the fOpen offset in the right location
|
||||||
|
u32 *pos_fopen = (u32 *)memsearch(off, "OPEN", reboot_bin_size, 4);
|
||||||
|
*pos_fopen = fOpenOffset;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
if(CONFIG(USECUSTOMPATH))
|
||||||
{
|
{
|
||||||
case 0:
|
const char *pathFile = "path.txt";
|
||||||
memcpy(pos + patches[i].offset[isN3DS ? 1 : 0], patches[i].patch.type0 + 1, patches[i].patch.type0[0]);
|
|
||||||
break;
|
u32 pathSize = getFileSize(pathFile);
|
||||||
case 2:
|
|
||||||
*(u16 *)(pos + patches[i].offset[isN3DS ? 1 : 0] + 2) = 0;
|
if(pathSize > 5 && pathSize < 58)
|
||||||
case 1:
|
{
|
||||||
*(u16 *)(pos + patches[i].offset[isN3DS ? 1 : 0]) = patches[i].patch.type1;
|
u8 path[pathSize];
|
||||||
break;
|
fileRead(path, pathFile, pathSize);
|
||||||
|
if(path[pathSize - 1] == 0xA) pathSize--;
|
||||||
|
if(path[pathSize - 1] == 0xD) pathSize--;
|
||||||
|
|
||||||
|
if(pathSize > 5 && pathSize < 56 && path[0] == '/' && memcmp(&path[pathSize - 4], ".bin", 4) == 0)
|
||||||
|
{
|
||||||
|
u16 finalPath[pathSize];
|
||||||
|
for(u32 i = 0; i < pathSize; i++)
|
||||||
|
finalPath[i] = (u16)path[i];
|
||||||
|
|
||||||
|
u8 *pos_path = memsearch(off, u"sd", reboot_bin_size, 4) + 0xA;
|
||||||
|
memcpy(pos_path, finalPath, pathSize * 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void patchArm9ExceptionHandlersInstall(u8 *pos, u32 size)
|
u32 patchFirmWrites(u8 *pos, u32 size)
|
||||||
{
|
{
|
||||||
const u8 pattern[] = {0x03, 0xA0, 0xE3, 0x18};
|
u32 ret;
|
||||||
|
|
||||||
u32 *off = (u32 *)(memsearch(pos, pattern, size, sizeof(pattern)) + 0x13);
|
//Look for FIRM writing code
|
||||||
|
u8 *off = memsearch(pos, "exe:", size, 4);
|
||||||
|
|
||||||
for(u32 r0 = 0x08000000; *off != 0xE3A01040; off++) //Until mov r1, #0x40
|
if(off == NULL) ret = 1;
|
||||||
|
else
|
||||||
{
|
{
|
||||||
//Discard everything that's not str rX, [r0, #imm](!)
|
const u8 pattern[] = {0x00, 0x28, 0x01, 0xDA};
|
||||||
if((*off & 0xFE5F0000) != 0xE4000000) continue;
|
|
||||||
|
|
||||||
u32 rD = (*off >> 12) & 0xF,
|
u16 *off2 = (u16 *)memsearch(off - 0x100, pattern, 0x100, sizeof(pattern));
|
||||||
offset = (*off & 0xFFF) * ((((*off >> 23) & 1) == 0) ? -1 : 1);
|
|
||||||
bool writeback = ((*off >> 21) & 1) != 0,
|
|
||||||
pre = ((*off >> 24) & 1) != 0;
|
|
||||||
|
|
||||||
u32 addr = r0 + ((pre || !writeback) ? offset : 0);
|
if(off2 == NULL) ret = 1;
|
||||||
if((addr & 7) != 0 && addr != 0x08000014 && addr != 0x08000004) *off = 0xE1A00000; //nop
|
else
|
||||||
else *off = 0xE5800000 | (rD << 12) | (addr & 0xFFF); //Preserve IRQ and SVC handlers
|
{
|
||||||
|
off2[0] = 0x2000;
|
||||||
|
off2[1] = 0x46C0;
|
||||||
|
|
||||||
if(!pre) addr += offset;
|
ret = 0;
|
||||||
if(writeback) r0 = addr;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 patchOldFirmWrites(u8 *pos, u32 size)
|
||||||
|
{
|
||||||
|
//Look for FIRM writing code
|
||||||
|
const u8 pattern[] = {0x04, 0x1E, 0x1D, 0xDB};
|
||||||
|
u32 ret;
|
||||||
|
|
||||||
|
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
|
if(off == NULL) ret = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
off[0] = 0x2400;
|
||||||
|
off[1] = 0xE01D;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space)
|
||||||
|
{
|
||||||
|
u32 ret = 0;
|
||||||
|
|
||||||
|
//Official implementation of svcBackdoor
|
||||||
|
const u8 svcBackdoor[] = {0xFF, 0x10, 0xCD, 0xE3, //bic r1, sp, #0xff
|
||||||
|
0x0F, 0x1C, 0x81, 0xE3, //orr r1, r1, #0xf00
|
||||||
|
0x28, 0x10, 0x81, 0xE2, //add r1, r1, #0x28
|
||||||
|
0x00, 0x20, 0x91, 0xE5, //ldr r2, [r1]
|
||||||
|
0x00, 0x60, 0x22, 0xE9, //stmdb r2!, {sp, lr}
|
||||||
|
0x02, 0xD0, 0xA0, 0xE1, //mov sp, r2
|
||||||
|
0x30, 0xFF, 0x2F, 0xE1, //blx r0
|
||||||
|
0x03, 0x00, 0xBD, 0xE8, //pop {r0, r1}
|
||||||
|
0x00, 0xD0, 0xA0, 0xE1, //mov sp, r0
|
||||||
|
0x11, 0xFF, 0x2F, 0xE1}; //bx r1
|
||||||
|
|
||||||
|
if(!arm11SvcTable[0x7B])
|
||||||
|
{
|
||||||
|
if(*(u32 *)(*freeK11Space + sizeof(svcBackdoor) - 4) != 0xFFFFFFFF) ret = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy(*freeK11Space, svcBackdoor, sizeof(svcBackdoor));
|
||||||
|
|
||||||
|
arm11SvcTable[0x7B] = baseK11VA + *freeK11Space - pos;
|
||||||
|
*freeK11Space += sizeof(svcBackdoor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 implementSvcGetCFWInfo(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space, bool isSafeMode)
|
||||||
|
{
|
||||||
|
u32 ret;
|
||||||
|
|
||||||
|
if(*(u32 *)(*freeK11Space + svcGetCFWInfo_bin_size - 4) != 0xFFFFFFFF) ret = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy(*freeK11Space, svcGetCFWInfo_bin, svcGetCFWInfo_bin_size);
|
||||||
|
|
||||||
|
struct CfwInfo
|
||||||
|
{
|
||||||
|
char magic[4];
|
||||||
|
|
||||||
|
u8 versionMajor;
|
||||||
|
u8 versionMinor;
|
||||||
|
u8 versionBuild;
|
||||||
|
u8 flags;
|
||||||
|
|
||||||
|
u32 commitHash;
|
||||||
|
|
||||||
|
u32 config;
|
||||||
|
} __attribute__((packed)) *info = (struct CfwInfo *)memsearch(*freeK11Space, "LUMA", svcGetCFWInfo_bin_size, 4);
|
||||||
|
|
||||||
|
const char *rev = REVISION;
|
||||||
|
|
||||||
|
info->commitHash = COMMIT_HASH;
|
||||||
|
info->config = configData.config;
|
||||||
|
info->versionMajor = (u8)(rev[1] - '0');
|
||||||
|
info->versionMinor = (u8)(rev[3] - '0');
|
||||||
|
|
||||||
|
bool isRelease;
|
||||||
|
|
||||||
|
if(rev[4] == '.')
|
||||||
|
{
|
||||||
|
info->versionBuild = (u8)(rev[5] - '0');
|
||||||
|
isRelease = rev[6] == 0;
|
||||||
|
}
|
||||||
|
else isRelease = rev[4] == 0;
|
||||||
|
|
||||||
|
if(isRelease) info->flags = 1;
|
||||||
|
|
||||||
|
if(ISN3DS) info->flags |= 1 << 4;
|
||||||
|
|
||||||
|
if(isSafeMode) info->flags |= 1 << 5;
|
||||||
|
|
||||||
|
arm11SvcTable[0x2E] = baseK11VA + *freeK11Space - pos; //Stubbed svc
|
||||||
|
*freeK11Space += svcGetCFWInfo_bin_size;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 patchTitleInstallMinVersionChecks(u8 *pos, u32 size, u32 firmVersion)
|
||||||
|
{
|
||||||
|
const u8 pattern[] = {0xFF, 0x00, 0x00, 0x02};
|
||||||
|
u32 ret;
|
||||||
|
|
||||||
|
u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
|
if(off == NULL) ret = firmVersion == 0xFFFFFFFF ? 0 : 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
off++;
|
||||||
|
|
||||||
|
memset32(off, 0, 8); //Zero out the first TitleID in the list
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 patchArm9ExceptionHandlersInstall(u8 *pos, u32 size)
|
||||||
|
{
|
||||||
|
const u8 pattern[] = {0x80, 0xE5, 0x40, 0x1C};
|
||||||
|
u32 ret;
|
||||||
|
|
||||||
|
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
|
if(temp == NULL) ret = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
u32 *off = (u32 *)(temp - 0xA);
|
||||||
|
|
||||||
|
for(u32 r0 = 0x08000000; *off != 0xE3A01040; off++) //Until mov r1, #0x40
|
||||||
|
{
|
||||||
|
//Discard everything that's not str rX, [r0, #imm](!)
|
||||||
|
if((*off & 0xFE5F0000) == 0xE4000000)
|
||||||
|
{
|
||||||
|
u32 rD = (*off >> 12) & 0xF,
|
||||||
|
offset = (*off & 0xFFF) * ((((*off >> 23) & 1) == 0) ? -1 : 1);
|
||||||
|
bool writeback = ((*off >> 21) & 1) != 0,
|
||||||
|
pre = ((*off >> 24) & 1) != 0;
|
||||||
|
|
||||||
|
u32 addr = r0 + ((pre || !writeback) ? offset : 0);
|
||||||
|
if((addr & 7) != 0 && addr != 0x08000014 && addr != 0x08000004) *off = 0xE1A00000; //nop
|
||||||
|
else *off = 0xE5800000 | (rD << 12) | (addr & 0xFFF); //Preserve IRQ and SVC handlers
|
||||||
|
|
||||||
|
if(!pre) addr += offset;
|
||||||
|
if(writeback) r0 = addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 getInfoForArm11ExceptionHandlers(u8 *pos, u32 size, u32 *codeSetOffset)
|
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));
|
||||||
|
|
||||||
*codeSetOffset = *loadCodeSet & 0xFFF;
|
if(loadCodeSet == NULL) ret = false;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
loadCodeSet -= 2;
|
||||||
|
*codeSetOffset = *loadCodeSet & 0xFFF;
|
||||||
|
}
|
||||||
|
|
||||||
return *(u32 *)(memsearch(pos, pattern2, size, sizeof(pattern2)) + 0xD);
|
u8 *temp = memsearch(pos, pattern2, size, sizeof(pattern2));
|
||||||
|
|
||||||
|
u32 stackAddress;
|
||||||
|
|
||||||
|
if(temp == NULL) ret = false;
|
||||||
|
else stackAddress = *(u32 *)(temp + 9);
|
||||||
|
|
||||||
|
if(!ret) error("Failed to get ARM11 exception handlers data.");
|
||||||
|
|
||||||
|
return stackAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
void patchSvcBreak9(u8 *pos, u32 size, u32 kernel9Address)
|
u32 patchSvcBreak9(u8 *pos, u32 size, u32 kernel9Address)
|
||||||
{
|
{
|
||||||
/* Stub svcBreak with "bkpt 65535" so we can debug the panic.
|
//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)
|
|
||||||
|
|
||||||
u32 *addr = (u32 *)(pos + arm9SvcTable[0x3C] - kernel9Address);
|
if(arm9SvcTable == NULL) ret = 1;
|
||||||
*addr = 0xE12FFF7F;
|
else
|
||||||
|
{
|
||||||
|
while(*arm9SvcTable != 0) arm9SvcTable++; //Look for SVC0 (NULL)
|
||||||
|
|
||||||
|
u32 *addr = (u32 *)(pos + arm9SvcTable[0x3C] - kernel9Address);
|
||||||
|
*addr = 0xE12FFF7F;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void patchSvcBreak11(u8 *pos, u32 *arm11SvcTable)
|
void patchSvcBreak11(u8 *pos, u32 *arm11SvcTable)
|
||||||
@@ -301,68 +406,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));
|
||||||
*off = 0xE12FFF7E;
|
|
||||||
|
if(temp == NULL) ret = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
u32 *off = (u32 *)(temp - 0x12);
|
||||||
|
*off = 0xE12FFF7E;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void patchKernel11Panic(u8 *pos, u32 size)
|
u32 patchKernel11Panic(u8 *pos, u32 size)
|
||||||
{
|
{
|
||||||
const u8 pattern[] = {0x02, 0x0B, 0x44, 0xE2};
|
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));
|
||||||
*off = 0xE12FFF7E;
|
|
||||||
|
if(off == NULL) ret = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*off = 0xE12FFF7E;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void patchP9AccessChecks(u8 *pos, u32 size)
|
u32 patchP9AccessChecks(u8 *pos, u32 size)
|
||||||
{
|
{
|
||||||
const u8 pattern[] = {0xE0, 0x00, 0x40, 0x39};
|
const u8 pattern[] = {0x00, 0x08, 0x49, 0x68};
|
||||||
|
u32 ret;
|
||||||
|
|
||||||
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern)) - 7;
|
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
off[0] = 0x2001; //mov r0, #1
|
if(temp == NULL) ret = 1;
|
||||||
off[1] = 0x4770; //bx lr
|
else
|
||||||
|
{
|
||||||
|
u16 *off = (u16 *)(temp - 3);
|
||||||
|
|
||||||
|
off[0] = 0x2001; //mov r0, #1
|
||||||
|
off[1] = 0x4770; //bx lr
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void patchArm11SvcAccessChecks(u32 *arm11SvcHandler)
|
u32 patchArm11SvcAccessChecks(u32 *arm11SvcHandler, u32 *endPos)
|
||||||
{
|
{
|
||||||
while(*arm11SvcHandler != 0xE11A0E1B) arm11SvcHandler++; //TST R10, R11,LSL LR
|
u32 ret;
|
||||||
*arm11SvcHandler = 0xE3B0A001; //MOVS R10, #1
|
|
||||||
|
while(*arm11SvcHandler != 0xE11A0E1B && arm11SvcHandler < endPos) arm11SvcHandler++; //TST R10, R11,LSL LR
|
||||||
|
|
||||||
|
if(arm11SvcHandler == endPos) ret = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*arm11SvcHandler = 0xE3B0A001; //MOVS R10, #1
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void patchK11ModuleChecks(u8 *pos, u32 size, u8 **freeK11Space)
|
u32 patchK11ModuleChecks(u8 *pos, u32 size, u8 **freeK11Space)
|
||||||
{
|
{
|
||||||
/* We have to detour a function in the ARM11 kernel because builtin modules
|
/* 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 */
|
||||||
|
|
||||||
//Check that we have enough free space
|
u32 ret;
|
||||||
if(*(u32 *)(*freeK11Space + k11modules_bin_size - 4) == 0xFFFFFFFF)
|
|
||||||
{
|
|
||||||
//Inject our code into the free space
|
|
||||||
memcpy(*freeK11Space, k11modules_bin, k11modules_bin_size);
|
|
||||||
|
|
||||||
|
//Check that we have enough free space
|
||||||
|
if(*(u32 *)(*freeK11Space + k11modules_bin_size - 4) != 0xFFFFFFFF) ret = 0;
|
||||||
|
else
|
||||||
|
{
|
||||||
//Look for the code that decompresses the .code section of the builtin modules
|
//Look for the code that decompresses the .code section of the builtin modules
|
||||||
const u8 pattern[] = {0xE5, 0x48, 0x00, 0x9D};
|
const u8 pattern[] = {0xE5, 0x48, 0x00, 0x9D};
|
||||||
|
|
||||||
u32 *off = (u32 *)(memsearch(pos, pattern, size, sizeof(pattern)) - 0xB);
|
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
//Inject a jump (BL) instruction to our code at the offset we found
|
if(temp == NULL) ret = 1;
|
||||||
*off = 0xEB000000 | (((((u32)*freeK11Space) - ((u32)off + 8)) >> 2) & 0xFFFFFF);
|
else
|
||||||
|
{
|
||||||
|
//Inject our code into the free space
|
||||||
|
memcpy(*freeK11Space, k11modules_bin, k11modules_bin_size);
|
||||||
|
|
||||||
*freeK11Space += k11modules_bin_size;
|
u32 *off = (u32 *)(temp - 0xB);
|
||||||
|
|
||||||
|
//Inject a jump (BL) instruction to our code at the offset we found
|
||||||
|
*off = 0xEB000000 | (((((u32)*freeK11Space) - ((u32)off + 8)) >> 2) & 0xFFFFFF);
|
||||||
|
|
||||||
|
*freeK11Space += k11modules_bin_size;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void patchUnitInfoValueSet(u8 *pos, u32 size)
|
u32 patchUnitInfoValueSet(u8 *pos, u32 size)
|
||||||
{
|
{
|
||||||
//Look for UNITINFO value being set during kernel sync
|
//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;
|
||||||
off[3] = 0xE3;
|
else
|
||||||
|
{
|
||||||
|
off[0] = ISDEVUNIT ? 0 : 1;
|
||||||
|
off[3] = 0xE3;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 patchLgySignatureChecks(u8 *pos, u32 size)
|
||||||
|
{
|
||||||
|
const u8 pattern[] = {0x47, 0xC1, 0x17, 0x49};
|
||||||
|
u32 ret;
|
||||||
|
|
||||||
|
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
|
if(temp == NULL) ret = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
u16 *off = (u16 *)(temp + 1);
|
||||||
|
|
||||||
|
off[0] = 0x2000;
|
||||||
|
off[1] = 0xB04E;
|
||||||
|
off[2] = 0xBD70;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 patchTwlInvalidSignatureChecks(u8 *pos, u32 size)
|
||||||
|
{
|
||||||
|
const u8 pattern[] = {0x20, 0xF6, 0xE7, 0x7F};
|
||||||
|
u32 ret;
|
||||||
|
|
||||||
|
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
|
if(temp == NULL) ret = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
u16 *off = (u16 *)(temp - 1);
|
||||||
|
|
||||||
|
*off = 0x2001; //mov r0, #1
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 patchTwlNintendoLogoChecks(u8 *pos, u32 size)
|
||||||
|
{
|
||||||
|
const u8 pattern[] = {0xC0, 0x30, 0x06, 0xF0};
|
||||||
|
u32 ret;
|
||||||
|
|
||||||
|
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
|
if(off == NULL) ret = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
off[1] = 0x2000;
|
||||||
|
off[2] = 0;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 patchTwlWhitelistChecks(u8 *pos, u32 size)
|
||||||
|
{
|
||||||
|
const u8 pattern[] = {0x22, 0x00, 0x20, 0x30};
|
||||||
|
u32 ret;
|
||||||
|
|
||||||
|
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
|
if(off == NULL) ret = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
off[2] = 0x2000;
|
||||||
|
off[3] = 0;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 patchTwlFlashcartChecks(u8 *pos, u32 size, u32 firmVersion)
|
||||||
|
{
|
||||||
|
const u8 pattern[] = {0x25, 0x20, 0x00, 0x0E};
|
||||||
|
u32 ret;
|
||||||
|
|
||||||
|
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
|
if(temp == NULL)
|
||||||
|
{
|
||||||
|
if(firmVersion == 0xFFFFFFFF) ret = patchOldTwlFlashcartChecks(pos, size);
|
||||||
|
else ret = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
u16 *off = (u16 *)(temp + 3);
|
||||||
|
|
||||||
|
off[0] = off[6] = off[0xC] = 0x2001; //mov r0, #1
|
||||||
|
off[1] = off[7] = off[0xD] = 0; //nop
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 patchOldTwlFlashcartChecks(u8 *pos, u32 size)
|
||||||
|
{
|
||||||
|
const u8 pattern[] = {0x06, 0xF0, 0xA0, 0xFD};
|
||||||
|
u32 ret;
|
||||||
|
|
||||||
|
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
|
if(off == NULL) ret = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
off[0] = off[6] = 0x2001; //mov r0, #1
|
||||||
|
off[1] = off[7] = 0; //nop
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 patchTwlShaHashChecks(u8 *pos, u32 size)
|
||||||
|
{
|
||||||
|
const u8 pattern[] = {0x10, 0xB5, 0x14, 0x22};
|
||||||
|
u32 ret;
|
||||||
|
|
||||||
|
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
|
if(off == NULL) ret = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
off[0] = 0x2001; //mov r0, #1
|
||||||
|
off[1] = 0x4770;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 patchAgbBootSplash(u8 *pos, u32 size)
|
||||||
|
{
|
||||||
|
const u8 pattern[] = {0x00, 0x00, 0x01, 0xEF};
|
||||||
|
u32 ret;
|
||||||
|
|
||||||
|
u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
|
if(off == NULL) ret = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
off[2] = 0x26;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -21,55 +21,43 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
* Signature patches by an unknown author
|
||||||
|
* firmlaunches patching code originally by delebile
|
||||||
|
* FIRM partition writes patches by delebile
|
||||||
* ARM11 modules patching code originally by Subv
|
* 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 patchTitleInstallMinVersionChecks(u8 *pos, u32 size, u32 firmVersion);
|
||||||
void patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr);
|
u32 patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr);
|
||||||
void patchFirmWrites(u8 *pos, u32 size);
|
u32 patchFirmWrites(u8 *pos, u32 size);
|
||||||
void patchOldFirmWrites(u8 *pos, u32 size);
|
u32 patchOldFirmWrites(u8 *pos, u32 size);
|
||||||
void reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space);
|
u32 reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space);
|
||||||
void implementSvcGetCFWInfo(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space);
|
u32 implementSvcGetCFWInfo(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space, bool isSafeMode);
|
||||||
void applyLegacyFirmPatches(u8 *pos, FirmwareType firmType);
|
u32 patchArm9ExceptionHandlersInstall(u8 *pos, u32 size);
|
||||||
void 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);
|
||||||
19
source/pin.c
19
source/pin.c
@@ -50,7 +50,7 @@ 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);
|
||||||
@@ -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,7 +108,7 @@ 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)
|
||||||
@@ -132,16 +132,19 @@ bool verifyPin(u32 pinMode)
|
|||||||
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);
|
|
||||||
message[messageSize] = 0;
|
if(fileRead(message, messageFile, messageSize) == messageSize)
|
||||||
drawString(message, false, 10, 10, COLOR_WHITE);
|
{
|
||||||
|
message[messageSize] = 0;
|
||||||
|
drawString(message, false, 10, 10, COLOR_WHITE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while(!unlock)
|
while(!unlock)
|
||||||
|
|||||||
11
source/pin.h
11
source/pin.h
@@ -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);
|
||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,7 +181,7 @@ void initScreens(void)
|
|||||||
__asm(".word 0xF10C01C0");
|
__asm(".word 0xF10C01C0");
|
||||||
|
|
||||||
u32 brightnessLevel = brightness[MULTICONFIG(BRIGHTNESS)];
|
u32 brightnessLevel = brightness[MULTICONFIG(BRIGHTNESS)];
|
||||||
|
|
||||||
*(vu32 *)0x10141200 = 0x1007F;
|
*(vu32 *)0x10141200 = 0x1007F;
|
||||||
*(vu32 *)0x10202014 = 0x00000001;
|
*(vu32 *)0x10202014 = 0x00000001;
|
||||||
*(vu32 *)0x1020200C &= 0xFFFEFFFE;
|
*(vu32 *)0x1020200C &= 0xFFFEFFFE;
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -28,17 +28,17 @@ _start:
|
|||||||
|
|
||||||
.global launchedFirmTidLow
|
.global launchedFirmTidLow
|
||||||
launchedFirmTidLow:
|
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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
@@ -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,
|
||||||
@@ -55,4 +98,6 @@ typedef enum FirmwareType
|
|||||||
AGB_FIRM,
|
AGB_FIRM,
|
||||||
SAFE_FIRM,
|
SAFE_FIRM,
|
||||||
NATIVE_FIRM1X2X
|
NATIVE_FIRM1X2X
|
||||||
} FirmwareType;
|
} FirmwareType;
|
||||||
|
|
||||||
|
extern u16 launchedFirmTidLow[8]; //Defined in start.s
|
||||||
@@ -54,24 +54,13 @@ 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();
|
||||||
|
|
||||||
i2cWriteRegister(I2C_DEV_MCU, 0x20, 1 << 0);
|
i2cWriteRegister(I2C_DEV_MCU, 0x20, 1 << 0);
|
||||||
while(true);
|
while(true);
|
||||||
}
|
}
|
||||||
@@ -112,14 +101,16 @@ 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);
|
||||||
|
u32 posY = drawString(message, true, 10, 30, COLOR_WHITE);
|
||||||
|
drawString("Press any button to shutdown", true, 10, posY + 2 * SPACING_Y, COLOR_WHITE);
|
||||||
|
|
||||||
drawString("An error has occurred:", true, 10, 10, COLOR_RED);
|
waitInput();
|
||||||
u32 posY = drawString(message, true, 10, 30, COLOR_WHITE);
|
}
|
||||||
drawString("Press any button to shutdown", true, 10, posY + 2 * SPACING_Y, COLOR_WHITE);
|
|
||||||
|
|
||||||
waitInput();
|
|
||||||
mcuPowerOff();
|
mcuPowerOff();
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
Reference in New Issue
Block a user