Compare commits
63 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc151f7595 | ||
|
|
2b4e97bec5 | ||
|
|
97ae8d2d44 | ||
|
|
201fe05a06 | ||
|
|
5cd1a207c9 | ||
|
|
7612e38d41 | ||
|
|
bd992fca15 | ||
|
|
a2bca96d47 | ||
|
|
fc3a69d9ca | ||
|
|
eba56d0f64 | ||
|
|
c93a97f8d7 | ||
|
|
9bc62ec12d | ||
|
|
263a5eda56 | ||
|
|
7fb17ce52f | ||
|
|
631fad24c0 | ||
|
|
c0f042bc2a | ||
|
|
8982ff259a | ||
|
|
0057ab9c5a | ||
|
|
a84f393bd5 | ||
|
|
5406d648bc | ||
|
|
a2003fba95 | ||
|
|
a76c15d018 | ||
|
|
f221915a95 | ||
|
|
7f93733107 | ||
|
|
06fe06f9f1 | ||
|
|
abf7c8e565 | ||
|
|
46227e6763 | ||
|
|
2ffe4a5451 | ||
|
|
18bd4bbcf6 | ||
|
|
8209433696 | ||
|
|
ad9e00acaa | ||
|
|
c96f96258f | ||
|
|
82699f3e00 | ||
|
|
e2d828a4a2 | ||
|
|
92328c6a7e | ||
|
|
3f8ad17e86 | ||
|
|
52999db43a | ||
|
|
760aa99709 | ||
|
|
51c514de84 | ||
|
|
317899b4bf | ||
|
|
4c93d2b1f9 | ||
|
|
71c5404bfe | ||
|
|
9c5248b87a | ||
|
|
83a0293af4 | ||
|
|
a5c6b908b6 | ||
|
|
94f0d873df | ||
|
|
d6f66d24fa | ||
|
|
31458e9938 | ||
|
|
c711ed6253 | ||
|
|
356268eae5 | ||
|
|
2dd64b8a92 | ||
|
|
b5cddedb7d | ||
|
|
7afdc2b3b5 | ||
|
|
60c4956290 | ||
|
|
7331a919e4 | ||
|
|
33238cee54 | ||
|
|
384dd2ad81 | ||
|
|
74ac76ba84 | ||
|
|
253e031f83 | ||
|
|
ef1ecf859c | ||
|
|
a302ad3bea | ||
|
|
b87dadbb72 | ||
|
|
50a2424001 |
14
Makefile
14
Makefile
@@ -13,6 +13,7 @@ OC := arm-none-eabi-objcopy
|
||||
|
||||
name := Luma3DS
|
||||
revision := $(shell git describe --tags --match v[0-9]* --abbrev=8 | sed 's/-[0-9]*-g/-/i')
|
||||
commit := $(shell git rev-parse --short=8 HEAD)
|
||||
|
||||
dir_source := source
|
||||
dir_patches := patches
|
||||
@@ -32,7 +33,8 @@ objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
||||
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
||||
$(call rwildcard, $(dir_source), *.s *.c)))
|
||||
|
||||
bundled = $(dir_build)/rebootpatch.h $(dir_build)/emunandpatch.h $(dir_build)/injector.h $(dir_build)/loader.h
|
||||
bundled = $(dir_build)/rebootpatch.h $(dir_build)/emunandpatch.h $(dir_build)/svcGetCFWInfopatch.h $(dir_build)/injector.h \
|
||||
$(dir_build)/loader.h
|
||||
|
||||
.PHONY: all
|
||||
all: launcher a9lh ninjhax
|
||||
@@ -55,7 +57,7 @@ clean:
|
||||
@$(MAKE) $(FLAGS) -C $(dir_ninjhax) clean
|
||||
@$(MAKE) -C $(dir_loader) clean
|
||||
@$(MAKE) -C $(dir_injector) clean
|
||||
@rm -rf $(dir_out) $(dir_build)
|
||||
@rm -rf $(dir_out) $(dir_build) exceptions
|
||||
|
||||
$(dir_out):
|
||||
@mkdir -p "$(dir_out)"
|
||||
@@ -91,6 +93,11 @@ $(dir_build)/rebootpatch.h: $(dir_patches)/reboot.s
|
||||
@armips $<
|
||||
@bin2c -o $@ -n reboot $(@D)/reboot.bin
|
||||
|
||||
$(dir_build)/svcGetCFWInfopatch.h: $(dir_patches)/svcGetCFWInfo.s
|
||||
@mkdir -p "$(@D)"
|
||||
@armips $<
|
||||
@bin2c -o $@ -n svcGetCFWInfo $(@D)/svcGetCFWInfo.bin
|
||||
|
||||
$(dir_build)/injector.h: $(dir_injector)/Makefile
|
||||
@mkdir -p "$(@D)"
|
||||
@$(MAKE) -C $(dir_injector)
|
||||
@@ -100,8 +107,9 @@ $(dir_build)/loader.h: $(dir_loader)/Makefile
|
||||
@$(MAKE) -C $(dir_loader)
|
||||
@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)/patches.o: CFLAGS += -DREVISION=\"$(revision)\" -DCOMMIT_HASH="0x$(commit)"
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.c $(bundled)
|
||||
@mkdir -p "$(@D)"
|
||||
|
||||
@@ -22,13 +22,13 @@ LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
INCLUDE := $(foreach dir,$(LIBDIRS),-I$(dir)/include)
|
||||
|
||||
ARCH := -mcpu=mpcore -mfloat-abi=hard -mtp=soft
|
||||
CFLAGS := -Wall -Wextra -MMD -MP -marm $(ARCH) -fno-builtin -std=c11 -O2 -flto -ffast-math -mword-relocations \
|
||||
ASFLAGS := -mcpu=mpcore -mfloat-abi=hard -mtp=soft
|
||||
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
|
||||
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, \
|
||||
$(call rwildcard, $(dir_source), *.c))
|
||||
$(call rwildcard, $(dir_source), *.s *.c))
|
||||
|
||||
.PHONY: all
|
||||
all: ../$(dir_build)/$(name).cxi
|
||||
@@ -48,4 +48,8 @@ $(dir_build)/memory.o : CFLAGS += -O3
|
||||
$(dir_build)/%.o: $(dir_source)/%.c
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.s
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.s) $(OUTPUT_OPTION) $<
|
||||
include $(call rwildcard, $(dir_build), *.d)
|
||||
|
||||
19
injector/source/CFWInfo.h
Normal file
19
injector/source/CFWInfo.h
Normal 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);
|
||||
9
injector/source/CFWInfo.s
Normal file
9
injector/source/CFWInfo.s
Normal file
@@ -0,0 +1,9 @@
|
||||
.text
|
||||
.arm
|
||||
.align 4
|
||||
|
||||
.global svcGetCFWInfo
|
||||
.type svcGetCFWInfo, %function
|
||||
svcGetCFWInfo:
|
||||
svc 0x2e
|
||||
bx lr
|
||||
@@ -2,13 +2,9 @@
|
||||
#include "memory.h"
|
||||
#include "patcher.h"
|
||||
#include "ifile.h"
|
||||
#include "CFWInfo.h"
|
||||
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX 255
|
||||
#define CONFIG(a) (((loadConfig() >> (a + 16)) & 1) != 0)
|
||||
#define MULTICONFIG(a) ((loadConfig() >> (a * 2 + 6)) & 3)
|
||||
#define BOOTCONFIG(a, b) ((loadConfig() >> a) & b)
|
||||
#endif
|
||||
static CFWInfo info;
|
||||
|
||||
static int memcmp(const void *buf1, const void *buf2, u32 size)
|
||||
{
|
||||
@@ -90,6 +86,22 @@ static int fileOpen(IFile *file, FS_ArchiveID archiveId, const char *path, int f
|
||||
return IFile_Open(file, archiveId, archivePath, filePath, flags);
|
||||
}
|
||||
|
||||
static void loadCFWInfo(void)
|
||||
{
|
||||
static bool infoLoaded = false;
|
||||
|
||||
if(!infoLoaded)
|
||||
{
|
||||
svcGetCFWInfo(&info);
|
||||
|
||||
IFile file;
|
||||
if(BOOTCFG_SAFEMODE != 0 && R_SUCCEEDED(fileOpen(&file, ARCHIVE_SDMC, "/", FS_OPEN_READ))) //Init SD card if SAFE_MODE is being booted
|
||||
IFile_Close(&file);
|
||||
|
||||
infoLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool secureInfoExists(void)
|
||||
{
|
||||
static bool exists = false;
|
||||
@@ -107,24 +119,6 @@ static bool secureInfoExists(void)
|
||||
return exists;
|
||||
}
|
||||
|
||||
static u32 loadConfig(void)
|
||||
{
|
||||
static u32 config = 0;
|
||||
|
||||
if(!config)
|
||||
{
|
||||
IFile file;
|
||||
if(R_SUCCEEDED(fileOpen(&file, ARCHIVE_SDMC, "/luma/config.bin", FS_OPEN_READ)))
|
||||
{
|
||||
u64 total;
|
||||
if(R_SUCCEEDED(IFile_Read(&file, &total, &config, 4))) config |= 1 << 4;
|
||||
IFile_Close(&file);
|
||||
}
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
static void progIdToStr(char *strEnd, u64 progId)
|
||||
{
|
||||
while(progId)
|
||||
@@ -148,12 +142,12 @@ static void loadTitleCodeSection(u64 progId, u8 *code, u32 size)
|
||||
|
||||
if(R_SUCCEEDED(ret))
|
||||
{
|
||||
u64 fileSize, total;
|
||||
|
||||
u64 fileSize;
|
||||
ret = IFile_GetSize(&file, &fileSize);
|
||||
|
||||
if(R_SUCCEEDED(ret) && fileSize <= size)
|
||||
{
|
||||
u64 total;
|
||||
ret = IFile_Read(&file, &total, code, fileSize);
|
||||
IFile_Close(&file);
|
||||
}
|
||||
@@ -319,6 +313,8 @@ static void patchCfgGetRegion(u8 *code, u32 size, u8 regionId, u32 CFGUHandleOff
|
||||
|
||||
void patchCode(u64 progId, u8 *code, u32 size)
|
||||
{
|
||||
loadCFWInfo();
|
||||
|
||||
switch(progId)
|
||||
{
|
||||
case 0x0004003000008F02LL: // USA Menu
|
||||
@@ -329,7 +325,7 @@ void patchCode(u64 progId, u8 *code, u32 size)
|
||||
case 0x000400300000B102LL: // TWN Menu
|
||||
{
|
||||
static const u8 regionFreePattern[] = {
|
||||
0x00, 0x00, 0x55, 0xE3, 0x01, 0x10, 0xA0, 0xE3
|
||||
0x00, 0x00, 0x55, 0xE3, 0x01, 0x10, 0xA0
|
||||
};
|
||||
static const u8 regionFreePatch[] = {
|
||||
0x01, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1
|
||||
@@ -364,7 +360,7 @@ void patchCode(u64 progId, u8 *code, u32 size)
|
||||
);
|
||||
|
||||
//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[] = {
|
||||
0x30, 0xB5, 0xF1, 0xB0
|
||||
@@ -388,7 +384,7 @@ void patchCode(u64 progId, u8 *code, u32 size)
|
||||
case 0x0004013000003202LL: // FRIENDS
|
||||
{
|
||||
static const u8 fpdVerPattern[] = {
|
||||
0xE0, 0x1E, 0xFF, 0x2F, 0xE1, 0x01, 0x01, 0x01
|
||||
0xE0, 0x1E, 0xFF, 0x2F, 0xE1, 0x01, 0x01
|
||||
};
|
||||
|
||||
static const u8 mostRecentFpdVer = 0x06;
|
||||
@@ -408,7 +404,7 @@ void patchCode(u64 progId, u8 *code, u32 size)
|
||||
case 0x0004001000027000LL: // KOR MSET
|
||||
case 0x0004001000028000LL: // TWN MSET
|
||||
{
|
||||
if(CONFIG(4))
|
||||
if(CONFIG_SHOWNAND)
|
||||
{
|
||||
static const u16 verPattern[] = u"Ver.";
|
||||
const u32 currentNand = BOOTCONFIG(0, 3);
|
||||
@@ -444,12 +440,12 @@ void patchCode(u64 progId, u8 *code, u32 size)
|
||||
sizeof(stopCartUpdatesPatch), 2
|
||||
);
|
||||
|
||||
u32 cpuSetting = MULTICONFIG(1);
|
||||
u32 cpuSetting = CONFIG_NEWCPU;
|
||||
|
||||
if(cpuSetting)
|
||||
{
|
||||
static const u8 cfgN3dsCpuPattern[] = {
|
||||
0x00, 0x40, 0xA0, 0xE1, 0x07, 0x00
|
||||
0x00, 0x40, 0xA0, 0xE1, 0x07
|
||||
};
|
||||
|
||||
u32 *cfgN3dsCpuLoc = (u32 *)memsearch(code, cfgN3dsCpuPattern, size, sizeof(cfgN3dsCpuPattern));
|
||||
@@ -468,7 +464,7 @@ void patchCode(u64 progId, u8 *code, u32 size)
|
||||
case 0x0004013000001702LL: // CFG
|
||||
{
|
||||
static const u8 secureinfoSigCheckPattern[] = {
|
||||
0x06, 0x46, 0x10, 0x48, 0xFC
|
||||
0x06, 0x46, 0x10, 0x48
|
||||
};
|
||||
static const u8 secureinfoSigCheckPatch[] = {
|
||||
0x00, 0x26
|
||||
@@ -543,7 +539,7 @@ void patchCode(u64 progId, u8 *code, u32 size)
|
||||
}
|
||||
|
||||
default:
|
||||
if(CONFIG(3))
|
||||
if(CONFIG_USELANGEMUANDCODE)
|
||||
{
|
||||
u32 tidHigh = (progId & 0xFFFFFFF000000000LL) >> 0x24;
|
||||
|
||||
|
||||
@@ -2,4 +2,18 @@
|
||||
|
||||
#include <3ds/types.h>
|
||||
|
||||
#define PATH_MAX 255
|
||||
|
||||
#define CONFIG(a) (((info.config >> (a + 20)) & 1) != 0)
|
||||
#define MULTICONFIG(a) ((info.config >> (a * 2 + 6)) & 3)
|
||||
#define BOOTCONFIG(a, b) ((info.config >> a) & b)
|
||||
|
||||
#define BOOTCFG_NAND BOOTCONFIG(0, 3)
|
||||
#define BOOTCFG_FIRM BOOTCONFIG(2, 1)
|
||||
#define BOOTCFG_SAFEMODE BOOTCONFIG(5, 1)
|
||||
#define CONFIG_NEWCPU MULTICONFIG(2)
|
||||
#define CONFIG_USESYSFIRM CONFIG(1)
|
||||
#define CONFIG_USELANGEMUANDCODE CONFIG(3)
|
||||
#define CONFIG_SHOWNAND CONFIG(4)
|
||||
|
||||
void patchCode(u64 progId, u8 *code, u32 size);
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "memory.h"
|
||||
#include "cache.h"
|
||||
|
||||
extern u32 payloadSize; //defined in start.s
|
||||
extern u32 payloadSize; //Defined in start.s
|
||||
|
||||
void main(void)
|
||||
{
|
||||
|
||||
@@ -43,4 +43,4 @@ nand_sd:
|
||||
sdmmc: .ascii "SDMC"
|
||||
nand_offset: .ascii "NAND" ; for rednand this should be 1
|
||||
ncsd_header_offset: .ascii "NCSD" ; depends on nand manufacturer + emunand type (GW/RED)
|
||||
.close
|
||||
.close
|
||||
@@ -5,10 +5,14 @@ payload_maxsize equ 0x10000 ; Maximum size for the payload (maximum that CakeB
|
||||
|
||||
.create "build/reboot.bin", 0
|
||||
.arm
|
||||
; Interesting registers and locations to keep in mind, set before this code is ran:
|
||||
; - sp + 0x3A8 - 0x70: FIRM path in exefs.
|
||||
; - r7 (which is sp + 0x3A8 - 0x198): Reserved space for file handle
|
||||
; - *(sp + 0x3A8 - 0x198) + 0x28: fread function.
|
||||
; Interesting registers and locations to keep in mind, set just before this code is ran:
|
||||
; - r1: FIRM path in exefs.
|
||||
; - r7: pointer to file object
|
||||
; - *r7: vtable
|
||||
; - *(vtable + 0x28): fread function
|
||||
; - *(r7 + 8): file handle
|
||||
|
||||
mov r8, r1
|
||||
|
||||
pxi_wait_recv:
|
||||
ldr r2, =0x44846
|
||||
@@ -47,7 +51,7 @@ payload_maxsize equ 0x10000 ; Maximum size for the payload (maximum that CakeB
|
||||
cmp r4, #0
|
||||
movne r3, #0x12000 ; Skip the first 0x12000 bytes.
|
||||
moveq r3, payload_maxsize
|
||||
ldr r6, [sp, #0x3A8-0x198]
|
||||
ldr r6, [r7]
|
||||
ldr r6, [r6, #0x28]
|
||||
blx r6
|
||||
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.
|
||||
|
||||
; 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, 0x1A
|
||||
add r0, r8, 0x1A
|
||||
add r1, r0, #0x10
|
||||
ldr r2, =payload_addr + 4
|
||||
copy_TID_low:
|
||||
@@ -75,7 +78,7 @@ payload_maxsize equ 0x10000 ; Maximum size for the payload (maximum that CakeB
|
||||
goto_reboot:
|
||||
; Jump to reboot code
|
||||
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
|
||||
|
||||
die:
|
||||
@@ -122,4 +125,4 @@ dat_fname: .dcw "sdmc:/Luma3DS.dat"
|
||||
bx r0
|
||||
|
||||
.pool
|
||||
.close
|
||||
.close
|
||||
47
patches/svcGetCFWInfo.s
Normal file
47
patches/svcGetCFWInfo.s
Normal file
@@ -0,0 +1,47 @@
|
||||
;
|
||||
; 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/svcGetCFWInfo.bin", 0
|
||||
|
||||
.arm
|
||||
|
||||
adr r1, infoStart
|
||||
add r2, r0, #(infoEnd - infoStart)
|
||||
|
||||
loop:
|
||||
ldrb r3, [r1], #1
|
||||
strbt r3, [r0], #1
|
||||
cmp r0, r2
|
||||
blo loop
|
||||
|
||||
bx lr
|
||||
|
||||
.pool
|
||||
infoStart:
|
||||
.ascii "LUMA" ; magic
|
||||
.word 0 ; version
|
||||
.word 0 ; truncated commit hash
|
||||
.word 0 ; config
|
||||
infoEnd:
|
||||
.close
|
||||
@@ -43,4 +43,4 @@
|
||||
#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 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)
|
||||
@@ -21,13 +21,50 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "memory.h"
|
||||
#include "fs.h"
|
||||
#include "utils.h"
|
||||
#include "screen.h"
|
||||
#include "draw.h"
|
||||
#include "fs.h"
|
||||
#include "buttons.h"
|
||||
#include "pin.h"
|
||||
|
||||
void configureCFW(const char *configPath)
|
||||
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();
|
||||
|
||||
@@ -35,6 +72,7 @@ void configureCFW(const char *configPath)
|
||||
drawString("Press A to select, START to save", 10, 30, COLOR_WHITE);
|
||||
|
||||
const char *multiOptionsText[] = { "Screen brightness: 4( ) 3( ) 2( ) 1( )",
|
||||
"PIN lock: Off( ) 4( ) 6( ) 8( ) digits",
|
||||
"New 3DS CPU: Off( ) Clock( ) L2( ) Clock+L2( )" };
|
||||
|
||||
const char *singleOptionsText[] = { "( ) Autoboot SysNAND",
|
||||
@@ -43,8 +81,7 @@ void configureCFW(const char *configPath)
|
||||
"( ) Enable region/language emu. and ext. .code",
|
||||
"( ) Show current NAND in System Settings",
|
||||
"( ) Show GBA boot screen in patched AGB_FIRM",
|
||||
"( ) Display splash screen before payloads",
|
||||
"( ) Use a PIN" };
|
||||
"( ) Display splash screen before payloads" };
|
||||
|
||||
struct multiOption {
|
||||
int posXs[4];
|
||||
@@ -52,6 +89,7 @@ void configureCFW(const char *configPath)
|
||||
u32 enabled;
|
||||
} multiOptions[] = {
|
||||
{ .posXs = {21, 26, 31, 36} },
|
||||
{ .posXs = {14, 19, 24, 29} },
|
||||
{ .posXs = {17, 26, 32, 44} }
|
||||
};
|
||||
|
||||
@@ -184,22 +222,22 @@ void configureCFW(const char *configPath)
|
||||
}
|
||||
}
|
||||
|
||||
u32 oldPinLength = CONFIG_PIN;
|
||||
|
||||
//Preserve the last-used boot options (last 12 bits)
|
||||
config &= 0x3F;
|
||||
configData.config &= 0x3F;
|
||||
|
||||
//Parse and write the new configuration
|
||||
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++)
|
||||
config |= (singleOptions[i].enabled ? 1 : 0) << (i + 16);
|
||||
configData.config |= (singleOptions[i].enabled ? 1 : 0) << (i + 20);
|
||||
|
||||
if(!fileWrite(&config, configPath, 4))
|
||||
{
|
||||
createDirectory("luma");
|
||||
if(!fileWrite(&config, configPath, 4))
|
||||
error("Error writing the configuration file");
|
||||
}
|
||||
if(CONFIG_PIN != 0) newPin(oldPinStatus && CONFIG_PIN == oldPinLength);
|
||||
else if(oldPinStatus) fileDelete(PIN_PATH);
|
||||
|
||||
//Wait for the pressed buttons to change
|
||||
while(HID_PAD == BUTTON_START);
|
||||
while(HID_PAD & PIN_BUTTONS);
|
||||
|
||||
chrono(2);
|
||||
}
|
||||
@@ -24,10 +24,44 @@
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define CONFIG(a) (((config >> (a + 16)) & 1) != 0)
|
||||
#define MULTICONFIG(a) ((config >> (a * 2 + 6)) & 3)
|
||||
#define BOOTCONFIG(a, b) ((config >> a) & b)
|
||||
#define CONFIG(a) (((configData.config >> (a + 20)) & 1) != 0)
|
||||
#define MULTICONFIG(a) ((configData.config >> (a * 2 + 6)) & 3)
|
||||
#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(const char *configPath);
|
||||
#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);
|
||||
@@ -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))) cipherText[0x10];
|
||||
|
||||
sdmmc_get_cid(1, (u32 *)cid);
|
||||
|
||||
aes_use_keyslot(4); // console-unique keyslot which keys are set by the Arm9 bootROM
|
||||
aes(cipherText, in, blockCount, cid, AES_CBC_ENCRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
|
||||
aes_use_keyslot(4); //Console-unique keyslot whose keys are set by the ARM9 bootROM
|
||||
aes(cipherText, in, 1, cid, AES_CBC_ENCRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
sha(out, cipherText, 0x10, SHA_256_MODE);
|
||||
}
|
||||
@@ -100,8 +100,7 @@
|
||||
#define SHA_1_HASH_SIZE (160 / 8)
|
||||
|
||||
extern u32 emuOffset;
|
||||
extern bool isN3DS;
|
||||
extern bool isDevUnit;
|
||||
extern bool isN3DS, isDevUnit;
|
||||
extern FirmwareSource firmSource;
|
||||
|
||||
void ctrNandInit(void);
|
||||
@@ -109,5 +108,4 @@ u32 ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf);
|
||||
void setRSAMod0DerivedKeys(void);
|
||||
void decryptExeFs(u8 *inbuf);
|
||||
void arm9Loader(u8 *arm9Section);
|
||||
|
||||
void computePINHash(u8 out[32], u8 *in, u32 blockCount);
|
||||
void computePinHash(u8 *out, u8 *in);
|
||||
@@ -26,24 +26,16 @@
|
||||
*/
|
||||
|
||||
#include "draw.h"
|
||||
#include "strings.h"
|
||||
#include "screen.h"
|
||||
#include "utils.h"
|
||||
#include "fs.h"
|
||||
#include "font.h"
|
||||
|
||||
static inline int strlen(const char *string)
|
||||
{
|
||||
char *stringEnd = (char *)string;
|
||||
|
||||
while(*stringEnd) stringEnd++;
|
||||
|
||||
return stringEnd - string;
|
||||
}
|
||||
|
||||
bool loadSplash(void)
|
||||
{
|
||||
//Don't delay boot nor init the screens if no splash image is on the SD
|
||||
if(getFileSize("/luma/splash.bin") + getFileSize("/luma/splash.bin") == 0)
|
||||
if(getFileSize("/luma/splash.bin") + getFileSize("/luma/splashbottom.bin") == 0)
|
||||
return false;
|
||||
|
||||
initScreens();
|
||||
|
||||
@@ -25,12 +25,12 @@
|
||||
#include "fatfs/sdmmc/sdmmc.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];
|
||||
|
||||
const u32 nandSize = getMMCDevice(0)->total_size;
|
||||
u32 nandOffset = *emuNAND == FIRMWARE_EMUNAND ? 0 :
|
||||
u32 nandOffset = *emuNand == FIRMWARE_EMUNAND ? 0 :
|
||||
(nandSize > 0x200000 ? 0x400000 : 0x200000);
|
||||
|
||||
//Check for RedNAND
|
||||
@@ -53,37 +53,37 @@ void locateEmuNAND(u32 *off, u32 *head, FirmwareSource *emuNAND)
|
||||
or to SysNAND if there isn't any */
|
||||
else
|
||||
{
|
||||
*emuNAND = (*emuNAND == FIRMWARE_EMUNAND2) ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND;
|
||||
if(*emuNAND) locateEmuNAND(off, head, emuNAND);
|
||||
*emuNand = (*emuNand == FIRMWARE_EMUNAND2) ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND;
|
||||
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};
|
||||
|
||||
//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
|
||||
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);
|
||||
}
|
||||
|
||||
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};
|
||||
|
||||
//Look for read/write code
|
||||
const u8 pattern[] = {0x1E, 0x00, 0xC8, 0x05};
|
||||
|
||||
u16 *readOffset = (u16 *)memsearch(pos, pattern, size, 4) - 3,
|
||||
*writeOffset = (u16 *)memsearch((u8 *)(readOffset + 5), pattern, 0x100, 4) - 3;
|
||||
u16 *readOffset = (u16 *)memsearch(pos, pattern, size, sizeof(pattern)) - 3,
|
||||
*writeOffset = (u16 *)memsearch((u8 *)(readOffset + 5), pattern, 0x100, sizeof(pattern)) - 3;
|
||||
|
||||
*readOffset = nandRedir[0];
|
||||
readOffset[1] = nandRedir[1];
|
||||
@@ -93,40 +93,38 @@ static inline void patchNANDRW(u8 *pos, u32 size, u32 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
|
||||
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[6] = mpuPatch[1];
|
||||
off[9] = mpuPatch[2];
|
||||
off[0] = 0x00360003;
|
||||
off[6] = 0x00200603;
|
||||
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
|
||||
void *emuCodeOffset = getEmuCode(arm9Section, arm9SectionSize);
|
||||
memcpy(emuCodeOffset, emunand, emunand_size);
|
||||
void *freeK9Space = getFreeK9Space(arm9Section, arm9SectionSize);
|
||||
memcpy(freeK9Space, emunand, emunand_size);
|
||||
|
||||
//Add the data of the found emuNAND
|
||||
u32 *pos_offset = (u32 *)memsearch(emuCodeOffset, "NAND", emunand_size, 4),
|
||||
*pos_header = (u32 *)memsearch(emuCodeOffset, "NCSD", emunand_size, 4);
|
||||
*pos_offset = emuOffset;
|
||||
*pos_header = emuHeader;
|
||||
u32 *posOffset = (u32 *)memsearch(freeK9Space, "NAND", emunand_size, 4),
|
||||
*posHeader = (u32 *)memsearch(freeK9Space, "NCSD", emunand_size, 4);
|
||||
*posOffset = emuOffset;
|
||||
*posHeader = emuHeader;
|
||||
|
||||
//Find and add the SDMMC struct
|
||||
u32 *pos_sdmmc = (u32 *)memsearch(emuCodeOffset, "SDMC", emunand_size, 4);
|
||||
*pos_sdmmc = getSDMMC(process9Offset, process9Size);
|
||||
u32 *posSdmmc = (u32 *)memsearch(freeK9Space, "SDMC", emunand_size, 4);
|
||||
*posSdmmc = getSdmmc(process9Offset, process9Size);
|
||||
|
||||
//Add emuNAND hooks
|
||||
u32 branchOffset = (u32)emuCodeOffset - branchAdditive;
|
||||
patchNANDRW(process9Offset, process9Size, branchOffset);
|
||||
u32 branchOffset = (u32)freeK9Space - branchAdditive;
|
||||
patchNandRw(process9Offset, process9Size, branchOffset);
|
||||
|
||||
//Set MPU for emu code region
|
||||
patchMPU(arm9Section, arm9SectionSize);
|
||||
patchMpu(arm9Section, arm9SectionSize);
|
||||
}
|
||||
@@ -26,5 +26,7 @@
|
||||
|
||||
#define NCSD_MAGIC 0x4453434E
|
||||
|
||||
void locateEmuNAND(u32 *off, u32 *head, FirmwareSource *emuNAND);
|
||||
void patchEmuNAND(u8 *arm9Section, u32 arm9SectionSize, u8 *process9Offset, u32 process9Size, u32 emuOffset, u32 emuHeader, u32 branchAdditive);
|
||||
extern u32 emuOffset;
|
||||
|
||||
void locateEmuNand(u32 *off, u32 *head, FirmwareSource *emuNand);
|
||||
void patchEmuNand(u8 *arm9Section, u32 arm9SectionSize, u8 *process9Offset, u32 process9Size, u32 emuHeader, u32 branchAdditive);
|
||||
@@ -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;
|
||||
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.objsize += (DWORD)fs->csize * SS(fs); /* Increase object size by cluster size */
|
||||
res = fill_fat_chain(&dp->obj); /* Complement FAT chain if needed */
|
||||
@@ -2430,7 +2430,7 @@ void get_fileinfo ( /* No return code */
|
||||
}
|
||||
#endif
|
||||
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 */
|
||||
}
|
||||
@@ -3680,7 +3680,9 @@ FRESULT f_sync (
|
||||
FATFS *fs;
|
||||
DWORD tm;
|
||||
BYTE *dir;
|
||||
|
||||
#if _FS_EXFAT
|
||||
DEF_NAMBUF
|
||||
#endif
|
||||
|
||||
res = validate(fp, &fs); /* Check validity of the object */
|
||||
if (res == FR_OK) {
|
||||
@@ -3990,7 +3992,7 @@ FRESULT f_lseek (
|
||||
#if !_FS_TINY
|
||||
#if !_FS_READONLY
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
@@ -4007,6 +4009,9 @@ FRESULT f_lseek (
|
||||
|
||||
/* 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 */
|
||||
ofs = fp->obj.objsize;
|
||||
}
|
||||
@@ -4016,7 +4021,7 @@ FRESULT f_lseek (
|
||||
bcs = (DWORD)fs->csize * SS(fs); /* Cluster size (byte) */
|
||||
if (ifptr > 0 &&
|
||||
(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;
|
||||
clst = fp->clust;
|
||||
} else { /* When seek to back cluster, */
|
||||
@@ -4033,11 +4038,16 @@ FRESULT f_lseek (
|
||||
}
|
||||
if (clst != 0) {
|
||||
while (ofs > bcs) { /* Cluster following loop */
|
||||
ofs -= bcs; fp->fptr += bcs;
|
||||
#if !_FS_READONLY
|
||||
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 */
|
||||
if (clst == 0) { /* When disk gets full, clip file size */
|
||||
ofs = bcs; break;
|
||||
ofs = 0; break;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
@@ -4045,8 +4055,6 @@ FRESULT f_lseek (
|
||||
if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
|
||||
if (clst <= 1 || clst >= fs->n_fatent) ABORT(fs, FR_INT_ERR);
|
||||
fp->clust = clst;
|
||||
fp->fptr += bcs;
|
||||
ofs -= bcs;
|
||||
}
|
||||
fp->fptr += ofs;
|
||||
if (ofs % SS(fs)) {
|
||||
@@ -5298,7 +5306,7 @@ FRESULT f_mkfs (
|
||||
|
||||
#if _FS_EXFAT
|
||||
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;
|
||||
UINT j, st;
|
||||
BYTE b;
|
||||
@@ -5363,32 +5371,37 @@ FRESULT f_mkfs (
|
||||
tbl[1] = (szb_case + au * ss - 1) / (au * ss); /* Number of up-case clusters */
|
||||
|
||||
/* 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 */
|
||||
do { /* Fill allocation bitmap sectors */
|
||||
ns = (n > sz_buf) ? sz_buf : n;
|
||||
nb = tbl[0] + tbl[1] + tbl[2]; /* Number of clusters in-use by system */
|
||||
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;
|
||||
sect += ns;
|
||||
mem_set(buf, 0, ss);
|
||||
} while (n -= ns);
|
||||
sect += ns; n -= ns;
|
||||
} while (n);
|
||||
|
||||
/* 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 */
|
||||
do { /* Fill FAT sectors */
|
||||
ns = (n > sz_buf) ? sz_buf : n;
|
||||
j = nb = cl = 0;
|
||||
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;
|
||||
sect += ns;
|
||||
mem_set(buf, 0, ss);
|
||||
} while (n -= ns);
|
||||
sect += ns; n -= ns;
|
||||
} while (n);
|
||||
|
||||
/* Initialize the root directory */
|
||||
mem_set(buf, 0, ss);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "../../types.h"
|
||||
207
source/firm.c
207
source/firm.c
@@ -35,24 +35,25 @@
|
||||
#include "pin.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 const firmSectionHeader *section;
|
||||
|
||||
u32 config,
|
||||
emuOffset;
|
||||
u32 emuOffset;
|
||||
|
||||
bool isN3DS, isDevUnit;
|
||||
bool isN3DS,
|
||||
isDevUnit,
|
||||
isFirmlaunch;
|
||||
|
||||
cfgData configData;
|
||||
FirmwareSource firmSource;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
bool isFirmlaunch,
|
||||
isA9lh;
|
||||
bool isA9lh;
|
||||
|
||||
u32 newConfig,
|
||||
u32 configTemp,
|
||||
emuHeader;
|
||||
|
||||
FirmwareType firmType;
|
||||
@@ -68,40 +69,36 @@ void main(void)
|
||||
//Mount filesystems. CTRNAND will be mounted only if/when needed
|
||||
mountFs();
|
||||
|
||||
const char configPath[] = "/luma/config.bin";
|
||||
|
||||
//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
|
||||
if(launchedFirmTIDLow[5] != 0)
|
||||
if(launchedFirmTidLow[5] != 0)
|
||||
{
|
||||
if(needConfig == CREATE_CONFIGURATION) mcuReboot();
|
||||
|
||||
isFirmlaunch = true;
|
||||
|
||||
//'0' = NATIVE_FIRM, '1' = TWL_FIRM, '2' = AGB_FIRM
|
||||
firmType = launchedFirmTIDLow[7] == u'3' ? SAFE_FIRM : (FirmwareType)(launchedFirmTIDLow[5] - u'0');
|
||||
if(needConfig == CREATE_CONFIGURATION) mcuReboot();
|
||||
|
||||
nandType = (FirmwareSource)BOOTCONFIG(0, 3);
|
||||
firmSource = (FirmwareSource)BOOTCONFIG(2, 1);
|
||||
isA9lh = BOOTCONFIG(3, 1) != 0;
|
||||
//'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
|
||||
{
|
||||
//Get pressed buttons
|
||||
u32 pressed = HID_PAD;
|
||||
|
||||
isFirmlaunch = false;
|
||||
firmType = NATIVE_FIRM;
|
||||
|
||||
//Determine if booting with A9LH
|
||||
isA9lh = !PDN_SPI_CNT;
|
||||
|
||||
//Determine if the user chose to use the SysNAND FIRM as default for a R boot
|
||||
bool useSysAsDefault = isA9lh ? CONFIG(1) : false;
|
||||
//Get pressed buttons
|
||||
u32 pressed = HID_PAD;
|
||||
|
||||
newConfig = (u32)isA9lh << 3;
|
||||
//Save old options and begin saving the new boot configuration
|
||||
configTemp = (configData.config & 0xFFFFFFC0) | ((u32)isA9lh << 3);
|
||||
|
||||
//If it's a MCU reboot, try to force boot options
|
||||
if(isA9lh && CFG_BOOTENV)
|
||||
@@ -110,19 +107,19 @@ void main(void)
|
||||
if(CFG_BOOTENV == 7)
|
||||
{
|
||||
nandType = FIRMWARE_SYSNAND;
|
||||
firmSource = useSysAsDefault ? FIRMWARE_SYSNAND : (FirmwareSource)BOOTCONFIG(2, 1);
|
||||
firmSource = CONFIG_USESYSFIRM ? FIRMWARE_SYSNAND : (FirmwareSource)BOOTCFG_FIRM;
|
||||
needConfig = DONT_CONFIGURE;
|
||||
|
||||
//Flag to prevent multiple boot options-forcing
|
||||
newConfig |= 1 << 4;
|
||||
configTemp |= 1 << 4;
|
||||
}
|
||||
|
||||
/* Else, force the last used boot options unless a button is pressed
|
||||
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);
|
||||
firmSource = (FirmwareSource)BOOTCONFIG(2, 1);
|
||||
nandType = (FirmwareSource)BOOTCFG_NAND;
|
||||
firmSource = (FirmwareSource)BOOTCFG_FIRM;
|
||||
needConfig = DONT_CONFIGURE;
|
||||
}
|
||||
}
|
||||
@@ -130,23 +127,14 @@ void main(void)
|
||||
//Boot options aren't being forced
|
||||
if(needConfig != DONT_CONFIGURE)
|
||||
{
|
||||
PINData pin;
|
||||
|
||||
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);
|
||||
bool pinExists = CONFIG_PIN != 0 && verifyPin();
|
||||
|
||||
//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(configPath);
|
||||
|
||||
if(!pinExists && CONFIG(7)) newPin();
|
||||
|
||||
chrono(2);
|
||||
configMenu(pinExists);
|
||||
|
||||
//Update pressed buttons
|
||||
pressed = HID_PAD;
|
||||
@@ -156,37 +144,50 @@ void main(void)
|
||||
{
|
||||
nandType = FIRMWARE_SYSNAND;
|
||||
firmSource = FIRMWARE_SYSNAND;
|
||||
|
||||
//Flag to tell loader to init SD
|
||||
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
|
||||
{
|
||||
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,
|
||||
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));
|
||||
|
||||
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(pressed & BUTTON_R1)
|
||||
{
|
||||
nandType = (useSysAsDefault) ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND;
|
||||
firmSource = (useSysAsDefault) ? FIRMWARE_SYSNAND : FIRMWARE_EMUNAND;
|
||||
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(0) != !(pressed & BUTTON_L1)) ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND;
|
||||
nandType = (CONFIG_AUTOBOOTSYS != !(pressed & BUTTON_L1)) ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND;
|
||||
firmSource = nandType;
|
||||
}
|
||||
|
||||
/* If we're booting emuNAND the second emuNAND is set as default and B isn't pressed,
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -194,31 +195,21 @@ void main(void)
|
||||
//If we need to boot emuNAND, make sure it exists
|
||||
if(nandType != FIRMWARE_SYSNAND)
|
||||
{
|
||||
locateEmuNAND(&emuOffset, &emuHeader, &nandType);
|
||||
locateEmuNand(&emuOffset, &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(&emuOffset, &emuHeader, &firmSource);
|
||||
locateEmuNand(&emuOffset, &emuHeader, &firmSource);
|
||||
|
||||
if(!isFirmlaunch)
|
||||
{
|
||||
newConfig |= (u32)nandType | ((u32)firmSource << 2);
|
||||
|
||||
/* If the boot configuration is different from previously, overwrite it.
|
||||
Just the no-forcing flag being set is not enough */
|
||||
if((newConfig & 0x2F) != (config & 0x3F))
|
||||
{
|
||||
//Preserve user settings (last 26 bits)
|
||||
newConfig |= config & 0xFFFFFFC0;
|
||||
|
||||
if(!fileWrite(&newConfig, configPath, 4))
|
||||
error("Error writing the configuration file");
|
||||
}
|
||||
configTemp |= (u32)nandType | ((u32)firmSource << 2);
|
||||
writeConfig(needConfig, configTemp);
|
||||
}
|
||||
|
||||
u32 firmVersion = loadFirm(firmType);
|
||||
u32 firmVersion = loadFirm(&firmType, firmSource);
|
||||
|
||||
switch(firmType)
|
||||
{
|
||||
@@ -226,7 +217,8 @@ void main(void)
|
||||
patchNativeFirm(firmVersion, nandType, emuHeader, isA9lh);
|
||||
break;
|
||||
case SAFE_FIRM:
|
||||
patchSafeFirm();
|
||||
case NATIVE_FIRM1X2X:
|
||||
if(isA9lh) patch1x2xNativeAndSafeFirm();
|
||||
break;
|
||||
default:
|
||||
//Skip patching on unsupported O3DS AGB/TWL FIRMs
|
||||
@@ -234,25 +226,41 @@ void main(void)
|
||||
break;
|
||||
}
|
||||
|
||||
launchFirm(firmType, isFirmlaunch);
|
||||
launchFirm(firmType);
|
||||
}
|
||||
|
||||
static inline u32 loadFirm(FirmwareType firmType)
|
||||
static inline u32 loadFirm(FirmwareType *firmType, FirmwareSource firmSource)
|
||||
{
|
||||
section = firm->section;
|
||||
|
||||
//Load FIRM from CTRNAND, unless it's an O3DS and we're loading a pre-5.0 NATIVE FIRM
|
||||
u32 firmVersion = firmRead(firm, (u32)firmType);
|
||||
//Load FIRM from CTRNAND
|
||||
u32 firmVersion = firmRead(firm, (u32)*firmType);
|
||||
|
||||
if(!isN3DS && firmType == NATIVE_FIRM && firmVersion < 0x25)
|
||||
if(!isN3DS && *firmType == NATIVE_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");
|
||||
if(firmVersion < 0x18)
|
||||
{
|
||||
//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");
|
||||
|
||||
//No assumption regarding FIRM version
|
||||
firmVersion = 0xffffffff;
|
||||
if(BOOTCFG_SAFEMODE != 0) error("SAFE_MODE is not supported on 1.x/2.x FIRM");
|
||||
|
||||
*firmType = NATIVE_FIRM1X2X;
|
||||
}
|
||||
|
||||
//We can't boot a 3.x/4.x NATIVE_FIRM, load one from SD
|
||||
else if(firmVersion < 0x25)
|
||||
{
|
||||
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;
|
||||
}
|
||||
@@ -260,6 +268,7 @@ static inline u32 loadFirm(FirmwareType firmType)
|
||||
static inline void patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 emuHeader, bool isA9lh)
|
||||
{
|
||||
u8 *arm9Section = (u8 *)firm + section[2].offset;
|
||||
u8 *arm11Section1 = (u8 *)firm + section[1].offset;
|
||||
|
||||
if(isN3DS)
|
||||
{
|
||||
@@ -276,6 +285,10 @@ static inline void patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32
|
||||
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
|
||||
patchSignatureChecks(process9Offset, process9Size);
|
||||
|
||||
@@ -283,7 +296,7 @@ static inline void patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32
|
||||
if(nandType != FIRMWARE_SYSNAND)
|
||||
{
|
||||
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
|
||||
@@ -299,8 +312,10 @@ static inline void patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32
|
||||
patchTitleInstallMinVersionCheck(process9Offset, process9Size);
|
||||
|
||||
//Restore svcBackdoor
|
||||
reimplementSvcBackdoor((u8 *)firm + section[1].offset, section[1].size);
|
||||
reimplementSvcBackdoor(arm11Section1, arm11SvcTable, &freeK11Space);
|
||||
}
|
||||
|
||||
implementSvcGetCFWInfo(arm11Section1, arm11SvcTable, &freeK11Space);
|
||||
}
|
||||
|
||||
static inline void patchLegacyFirm(FirmwareType firmType)
|
||||
@@ -315,7 +330,7 @@ static inline void patchLegacyFirm(FirmwareType firmType)
|
||||
applyLegacyFirmPatches((u8 *)firm, firmType);
|
||||
}
|
||||
|
||||
static inline void patchSafeFirm(void)
|
||||
static inline void patch1x2xNativeAndSafeFirm(void)
|
||||
{
|
||||
u8 *arm9Section = (u8 *)firm + section[2].offset;
|
||||
|
||||
@@ -327,28 +342,44 @@ static inline void patchSafeFirm(void)
|
||||
|
||||
patchFirmWrites(arm9Section, section[2].size);
|
||||
}
|
||||
else patchFirmWriteSafe(arm9Section, section[2].size);
|
||||
else patchOldFirmWrites(arm9Section, section[2].size);
|
||||
}
|
||||
|
||||
static inline void copySection0AndInjectLoader(void)
|
||||
static inline void copySection0AndInjectSystemModules(void)
|
||||
{
|
||||
u8 *arm11Section0 = (u8 *)firm + section[0].offset;
|
||||
u32 srcModuleSize,
|
||||
dstModuleSize;
|
||||
|
||||
u32 loaderSize;
|
||||
u32 loaderOffset = getLoader(arm11Section0, &loaderSize);
|
||||
for(u8 *src = (u8 *)firm + section[0].offset, *srcEnd = src + section[0].size, *dst = section[0].address;
|
||||
src < srcEnd; src += srcModuleSize, dst += dstModuleSize)
|
||||
{
|
||||
srcModuleSize = *(u32 *)(src + 0x104) * 0x200;
|
||||
char *moduleName = (char *)(src + 0x200);
|
||||
|
||||
memcpy(section[0].address, arm11Section0, loaderOffset);
|
||||
memcpy(section[0].address + loaderOffset, injector, injector_size);
|
||||
memcpy(section[0].address + loaderOffset + injector_size, arm11Section0 + loaderOffset + loaderSize, section[0].size - (loaderOffset + loaderSize));
|
||||
void *module;
|
||||
|
||||
if(memcmp(moduleName, "loader", 6) == 0)
|
||||
{
|
||||
module = (void *)injector;
|
||||
dstModuleSize = injector_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
module = src;
|
||||
dstModuleSize = srcModuleSize;
|
||||
}
|
||||
|
||||
memcpy(dst, module, dstModuleSize);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void launchFirm(FirmwareType firmType, bool isFirmlaunch)
|
||||
static inline void launchFirm(FirmwareType firmType)
|
||||
{
|
||||
//If we're booting NATIVE_FIRM, section0 needs to be copied separately to inject 3ds_injector
|
||||
u32 sectionNum;
|
||||
if(firmType == NATIVE_FIRM)
|
||||
{
|
||||
copySection0AndInjectLoader();
|
||||
copySection0AndInjectSystemModules();
|
||||
sectionNum = 1;
|
||||
}
|
||||
else sectionNum = 0;
|
||||
|
||||
@@ -24,10 +24,10 @@
|
||||
|
||||
#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)
|
||||
#define CFG_BOOTENV (*(vu32 *)0x10010000)
|
||||
#define CFG_UNITINFO (*(vu8 *)0x10010010)
|
||||
|
||||
//FIRM Header layout
|
||||
typedef struct firmSectionHeader {
|
||||
@@ -46,17 +46,10 @@ typedef struct firmHeader {
|
||||
u8 reserved2[0x30];
|
||||
firmSectionHeader section[4];
|
||||
} 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 patchLegacyFirm(FirmwareType firmType);
|
||||
static inline void patchSafeFirm(void);
|
||||
static inline void copySection0AndInjectLoader(void);
|
||||
static inline void launchFirm(FirmwareType firmType, bool isFirmlaunch);
|
||||
static inline void patch1x2xNativeAndSafeFirm(void);
|
||||
static inline void copySection0AndInjectSystemModules(void);
|
||||
static inline void launchFirm(FirmwareType firmType);
|
||||
46
source/fs.c
46
source/fs.c
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "fs.h"
|
||||
#include "memory.h"
|
||||
#include "strings.h"
|
||||
#include "cache.h"
|
||||
#include "screen.h"
|
||||
#include "fatfs/ff.h"
|
||||
@@ -64,7 +65,9 @@ bool fileWrite(const void *buffer, const char *path, u32 size)
|
||||
{
|
||||
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;
|
||||
f_write(&file, buffer, size, &written);
|
||||
@@ -72,13 +75,26 @@ bool fileWrite(const void *buffer, const char *path, u32 size)
|
||||
|
||||
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)
|
||||
@@ -112,8 +128,8 @@ void loadPayload(u32 pressed)
|
||||
|
||||
memcpy(loaderAddress, loader, loader_size);
|
||||
|
||||
path[14] = '/';
|
||||
memcpy(&path[15], info.altname, 13);
|
||||
concatenateStrings(path, "/");
|
||||
concatenateStrings(path, info.altname);
|
||||
|
||||
loaderAddress[1] = fileRead((void *)0x24F00000, path);
|
||||
|
||||
@@ -131,8 +147,9 @@ u32 firmRead(void *dest, u32 firmType)
|
||||
{ "00000202", "20000202" },
|
||||
{ "00000003", "20000003" }};
|
||||
|
||||
char path[48] = "1:/title/00040138/00000000/content";
|
||||
memcpy(&path[18], firmFolders[firmType][isN3DS ? 1 : 0], 8);
|
||||
char path[48] = "1:/title/00040138/";
|
||||
concatenateStrings(path, firmFolders[firmType][isN3DS ? 1 : 0]);
|
||||
concatenateStrings(path, "/content");
|
||||
|
||||
DIR dir;
|
||||
FILINFO info;
|
||||
@@ -162,19 +179,10 @@ u32 firmRead(void *dest, u32 firmType)
|
||||
f_closedir(&dir);
|
||||
|
||||
//Complete the string with the .app name
|
||||
memcpy(&path[34], "/00000000.app", 14);
|
||||
|
||||
//Last digit of the .app
|
||||
u32 i = 42;
|
||||
concatenateStrings(path, "/00000000.app");
|
||||
|
||||
//Convert back the .app name from integer to array
|
||||
u32 tempVersion = firmVersion;
|
||||
while(tempVersion)
|
||||
{
|
||||
static const char hexDigits[] = "0123456789ABCDEF";
|
||||
path[i--] = hexDigits[tempVersion & 0xF];
|
||||
tempVersion >>= 4;
|
||||
}
|
||||
hexItoa(firmVersion, &path[35]);
|
||||
|
||||
fileRead(dest, path);
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define PATTERN(a) a "_*.bin"
|
||||
#define PATTERN(a) a "_*.bin"
|
||||
|
||||
extern bool isN3DS;
|
||||
|
||||
@@ -32,6 +32,6 @@ void mountFs(void);
|
||||
u32 fileRead(void *dest, const char *path);
|
||||
u32 getFileSize(const char *path);
|
||||
bool fileWrite(const void *buffer, const char *path, u32 size);
|
||||
void createDirectory(const char *path);
|
||||
void fileDelete(const char *path);
|
||||
void loadPayload(u32 pressed);
|
||||
u32 firmRead(void *dest, u32 firmType);
|
||||
@@ -41,7 +41,7 @@ void memset32(void *dest, u32 filler, u32 size)
|
||||
{
|
||||
u32 *dest32 = (u32 *)dest;
|
||||
|
||||
for (u32 i = 0; i < size / 4; i++)
|
||||
for(u32 i = 0; i < size / 4; i++)
|
||||
dest32[i] = filler;
|
||||
}
|
||||
|
||||
|
||||
134
source/patches.c
134
source/patches.c
@@ -24,6 +24,7 @@
|
||||
#include "memory.h"
|
||||
#include "config.h"
|
||||
#include "../build/rebootpatch.h"
|
||||
#include "../build/svcGetCFWInfopatch.h"
|
||||
|
||||
u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr)
|
||||
{
|
||||
@@ -36,6 +37,22 @@ u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr)
|
||||
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)
|
||||
{
|
||||
const u16 sigPatch[2] = {0x2000, 0x4770};
|
||||
@@ -44,8 +61,8 @@ void patchSignatureChecks(u8 *pos, u32 size)
|
||||
const u8 pattern[] = {0xC0, 0x1C, 0x76, 0xE7},
|
||||
pattern2[] = {0xB5, 0x22, 0x4D, 0x0C};
|
||||
|
||||
u16 *off = (u16 *)memsearch(pos, pattern, size, 4),
|
||||
*off2 = (u16 *)(memsearch(pos, pattern2, size, 4) - 1);
|
||||
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern)),
|
||||
*off2 = (u16 *)(memsearch(pos, pattern2, size, sizeof(pattern2)) - 1);
|
||||
|
||||
*off = sigPatch[0];
|
||||
off2[0] = sigPatch[0];
|
||||
@@ -55,9 +72,9 @@ void patchSignatureChecks(u8 *pos, u32 size)
|
||||
void patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr)
|
||||
{
|
||||
//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
|
||||
u32 fOpenOffset = (u32)(off + 9 - (-((*(u32 *)off & 0x00FFFFFF) << 2) & (0xFFFFFF << 2)) - pos + process9MemAddr);
|
||||
@@ -72,70 +89,82 @@ void patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr)
|
||||
|
||||
void patchFirmWrites(u8 *pos, u32 size)
|
||||
{
|
||||
const u16 writeBlock[2] = {0x2000, 0x46C0};
|
||||
|
||||
//Look for FIRM writing code
|
||||
u8 *const off1 = memsearch(pos, "exe:", size, 4);
|
||||
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[1] = writeBlock[1];
|
||||
off2[0] = 0x2000;
|
||||
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
|
||||
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[1] = writeBlockSafe[1];
|
||||
off[0] = 0x2400;
|
||||
off[1] = 0xE01D;
|
||||
}
|
||||
|
||||
void reimplementSvcBackdoor(u8 *pos, u32 size)
|
||||
void reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, 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
|
||||
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
|
||||
|
||||
const u8 pattern[] = {0x00, 0xB0, 0x9C, 0xE5}; //cpsid aif
|
||||
|
||||
u32 *exceptionsPage = (u32 *)memsearch(pos, pattern, size, 4) - 0xB;
|
||||
|
||||
u32 svcOffset = (-((exceptionsPage[2] & 0xFFFFFF) << 2) & (0xFFFFFF << 2)) - 8; //Branch offset + 8 for prefetch
|
||||
u32 *svcTable = (u32 *)(pos + *(u32 *)(pos + 0xFFFF0008 - svcOffset - 0xFFF00000 + 8) - 0xFFF00000); //SVC handler address
|
||||
while(*svcTable) svcTable++; //Look for SVC0 (NULL)
|
||||
|
||||
if(svcTable[0x7B] == 0)
|
||||
if(!arm11SvcTable[0x7B])
|
||||
{
|
||||
u32 *freeSpace;
|
||||
for(freeSpace = exceptionsPage; *freeSpace != 0xFFFFFFFF; freeSpace++);
|
||||
memcpy(*freeK11Space, svcBackdoor, 40);
|
||||
|
||||
memcpy(freeSpace, svcBackdoor, 40);
|
||||
|
||||
svcTable[0x7B] = 0xFFFF0000 + ((u8 *)freeSpace - (u8 *)exceptionsPage);
|
||||
arm11SvcTable[0x7B] = 0xFFF00000 + *freeK11Space - pos;
|
||||
*freeK11Space += 40;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
bool isRelease;
|
||||
|
||||
info->commitHash = COMMIT_HASH;
|
||||
info->config = configData.config;
|
||||
info->versionMajor = (u8)(rev[1] - '0');
|
||||
info->versionMinor = (u8)(rev[3] - '0');
|
||||
if(rev[4] == '.')
|
||||
{
|
||||
info->versionBuild = (u8)(rev[5] - '0');
|
||||
isRelease = rev[6] == 0;
|
||||
}
|
||||
else isRelease = rev[4] == 0;
|
||||
|
||||
info->flags = 0 /* master branch */ | ((isRelease ? 1 : 0) << 1) /* is release */;
|
||||
|
||||
arm11SvcTable[0x2E] = 0xFFF00000 + *freeK11Space - pos; //Stubbed svc
|
||||
*freeK11Space += svcGetCFWInfo_size;
|
||||
}
|
||||
|
||||
void patchTitleInstallMinVersionCheck(u8 *pos, u32 size)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -160,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
|
||||
if the matching option was enabled (keep it as last) */
|
||||
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;
|
||||
|
||||
//Patch
|
||||
@@ -178,21 +207,4 @@ void applyLegacyFirmPatches(u8 *pos, FirmwareType firmType)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u32 getLoader(u8 *pos, u32 *loaderSize)
|
||||
{
|
||||
u8 *off = pos;
|
||||
u32 size;
|
||||
|
||||
while(true)
|
||||
{
|
||||
size = *(u32 *)(off + 0x104) * 0x200;
|
||||
if(*(u32 *)(off + 0x200) == 0x64616F6C) break;
|
||||
off += size;
|
||||
}
|
||||
|
||||
*loaderSize = size;
|
||||
|
||||
return (u32)(off - pos);
|
||||
}
|
||||
@@ -33,14 +33,29 @@ typedef struct patchData {
|
||||
u32 type;
|
||||
} patchData;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
char magic[4];
|
||||
|
||||
u8 versionMajor;
|
||||
u8 versionMinor;
|
||||
u8 versionBuild;
|
||||
u8 flags;
|
||||
|
||||
u32 commitHash;
|
||||
|
||||
u32 config;
|
||||
} CFWInfo;
|
||||
|
||||
extern bool isN3DS;
|
||||
|
||||
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 patchTitleInstallMinVersionCheck(u8 *pos, u32 size);
|
||||
void patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr);
|
||||
void patchFirmWrites(u8 *pos, u32 size);
|
||||
void patchFirmWriteSafe(u8 *pos, u32 size);
|
||||
void reimplementSvcBackdoor(u8 *pos, u32 size);
|
||||
void applyLegacyFirmPatches(u8 *pos, FirmwareType firmType);
|
||||
u32 getLoader(u8 *pos, u32 *loaderSize);
|
||||
void patchOldFirmWrites(u8 *pos, u32 size);
|
||||
void reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u8 **freeK11Space);
|
||||
void implementSvcGetCFWInfo(u8 *pos, u32 *arm11SvcTable, u8 **freeK11Space);
|
||||
void applyLegacyFirmPatches(u8 *pos, FirmwareType firmType);
|
||||
184
source/pin.c
184
source/pin.c
@@ -26,6 +26,7 @@
|
||||
*/
|
||||
|
||||
#include "draw.h"
|
||||
#include "config.h"
|
||||
#include "screen.h"
|
||||
#include "utils.h"
|
||||
#include "memory.h"
|
||||
@@ -34,95 +35,32 @@
|
||||
#include "pin.h"
|
||||
#include "crypto.h"
|
||||
|
||||
bool readPin(PINData *out)
|
||||
static char pinKeyToLetter(u32 pressed)
|
||||
{
|
||||
u8 __attribute__((aligned(4))) zeroes[16] = {0};
|
||||
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";
|
||||
const char keys[] = "AB--RLUD--XY";
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
void newPin(void)
|
||||
void newPin(bool allowSkipping)
|
||||
{
|
||||
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.
|
||||
u8 __attribute__((aligned(4))) enteredPassword[16 * ((PIN_LENGTH + 15) / 16)] = {0}; // pad to AES block length
|
||||
//Pad to AES block length with zeroes
|
||||
u8 __attribute__((aligned(4))) enteredPassword[0x10] = {0};
|
||||
|
||||
u32 cnt = 0;
|
||||
int charDrawPos = 20 * SPACING_X;
|
||||
|
||||
while(cnt < PIN_LENGTH)
|
||||
{
|
||||
u32 pressed;
|
||||
do
|
||||
{
|
||||
pressed = waitInput();
|
||||
}
|
||||
while(!(pressed & PIN_BUTTONS & ~BUTTON_START));
|
||||
|
||||
pressed &= PIN_BUTTONS & ~BUTTON_START;
|
||||
|
||||
if(!pressed) continue;
|
||||
char key = PINKeyToLetter(pressed);
|
||||
enteredPassword[cnt++] = (u8)key; // add character to password.
|
||||
|
||||
// visualize character on screen.
|
||||
drawCharacter(key, 10 + charDrawPos, 10, COLOR_WHITE);
|
||||
charDrawPos += 2 * SPACING_X;
|
||||
}
|
||||
|
||||
PINData pin = {0};
|
||||
u8 __attribute__((aligned(4))) tmp[32] = {0};
|
||||
u8 __attribute__((aligned(4))) zeroes[16] = {0};
|
||||
|
||||
memcpy(pin.magic, "PINF", 4);
|
||||
pin.formatVersionMajor = 1;
|
||||
pin.formatVersionMinor = 0;
|
||||
|
||||
computePINHash(tmp, zeroes, 1);
|
||||
memcpy(pin.testHash, tmp, 32);
|
||||
|
||||
computePINHash(tmp, enteredPassword, (PIN_LENGTH + 15) / 16);
|
||||
memcpy(pin.hash, tmp, 32);
|
||||
|
||||
fileWrite(&pin, "/luma/pin.bin", sizeof(PINData));
|
||||
|
||||
while(HID_PAD & PIN_BUTTONS);
|
||||
}
|
||||
|
||||
void verifyPin(PINData *in)
|
||||
{
|
||||
initScreens();
|
||||
|
||||
drawString("Press START to shutdown or enter pin to proceed.", 10, 10, COLOR_WHITE);
|
||||
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.
|
||||
u8 __attribute__((aligned(4))) enteredPassword[16 * ((PIN_LENGTH + 15) / 16)] = {0};
|
||||
|
||||
u32 cnt = 0;
|
||||
bool unlock = false;
|
||||
u8 length = 4 + 2 * (CONFIG_PIN - 1),
|
||||
cnt = 0;
|
||||
int charDrawPos = 5 * SPACING_X;
|
||||
|
||||
while(!unlock)
|
||||
while(cnt < length)
|
||||
{
|
||||
u32 pressed;
|
||||
do
|
||||
@@ -131,25 +69,97 @@ void verifyPin(PINData *in)
|
||||
}
|
||||
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;
|
||||
|
||||
char key = pinKeyToLetter(pressed);
|
||||
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;
|
||||
}
|
||||
|
||||
PINData pin;
|
||||
|
||||
memcpy(pin.magic, "PINF", 4);
|
||||
pin.formatVersionMajor = PIN_VERSIONMAJOR;
|
||||
pin.formatVersionMinor = PIN_VERSIONMINOR;
|
||||
pin.length = length;
|
||||
|
||||
u8 __attribute__((aligned(4))) tmp[0x20];
|
||||
u8 __attribute__((aligned(4))) zeroes[0x10] = {0};
|
||||
|
||||
computePinHash(tmp, zeroes);
|
||||
memcpy(pin.testHash, tmp, sizeof(tmp));
|
||||
|
||||
computePinHash(tmp, enteredPassword);
|
||||
memcpy(pin.hash, tmp, sizeof(tmp));
|
||||
|
||||
if(!fileWrite(&pin, PIN_PATH, sizeof(PINData)))
|
||||
error("Error writing the PIN file");
|
||||
}
|
||||
|
||||
bool verifyPin(void)
|
||||
{
|
||||
initScreens();
|
||||
|
||||
PINData pin;
|
||||
|
||||
if(fileRead(&pin, PIN_PATH) != sizeof(PINData) ||
|
||||
memcmp(pin.magic, "PINF", 4) != 0 ||
|
||||
pin.formatVersionMajor != PIN_VERSIONMAJOR ||
|
||||
pin.formatVersionMinor != PIN_VERSIONMINOR ||
|
||||
pin.length != 4 + 2 * (CONFIG_PIN - 1))
|
||||
return false;
|
||||
|
||||
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;
|
||||
int charDrawPos = 5 * SPACING_X;
|
||||
|
||||
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;
|
||||
do
|
||||
{
|
||||
pressed = waitInput();
|
||||
}
|
||||
while(!(pressed & PIN_BUTTONS));
|
||||
|
||||
if(pressed & BUTTON_START) mcuPowerOff();
|
||||
|
||||
char key = PINKeyToLetter(pressed);
|
||||
enteredPassword[cnt++] = (u8)key; // add character to password.
|
||||
pressed &= PIN_BUTTONS;
|
||||
|
||||
// visualize character on screen.
|
||||
if(!pressed) continue;
|
||||
|
||||
char key = pinKeyToLetter(pressed);
|
||||
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;
|
||||
|
||||
if(cnt >= PIN_LENGTH)
|
||||
if(cnt >= pin.length)
|
||||
{
|
||||
u8 __attribute__((aligned(4))) tmp[32] = {0};
|
||||
|
||||
computePINHash(tmp, enteredPassword, (PIN_LENGTH + 15) / 16);
|
||||
unlock = memcmp(in->hash, tmp, 32) == 0;
|
||||
computePinHash(tmp, enteredPassword);
|
||||
unlock = memcmp(pin.hash, tmp, sizeof(tmp)) == 0;
|
||||
|
||||
if(!unlock)
|
||||
{
|
||||
@@ -158,10 +168,10 @@ void verifyPin(PINData *in)
|
||||
|
||||
clearScreens();
|
||||
|
||||
drawString("Press START to shutdown or enter pin to proceed.", 10, 10, COLOR_WHITE);
|
||||
drawString("Pin: ", 10, 10 + 2 * SPACING_Y, COLOR_WHITE);
|
||||
drawString("Wrong pin! Try again!", 10, 10 + 3 * SPACING_Y, COLOR_RED);
|
||||
drawString("Wrong PIN, try again", 10, 10 + 4 * SPACING_Y, COLOR_RED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
10
source/pin.h
10
source/pin.h
@@ -30,17 +30,19 @@
|
||||
|
||||
#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))
|
||||
{
|
||||
char magic[4];
|
||||
u16 formatVersionMajor, formatVersionMinor;
|
||||
|
||||
u8 length;
|
||||
u8 testHash[32];
|
||||
u8 hash[32];
|
||||
} PINData;
|
||||
|
||||
bool readPin(PINData* out);
|
||||
void newPin(void);
|
||||
void verifyPin(PINData *in);
|
||||
void newPin(bool allowSkipping);
|
||||
bool verifyPin(void);
|
||||
@@ -147,7 +147,7 @@ void initScreens(void)
|
||||
//Disable interrupts
|
||||
__asm(".word 0xF10C01C0");
|
||||
|
||||
u32 brightnessLevel = brightness[MULTICONFIG(0)];
|
||||
u32 brightnessLevel = brightness[CONFIG_BRIGHTNESS];
|
||||
|
||||
*(vu32 *)0x10141200 = 0x1007F;
|
||||
*(vu32 *)0x10202014 = 0x00000001;
|
||||
@@ -242,7 +242,7 @@ void initScreens(void)
|
||||
|
||||
if(PDN_GPU_CNT == 1)
|
||||
{
|
||||
flushDCacheRange(&config, 4);
|
||||
flushDCacheRange(&configData, sizeof(cfgData));
|
||||
flushDCacheRange((void *)fb, sizeof(struct fb));
|
||||
invokeArm11Function(ARM11);
|
||||
|
||||
@@ -254,6 +254,6 @@ void initScreens(void)
|
||||
else
|
||||
{
|
||||
clearScreens();
|
||||
updateBrightness(MULTICONFIG(0));
|
||||
updateBrightness(CONFIG_BRIGHTNESS);
|
||||
}
|
||||
}
|
||||
@@ -29,9 +29,10 @@
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#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 WAIT_FOR_ARM9() *arm11Entry = 0; while(!*arm11Entry); ((void (*)())*arm11Entry)();
|
||||
#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 WAIT_FOR_ARM9() *arm11Entry = 0; while(!*arm11Entry); ((void (*)())*arm11Entry)();
|
||||
|
||||
static volatile struct fb {
|
||||
u8 *top_left;
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
_start:
|
||||
b start
|
||||
|
||||
.global launchedFirmTIDLow
|
||||
launchedFirmTIDLow:
|
||||
.global launchedFirmTidLow
|
||||
launchedFirmTidLow:
|
||||
.hword 0, 0, 0, 0, 0, 0, 0, 0
|
||||
|
||||
start:
|
||||
|
||||
55
source/strings.c
Normal file
55
source/strings.c
Normal 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
29
source/strings.h
Normal 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);
|
||||
@@ -36,7 +36,7 @@ typedef volatile u16 vu16;
|
||||
typedef volatile u32 vu32;
|
||||
typedef volatile u64 vu64;
|
||||
|
||||
//Used by multiple files:
|
||||
//Used by multiple files
|
||||
typedef enum FirmwareSource
|
||||
{
|
||||
FIRMWARE_SYSNAND = 0,
|
||||
@@ -49,5 +49,6 @@ typedef enum FirmwareType
|
||||
NATIVE_FIRM = 0,
|
||||
TWL_FIRM = 1,
|
||||
AGB_FIRM = 2,
|
||||
SAFE_FIRM = 3
|
||||
SAFE_FIRM = 3,
|
||||
NATIVE_FIRM1X2X = 4
|
||||
} FirmwareType;
|
||||
@@ -56,7 +56,7 @@ u32 waitInput(void)
|
||||
|
||||
void mcuReboot(void)
|
||||
{
|
||||
if(PDN_GPU_CNT != 1) clearScreens();
|
||||
if(!isFirmlaunch && PDN_GPU_CNT != 1) clearScreens();
|
||||
|
||||
flushEntireDCache(); //Ensure that all memory transfers have completed and that the data cache has been flushed
|
||||
|
||||
@@ -66,7 +66,7 @@ void mcuReboot(void)
|
||||
|
||||
void mcuPowerOff(void)
|
||||
{
|
||||
if(PDN_GPU_CNT != 1) clearScreens();
|
||||
if(!isFirmlaunch && PDN_GPU_CNT != 1) clearScreens();
|
||||
|
||||
flushEntireDCache(); //Ensure that all memory transfers have completed and that the data cache has been flushed
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
#define REG_TIMER_CNT(i) *(vu16 *)(0x10003002 + 4 * i)
|
||||
#define REG_TIMER_VAL(i) *(vu16 *)(0x10003000 + 4 * i)
|
||||
|
||||
extern bool isFirmlaunch;
|
||||
|
||||
u32 waitInput(void);
|
||||
void mcuReboot(void);
|
||||
void mcuPowerOff(void);
|
||||
|
||||
Reference in New Issue
Block a user