Compare commits

...

48 Commits

Author SHA1 Message Date
Aurora
fc151f7595 Consistency 2016-09-04 14:05:29 +02:00
Aurora
2b4e97bec5 Get rid of the PIN on/off toggle and add an Off status to the multi option, change the config layout to allow for more multi options, add macros for all options 2016-09-04 13:45:03 +02:00
Aurora
97ae8d2d44 Forgot this 2016-09-04 00:55:20 +02:00
Aurora
201fe05a06 Implement selectable PIN size, added support for the directional pad in PINs 2016-09-04 00:38:17 +02:00
Aurora
5cd1a207c9 Forgot an instance where the PIN path is used 2016-09-03 23:07:51 +02:00
Aurora
7612e38d41 Increase the config minor version 2016-09-03 22:00:49 +02:00
Aurora
bd992fca15 Remove the TWL dev launcher patch as it became unnecessary 2016-09-03 19:11:44 +02:00
Aurora
a2bca96d47 Cleanup, use sizeof() for pattern memsearches 2016-09-03 18:15:00 +02:00
Aurora
fc3a69d9ca Small cleanup 2016-09-02 22:56:57 +02:00
Aurora
eba56d0f64 Save 4 bytes as we are space constrained on N3DS 2016-09-02 15:40:28 +02:00
Aurora
c93a97f8d7 mcuReboot now checks for isFirmlaunch 2016-09-02 15:19:14 +02:00
Aurora
9bc62ec12d Move config and PIN paths to the respective headers 2016-09-02 14:14:19 +02:00
Aurora
263a5eda56 Update to latest FatFs patch 2016-09-02 13:40:20 +02:00
Aurora
7fb17ce52f Remove exceptions folder from Luma-dev when cleaning 2016-09-01 18:01:21 +02:00
Aurora
631fad24c0 Make firmware.bin check more readable 2016-09-01 17:56:33 +02:00
Aurora
c0f042bc2a Can not support safe_firm firmlaunches without 3ds_injector at the moment 2016-08-31 19:50:37 +02:00
TuxSH
8982ff259a Add support for 1.x NATIVE_FIRM (on sysNAND) and the O3DS v0 SAFE_FIRM 2016-08-31 19:30:45 +02:00
Aurora
0057ab9c5a Fix config not being written in a rare circumstance 2016-08-31 16:11:44 +02:00
Aurora
a84f393bd5 Move the itoa function to strings.c 2016-08-30 21:32:15 +02:00
Aurora
5406d648bc Fix derp 2016-08-30 20:01:45 +02:00
Aurora
a2003fba95 Add -O3 optimization for the string functions 2016-08-30 19:48:21 +02:00
Aurora
a76c15d018 Introduce a strcat replacement 2016-08-30 17:48:41 +02:00
Aurora
f221915a95 Get rid of createDirectory and make fileWrite handle directory tree creation 2016-08-30 17:04:29 +02:00
Aurora
7f93733107 Rewrite the module copying function 2016-08-30 02:18:32 +02:00
TuxSH
06fe06f9f1 Update CFWInfo.h 2016-08-29 18:23:06 +02:00
Aurora
abf7c8e565 Replace pinKeyToLetter with a C implementation, minor cleanup 2016-08-29 18:11:59 +02:00
TuxSH
46227e6763 Fix build issues on some environments. 2016-08-29 17:51:03 +02:00
Aurora
2ffe4a5451 Revert "Skip PIN verification after a MCU reboot on A9LH"
This reverts commit 18bd4bbcf6.
2016-08-29 16:04:17 +02:00
Aurora
18bd4bbcf6 Skip PIN verification after a MCU reboot on A9LH 2016-08-29 15:57:32 +02:00
Aurora
8209433696 Cleanup and refactoring 2016-08-29 15:42:54 +02:00
Aurora
ad9e00acaa Add delay if a PIN was just verified and SAFE_MODE is being booted 2016-08-29 13:56:23 +02:00
Aurora
c96f96258f Only patch 2.x NATIVE_FIRM on A9LH 2016-08-29 01:58:01 +02:00
Aurora
82699f3e00 Merge readPin and verifyPin 2016-08-28 23:41:41 +02:00
Aurora
e2d828a4a2 Fixed derp 2016-08-28 15:58:10 +02:00
Aurora
92328c6a7e Made it easier to change your PIN, added PIN file deletion when needed 2016-08-28 15:51:54 +02:00
Aurora
3f8ad17e86 Minor PIN cleanup and UI changes 2016-08-28 14:59:33 +02:00
Aurora
52999db43a Readability stuff 2016-08-28 14:41:18 +02:00
Aurora
760aa99709 Revert "Add back reboot patch on 2.x"
This reverts commit 51c514de84.
2016-08-28 12:33:00 +02:00
TuxSH
51c514de84 Add back reboot patch on 2.x 2016-08-28 11:48:35 +02:00
Aurora
317899b4bf Fix another derp 2016-08-28 02:56:46 +02:00
Aurora
4c93d2b1f9 Better to put this here 2016-08-28 02:49:53 +02:00
Aurora
71c5404bfe Fix derp 2016-08-28 02:41:00 +02:00
Aurora
9c5248b87a Cleanup previous commit 2016-08-28 02:38:52 +02:00
TuxSH
83a0293af4 Add support (firmprot and firmlaunch patches) for 2.x on sysNAND 2016-08-28 00:32:47 +02:00
TuxSH
a5c6b908b6 Fix reboot patch for 5.x/6.x 2016-08-27 18:10:51 +02:00
Aurora
94f0d873df Cleanup 2016-08-27 17:34:37 +02:00
Aurora
d6f66d24fa Consistency 2016-08-27 16:11:37 +02:00
Aurora
31458e9938 Implement a new config file format which allows invalidating the config with new releases, fix config-related bugs, cleanup 2016-08-27 16:00:15 +02:00
38 changed files with 617 additions and 629 deletions

View File

@@ -33,8 +33,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)/rebootpatch.h $(dir_build)/emunandpatch.h $(dir_build)/svcGetCFWInfopatch.h $(dir_build)/twl_k11modulespatch.h \ bundled = $(dir_build)/rebootpatch.h $(dir_build)/emunandpatch.h $(dir_build)/svcGetCFWInfopatch.h $(dir_build)/injector.h \
$(dir_build)/injector.h $(dir_build)/loader.h $(dir_build)/loader.h
.PHONY: all .PHONY: all
all: launcher a9lh ninjhax all: launcher a9lh ninjhax
@@ -57,7 +57,7 @@ clean:
@$(MAKE) $(FLAGS) -C $(dir_ninjhax) clean @$(MAKE) $(FLAGS) -C $(dir_ninjhax) clean
@$(MAKE) -C $(dir_loader) clean @$(MAKE) -C $(dir_loader) clean
@$(MAKE) -C $(dir_injector) clean @$(MAKE) -C $(dir_injector) clean
@rm -rf $(dir_out) $(dir_build) @rm -rf $(dir_out) $(dir_build) exceptions
$(dir_out): $(dir_out):
@mkdir -p "$(dir_out)" @mkdir -p "$(dir_out)"
@@ -98,11 +98,6 @@ $(dir_build)/svcGetCFWInfopatch.h: $(dir_patches)/svcGetCFWInfo.s
@armips $< @armips $<
@bin2c -o $@ -n svcGetCFWInfo $(@D)/svcGetCFWInfo.bin @bin2c -o $@ -n svcGetCFWInfo $(@D)/svcGetCFWInfo.bin
$(dir_build)/twl_k11modulespatch.h: $(dir_patches)/twl_k11modules.s
@mkdir -p "$(@D)"
@armips $<
@bin2c -o $@ -n twl_k11modules $(@D)/twl_k11modules.bin
$(dir_build)/injector.h: $(dir_injector)/Makefile $(dir_build)/injector.h: $(dir_injector)/Makefile
@mkdir -p "$(@D)" @mkdir -p "$(@D)"
@$(MAKE) -C $(dir_injector) @$(MAKE) -C $(dir_injector)
@@ -112,7 +107,7 @@ $(dir_build)/loader.h: $(dir_loader)/Makefile
@$(MAKE) -C $(dir_loader) @$(MAKE) -C $(dir_loader)
@bin2c -o $@ -n loader $(@D)/loader.bin @bin2c -o $@ -n loader $(@D)/loader.bin
$(dir_build)/memory.o: CFLAGS += -O3 $(dir_build)/memory.o $(dir_build)/strings.o: CFLAGS += -O3
$(dir_build)/config.o: CFLAGS += -DCONFIG_TITLE="\"$(name) $(revision) configuration\"" $(dir_build)/config.o: CFLAGS += -DCONFIG_TITLE="\"$(name) $(revision) configuration\""
$(dir_build)/patches.o: CFLAGS += -DREVISION=\"$(revision)\" -DCOMMIT_HASH="0x$(commit)" $(dir_build)/patches.o: CFLAGS += -DREVISION=\"$(revision)\" -DCOMMIT_HASH="0x$(commit)"

View File

@@ -22,13 +22,13 @@ LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
INCLUDE := $(foreach dir,$(LIBDIRS),-I$(dir)/include) INCLUDE := $(foreach dir,$(LIBDIRS),-I$(dir)/include)
ARCH := -mcpu=mpcore -mfloat-abi=hard -mtp=soft ASFLAGS := -mcpu=mpcore -mfloat-abi=hard -mtp=soft
CFLAGS := -Wall -Wextra -MMD -MP -marm $(ARCH) -fno-builtin -std=c11 -O2 -flto -ffast-math -mword-relocations \ CFLAGS := -Wall -Wextra -MMD -MP -marm $(ASFLAGS) -fno-builtin -std=c11 -O2 -flto -ffast-math -mword-relocations \
-ffunction-sections -fdata-sections $(INCLUDE) -DARM11 -D_3DS -ffunction-sections -fdata-sections $(INCLUDE) -DARM11 -D_3DS
LDFLAGS := -Xlinker --defsym="__start__=0x14000000" -specs=3dsx.specs $(ARCH) LDFLAGS := -Xlinker --defsym="__start__=0x14000000" -specs=3dsx.specs $(ASFLAGS)
objects = $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \ objects = $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
$(call rwildcard, $(dir_source), *.c)) $(call rwildcard, $(dir_source), *.s *.c))
.PHONY: all .PHONY: all
all: ../$(dir_build)/$(name).cxi all: ../$(dir_build)/$(name).cxi
@@ -48,4 +48,8 @@ $(dir_build)/memory.o : CFLAGS += -O3
$(dir_build)/%.o: $(dir_source)/%.c $(dir_build)/%.o: $(dir_source)/%.c
@mkdir -p "$(@D)" @mkdir -p "$(@D)"
$(COMPILE.c) $(OUTPUT_OPTION) $< $(COMPILE.c) $(OUTPUT_OPTION) $<
$(dir_build)/%.o: $(dir_source)/%.s
@mkdir -p "$(@D)"
$(COMPILE.s) $(OUTPUT_OPTION) $<
include $(call rwildcard, $(dir_build), *.d) include $(call rwildcard, $(dir_build), *.d)

19
injector/source/CFWInfo.h Normal file
View File

@@ -0,0 +1,19 @@
#pragma once
#include <3ds/types.h>
typedef struct __attribute__((packed))
{
char magic[4];
u8 versionMajor;
u8 versionMinor;
u8 versionBuild;
u8 flags; /* bit 0: dev branch; bit 1: is release */
u32 commitHash;
u32 config;
} CFWInfo;
void svcGetCFWInfo(CFWInfo *info);

View File

@@ -0,0 +1,9 @@
.text
.arm
.align 4
.global svcGetCFWInfo
.type svcGetCFWInfo, %function
svcGetCFWInfo:
svc 0x2e
bx lr

View File

@@ -2,8 +2,9 @@
#include "memory.h" #include "memory.h"
#include "patcher.h" #include "patcher.h"
#include "ifile.h" #include "ifile.h"
#include "CFWInfo.h"
static CFWInfo info = {0}; static CFWInfo info;
static int memcmp(const void *buf1, const void *buf2, u32 size) static int memcmp(const void *buf1, const void *buf2, u32 size)
{ {
@@ -85,11 +86,6 @@ 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);
} }
int __attribute__((naked)) svcGetCFWInfo(CFWInfo __attribute__((unused)) *out)
{
__asm__ volatile("svc 0x2E; bx lr");
}
static void loadCFWInfo(void) static void loadCFWInfo(void)
{ {
static bool infoLoaded = false; static bool infoLoaded = false;
@@ -97,11 +93,10 @@ static void loadCFWInfo(void)
if(!infoLoaded) if(!infoLoaded)
{ {
svcGetCFWInfo(&info); svcGetCFWInfo(&info);
IFile file; IFile file;
if(BOOTCONFIG(5, 1) && R_SUCCEEDED(fileOpen(&file, ARCHIVE_SDMC, "/", FS_OPEN_READ))) //Init SD card if SAFE_MODE is being booted if(BOOTCFG_SAFEMODE != 0 && 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;
} }
@@ -147,12 +142,12 @@ static void loadTitleCodeSection(u64 progId, u8 *code, u32 size)
if(R_SUCCEEDED(ret)) if(R_SUCCEEDED(ret))
{ {
u64 fileSize, total; u64 fileSize;
ret = IFile_GetSize(&file, &fileSize); ret = IFile_GetSize(&file, &fileSize);
if(R_SUCCEEDED(ret) && fileSize <= size) if(R_SUCCEEDED(ret) && fileSize <= size)
{ {
u64 total;
ret = IFile_Read(&file, &total, code, fileSize); ret = IFile_Read(&file, &total, code, fileSize);
IFile_Close(&file); IFile_Close(&file);
} }
@@ -330,7 +325,7 @@ void patchCode(u64 progId, u8 *code, u32 size)
case 0x000400300000B102LL: // TWN Menu case 0x000400300000B102LL: // TWN Menu
{ {
static const u8 regionFreePattern[] = { static const u8 regionFreePattern[] = {
0x00, 0x00, 0x55, 0xE3, 0x01, 0x10, 0xA0, 0xE3 0x00, 0x00, 0x55, 0xE3, 0x01, 0x10, 0xA0
}; };
static const u8 regionFreePatch[] = { static const u8 regionFreePatch[] = {
0x01, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1 0x01, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1
@@ -365,7 +360,7 @@ void patchCode(u64 progId, u8 *code, u32 size)
); );
//Apply only if the updated NAND hasn't been booted //Apply only if the updated NAND hasn't been booted
if((BOOTCONFIG(0, 3) != 0) == (BOOTCONFIG(2, 1) && CONFIG(1))) if((BOOTCFG_NAND != 0) == (BOOTCFG_FIRM != 0 && CONFIG_USESYSFIRM))
{ {
static const u8 skipEshopUpdateCheckPattern[] = { static const u8 skipEshopUpdateCheckPattern[] = {
0x30, 0xB5, 0xF1, 0xB0 0x30, 0xB5, 0xF1, 0xB0
@@ -389,7 +384,7 @@ void patchCode(u64 progId, u8 *code, u32 size)
case 0x0004013000003202LL: // FRIENDS case 0x0004013000003202LL: // FRIENDS
{ {
static const u8 fpdVerPattern[] = { static const u8 fpdVerPattern[] = {
0xE0, 0x1E, 0xFF, 0x2F, 0xE1, 0x01, 0x01, 0x01 0xE0, 0x1E, 0xFF, 0x2F, 0xE1, 0x01, 0x01
}; };
static const u8 mostRecentFpdVer = 0x06; static const u8 mostRecentFpdVer = 0x06;
@@ -409,7 +404,7 @@ void patchCode(u64 progId, u8 *code, u32 size)
case 0x0004001000027000LL: // KOR MSET case 0x0004001000027000LL: // KOR MSET
case 0x0004001000028000LL: // TWN MSET case 0x0004001000028000LL: // TWN MSET
{ {
if(CONFIG(4)) if(CONFIG_SHOWNAND)
{ {
static const u16 verPattern[] = u"Ver."; static const u16 verPattern[] = u"Ver.";
const u32 currentNand = BOOTCONFIG(0, 3); const u32 currentNand = BOOTCONFIG(0, 3);
@@ -445,12 +440,12 @@ void patchCode(u64 progId, u8 *code, u32 size)
sizeof(stopCartUpdatesPatch), 2 sizeof(stopCartUpdatesPatch), 2
); );
u32 cpuSetting = MULTICONFIG(1); u32 cpuSetting = CONFIG_NEWCPU;
if(cpuSetting) if(cpuSetting)
{ {
static const u8 cfgN3dsCpuPattern[] = { static const u8 cfgN3dsCpuPattern[] = {
0x00, 0x40, 0xA0, 0xE1, 0x07, 0x00 0x00, 0x40, 0xA0, 0xE1, 0x07
}; };
u32 *cfgN3dsCpuLoc = (u32 *)memsearch(code, cfgN3dsCpuPattern, size, sizeof(cfgN3dsCpuPattern)); u32 *cfgN3dsCpuLoc = (u32 *)memsearch(code, cfgN3dsCpuPattern, size, sizeof(cfgN3dsCpuPattern));
@@ -469,7 +464,7 @@ void patchCode(u64 progId, u8 *code, u32 size)
case 0x0004013000001702LL: // CFG case 0x0004013000001702LL: // CFG
{ {
static const u8 secureinfoSigCheckPattern[] = { static const u8 secureinfoSigCheckPattern[] = {
0x06, 0x46, 0x10, 0x48, 0xFC 0x06, 0x46, 0x10, 0x48
}; };
static const u8 secureinfoSigCheckPatch[] = { static const u8 secureinfoSigCheckPatch[] = {
0x00, 0x26 0x00, 0x26
@@ -544,7 +539,7 @@ void patchCode(u64 progId, u8 *code, u32 size)
} }
default: default:
if(CONFIG(3)) if(CONFIG_USELANGEMUANDCODE)
{ {
u32 tidHigh = (progId & 0xFFFFFFF000000000LL) >> 0x24; u32 tidHigh = (progId & 0xFFFFFFF000000000LL) >> 0x24;

View File

@@ -4,22 +4,16 @@
#define PATH_MAX 255 #define PATH_MAX 255
#define CONFIG(a) (((info.config >> (a + 16)) & 1) != 0) #define CONFIG(a) (((info.config >> (a + 20)) & 1) != 0)
#define MULTICONFIG(a) ((info.config >> (a * 2 + 6)) & 3) #define MULTICONFIG(a) ((info.config >> (a * 2 + 6)) & 3)
#define BOOTCONFIG(a, b) ((info.config >> a) & b) #define BOOTCONFIG(a, b) ((info.config >> a) & b)
typedef struct __attribute__((packed)) #define BOOTCFG_NAND BOOTCONFIG(0, 3)
{ #define BOOTCFG_FIRM BOOTCONFIG(2, 1)
char magic[4]; #define BOOTCFG_SAFEMODE BOOTCONFIG(5, 1)
#define CONFIG_NEWCPU MULTICONFIG(2)
u8 versionMajor; #define CONFIG_USESYSFIRM CONFIG(1)
u8 versionMinor; #define CONFIG_USELANGEMUANDCODE CONFIG(3)
u8 versionBuild; #define CONFIG_SHOWNAND CONFIG(4)
u8 flags; /* bit 0: dev branch; bit 1: is release */
u32 commitHash;
u32 config;
} CFWInfo;
void patchCode(u64 progId, u8 *code, u32 size); void patchCode(u64 progId, u8 *code, u32 size);

View File

@@ -23,7 +23,7 @@
#include "memory.h" #include "memory.h"
#include "cache.h" #include "cache.h"
extern u32 payloadSize; //defined in start.s extern u32 payloadSize; //Defined in start.s
void main(void) void main(void)
{ {

View File

@@ -43,4 +43,4 @@ nand_sd:
sdmmc: .ascii "SDMC" sdmmc: .ascii "SDMC"
nand_offset: .ascii "NAND" ; for rednand this should be 1 nand_offset: .ascii "NAND" ; for rednand this should be 1
ncsd_header_offset: .ascii "NCSD" ; depends on nand manufacturer + emunand type (GW/RED) ncsd_header_offset: .ascii "NCSD" ; depends on nand manufacturer + emunand type (GW/RED)
.close .close

View File

@@ -5,10 +5,14 @@ payload_maxsize equ 0x10000 ; Maximum size for the payload (maximum that CakeB
.create "build/reboot.bin", 0 .create "build/reboot.bin", 0
.arm .arm
; Interesting registers and locations to keep in mind, set before this code is ran: ; Interesting registers and locations to keep in mind, set just before this code is ran:
; - sp + 0x3A8 - 0x70: FIRM path in exefs. ; - r1: FIRM path in exefs.
; - r7 (which is sp + 0x3A8 - 0x198): Reserved space for file handle ; - r7: pointer to file object
; - *(sp + 0x3A8 - 0x198) + 0x28: fread function. ; - *r7: vtable
; - *(vtable + 0x28): fread function
; - *(r7 + 8): file handle
mov r8, r1
pxi_wait_recv: pxi_wait_recv:
ldr r2, =0x44846 ldr r2, =0x44846
@@ -47,7 +51,7 @@ payload_maxsize equ 0x10000 ; Maximum size for the payload (maximum that CakeB
cmp r4, #0 cmp r4, #0
movne r3, #0x12000 ; Skip the first 0x12000 bytes. movne r3, #0x12000 ; Skip the first 0x12000 bytes.
moveq r3, payload_maxsize moveq r3, payload_maxsize
ldr r6, [sp, #0x3A8-0x198] ldr r6, [r7]
ldr r6, [r6, #0x28] ldr r6, [r6, #0x28]
blx r6 blx r6
cmp r4, #0 cmp r4, #0
@@ -55,8 +59,7 @@ payload_maxsize equ 0x10000 ; Maximum size for the payload (maximum that CakeB
bne read_payload ; Go read the real payload. bne read_payload ; Go read the real payload.
; 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, sp, #0x3A8 - 0x70 add r0, r8, 0x1A
add r0, 0x1A
add r1, r0, #0x10 add r1, r0, #0x10
ldr r2, =payload_addr + 4 ldr r2, =payload_addr + 4
copy_TID_low: copy_TID_low:
@@ -75,7 +78,7 @@ payload_maxsize equ 0x10000 ; Maximum size for the payload (maximum that CakeB
goto_reboot: goto_reboot:
; Jump to reboot code ; Jump to reboot code
ldr r0, =(kernelcode_start - goto_reboot - 12) ldr r0, =(kernelcode_start - goto_reboot - 12)
add r0, pc add r0, pc ; pc is two instructions ahead of the instruction being executed (12 = 2*4 + 4)
swi 0x7B swi 0x7B
die: die:
@@ -122,4 +125,4 @@ dat_fname: .dcw "sdmc:/Luma3DS.dat"
bx r0 bx r0
.pool .pool
.close .close

View File

@@ -35,7 +35,6 @@
cmp r0, r2 cmp r0, r2
blo loop blo loop
mov r0, #0
bx lr bx lr
.pool .pool

View File

@@ -1,144 +0,0 @@
;
; 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.
;
.arm.little
.create "build/twl_k11modules.bin", 0
.align 4
.arm
patch:
; r4: Pointer to a pointer to the exheader of the current NCCH
; sp + 0xb0 - 0xa4: Pointer to the memory location where the NCCH text was loaded
add r3, sp, #(0xb0 - 0xa4)
add r1, sp, #(0xb0 - 0xac)
push {r0-r11, lr}
ldr r9, [r3] ; load the address of the code section
ldr r8, [r4] ; load the address of the exheader
ldr r7, [r8, #0x200] ; low titleID
ldr r6, =#0x000001ff
cmp r7, r6
bne end
ldr r7, =#0xabcdabcd ; offset of the dev launcher (will be replaced later)
add r7, r9
adr r5, patchesStart
add r6, r5, #(patchesEnd - patchesStart)
patchLoop:
ldrh r0, [r5, #4]
cmp r0, #0
moveq r4, r9
movne r4, r7
ldrh r2, [r5, #6]
add r1, r5, #8
ldr r0, [r5]
add r0, r4
blx memcmp
cmp r0, #0
bne skipPatch
ldrh r2, [r5, #6]
add r1, r5, #0x08
add r1, r2
ldr r0, [r5]
add r0, r4
blx memcpy
skipPatch:
ldrh r0, [r5, #6]
add r5, r5, #0x08
add r5, r0,lsl#1
cmp r5, r6
blo patchLoop
end:
pop {r0-r11, pc}
.align 2
.thumb
memcmp:
push {r4-r7, lr}
mov r4, #0
cmp_loop:
cmp r4, r2
bhs cmp_loop_end
ldrb r6, [r0, r4]
ldrb r7, [r1, r4]
add r4, #1
sub r6, r7
cmp r6, #0
beq cmp_loop
cmp_loop_end:
mov r0, r6
pop {r4-r7, pc}
memcpy:
push {r4-r5, lr}
mov r4, #0
copy_loop:
cmp r4, r2
bhs copy_loop_end
ldrb r5, [r1, r4]
strb r5, [r0, r4]
add r4, #1
b copy_loop
copy_loop_end:
pop {r4-r5, pc}
.align 4
; Available space for patches: 152 bytes on N3DS, 666 on O3DS
patchesStart:
; SCFG_EXT bit31 patches, based on https://github.com/ahezard/twl_firm_patcher (credits where they're due)
.word 0x07368 ; offset
.halfword 1 ; type (0: relative to the start of TwlBg's code; 1: relative to the start of the dev SRL launcher)
.halfword 4 ; size (must be a multiple of 4)
.byte 0x94, 0x09, 0xfc, 0xed ; expected data (decrypted = 0x08, 0x60, 0x87, 0x05)
.byte 0x24, 0x09, 0xbc, 0xe9 ; patched data (decrypted = 0xb8, 0x60, 0xc7, 0x01)
.word 0xa5888
.halfword 1
.halfword 8
.byte 0x83, 0x30, 0x2e, 0xa4, 0xb0, 0xe2, 0xc2, 0xd6 ; (decrypted = 0x02, 0x01, 0x1a, 0xe3, 0x08, 0x60, 0x87, 0x05)
.byte 0x83, 0x50, 0xf2, 0xa4, 0xb0, 0xe2, 0xc2, 0xd6 ; (decrypted = 0x02, 0x61, 0xc6, 0xe3, 0x08, 0x60, 0x87, 0xe5)
patchesEnd:
.pool
.close

View File

@@ -43,4 +43,4 @@
#define SINGLE_PAYLOAD_BUTTONS (BUTTON_LEFT | BUTTON_RIGHT | BUTTON_UP | BUTTON_DOWN | BUTTON_START | BUTTON_X | BUTTON_Y) #define SINGLE_PAYLOAD_BUTTONS (BUTTON_LEFT | BUTTON_RIGHT | BUTTON_UP | BUTTON_DOWN | BUTTON_START | BUTTON_X | BUTTON_Y)
#define L_PAYLOAD_BUTTONS (BUTTON_R1 | BUTTON_A | BUTTON_SELECT) #define L_PAYLOAD_BUTTONS (BUTTON_R1 | BUTTON_A | BUTTON_SELECT)
#define MENU_BUTTONS (BUTTON_LEFT | BUTTON_RIGHT | BUTTON_UP | BUTTON_DOWN | BUTTON_A | BUTTON_START) #define MENU_BUTTONS (BUTTON_LEFT | BUTTON_RIGHT | BUTTON_UP | BUTTON_DOWN | BUTTON_A | BUTTON_START)
#define PIN_BUTTONS (BUTTON_A | BUTTON_B | BUTTON_X | BUTTON_Y | BUTTON_START) #define PIN_BUTTONS (BUTTON_A | BUTTON_B | BUTTON_X | BUTTON_Y | BUTTON_LEFT | BUTTON_RIGHT | BUTTON_UP | BUTTON_DOWN | BUTTON_START)

View File

@@ -21,12 +21,50 @@
*/ */
#include "config.h" #include "config.h"
#include "memory.h"
#include "fs.h"
#include "utils.h" #include "utils.h"
#include "screen.h" #include "screen.h"
#include "draw.h" #include "draw.h"
#include "buttons.h" #include "buttons.h"
#include "pin.h"
void configureCFW(void) bool readConfig(void)
{
if(fileRead(&configData, CONFIG_PATH) != sizeof(cfgData) ||
memcmp(configData.magic, "CONF", 4) != 0 ||
configData.formatVersionMajor != CONFIG_VERSIONMAJOR ||
configData.formatVersionMinor != CONFIG_VERSIONMINOR)
{
configData.config = 0;
return false;
}
return true;
}
void writeConfig(ConfigurationStatus needConfig, u32 configTemp)
{
/* If the configuration is different from previously, overwrite it.
Just the no-forcing flag being set is not enough */
if(needConfig == CREATE_CONFIGURATION || (configTemp & 0xFFFFFFEF) != configData.config)
{
if(needConfig == CREATE_CONFIGURATION)
{
memcpy(configData.magic, "CONF", 4);
configData.formatVersionMajor = CONFIG_VERSIONMAJOR;
configData.formatVersionMinor = CONFIG_VERSIONMINOR;
}
//Merge the new options and new boot configuration
configData.config = (configData.config & 0xFFFFFFC0) | (configTemp & 0x3F);
if(!fileWrite(&configData, CONFIG_PATH, sizeof(cfgData)))
error("Error writing the configuration file");
}
}
void configMenu(bool oldPinStatus)
{ {
initScreens(); initScreens();
@@ -34,6 +72,7 @@ void configureCFW(void)
drawString("Press A to select, START to save", 10, 30, COLOR_WHITE); drawString("Press A to select, START to save", 10, 30, COLOR_WHITE);
const char *multiOptionsText[] = { "Screen brightness: 4( ) 3( ) 2( ) 1( )", const char *multiOptionsText[] = { "Screen brightness: 4( ) 3( ) 2( ) 1( )",
"PIN lock: Off( ) 4( ) 6( ) 8( ) digits",
"New 3DS CPU: Off( ) Clock( ) L2( ) Clock+L2( )" }; "New 3DS CPU: Off( ) Clock( ) L2( ) Clock+L2( )" };
const char *singleOptionsText[] = { "( ) Autoboot SysNAND", const char *singleOptionsText[] = { "( ) Autoboot SysNAND",
@@ -42,9 +81,7 @@ void configureCFW(void)
"( ) Enable region/language emu. and ext. .code", "( ) Enable region/language emu. and ext. .code",
"( ) Show current NAND in System Settings", "( ) Show current NAND in System Settings",
"( ) Show GBA boot screen in patched AGB_FIRM", "( ) Show GBA boot screen in patched AGB_FIRM",
"( ) Display splash screen before payloads", "( ) Display splash screen before payloads" };
"( ) Use a PIN",
"( ) Enable experimental TwlBg patches" };
struct multiOption { struct multiOption {
int posXs[4]; int posXs[4];
@@ -52,6 +89,7 @@ void configureCFW(void)
u32 enabled; u32 enabled;
} multiOptions[] = { } multiOptions[] = {
{ .posXs = {21, 26, 31, 36} }, { .posXs = {21, 26, 31, 36} },
{ .posXs = {14, 19, 24, 29} },
{ .posXs = {17, 26, 32, 44} } { .posXs = {17, 26, 32, 44} }
}; };
@@ -184,15 +222,22 @@ void configureCFW(void)
} }
} }
u32 oldPinLength = CONFIG_PIN;
//Preserve the last-used boot options (last 12 bits) //Preserve the last-used boot options (last 12 bits)
config &= 0x3F; configData.config &= 0x3F;
//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++)
config |= multiOptions[i].enabled << (i * 2 + 6); configData.config |= multiOptions[i].enabled << (i * 2 + 6);
for(u32 i = 0; i < singleOptionsAmount; i++) for(u32 i = 0; i < singleOptionsAmount; i++)
config |= (singleOptions[i].enabled ? 1 : 0) << (i + 16); configData.config |= (singleOptions[i].enabled ? 1 : 0) << (i + 20);
if(CONFIG_PIN != 0) newPin(oldPinStatus && CONFIG_PIN == oldPinLength);
else if(oldPinStatus) fileDelete(PIN_PATH);
//Wait for the pressed buttons to change //Wait for the pressed buttons to change
while(HID_PAD == BUTTON_START); while(HID_PAD & PIN_BUTTONS);
chrono(2);
} }

View File

@@ -24,10 +24,44 @@
#include "types.h" #include "types.h"
#define CONFIG(a) (((config >> (a + 16)) & 1) != 0) #define CONFIG(a) (((configData.config >> (a + 20)) & 1) != 0)
#define MULTICONFIG(a) ((config >> (a * 2 + 6)) & 3) #define MULTICONFIG(a) ((configData.config >> (a * 2 + 6)) & 3)
#define BOOTCONFIG(a, b) ((config >> a) & b) #define BOOTCONFIG(a, b) ((configData.config >> a) & b)
extern u32 config; #define CONFIG_PATH "/luma/config.bin"
#define CONFIG_VERSIONMAJOR 1
#define CONFIG_VERSIONMINOR 2
void configureCFW(void); #define BOOTCFG_NAND BOOTCONFIG(0, 3)
#define BOOTCFG_FIRM BOOTCONFIG(2, 1)
#define BOOTCFG_A9LH BOOTCONFIG(3, 1)
#define BOOTCFG_NOFORCEFLAG BOOTCONFIG(4, 1)
#define BOOTCFG_SAFEMODE BOOTCONFIG(5, 1)
#define CONFIG_BRIGHTNESS MULTICONFIG(0)
#define CONFIG_PIN MULTICONFIG(1)
#define CONFIG_AUTOBOOTSYS CONFIG(0)
#define CONFIG_USESYSFIRM CONFIG(1)
#define CONFIG_USESECONDEMU CONFIG(2)
#define CONFIG_SHOWGBABOOT CONFIG(5)
#define CONFIG_PAYLOADSPLASH CONFIG(6)
typedef struct __attribute__((packed))
{
char magic[4];
u16 formatVersionMajor, formatVersionMinor;
u32 config;
} cfgData;
typedef enum ConfigurationStatus
{
DONT_CONFIGURE = 0,
MODIFY_CONFIGURATION = 1,
CREATE_CONFIGURATION = 2
} ConfigurationStatus;
extern cfgData configData;
bool readConfig(void);
void writeConfig(ConfigurationStatus needConfig, u32 configTemp);
void configMenu(bool oldPinStatus);

View File

@@ -457,14 +457,13 @@ void arm9Loader(u8 *arm9Section)
} }
} }
void computePINHash(u8 out[32], u8 *in, u32 blockCount) void computePinHash(u8 *out, u8 *in)
{ {
u8 __attribute__((aligned(4))) cid[0x10]; u8 __attribute__((aligned(4))) cid[0x10];
u8 __attribute__((aligned(4))) cipherText[0x10]; u8 __attribute__((aligned(4))) cipherText[0x10];
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 which keys are set by the Arm9 bootROM aes(cipherText, in, 1, cid, AES_CBC_ENCRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
aes(cipherText, in, blockCount, cid, AES_CBC_ENCRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
sha(out, cipherText, 0x10, SHA_256_MODE); sha(out, cipherText, 0x10, SHA_256_MODE);
} }

View File

@@ -100,8 +100,7 @@
#define SHA_1_HASH_SIZE (160 / 8) #define SHA_1_HASH_SIZE (160 / 8)
extern u32 emuOffset; extern u32 emuOffset;
extern bool isN3DS; extern bool isN3DS, isDevUnit;
extern bool isDevUnit;
extern FirmwareSource firmSource; extern FirmwareSource firmSource;
void ctrNandInit(void); void ctrNandInit(void);
@@ -109,5 +108,4 @@ u32 ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf);
void setRSAMod0DerivedKeys(void); void setRSAMod0DerivedKeys(void);
void decryptExeFs(u8 *inbuf); void decryptExeFs(u8 *inbuf);
void arm9Loader(u8 *arm9Section); void arm9Loader(u8 *arm9Section);
void computePinHash(u8 *out, u8 *in);
void computePINHash(u8 out[32], u8 *in, u32 blockCount);

View File

@@ -26,20 +26,12 @@
*/ */
#include "draw.h" #include "draw.h"
#include "strings.h"
#include "screen.h" #include "screen.h"
#include "utils.h" #include "utils.h"
#include "fs.h" #include "fs.h"
#include "font.h" #include "font.h"
static inline int strlen(const char *string)
{
char *stringEnd = (char *)string;
while(*stringEnd) stringEnd++;
return stringEnd - string;
}
bool loadSplash(void) bool loadSplash(void)
{ {
//Don't delay boot nor init the screens if no splash image is on the SD //Don't delay boot nor init the screens if no splash image is on the SD

View File

@@ -25,12 +25,12 @@
#include "fatfs/sdmmc/sdmmc.h" #include "fatfs/sdmmc/sdmmc.h"
#include "../build/emunandpatch.h" #include "../build/emunandpatch.h"
void locateEmuNAND(u32 *off, u32 *head, FirmwareSource *emuNAND) void locateEmuNand(u32 *off, u32 *head, FirmwareSource *emuNand)
{ {
static u8 temp[0x200]; static u8 temp[0x200];
const u32 nandSize = getMMCDevice(0)->total_size; const u32 nandSize = getMMCDevice(0)->total_size;
u32 nandOffset = *emuNAND == FIRMWARE_EMUNAND ? 0 : u32 nandOffset = *emuNand == FIRMWARE_EMUNAND ? 0 :
(nandSize > 0x200000 ? 0x400000 : 0x200000); (nandSize > 0x200000 ? 0x400000 : 0x200000);
//Check for RedNAND //Check for RedNAND
@@ -53,37 +53,37 @@ void locateEmuNAND(u32 *off, u32 *head, FirmwareSource *emuNAND)
or to SysNAND if there isn't any */ or to SysNAND if there isn't any */
else else
{ {
*emuNAND = (*emuNAND == FIRMWARE_EMUNAND2) ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND; *emuNand = (*emuNand == FIRMWARE_EMUNAND2) ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND;
if(*emuNAND) locateEmuNAND(off, head, emuNAND); if(*emuNand) locateEmuNand(off, head, emuNand);
} }
} }
static inline void *getEmuCode(u8 *pos, u32 size) static inline void *getFreeK9Space(u8 *pos, u32 size)
{ {
const u8 pattern[] = {0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00}; const u8 pattern[] = {0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00};
//Looking for the last free space before Process9 //Looking for the last free space before Process9
return memsearch(pos + 0x13500, pattern, size - 0x13500, 6) + 0x455; return memsearch(pos + 0x13500, pattern, size - 0x13500, sizeof(pattern)) + 0x455;
} }
static inline u32 getSDMMC(u8 *pos, u32 size) static inline u32 getSdmmc(u8 *pos, u32 size)
{ {
//Look for struct code //Look for struct code
const u8 pattern[] = {0x21, 0x20, 0x18, 0x20}; const u8 pattern[] = {0x21, 0x20, 0x18, 0x20};
const u8 *off = memsearch(pos, pattern, size, 4); const u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
return *(u32 *)(off + 9) + *(u32 *)(off + 0xD); return *(u32 *)(off + 9) + *(u32 *)(off + 0xD);
} }
static inline void patchNANDRW(u8 *pos, u32 size, u32 branchOffset) static inline void patchNandRw(u8 *pos, u32 size, u32 branchOffset)
{ {
const u16 nandRedir[2] = {0x4C00, 0x47A0}; const u16 nandRedir[2] = {0x4C00, 0x47A0};
//Look for read/write code //Look for read/write code
const u8 pattern[] = {0x1E, 0x00, 0xC8, 0x05}; const u8 pattern[] = {0x1E, 0x00, 0xC8, 0x05};
u16 *readOffset = (u16 *)memsearch(pos, pattern, size, 4) - 3, u16 *readOffset = (u16 *)memsearch(pos, pattern, size, sizeof(pattern)) - 3,
*writeOffset = (u16 *)memsearch((u8 *)(readOffset + 5), pattern, 0x100, 4) - 3; *writeOffset = (u16 *)memsearch((u8 *)(readOffset + 5), pattern, 0x100, sizeof(pattern)) - 3;
*readOffset = nandRedir[0]; *readOffset = nandRedir[0];
readOffset[1] = nandRedir[1]; readOffset[1] = nandRedir[1];
@@ -93,40 +93,38 @@ static inline void patchNANDRW(u8 *pos, u32 size, u32 branchOffset)
((u32 *)writeOffset)[1] = branchOffset; ((u32 *)writeOffset)[1] = branchOffset;
} }
static inline void patchMPU(u8 *pos, u32 size) static inline void patchMpu(u8 *pos, u32 size)
{ {
const u32 mpuPatch[3] = {0x00360003, 0x00200603, 0x001C0603};
//Look for MPU pattern //Look for MPU pattern
const u8 pattern[] = {0x03, 0x00, 0x24, 0x00}; const u8 pattern[] = {0x03, 0x00, 0x24, 0x00};
u32 *off = (u32 *)memsearch(pos, pattern, size, 4); u32 *off = (u32 *)memsearch(pos, pattern, size, sizeof(pattern));
off[0] = mpuPatch[0]; off[0] = 0x00360003;
off[6] = mpuPatch[1]; off[6] = 0x00200603;
off[9] = mpuPatch[2]; off[9] = 0x001C0603;
} }
void patchEmuNAND(u8 *arm9Section, u32 arm9SectionSize, u8 *process9Offset, u32 process9Size, u32 emuOffset, u32 emuHeader, u32 branchAdditive) void patchEmuNand(u8 *arm9Section, u32 arm9SectionSize, u8 *process9Offset, u32 process9Size, u32 emuHeader, u32 branchAdditive)
{ {
//Copy emuNAND code //Copy emuNAND code
void *emuCodeOffset = getEmuCode(arm9Section, arm9SectionSize); void *freeK9Space = getFreeK9Space(arm9Section, arm9SectionSize);
memcpy(emuCodeOffset, emunand, emunand_size); memcpy(freeK9Space, emunand, emunand_size);
//Add the data of the found emuNAND //Add the data of the found emuNAND
u32 *pos_offset = (u32 *)memsearch(emuCodeOffset, "NAND", emunand_size, 4), u32 *posOffset = (u32 *)memsearch(freeK9Space, "NAND", emunand_size, 4),
*pos_header = (u32 *)memsearch(emuCodeOffset, "NCSD", emunand_size, 4); *posHeader = (u32 *)memsearch(freeK9Space, "NCSD", emunand_size, 4);
*pos_offset = emuOffset; *posOffset = emuOffset;
*pos_header = emuHeader; *posHeader = emuHeader;
//Find and add the SDMMC struct //Find and add the SDMMC struct
u32 *pos_sdmmc = (u32 *)memsearch(emuCodeOffset, "SDMC", emunand_size, 4); u32 *posSdmmc = (u32 *)memsearch(freeK9Space, "SDMC", emunand_size, 4);
*pos_sdmmc = getSDMMC(process9Offset, process9Size); *posSdmmc = getSdmmc(process9Offset, process9Size);
//Add emuNAND hooks //Add emuNAND hooks
u32 branchOffset = (u32)emuCodeOffset - branchAdditive; u32 branchOffset = (u32)freeK9Space - branchAdditive;
patchNANDRW(process9Offset, process9Size, branchOffset); patchNandRw(process9Offset, process9Size, branchOffset);
//Set MPU for emu code region //Set MPU for emu code region
patchMPU(arm9Section, arm9SectionSize); patchMpu(arm9Section, arm9SectionSize);
} }

View File

@@ -26,5 +26,7 @@
#define NCSD_MAGIC 0x4453434E #define NCSD_MAGIC 0x4453434E
void locateEmuNAND(u32 *off, u32 *head, FirmwareSource *emuNAND); extern u32 emuOffset;
void patchEmuNAND(u8 *arm9Section, u32 arm9SectionSize, u8 *process9Offset, u32 process9Size, u32 emuOffset, u32 emuHeader, u32 branchAdditive);
void locateEmuNand(u32 *off, u32 *head, FirmwareSource *emuNand);
void patchEmuNand(u8 *arm9Section, u32 arm9SectionSize, u8 *process9Offset, u32 process9Size, u32 emuHeader, u32 branchAdditive);

View File

@@ -2266,7 +2266,7 @@ FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too many S
if (res != FR_OK) return res; if (res != FR_OK) return res;
dp->blk_ofs = dp->dptr - SZDIRE * (nent - 1); /* Set block position */ dp->blk_ofs = dp->dptr - SZDIRE * (nent - 1); /* Set block position */
if (dp->obj.stat & 4) { /* Has the sub-directory been stretched? */ if (dp->obj.sclust != 0 && (dp->obj.stat & 4)) { /* Has the sub-directory been stretched? */
dp->obj.stat &= 3; dp->obj.stat &= 3;
dp->obj.objsize += (DWORD)fs->csize * SS(fs); /* Increase object size by cluster size */ dp->obj.objsize += (DWORD)fs->csize * SS(fs); /* Increase object size by cluster size */
res = fill_fat_chain(&dp->obj); /* Complement FAT chain if needed */ res = fill_fat_chain(&dp->obj); /* Complement FAT chain if needed */
@@ -2430,7 +2430,7 @@ void get_fileinfo ( /* No return code */
} }
#endif #endif
if (i >= _MAX_LFN) { i = 0; break; } /* No LFN if buffer overflow */ if (i >= _MAX_LFN) { i = 0; break; } /* No LFN if buffer overflow */
fno->fname[i++] = (char)w; fno->fname[i++] = (TCHAR)w;
} }
fno->fname[i] = 0; /* Terminate the LFN */ fno->fname[i] = 0; /* Terminate the LFN */
} }
@@ -3680,7 +3680,9 @@ FRESULT f_sync (
FATFS *fs; FATFS *fs;
DWORD tm; DWORD tm;
BYTE *dir; BYTE *dir;
#if _FS_EXFAT
DEF_NAMBUF
#endif
res = validate(fp, &fs); /* Check validity of the object */ res = validate(fp, &fs); /* Check validity of the object */
if (res == FR_OK) { if (res == FR_OK) {
@@ -3990,7 +3992,7 @@ FRESULT f_lseek (
#if !_FS_TINY #if !_FS_TINY
#if !_FS_READONLY #if !_FS_READONLY
if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */
if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fp, FR_DISK_ERR); if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
fp->flag &= ~FA_DIRTY; fp->flag &= ~FA_DIRTY;
} }
#endif #endif
@@ -4007,6 +4009,9 @@ FRESULT f_lseek (
/* Normal Seek */ /* Normal Seek */
{ {
#if _FS_EXFAT
if (fs->fs_type != FS_EXFAT && ofs >= 0x100000000) ofs = 0xFFFFFFFF; /* Clip at 4GiB-1 if at FATxx */
#endif
if (ofs > fp->obj.objsize && (_FS_READONLY || !(fp->flag & FA_WRITE))) { /* In read-only mode, clip offset with the file size */ if (ofs > fp->obj.objsize && (_FS_READONLY || !(fp->flag & FA_WRITE))) { /* In read-only mode, clip offset with the file size */
ofs = fp->obj.objsize; ofs = fp->obj.objsize;
} }
@@ -4016,7 +4021,7 @@ FRESULT f_lseek (
bcs = (DWORD)fs->csize * SS(fs); /* Cluster size (byte) */ bcs = (DWORD)fs->csize * SS(fs); /* Cluster size (byte) */
if (ifptr > 0 && if (ifptr > 0 &&
(ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */ (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */
fp->fptr = (ifptr - 1) & ~(bcs - 1); /* start from the current cluster */ fp->fptr = (ifptr - 1) & ~(QWORD)(bcs - 1); /* start from the current cluster */
ofs -= fp->fptr; ofs -= fp->fptr;
clst = fp->clust; clst = fp->clust;
} else { /* When seek to back cluster, */ } else { /* When seek to back cluster, */
@@ -4033,11 +4038,16 @@ FRESULT f_lseek (
} }
if (clst != 0) { if (clst != 0) {
while (ofs > bcs) { /* Cluster following loop */ while (ofs > bcs) { /* Cluster following loop */
ofs -= bcs; fp->fptr += bcs;
#if !_FS_READONLY #if !_FS_READONLY
if (fp->flag & FA_WRITE) { /* Check if in write mode or not */ if (fp->flag & FA_WRITE) { /* Check if in write mode or not */
if (_FS_EXFAT && fp->fptr > fp->obj.objsize) { /* No FAT chain object needs correct objsize to generate FAT value */
fp->obj.objsize = fp->fptr;
fp->flag |= FA_MODIFIED;
}
clst = create_chain(&fp->obj, clst); /* Force stretch if in write mode */ clst = create_chain(&fp->obj, clst); /* Force stretch if in write mode */
if (clst == 0) { /* When disk gets full, clip file size */ if (clst == 0) { /* When disk gets full, clip file size */
ofs = bcs; break; ofs = 0; break;
} }
} else } else
#endif #endif
@@ -4045,8 +4055,6 @@ FRESULT f_lseek (
if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
if (clst <= 1 || clst >= fs->n_fatent) ABORT(fs, FR_INT_ERR); if (clst <= 1 || clst >= fs->n_fatent) ABORT(fs, FR_INT_ERR);
fp->clust = clst; fp->clust = clst;
fp->fptr += bcs;
ofs -= bcs;
} }
fp->fptr += ofs; fp->fptr += ofs;
if (ofs % SS(fs)) { if (ofs % SS(fs)) {
@@ -5298,7 +5306,7 @@ FRESULT f_mkfs (
#if _FS_EXFAT #if _FS_EXFAT
if (fmt == FS_EXFAT) { /* Create an exFAT volume */ if (fmt == FS_EXFAT) { /* Create an exFAT volume */
DWORD sum, szb_bit, szb_case; DWORD szb_bit, szb_case, sum, nb, cl;
WCHAR ch, si; WCHAR ch, si;
UINT j, st; UINT j, st;
BYTE b; BYTE b;
@@ -5363,32 +5371,37 @@ FRESULT f_mkfs (
tbl[1] = (szb_case + au * ss - 1) / (au * ss); /* Number of up-case clusters */ tbl[1] = (szb_case + au * ss - 1) / (au * ss); /* Number of up-case clusters */
/* Initialize the allocation bitmap */ /* Initialize the allocation bitmap */
mem_set(buf, 0, szb_buf); /* Set in-use flags of bitmap, up-case and root dir */
for (i = 0, n = tbl[0] + tbl[1] + tbl[2]; n >= 8; buf[i++] = 0xFF, n -= 8) ;
for (b = 1; n; buf[i] |= b, b <<= 1, n--) ;
sect = b_data; n = (szb_bit + ss - 1) / ss; /* Start of bitmap and number of the sectors */ sect = b_data; n = (szb_bit + ss - 1) / ss; /* Start of bitmap and number of the sectors */
do { /* Fill allocation bitmap sectors */ nb = tbl[0] + tbl[1] + tbl[2]; /* Number of clusters in-use by system */
ns = (n > sz_buf) ? sz_buf : n; do {
mem_set(buf, 0, szb_buf);
for (i = 0; nb >= 8 && i < szb_buf; buf[i++] = 0xFF, nb -= 8) ;
for (b = 1; nb && i < szb_buf; buf[i] |= b, b <<= 1, nb--) ;
ns = (n > sz_buf) ? sz_buf : n; /* Write the buffered data */
if (disk_write(pdrv, buf, sect, ns) != RES_OK) return FR_DISK_ERR; if (disk_write(pdrv, buf, sect, ns) != RES_OK) return FR_DISK_ERR;
sect += ns; sect += ns; n -= ns;
mem_set(buf, 0, ss); } while (n);
} while (n -= ns);
/* Initialize the FAT */ /* Initialize the FAT */
st_qword(buf, 0xFFFFFFFFFFFFFFF8); /* Entry 0 and 1 */
for (j = 0, i = 2; j < 3; j++) { /* Set entries of bitmap, up-case and root dir */
for (n = tbl[j]; n; n--) {
st_dword(buf + i * 4, (n >= 2) ? i + 1 : 0xFFFFFFFF);
i++;
}
}
sect = b_fat; n = sz_fat; /* Start of FAT and number of the sectors */ sect = b_fat; n = sz_fat; /* Start of FAT and number of the sectors */
do { /* Fill FAT sectors */ j = nb = cl = 0;
ns = (n > sz_buf) ? sz_buf : n; do {
mem_set(buf, 0, szb_buf); i = 0;
if (cl == 0) { /* Set entry 0 and 1 */
st_dword(buf + i, 0xFFFFFFF8); i += 4; cl++;
st_dword(buf + i, 0xFFFFFFFF); i += 4; cl++;
}
do { /* Create chains of bitmap, up-case and root dir */
while (nb && i < szb_buf) { /* Create a chain */
st_dword(buf + i, (nb > 1) ? cl + 1 : 0xFFFFFFFF);
i += 4; cl++; nb--;
}
if (!nb && j < 3) nb = tbl[j++]; /* Next chain */
} while (nb && i < szb_buf);
ns = (n > sz_buf) ? sz_buf : n; /* Write the buffered data */
if (disk_write(pdrv, buf, sect, ns) != RES_OK) return FR_DISK_ERR; if (disk_write(pdrv, buf, sect, ns) != RES_OK) return FR_DISK_ERR;
sect += ns; sect += ns; n -= ns;
mem_set(buf, 0, ss); } while (n);
} while (n -= ns);
/* Initialize the root directory */ /* Initialize the root directory */
mem_set(buf, 0, ss); mem_set(buf, 0, ss);

View File

@@ -1,4 +1,3 @@
#pragma once #pragma once
#include <stdbool.h>
#include "../../types.h" #include "../../types.h"

View File

@@ -35,18 +35,18 @@
#include "pin.h" #include "pin.h"
#include "../build/injector.h" #include "../build/injector.h"
extern u16 launchedFirmTIDLow[8]; //defined in start.s extern u16 launchedFirmTidLow[8]; //Defined in start.s
static firmHeader *const firm = (firmHeader *)0x24000000; static firmHeader *const firm = (firmHeader *)0x24000000;
static const firmSectionHeader *section; static const firmSectionHeader *section;
u32 config, u32 emuOffset;
emuOffset;
bool isN3DS, bool isN3DS,
isDevUnit, isDevUnit,
isFirmlaunch; isFirmlaunch;
cfgData configData;
FirmwareSource firmSource; FirmwareSource firmSource;
void main(void) void main(void)
@@ -69,41 +69,36 @@ void main(void)
//Mount filesystems. CTRNAND will be mounted only if/when needed //Mount filesystems. CTRNAND will be mounted only if/when needed
mountFs(); mountFs();
const char configPath[] = "/luma/config.bin";
//Attempt to read the configuration file //Attempt to read the configuration file
needConfig = fileRead(&config, configPath) ? MODIFY_CONFIGURATION : CREATE_CONFIGURATION; needConfig = readConfig() ? MODIFY_CONFIGURATION : CREATE_CONFIGURATION;
//Determine if this is a firmlaunch boot //Determine if this is a firmlaunch boot
if(launchedFirmTIDLow[5] != 0) if(launchedFirmTidLow[5] != 0)
{ {
if(needConfig == CREATE_CONFIGURATION) mcuReboot();
isFirmlaunch = true; isFirmlaunch = true;
//'0' = NATIVE_FIRM, '1' = TWL_FIRM, '2' = AGB_FIRM if(needConfig == CREATE_CONFIGURATION) mcuReboot();
firmType = launchedFirmTIDLow[7] == u'3' ? SAFE_FIRM : (FirmwareType)(launchedFirmTIDLow[5] - u'0');
nandType = (FirmwareSource)BOOTCONFIG(0, 3); //'0' = NATIVE_FIRM, '1' = TWL_FIRM, '2' = AGB_FIRM
firmSource = (FirmwareSource)BOOTCONFIG(2, 1); firmType = launchedFirmTidLow[7] == u'3' ? SAFE_FIRM : (FirmwareType)(launchedFirmTidLow[5] - u'0');
isA9lh = BOOTCONFIG(3, 1) != 0;
nandType = (FirmwareSource)BOOTCFG_NAND;
firmSource = (FirmwareSource)BOOTCFG_FIRM;
isA9lh = BOOTCFG_A9LH != 0;
} }
else else
{ {
//Get pressed buttons
u32 pressed = HID_PAD;
isFirmlaunch = false; isFirmlaunch = false;
firmType = NATIVE_FIRM; firmType = NATIVE_FIRM;
//Determine if booting with A9LH //Determine if booting with A9LH
isA9lh = !PDN_SPI_CNT; isA9lh = !PDN_SPI_CNT;
//Determine if the user chose to use the SysNAND FIRM as default for a R boot //Get pressed buttons
bool useSysAsDefault = isA9lh ? CONFIG(1) : false; u32 pressed = HID_PAD;
//Save old options and begin saving the new boot configuration //Save old options and begin saving the new boot configuration
configTemp = (config & 0xFFFFFFC0) | ((u32)isA9lh << 3); configTemp = (configData.config & 0xFFFFFFC0) | ((u32)isA9lh << 3);
//If it's a MCU reboot, try to force boot options //If it's a MCU reboot, try to force boot options
if(isA9lh && CFG_BOOTENV) if(isA9lh && CFG_BOOTENV)
@@ -112,7 +107,7 @@ void main(void)
if(CFG_BOOTENV == 7) if(CFG_BOOTENV == 7)
{ {
nandType = FIRMWARE_SYSNAND; nandType = FIRMWARE_SYSNAND;
firmSource = useSysAsDefault ? FIRMWARE_SYSNAND : (FirmwareSource)BOOTCONFIG(2, 1); firmSource = CONFIG_USESYSFIRM ? FIRMWARE_SYSNAND : (FirmwareSource)BOOTCFG_FIRM;
needConfig = DONT_CONFIGURE; needConfig = DONT_CONFIGURE;
//Flag to prevent multiple boot options-forcing //Flag to prevent multiple boot options-forcing
@@ -121,10 +116,10 @@ void main(void)
/* Else, force the last used boot options unless a button is pressed /* Else, force the last used boot options unless a button is pressed
or the no-forcing flag is set */ or the no-forcing flag is set */
else if(!pressed && !BOOTCONFIG(4, 1)) else if(needConfig != CREATE_CONFIGURATION && !pressed && !BOOTCFG_NOFORCEFLAG)
{ {
nandType = (FirmwareSource)BOOTCONFIG(0, 3); nandType = (FirmwareSource)BOOTCFG_NAND;
firmSource = (FirmwareSource)BOOTCONFIG(2, 1); firmSource = (FirmwareSource)BOOTCFG_FIRM;
needConfig = DONT_CONFIGURE; needConfig = DONT_CONFIGURE;
} }
} }
@@ -132,23 +127,14 @@ void main(void)
//Boot options aren't being forced //Boot options aren't being forced
if(needConfig != DONT_CONFIGURE) if(needConfig != DONT_CONFIGURE)
{ {
PINData pin; bool pinExists = CONFIG_PIN != 0 && verifyPin();
bool pinExists = CONFIG(7) && readPin(&pin);
//If we get here we should check the PIN (if it exists) in all cases
if(pinExists) verifyPin(&pin);
//If no configuration file exists or SELECT is held, load configuration menu //If no configuration file exists or SELECT is held, load configuration menu
bool shouldLoadConfigurationMenu = needConfig == CREATE_CONFIGURATION || ((pressed & BUTTON_SELECT) && !(pressed & BUTTON_L1)); bool shouldLoadConfigMenu = needConfig == CREATE_CONFIGURATION || ((pressed & BUTTON_SELECT) && !(pressed & BUTTON_L1));
if(shouldLoadConfigurationMenu) if(shouldLoadConfigMenu)
{ {
configureCFW(); configMenu(pinExists);
if(!pinExists && CONFIG(7)) newPin();
chrono(2);
//Update pressed buttons //Update pressed buttons
pressed = HID_PAD; pressed = HID_PAD;
@@ -161,37 +147,47 @@ void main(void)
//Flag to tell loader to init SD //Flag to tell loader to init SD
configTemp |= 1 << 5; configTemp |= 1 << 5;
//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 else
{ {
if(CONFIG(6) && loadSplash()) pressed = HID_PAD; if(CONFIG_PAYLOADSPLASH && loadSplash()) pressed = HID_PAD;
/* If L and R/A/Select or one of the single payload buttons are pressed, /* If L and R/A/Select or one of the single payload buttons are pressed,
chainload an external payload (the PIN, if any, has been verified)*/ chainload an external payload */
bool shouldLoadPayload = (pressed & SINGLE_PAYLOAD_BUTTONS) || ((pressed & BUTTON_L1) && (pressed & L_PAYLOAD_BUTTONS)); bool shouldLoadPayload = (pressed & SINGLE_PAYLOAD_BUTTONS) || ((pressed & BUTTON_L1) && (pressed & L_PAYLOAD_BUTTONS));
if(shouldLoadPayload) loadPayload(pressed); if(shouldLoadPayload) loadPayload(pressed);
if(!CONFIG(6)) loadSplash(); if(!CONFIG_PAYLOADSPLASH) 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 R is pressed, boot the non-updated NAND with the FIRM of the opposite one
if(pressed & BUTTON_R1) if(pressed & BUTTON_R1)
{ {
nandType = (useSysAsDefault) ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND; nandType = useSysAsDefault ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND;
firmSource = (useSysAsDefault) ? FIRMWARE_SYSNAND : FIRMWARE_EMUNAND; firmSource = useSysAsDefault ? FIRMWARE_SYSNAND : FIRMWARE_EMUNAND;
} }
/* Else, boot the NAND the user set to autoboot or the opposite one, depending on L, /* Else, boot the NAND the user set to autoboot or the opposite one, depending on L,
with their own FIRM */ with their own FIRM */
else else
{ {
nandType = (CONFIG(0) != !(pressed & BUTTON_L1)) ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND; nandType = (CONFIG_AUTOBOOTSYS != !(pressed & BUTTON_L1)) ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND;
firmSource = nandType; firmSource = nandType;
} }
/* If we're booting emuNAND the second emuNAND is set as default and B isn't pressed, /* If we're booting emuNAND the second emuNAND is set as default and B isn't pressed,
or vice-versa, boot the second emuNAND */ or vice-versa, boot the second emuNAND */
if(nandType != FIRMWARE_SYSNAND && (CONFIG(2) == !(pressed & BUTTON_B))) nandType = FIRMWARE_EMUNAND2; if(nandType != FIRMWARE_SYSNAND && (CONFIG_USESECONDEMU == !(pressed & BUTTON_B))) nandType = FIRMWARE_EMUNAND2;
} }
} }
} }
@@ -199,35 +195,21 @@ void main(void)
//If we need to boot emuNAND, make sure it exists //If we need to boot emuNAND, make sure it exists
if(nandType != FIRMWARE_SYSNAND) if(nandType != FIRMWARE_SYSNAND)
{ {
locateEmuNAND(&emuOffset, &emuHeader, &nandType); locateEmuNand(&emuOffset, &emuHeader, &nandType);
if(nandType == FIRMWARE_SYSNAND) firmSource = FIRMWARE_SYSNAND; if(nandType == FIRMWARE_SYSNAND) firmSource = FIRMWARE_SYSNAND;
} }
//Same if we're using emuNAND as the FIRM source //Same if we're using emuNAND as the FIRM source
else if(firmSource != FIRMWARE_SYSNAND) else if(firmSource != FIRMWARE_SYSNAND)
locateEmuNAND(&emuOffset, &emuHeader, &firmSource); locateEmuNand(&emuOffset, &emuHeader, &firmSource);
if(!isFirmlaunch) if(!isFirmlaunch)
{ {
configTemp |= (u32)nandType | ((u32)firmSource << 2); configTemp |= (u32)nandType | ((u32)firmSource << 2);
writeConfig(needConfig, configTemp);
/* If the configuration is different from previously, overwrite it.
Just the no-forcing flag being set is not enough */
if((configTemp & 0xFFFFFFEF) != config)
{
//Merge the new options and new boot configuration
config = (config & 0xFFFFFFC0) | (configTemp & 0x3F);
if(!fileWrite(&config, configPath, 4))
{
createDirectory("luma");
if(!fileWrite(&config, configPath, 4))
error("Error writing the configuration file");
}
}
} }
u32 firmVersion = loadFirm(firmType); u32 firmVersion = loadFirm(&firmType, firmSource);
switch(firmType) switch(firmType)
{ {
@@ -235,7 +217,8 @@ void main(void)
patchNativeFirm(firmVersion, nandType, emuHeader, isA9lh); patchNativeFirm(firmVersion, nandType, emuHeader, isA9lh);
break; break;
case SAFE_FIRM: case SAFE_FIRM:
patchSafeFirm(); case NATIVE_FIRM1X2X:
if(isA9lh) patch1x2xNativeAndSafeFirm();
break; break;
default: default:
//Skip patching on unsupported O3DS AGB/TWL FIRMs //Skip patching on unsupported O3DS AGB/TWL FIRMs
@@ -246,27 +229,38 @@ void main(void)
launchFirm(firmType); launchFirm(firmType);
} }
static inline u32 loadFirm(FirmwareType firmType) static inline u32 loadFirm(FirmwareType *firmType, FirmwareSource firmSource)
{ {
section = firm->section; section = firm->section;
//Load FIRM from CTRNAND, unless it's an O3DS and we're loading a pre-5.0 NATIVE FIRM //Load FIRM from CTRNAND
u32 firmVersion = firmRead(firm, (u32)firmType); u32 firmVersion = firmRead(firm, (u32)*firmType);
if(!isN3DS && firmType == NATIVE_FIRM && firmVersion < 0x25) if(!isN3DS && *firmType == NATIVE_FIRM)
{ {
//We can't boot < 3.x NANDs
if(firmVersion < 0x18) if(firmVersion < 0x18)
error("An old unsupported NAND has been detected.\nLuma3DS is unable to boot it."); {
//We can't boot < 3.x EmuNANDs
if(firmSource != FIRMWARE_SYSNAND)
error("An old unsupported EmuNAND has been detected.\nLuma3DS is unable to boot it");
//We can't boot a 4.x NATIVE_FIRM, load one from SD if(BOOTCFG_SAFEMODE != 0) error("SAFE_MODE is not supported on 1.x/2.x FIRM");
if(!fileRead(firm, "/luma/firmware.bin") || (((u32)section[2].address >> 8) & 0xFF) != 0x68)
error("An old unsupported FIRM has been detected.\nCopy firmware.bin in /luma to boot");
//No assumption regarding FIRM version *firmType = NATIVE_FIRM1X2X;
firmVersion = 0xffffffff; }
//We can't boot a 3.x/4.x NATIVE_FIRM, load one from SD
else if(firmVersion < 0x25)
{
if(!fileRead(firm, "/luma/firmware.bin") || section[2].address != (u8 *)0x8006800)
error("An old unsupported FIRM has been detected.\nCopy firmware.bin in /luma to boot");
//No assumption regarding FIRM version
firmVersion = 0xFFFFFFFF;
}
} }
else decryptExeFs((u8 *)firm);
if(firmVersion != 0xFFFFFFFF) decryptExeFs((u8 *)firm);
return firmVersion; return firmVersion;
} }
@@ -291,6 +285,10 @@ static inline void patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32
process9MemAddr; process9MemAddr;
u8 *process9Offset = getProcess9(arm9Section + 0x15000, section[2].size - 0x15000, &process9Size, &process9MemAddr); u8 *process9Offset = getProcess9(arm9Section + 0x15000, section[2].size - 0x15000, &process9Size, &process9MemAddr);
//Find Kernel11 SVC table and free space locations
u8 *freeK11Space;
u32 *arm11SvcTable = getKernel11Info(arm11Section1, section[1].size, &freeK11Space);
//Apply signature patches //Apply signature patches
patchSignatureChecks(process9Offset, process9Size); patchSignatureChecks(process9Offset, process9Size);
@@ -298,7 +296,7 @@ static inline void patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32
if(nandType != FIRMWARE_SYSNAND) if(nandType != FIRMWARE_SYSNAND)
{ {
u32 branchAdditive = (u32)firm + section[2].offset - (u32)section[2].address; u32 branchAdditive = (u32)firm + section[2].offset - (u32)section[2].address;
patchEmuNAND(arm9Section, section[2].size, process9Offset, process9Size, emuOffset, emuHeader, branchAdditive); 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
@@ -314,10 +312,10 @@ static inline void patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32
patchTitleInstallMinVersionCheck(process9Offset, process9Size); patchTitleInstallMinVersionCheck(process9Offset, process9Size);
//Restore svcBackdoor //Restore svcBackdoor
reimplementSvcBackdoor(arm11Section1, section[1].size); reimplementSvcBackdoor(arm11Section1, arm11SvcTable, &freeK11Space);
} }
implementSvcGetCFWInfo(arm11Section1, section[1].size); implementSvcGetCFWInfo(arm11Section1, arm11SvcTable, &freeK11Space);
} }
static inline void patchLegacyFirm(FirmwareType firmType) static inline void patchLegacyFirm(FirmwareType firmType)
@@ -330,12 +328,9 @@ static inline void patchLegacyFirm(FirmwareType firmType)
} }
applyLegacyFirmPatches((u8 *)firm, firmType); applyLegacyFirmPatches((u8 *)firm, firmType);
if(firmType == TWL_FIRM && CONFIG(8))
patchTwlBg((u8 *)firm + section[1].offset);
} }
static inline void patchSafeFirm(void) static inline void patch1x2xNativeAndSafeFirm(void)
{ {
u8 *arm9Section = (u8 *)firm + section[2].offset; u8 *arm9Section = (u8 *)firm + section[2].offset;
@@ -347,38 +342,35 @@ static inline void patchSafeFirm(void)
patchFirmWrites(arm9Section, section[2].size); patchFirmWrites(arm9Section, section[2].size);
} }
else patchFirmWriteSafe(arm9Section, section[2].size); else patchOldFirmWrites(arm9Section, section[2].size);
} }
static inline void copySection0AndInjectSystemModules(void) static inline void copySection0AndInjectSystemModules(void)
{ {
u8 *arm11Section0 = (u8 *)firm + section[0].offset; u32 srcModuleSize,
dstModuleSize;
struct for(u8 *src = (u8 *)firm + section[0].offset, *srcEnd = src + section[0].size, *dst = section[0].address;
src < srcEnd; src += srcModuleSize, dst += dstModuleSize)
{ {
u32 size; srcModuleSize = *(u32 *)(src + 0x104) * 0x200;
const u8 *addr; char *moduleName = (char *)(src + 0x200);
} modules[5];
u32 n = 0, void *module;
loaderIndex;
u8 *pos = arm11Section0;
for(u8 *end = pos + section[0].size; pos < end; pos += modules[n++].size) if(memcmp(moduleName, "loader", 6) == 0)
{ {
modules[n].addr = pos; module = (void *)injector;
modules[n].size = *(u32 *)(pos + 0x104) * 0x200; dstModuleSize = injector_size;
}
else
{
module = src;
dstModuleSize = srcModuleSize;
}
if(memcmp(modules[n].addr + 0x200, "loader", 7) == 0) loaderIndex = n; memcpy(dst, module, dstModuleSize);
} }
modules[loaderIndex].addr = injector;
modules[loaderIndex].size = injector_size;
pos = section[0].address;
for(u32 i = 0; i < n; pos += modules[i++].size)
memcpy(pos, modules[i].addr, modules[i].size);
} }
static inline void launchFirm(FirmwareType firmType) static inline void launchFirm(FirmwareType firmType)

View File

@@ -24,6 +24,11 @@
#include "types.h" #include "types.h"
#define CFG_BOOTENV (*(vu32 *)0x10010000)
#define CFG_UNITINFO (*(vu8 *)0x10010010)
#define PDN_MPCORE_CFG (*(vu32 *)0x10140FFC)
#define PDN_SPI_CNT (*(vu32 *)0x101401C0)
//FIRM Header layout //FIRM Header layout
typedef struct firmSectionHeader { typedef struct firmSectionHeader {
u32 offset; u32 offset;
@@ -41,17 +46,10 @@ typedef struct firmHeader {
u8 reserved2[0x30]; u8 reserved2[0x30];
firmSectionHeader section[4]; firmSectionHeader section[4];
} firmHeader; } firmHeader;
typedef enum ConfigurationStatus
{
DONT_CONFIGURE = 0,
MODIFY_CONFIGURATION = 1,
CREATE_CONFIGURATION = 2
} ConfigurationStatus;
static inline u32 loadFirm(FirmwareType firmType); static inline u32 loadFirm(FirmwareType *firmType, FirmwareSource firmSource);
static inline void patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 emuHeader, bool isA9lh); static inline void patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 emuHeader, bool isA9lh);
static inline void patchLegacyFirm(FirmwareType firmType); static inline void patchLegacyFirm(FirmwareType firmType);
static inline void patchSafeFirm(void); static inline void patch1x2xNativeAndSafeFirm(void);
static inline void copySection0AndInjectSystemModules(void); static inline void copySection0AndInjectSystemModules(void);
static inline void launchFirm(FirmwareType firmType); static inline void launchFirm(FirmwareType firmType);

View File

@@ -22,6 +22,7 @@
#include "fs.h" #include "fs.h"
#include "memory.h" #include "memory.h"
#include "strings.h"
#include "cache.h" #include "cache.h"
#include "screen.h" #include "screen.h"
#include "fatfs/ff.h" #include "fatfs/ff.h"
@@ -64,7 +65,9 @@ bool fileWrite(const void *buffer, const char *path, u32 size)
{ {
FIL file; FIL file;
if(f_open(&file, path, FA_WRITE | FA_OPEN_ALWAYS) == FR_OK) FRESULT result = f_open(&file, path, FA_WRITE | FA_OPEN_ALWAYS);
if(result == FR_OK)
{ {
unsigned int written; unsigned int written;
f_write(&file, buffer, size, &written); f_write(&file, buffer, size, &written);
@@ -72,13 +75,26 @@ bool fileWrite(const void *buffer, const char *path, u32 size)
return true; return true;
} }
else if(result == FR_NO_PATH)
{
char folder[256];
return false; for(u32 i = 1; path[i] != 0; i++)
if(path[i] == '/')
{
memcpy(folder, path, i);
folder[i] = 0;
f_mkdir(folder);
}
return fileWrite(buffer, path, size);
}
else return false;
} }
void createDirectory(const char *path) void fileDelete(const char *path)
{ {
f_mkdir(path); f_unlink(path);
} }
void loadPayload(u32 pressed) void loadPayload(u32 pressed)
@@ -112,8 +128,8 @@ void loadPayload(u32 pressed)
memcpy(loaderAddress, loader, loader_size); memcpy(loaderAddress, loader, loader_size);
path[14] = '/'; concatenateStrings(path, "/");
memcpy(&path[15], info.altname, 13); concatenateStrings(path, info.altname);
loaderAddress[1] = fileRead((void *)0x24F00000, path); loaderAddress[1] = fileRead((void *)0x24F00000, path);
@@ -131,8 +147,9 @@ u32 firmRead(void *dest, u32 firmType)
{ "00000202", "20000202" }, { "00000202", "20000202" },
{ "00000003", "20000003" }}; { "00000003", "20000003" }};
char path[48] = "1:/title/00040138/00000000/content"; char path[48] = "1:/title/00040138/";
memcpy(&path[18], firmFolders[firmType][isN3DS ? 1 : 0], 8); concatenateStrings(path, firmFolders[firmType][isN3DS ? 1 : 0]);
concatenateStrings(path, "/content");
DIR dir; DIR dir;
FILINFO info; FILINFO info;
@@ -162,19 +179,10 @@ u32 firmRead(void *dest, u32 firmType)
f_closedir(&dir); f_closedir(&dir);
//Complete the string with the .app name //Complete the string with the .app name
memcpy(&path[34], "/00000000.app", 14); concatenateStrings(path, "/00000000.app");
//Last digit of the .app
u32 i = 42;
//Convert back the .app name from integer to array //Convert back the .app name from integer to array
u32 tempVersion = firmVersion; hexItoa(firmVersion, &path[35]);
while(tempVersion)
{
static const char hexDigits[] = "0123456789ABCDEF";
path[i--] = hexDigits[tempVersion & 0xF];
tempVersion >>= 4;
}
fileRead(dest, path); fileRead(dest, path);

View File

@@ -24,7 +24,7 @@
#include "types.h" #include "types.h"
#define PATTERN(a) a "_*.bin" #define PATTERN(a) a "_*.bin"
extern bool isN3DS; extern bool isN3DS;
@@ -32,6 +32,6 @@ void mountFs(void);
u32 fileRead(void *dest, const char *path); u32 fileRead(void *dest, const char *path);
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);
void createDirectory(const char *path); void fileDelete(const char *path);
void loadPayload(u32 pressed); void loadPayload(u32 pressed);
u32 firmRead(void *dest, u32 firmType); u32 firmRead(void *dest, u32 firmType);

View File

@@ -41,7 +41,7 @@ void memset32(void *dest, u32 filler, u32 size)
{ {
u32 *dest32 = (u32 *)dest; u32 *dest32 = (u32 *)dest;
for (u32 i = 0; i < size / 4; i++) for(u32 i = 0; i < size / 4; i++)
dest32[i] = filler; dest32[i] = filler;
} }

View File

@@ -25,48 +25,6 @@
#include "config.h" #include "config.h"
#include "../build/rebootpatch.h" #include "../build/rebootpatch.h"
#include "../build/svcGetCFWInfopatch.h" #include "../build/svcGetCFWInfopatch.h"
#include "../build/twl_k11modulespatch.h"
static u32 *arm11ExceptionsPage = NULL;
static u32 *arm11SvcTable = NULL;
static u32 *arm11SvcHandler = NULL;
static u8 *freeK11Space = NULL; //other than the one used for svcBackdoor
static void findArm11ExceptionsPageAndSvcHandlerAndTable(u8 *pos, u32 size)
{
const u8 arm11ExceptionsPagePattern[] = {0x00, 0xB0, 0x9C, 0xE5};
if(arm11ExceptionsPage == NULL) arm11ExceptionsPage = (u32 *)memsearch(pos, arm11ExceptionsPagePattern, size, 4) - 0xB;
if((arm11SvcTable == NULL || arm11SvcHandler == NULL) && arm11ExceptionsPage != NULL)
{
u32 svcOffset = (-((arm11ExceptionsPage[2] & 0xFFFFFF) << 2) & (0xFFFFFF << 2)) - 8; //Branch offset + 8 for prefetch
arm11SvcHandler = arm11SvcTable = (u32 *)(pos + *(u32 *)(pos + 0xFFFF0008 - svcOffset - 0xFFF00000 + 8) - 0xFFF00000); //SVC handler address
while(*arm11SvcTable) arm11SvcTable++; //Look for SVC0 (NULL)
}
}
static void findFreeK11Space(u8 *pos, u32 size)
{
if(freeK11Space == NULL)
{
const u8 bogus_pattern[] = { 0x1E, 0xFF, 0x2F, 0xE1, 0x1E, 0xFF, 0x2F, 0xE1, 0x1E, 0xFF,
0x2F, 0xE1, 0x00, 0x10, 0xA0, 0xE3, 0x00, 0x10, 0xC0, 0xE5,
0x1E, 0xFF, 0x2F, 0xE1 };
u32 *someSpace = (u32 *)memsearch(pos, bogus_pattern, size, 24);
// We couldn't find the place where to begin our search of an empty block
if (someSpace == NULL)
return;
// Advance until we reach the padding area (filled with 0xFF)
u32 *freeSpace;
for(freeSpace = someSpace; *freeSpace != 0xFFFFFFFF; freeSpace++);
freeK11Space = (u8 *)freeSpace;
}
}
u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr) u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr)
{ {
@@ -79,6 +37,22 @@ u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr)
return off - 0x204 + (*(u32 *)(off - 0x64) * 0x200) + 0x200; return off - 0x204 + (*(u32 *)(off - 0x64) * 0x200) + 0x200;
} }
u32 *getKernel11Info(u8 *pos, u32 size, u8 **freeK11Space)
{
const u8 pattern[] = {0x00, 0xB0, 0x9C, 0xE5};
u32 *arm11ExceptionsPage = (u32 *)memsearch(pos, pattern, size, sizeof(pattern)) - 0xB;
u32 svcOffset = (-((arm11ExceptionsPage[2] & 0xFFFFFF) << 2) & (0xFFFFFF << 2)) - 8; //Branch offset + 8 for prefetch
u32 *arm11SvcTable = (u32 *)(pos + *(u32 *)(pos + 0xFFFF0008 - svcOffset - 0xFFF00000 + 8) - 0xFFF00000); //SVC handler address
while(*arm11SvcTable) arm11SvcTable++; //Look for SVC0 (NULL)
const u8 pattern2[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
*freeK11Space = memsearch(pos, pattern2, size, sizeof(pattern2)) + 1;
return arm11SvcTable;
}
void patchSignatureChecks(u8 *pos, u32 size) void patchSignatureChecks(u8 *pos, u32 size)
{ {
const u16 sigPatch[2] = {0x2000, 0x4770}; const u16 sigPatch[2] = {0x2000, 0x4770};
@@ -87,8 +61,8 @@ void patchSignatureChecks(u8 *pos, u32 size)
const u8 pattern[] = {0xC0, 0x1C, 0x76, 0xE7}, const u8 pattern[] = {0xC0, 0x1C, 0x76, 0xE7},
pattern2[] = {0xB5, 0x22, 0x4D, 0x0C}; pattern2[] = {0xB5, 0x22, 0x4D, 0x0C};
u16 *off = (u16 *)memsearch(pos, pattern, size, 4), u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern)),
*off2 = (u16 *)(memsearch(pos, pattern2, size, 4) - 1); *off2 = (u16 *)(memsearch(pos, pattern2, size, sizeof(pattern2)) - 1);
*off = sigPatch[0]; *off = sigPatch[0];
off2[0] = sigPatch[0]; off2[0] = sigPatch[0];
@@ -98,9 +72,9 @@ void patchSignatureChecks(u8 *pos, u32 size)
void patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr) void patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr)
{ {
//Look for firmlaunch code //Look for firmlaunch code
const u8 pattern[] = {0xDE, 0x1F, 0x8D, 0xE2}; const u8 pattern[] = {0xE2, 0x20, 0x20, 0x90};
u8 *off = memsearch(pos, pattern, size, 4) - 0x10; u8 *off = memsearch(pos, pattern, size, sizeof(pattern)) - 0x13;
//Firmlaunch function offset - offset in BLX opcode (A4-16 - ARM DDI 0100E) + 1 //Firmlaunch function offset - offset in BLX opcode (A4-16 - ARM DDI 0100E) + 1
u32 fOpenOffset = (u32)(off + 9 - (-((*(u32 *)off & 0x00FFFFFF) << 2) & (0xFFFFFF << 2)) - pos + process9MemAddr); u32 fOpenOffset = (u32)(off + 9 - (-((*(u32 *)off & 0x00FFFFFF) << 2) & (0xFFFFFF << 2)) - pos + process9MemAddr);
@@ -115,72 +89,61 @@ void patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr)
void patchFirmWrites(u8 *pos, u32 size) void patchFirmWrites(u8 *pos, u32 size)
{ {
const u16 writeBlock[2] = {0x2000, 0x46C0};
//Look for FIRM writing code //Look for FIRM writing code
u8 *const off1 = memsearch(pos, "exe:", size, 4); u8 *const off1 = memsearch(pos, "exe:", size, 4);
const u8 pattern[] = {0x00, 0x28, 0x01, 0xDA}; const u8 pattern[] = {0x00, 0x28, 0x01, 0xDA};
u16 *off2 = (u16 *)memsearch(off1 - 0x100, pattern, 0x100, 4); u16 *off2 = (u16 *)memsearch(off1 - 0x100, pattern, 0x100, sizeof(pattern));
off2[0] = writeBlock[0]; off2[0] = 0x2000;
off2[1] = writeBlock[1]; off2[1] = 0x46C0;
} }
void patchFirmWriteSafe(u8 *pos, u32 size) void patchOldFirmWrites(u8 *pos, u32 size)
{ {
const u16 writeBlockSafe[2] = {0x2400, 0xE01D};
//Look for FIRM writing code //Look for FIRM writing code
const u8 pattern[] = {0x04, 0x1E, 0x1D, 0xDB}; const u8 pattern[] = {0x04, 0x1E, 0x1D, 0xDB};
u16 *off = (u16 *)memsearch(pos, pattern, size, 4); u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
off[0] = writeBlockSafe[0]; off[0] = 0x2400;
off[1] = writeBlockSafe[1]; off[1] = 0xE01D;
} }
void reimplementSvcBackdoor(u8 *pos, u32 size) void reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u8 **freeK11Space)
{ {
//Official implementation of svcBackdoor //Official implementation of svcBackdoor
const u8 svcBackdoor[40] = {0xFF, 0x10, 0xCD, 0xE3, //bic r1, sp, #0xff const u8 svcBackdoor[40] = {0xFF, 0x10, 0xCD, 0xE3, //bic r1, sp, #0xff
0x0F, 0x1C, 0x81, 0xE3, //orr r1, r1, #0xf00 0x0F, 0x1C, 0x81, 0xE3, //orr r1, r1, #0xf00
0x28, 0x10, 0x81, 0xE2, //add r1, r1, #0x28 0x28, 0x10, 0x81, 0xE2, //add r1, r1, #0x28
0x00, 0x20, 0x91, 0xE5, //ldr r2, [r1] 0x00, 0x20, 0x91, 0xE5, //ldr r2, [r1]
0x00, 0x60, 0x22, 0xE9, //stmdb r2!, {sp, lr} 0x00, 0x60, 0x22, 0xE9, //stmdb r2!, {sp, lr}
0x02, 0xD0, 0xA0, 0xE1, //mov sp, r2 0x02, 0xD0, 0xA0, 0xE1, //mov sp, r2
0x30, 0xFF, 0x2F, 0xE1, //blx r0 0x30, 0xFF, 0x2F, 0xE1, //blx r0
0x03, 0x00, 0xBD, 0xE8, //pop {r0, r1} 0x03, 0x00, 0xBD, 0xE8, //pop {r0, r1}
0x00, 0xD0, 0xA0, 0xE1, //mov sp, r0 0x00, 0xD0, 0xA0, 0xE1, //mov sp, r0
0x11, 0xFF, 0x2F, 0xE1}; //bx r1 0x11, 0xFF, 0x2F, 0xE1}; //bx r1
findArm11ExceptionsPageAndSvcHandlerAndTable(pos, size);
if(!arm11SvcTable[0x7B]) if(!arm11SvcTable[0x7B])
{ {
u32 *freeSpace; memcpy(*freeK11Space, svcBackdoor, 40);
for(freeSpace = arm11ExceptionsPage; *freeSpace != 0xFFFFFFFF; freeSpace++);
memcpy(freeSpace, svcBackdoor, 40); arm11SvcTable[0x7B] = 0xFFF00000 + *freeK11Space - pos;
*freeK11Space += 40;
arm11SvcTable[0x7B] = 0xFFFF0000 + ((u8 *)freeSpace - (u8 *)arm11ExceptionsPage);
} }
} }
void implementSvcGetCFWInfo(u8 *pos, u32 size) void implementSvcGetCFWInfo(u8 *pos, u32 *arm11SvcTable, u8 **freeK11Space)
{ {
memcpy(*freeK11Space, svcGetCFWInfo, svcGetCFWInfo_size);
CFWInfo *info = (CFWInfo *)memsearch(*freeK11Space, "LUMA", svcGetCFWInfo_size, 4);
const char *rev = REVISION; const char *rev = REVISION;
bool isRelease; bool isRelease;
findArm11ExceptionsPageAndSvcHandlerAndTable(pos, size);
findFreeK11Space(pos, size);
memcpy(freeK11Space, svcGetCFWInfo, svcGetCFWInfo_size);
CFWInfo *info = (CFWInfo *)memsearch(freeK11Space, "LUMA", svcGetCFWInfo_size, 4);
info->commitHash = COMMIT_HASH; info->commitHash = COMMIT_HASH;
info->config = config; info->config = configData.config;
info->versionMajor = (u8)(rev[1] - '0'); info->versionMajor = (u8)(rev[1] - '0');
info->versionMinor = (u8)(rev[3] - '0'); info->versionMinor = (u8)(rev[3] - '0');
if(rev[4] == '.') if(rev[4] == '.')
@@ -188,21 +151,20 @@ void implementSvcGetCFWInfo(u8 *pos, u32 size)
info->versionBuild = (u8)(rev[5] - '0'); info->versionBuild = (u8)(rev[5] - '0');
isRelease = rev[6] == 0; isRelease = rev[6] == 0;
} }
else else isRelease = rev[4] == 0;
isRelease = rev[4] == 0;
info->flags = 0 /* master branch */ | (((isRelease) ? 1 : 0) << 1) /* is release */; info->flags = 0 /* master branch */ | ((isRelease ? 1 : 0) << 1) /* is release */;
arm11SvcTable[0x2E] = 0xFFF00000 + freeK11Space - pos; //stubbed svc arm11SvcTable[0x2E] = 0xFFF00000 + *freeK11Space - pos; //Stubbed svc
freeK11Space += svcGetCFWInfo_size; *freeK11Space += svcGetCFWInfo_size;
} }
void patchTitleInstallMinVersionCheck(u8 *pos, u32 size) void patchTitleInstallMinVersionCheck(u8 *pos, u32 size)
{ {
const u8 pattern[] = {0x0A, 0x81, 0x42, 0x02}; const u8 pattern[] = {0x0A, 0x81, 0x42, 0x02};
u8 *off = memsearch(pos, pattern, size, 4); u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
if(off != NULL) off[4] = 0xE0; if(off != NULL) off[4] = 0xE0;
} }
@@ -227,7 +189,7 @@ void applyLegacyFirmPatches(u8 *pos, FirmwareType firmType)
/* Calculate the amount of patches to apply. Only count the boot screen patch for AGB_FIRM /* 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) */ if the matching option was enabled (keep it as last) */
u32 numPatches = firmType == TWL_FIRM ? (sizeof(twlPatches) / sizeof(patchData)) : u32 numPatches = firmType == TWL_FIRM ? (sizeof(twlPatches) / sizeof(patchData)) :
(sizeof(agbPatches) / sizeof(patchData) - !CONFIG(5)); (sizeof(agbPatches) / sizeof(patchData) - !CONFIG_SHOWGBABOOT);
const patchData *patches = firmType == TWL_FIRM ? twlPatches : agbPatches; const patchData *patches = firmType == TWL_FIRM ? twlPatches : agbPatches;
//Patch //Patch
@@ -245,22 +207,4 @@ void applyLegacyFirmPatches(u8 *pos, FirmwareType firmType)
break; break;
} }
} }
}
void patchTwlBg(u8 *pos)
{
u8 *dst = pos + ((isN3DS) ? 0xFEA4 : 0xFCA0);
u16 *src1 = (u16 *)(pos + ((isN3DS) ? 0xE38 : 0xE3C)), *src2 = (u16 *)(pos + ((isN3DS) ? 0xE54 : 0xE58));
memcpy(dst, twl_k11modules, twl_k11modules_size); //install k11 hook
u32 *off;
for(off = (u32 *)dst; *off != 0xABCDABCD; off++);
*off = (isN3DS) ? 0xCDE88 : 0xCD5F8; //dev SRL launcher offset
//Construct BLX instructions:
src1[0] = 0xF000 | ((((u32)dst - (u32)src1 - 4) & (0xFFF << 11)) >> 12);
src1[1] = 0xE800 | ((((u32)dst - (u32)src1 - 4) & 0xFFF) >> 1);
src2[0] = 0xF000 | ((((u32)dst - (u32)src2 - 4) & (0xFFF << 11)) >> 12);
src2[1] = 0xE800 | ((((u32)dst - (u32)src2 - 4) & 0xFFF) >> 1);
} }

View File

@@ -48,15 +48,14 @@ typedef struct __attribute__((packed))
} CFWInfo; } CFWInfo;
extern bool isN3DS; extern bool isN3DS;
extern u32 config;
u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr); u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr);
u32 *getKernel11Info(u8 *pos, u32 size, u8 **freeK11Space);
void patchSignatureChecks(u8 *pos, u32 size); void patchSignatureChecks(u8 *pos, u32 size);
void patchTitleInstallMinVersionCheck(u8 *pos, u32 size); void patchTitleInstallMinVersionCheck(u8 *pos, u32 size);
void patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr); void patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr);
void patchFirmWrites(u8 *pos, u32 size); void patchFirmWrites(u8 *pos, u32 size);
void patchFirmWriteSafe(u8 *pos, u32 size); void patchOldFirmWrites(u8 *pos, u32 size);
void reimplementSvcBackdoor(u8 *pos, u32 size); void reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u8 **freeK11Space);
void implementSvcGetCFWInfo(u8 *pos, u32 size); void implementSvcGetCFWInfo(u8 *pos, u32 *arm11SvcTable, u8 **freeK11Space);
void applyLegacyFirmPatches(u8 *pos, FirmwareType firmType); void applyLegacyFirmPatches(u8 *pos, FirmwareType firmType);
void patchTwlBg(u8 *pos);

View File

@@ -26,6 +26,7 @@
*/ */
#include "draw.h" #include "draw.h"
#include "config.h"
#include "screen.h" #include "screen.h"
#include "utils.h" #include "utils.h"
#include "memory.h" #include "memory.h"
@@ -34,96 +35,107 @@
#include "pin.h" #include "pin.h"
#include "crypto.h" #include "crypto.h"
bool readPin(PINData *out) static char pinKeyToLetter(u32 pressed)
{ {
u8 __attribute__((aligned(4))) zeroes[16] = {0}; const char keys[] = "AB--RLUD--XY";
u8 __attribute__((aligned(4))) tmp[32] = {0};
if(fileRead(out, "/luma/pin.bin") != sizeof(PINData)) return false;
if(memcmp(out->magic, "PINF", 4) != 0) return false;
computePINHash(tmp, zeroes, 1);
return memcmp(out->testHash, tmp, 32) == 0; //test vector verification (SD card has (or hasn't) been used on another console)
}
static inline char PINKeyToLetter(u32 pressed)
{
const char keys[] = "AB--------XY";
u32 i; u32 i;
__asm__ volatile("clz %[i], %[pressed]" : [i] "=r" (i) : [pressed] "r" (pressed)); for(i = 31; pressed > 1; i--) pressed /= 2;
return keys[31 - i]; return keys[31 - i];
} }
void newPin(void) void newPin(bool allowSkipping)
{ {
clearScreens(); clearScreens();
drawString("Enter your NEW PIN: ", 10, 10, COLOR_WHITE); char *title = allowSkipping ? "Press START to skip or enter a new PIN" : "Enter a new PIN to proceed";
drawString(title, 10, 10, COLOR_TITLE);
drawString("PIN: ", 10, 10 + 2 * SPACING_Y, COLOR_WHITE);
// Set the default value as 0x00 so we can check if there are any unentered characters. //Pad to AES block length with zeroes
u8 __attribute__((aligned(4))) enteredPassword[16 * ((PIN_LENGTH + 15) / 16)] = {0}; // pad to AES block length u8 __attribute__((aligned(4))) enteredPassword[0x10] = {0};
u32 cnt = 0; u8 length = 4 + 2 * (CONFIG_PIN - 1),
int charDrawPos = 20 * SPACING_X; cnt = 0;
int charDrawPos = 5 * SPACING_X;
while(cnt < PIN_LENGTH) while(cnt < length)
{ {
u32 pressed; u32 pressed;
do do
{ {
pressed = waitInput(); pressed = waitInput();
} }
while(!(pressed & PIN_BUTTONS & ~BUTTON_START)); while(!(pressed & PIN_BUTTONS));
pressed &= PIN_BUTTONS & ~BUTTON_START; pressed &= PIN_BUTTONS;
if(!allowSkipping) pressed &= ~BUTTON_START;
if(pressed & BUTTON_START) return;
if(!pressed) continue; if(!pressed) continue;
char key = PINKeyToLetter(pressed);
enteredPassword[cnt++] = (u8)key; // add character to password.
// visualize character on screen. char key = pinKeyToLetter(pressed);
drawCharacter(key, 10 + charDrawPos, 10, COLOR_WHITE); enteredPassword[cnt++] = (u8)key; //Add character to password
//Visualize character on screen
drawCharacter(key, 10 + charDrawPos, 10 + 2 * SPACING_Y, COLOR_WHITE);
charDrawPos += 2 * SPACING_X; charDrawPos += 2 * SPACING_X;
} }
PINData pin = {0}; PINData pin;
u8 __attribute__((aligned(4))) tmp[32] = {0};
u8 __attribute__((aligned(4))) zeroes[16] = {0};
memcpy(pin.magic, "PINF", 4); memcpy(pin.magic, "PINF", 4);
pin.formatVersionMajor = 1; pin.formatVersionMajor = PIN_VERSIONMAJOR;
pin.formatVersionMinor = 0; pin.formatVersionMinor = PIN_VERSIONMINOR;
pin.length = length;
computePINHash(tmp, zeroes, 1); u8 __attribute__((aligned(4))) tmp[0x20];
memcpy(pin.testHash, tmp, 32); u8 __attribute__((aligned(4))) zeroes[0x10] = {0};
computePINHash(tmp, enteredPassword, (PIN_LENGTH + 15) / 16); computePinHash(tmp, zeroes);
memcpy(pin.hash, tmp, 32); memcpy(pin.testHash, tmp, sizeof(tmp));
fileWrite(&pin, "/luma/pin.bin", sizeof(PINData)); computePinHash(tmp, enteredPassword);
memcpy(pin.hash, tmp, sizeof(tmp));
while(HID_PAD & PIN_BUTTONS);
if(!fileWrite(&pin, PIN_PATH, sizeof(PINData)))
error("Error writing the PIN file");
} }
void verifyPin(PINData *in) bool verifyPin(void)
{ {
initScreens(); initScreens();
drawString("Press START to shutdown or enter pin to proceed.", 10, 10, COLOR_WHITE); PINData pin;
drawString("Pin: ", 10, 10 + 2 * SPACING_Y, COLOR_WHITE);
// Set the default characters as 0x00 so we can check if there are any unentered characters. if(fileRead(&pin, PIN_PATH) != sizeof(PINData) ||
u8 __attribute__((aligned(4))) enteredPassword[16 * ((PIN_LENGTH + 15) / 16)] = {0}; memcmp(pin.magic, "PINF", 4) != 0 ||
pin.formatVersionMajor != PIN_VERSIONMAJOR ||
pin.formatVersionMinor != PIN_VERSIONMINOR ||
pin.length != 4 + 2 * (CONFIG_PIN - 1))
return false;
u32 cnt = 0; u8 __attribute__((aligned(4))) zeroes[0x10] = {0};
u8 __attribute__((aligned(4))) tmp[0x20];
computePinHash(tmp, zeroes);
//Test vector verification (SD card has, or hasn't been used on another console)
if(memcmp(pin.testHash, tmp, sizeof(tmp)) != 0) return false;
//Pad to AES block length with zeroes
u8 __attribute__((aligned(4))) enteredPassword[0x10] = {0};
u8 cnt = 0;
bool unlock = false; bool unlock = false;
int charDrawPos = 5 * SPACING_X; int charDrawPos = 5 * SPACING_X;
while(!unlock) while(!unlock)
{ {
drawString("Press START to shutdown or enter PIN to proceed", 10, 10, COLOR_TITLE);
drawString("PIN: ", 10, 10 + 2 * SPACING_Y, COLOR_WHITE);
u32 pressed; u32 pressed;
do do
{ {
@@ -133,22 +145,21 @@ void verifyPin(PINData *in)
if(pressed & BUTTON_START) mcuPowerOff(); if(pressed & BUTTON_START) mcuPowerOff();
pressed &= PIN_BUTTONS & ~BUTTON_START; pressed &= PIN_BUTTONS;
if(!pressed) continue; if(!pressed) continue;
char key = PINKeyToLetter(pressed); char key = pinKeyToLetter(pressed);
enteredPassword[cnt++] = (u8)key; // add character to password. enteredPassword[cnt++] = (u8)key; //Add character to password
// visualize character on screen. //Visualize character on screen
drawCharacter(key, 10 + charDrawPos, 10 + 2 * SPACING_Y, COLOR_WHITE); drawCharacter(key, 10 + charDrawPos, 10 + 2 * SPACING_Y, COLOR_WHITE);
charDrawPos += 2 * SPACING_X; charDrawPos += 2 * SPACING_X;
if(cnt >= PIN_LENGTH) if(cnt >= pin.length)
{ {
u8 __attribute__((aligned(4))) tmp[32] = {0}; computePinHash(tmp, enteredPassword);
unlock = memcmp(pin.hash, tmp, sizeof(tmp)) == 0;
computePINHash(tmp, enteredPassword, (PIN_LENGTH + 15) / 16);
unlock = memcmp(in->hash, tmp, 32) == 0;
if(!unlock) if(!unlock)
{ {
@@ -157,10 +168,10 @@ void verifyPin(PINData *in)
clearScreens(); clearScreens();
drawString("Press START to shutdown or enter pin to proceed.", 10, 10, COLOR_WHITE); drawString("Wrong PIN, try again", 10, 10 + 4 * SPACING_Y, COLOR_RED);
drawString("Pin: ", 10, 10 + 2 * SPACING_Y, COLOR_WHITE);
drawString("Wrong pin! Try again!", 10, 10 + 3 * SPACING_Y, COLOR_RED);
} }
} }
} }
return true;
} }

View File

@@ -30,17 +30,19 @@
#include "types.h" #include "types.h"
#define PIN_LENGTH 4 #define PIN_PATH "/luma/pin.bin"
#define PIN_VERSIONMAJOR 1
#define PIN_VERSIONMINOR 2
typedef struct __attribute__((packed)) typedef struct __attribute__((packed))
{ {
char magic[4]; char magic[4];
u16 formatVersionMajor, formatVersionMinor; u16 formatVersionMajor, formatVersionMinor;
u8 length;
u8 testHash[32]; u8 testHash[32];
u8 hash[32]; u8 hash[32];
} PINData; } PINData;
bool readPin(PINData* out); void newPin(bool allowSkipping);
void newPin(void); bool verifyPin(void);
void verifyPin(PINData *in);

View File

@@ -147,7 +147,7 @@ void initScreens(void)
//Disable interrupts //Disable interrupts
__asm(".word 0xF10C01C0"); __asm(".word 0xF10C01C0");
u32 brightnessLevel = brightness[MULTICONFIG(0)]; u32 brightnessLevel = brightness[CONFIG_BRIGHTNESS];
*(vu32 *)0x10141200 = 0x1007F; *(vu32 *)0x10141200 = 0x1007F;
*(vu32 *)0x10202014 = 0x00000001; *(vu32 *)0x10202014 = 0x00000001;
@@ -242,7 +242,7 @@ void initScreens(void)
if(PDN_GPU_CNT == 1) if(PDN_GPU_CNT == 1)
{ {
flushDCacheRange(&config, 4); flushDCacheRange(&configData, sizeof(cfgData));
flushDCacheRange((void *)fb, sizeof(struct fb)); flushDCacheRange((void *)fb, sizeof(struct fb));
invokeArm11Function(ARM11); invokeArm11Function(ARM11);
@@ -254,6 +254,6 @@ void initScreens(void)
else else
{ {
clearScreens(); clearScreens();
updateBrightness(MULTICONFIG(0)); updateBrightness(CONFIG_BRIGHTNESS);
} }
} }

View File

@@ -29,8 +29,10 @@
#include "types.h" #include "types.h"
#define ARM11_STUB_ADDRESS (0x25000000 - 0x30) //It's currently only 0x28 bytes large. We're putting 0x30 just to be sure here #define PDN_GPU_CNT (*(vu8 *)0x10141200)
#define WAIT_FOR_ARM9() *arm11Entry = 0; while(!*arm11Entry); ((void (*)())*arm11Entry)();
#define ARM11_STUB_ADDRESS (0x25000000 - 0x30) //It's currently only 0x28 bytes large. We're putting 0x30 just to be sure here
#define WAIT_FOR_ARM9() *arm11Entry = 0; while(!*arm11Entry); ((void (*)())*arm11Entry)();
static volatile struct fb { static volatile struct fb {
u8 *top_left; u8 *top_left;

View File

@@ -26,8 +26,8 @@
_start: _start:
b start b 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:

55
source/strings.c Normal file
View File

@@ -0,0 +1,55 @@
/*
* 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 "strings.h"
#include "memory.h"
int strlen(const char *string)
{
char *stringEnd = (char *)string;
while(*stringEnd) stringEnd++;
return stringEnd - string;
}
void concatenateStrings(char *destination, const char *source)
{
int i = strlen(source),
j = strlen(destination);
memcpy(&destination[j], source, i + 1);
}
void hexItoa(u32 number, char *out)
{
const char hexDigits[] = "0123456789ABCDEF";
u32 i = 0;
while(number > 0)
{
out[7 - i++] = hexDigits[number & 0xF];
number >>= 4;
}
for(; i < 8; i++) out[7 - i] = '0';
}

29
source/strings.h Normal file
View File

@@ -0,0 +1,29 @@
/*
* 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.
*/
#pragma once
#include "types.h"
int strlen(const char *string);
void concatenateStrings(char *destination, const char *source);
void hexItoa(u32 number, char *out);

View File

@@ -26,13 +26,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdbool.h> #include <stdbool.h>
#define CFG_BOOTENV (*(vu32 *)0x10010000)
#define CFG_UNITINFO (*(vu8 *)0x10010010)
#define PDN_MPCORE_CFG (*(vu32 *)0x10140FFC)
#define PDN_SPI_CNT (*(vu32 *)0x101401C0)
#define PDN_GPU_CNT (*(vu8 *)0x10141200)
//Common data types //Common data types
typedef uint8_t u8; typedef uint8_t u8;
typedef uint16_t u16; typedef uint16_t u16;
@@ -43,7 +36,7 @@ typedef volatile u16 vu16;
typedef volatile u32 vu32; typedef volatile u32 vu32;
typedef volatile u64 vu64; typedef volatile u64 vu64;
//Used by multiple files: //Used by multiple files
typedef enum FirmwareSource typedef enum FirmwareSource
{ {
FIRMWARE_SYSNAND = 0, FIRMWARE_SYSNAND = 0,
@@ -56,5 +49,6 @@ typedef enum FirmwareType
NATIVE_FIRM = 0, NATIVE_FIRM = 0,
TWL_FIRM = 1, TWL_FIRM = 1,
AGB_FIRM = 2, AGB_FIRM = 2,
SAFE_FIRM = 3 SAFE_FIRM = 3,
NATIVE_FIRM1X2X = 4
} FirmwareType; } FirmwareType;

View File

@@ -27,8 +27,6 @@
#include "draw.h" #include "draw.h"
#include "cache.h" #include "cache.h"
extern bool isFirmlaunch;
u32 waitInput(void) u32 waitInput(void)
{ {
u32 pressedKey = 0, u32 pressedKey = 0,

View File

@@ -28,6 +28,8 @@
#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 mcuReboot(void);
void mcuPowerOff(void); void mcuPowerOff(void);