mirror of
https://github.com/d0k3/SafeB9SInstaller.git
synced 2025-06-26 13:42:45 +00:00
Compare commits
18 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
107675e006 | ||
|
67c4eeaf1b | ||
|
07207a2fb6 | ||
|
2d6def3c08 | ||
|
30c728b4a8 | ||
|
6c493ac132 | ||
|
49b5b55221 | ||
|
8282c8e769 | ||
|
88bc7955d8 | ||
|
73bcc07349 | ||
|
3135962edb | ||
|
0369452723 | ||
|
bb00614532 | ||
|
40d01c3930 | ||
|
57afda82ad | ||
|
1f55ec9ca3 | ||
|
098a64d3cd | ||
|
58433ba99c |
4
.gitmodules
vendored
4
.gitmodules
vendored
@ -13,7 +13,3 @@
|
|||||||
path = 2xrsa
|
path = 2xrsa
|
||||||
url = https://github.com/b1l1s/2xrsa.git
|
url = https://github.com/b1l1s/2xrsa.git
|
||||||
ignore = dirty
|
ignore = dirty
|
||||||
[submodule "firmtool"]
|
|
||||||
path = firmtool
|
|
||||||
url = https://github.com/TuxSH/firmtool.git
|
|
||||||
ignore = dirty
|
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 790de3b9e83191249f737d8af9408b3c61b6e24d
|
Subproject commit d181cdb97d9ace244cf06845e914a59c1749aaaf
|
18
Makefile
18
Makefile
@ -25,15 +25,15 @@ INCLUDES := source source/common source/font source/fs source/crypto source/fatf
|
|||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
# options for code generation
|
# options for code generation
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
ARCH := -mthumb -mthumb-interwork -flto
|
ARCH := -mthumb -mthumb-interwork
|
||||||
|
|
||||||
CFLAGS := -g -Wall -Wextra -Wpedantic -Wcast-align -Wno-main -O2\
|
CFLAGS := -g -Wall -Wextra -Wpedantic -Wcast-align -Wno-main -O2\
|
||||||
-march=armv5te -mtune=arm946e-s -fomit-frame-pointer -ffast-math -std=gnu99\
|
-march=armv5te -mtune=arm946e-s -fomit-frame-pointer -ffast-math -std=gnu11\
|
||||||
$(ARCH)
|
-fno-builtin-memcpy $(ARCH) -fdata-sections -ffunction-sections
|
||||||
|
|
||||||
CFLAGS += $(INCLUDE) -DARM9
|
CFLAGS += $(INCLUDE) -DARM9
|
||||||
|
|
||||||
CFLAGS += -DBUILD_NAME="\"$(TARGET) (`date +'%Y/%m/%d'`)\""
|
CFLAGS += -DBUILD_NAME="\"$(TARGET) `date +'%Y/%m/%d'`\""
|
||||||
|
|
||||||
ifeq ($(FONT),ORIG)
|
ifeq ($(FONT),ORIG)
|
||||||
CFLAGS += -DFONT_ORIGINAL
|
CFLAGS += -DFONT_ORIGINAL
|
||||||
@ -47,9 +47,13 @@ else
|
|||||||
CFLAGS += -DFONT_6X10
|
CFLAGS += -DFONT_6X10
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ($(OPEN),1)
|
||||||
|
CFLAGS += -DOPEN_INSTALLER
|
||||||
|
endif
|
||||||
|
|
||||||
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
|
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
|
||||||
|
|
||||||
ASFLAGS := -g $(ARCH)
|
ASFLAGS := -g -mcpu=arm946e-s $(ARCH)
|
||||||
LDFLAGS = -T../link.ld -nostartfiles -g $(ARCH) -Wl,-Map,$(TARGET).map
|
LDFLAGS = -T../link.ld -nostartfiles -g $(ARCH) -Wl,-Map,$(TARGET).map
|
||||||
|
|
||||||
LIBS :=
|
LIBS :=
|
||||||
@ -107,7 +111,7 @@ export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
|||||||
.PHONY: common clean all gateway firm 2xrsa binary cakehax cakerop brahma release
|
.PHONY: common clean all gateway firm 2xrsa binary cakehax cakerop brahma release
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
all: binary
|
all: firm
|
||||||
|
|
||||||
common:
|
common:
|
||||||
@[ -d $(OUTPUT_D) ] || mkdir -p $(OUTPUT_D)
|
@[ -d $(OUTPUT_D) ] || mkdir -p $(OUTPUT_D)
|
||||||
@ -120,7 +124,7 @@ binary: common
|
|||||||
@make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
@make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||||
|
|
||||||
firm: binary
|
firm: binary
|
||||||
@firmtool/firmtool build $(OUTPUT).firm -n 0x23F00000 -e 0 -D $(OUTPUT).bin -A 0x23F00000 -C NDMA -i
|
@firmtool build $(OUTPUT).firm -n 0x23F00000 -e 0 -D $(OUTPUT).bin -A 0x23F00000 -C NDMA -i
|
||||||
|
|
||||||
gateway: binary
|
gateway: binary
|
||||||
@cp resources/LauncherTemplate.dat $(OUTPUT_D)/Launcher.dat
|
@cp resources/LauncherTemplate.dat $(OUTPUT_D)/Launcher.dat
|
||||||
|
1
firmtool
1
firmtool
@ -1 +0,0 @@
|
|||||||
Subproject commit 375e66c946968c8d39258b01038817a628b88e89
|
|
@ -37,27 +37,42 @@
|
|||||||
#define align(v,a) \
|
#define align(v,a) \
|
||||||
(((v) % (a)) ? ((v) + (a) - ((v) % (a))) : (v))
|
(((v) % (a)) ? ((v) + (a) - ((v) % (a))) : (v))
|
||||||
|
|
||||||
#define ENTRY_BRAHMA (1)
|
|
||||||
#define ENTRY_GATEWAY (2)
|
|
||||||
|
|
||||||
// SafeB9SInstaller version
|
// SafeB9SInstaller version
|
||||||
#define VERSION "0.0.6"
|
#define VERSION "0.0.7"
|
||||||
|
|
||||||
|
// name of the FIRM to install (also name of the input path)
|
||||||
|
#ifndef OPEN_INSTALLER
|
||||||
|
#define NAME_FIRM "boot9strap"
|
||||||
|
#else
|
||||||
|
#define NAME_FIRM "sighax"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// input / output paths
|
||||||
|
#define INPUT_PATH "0:/" NAME_FIRM
|
||||||
|
|
||||||
|
// cosmetic stuff (for installer status)
|
||||||
|
#ifndef OPEN_INSTALLER
|
||||||
|
#define APP_TITLE "SafeB9SInstaller" " v" VERSION
|
||||||
|
#define APP_URL "https://github.com/d0k3/SafeB9SInstaller"
|
||||||
|
#define APP_USAGE "Usage instructions: https://%s.hacks.guide/", IS_DEVKIT ? "panda" : "3ds"
|
||||||
|
#else
|
||||||
|
#define APP_TITLE "OpenFirmInstaller" " v" VERSION
|
||||||
|
#define APP_URL "https://github.com/d0k3/SafeB9SInstaller"
|
||||||
|
#define APP_USAGE "Based on SafeB9SInstaller by d0k3"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// buffer area defines (big buffer for firm)
|
||||||
|
#define WORK_BUFFER ((u8*) 0x21000000)
|
||||||
|
#define WORK_BUFFER_SIZE (0x400000)
|
||||||
|
#define FIRM_BUFFER ((u8*) 0x21400000)
|
||||||
|
#define FIRM_BUFFER_SIZE (0x400000)
|
||||||
|
#define NAND_BUFFER ((u8*) 0x21800000)
|
||||||
|
#define NAND_BUFFER_SIZE (0x100000)
|
||||||
|
|
||||||
// testfing flags, only useful to devs
|
// testfing flags, only useful to devs
|
||||||
// #define NO_WRITE // disables all NAND writes, just for testing
|
// #define NO_WRITE // disables all NAND writes, just for testing
|
||||||
// #define FAIL_TEST // to test the emergency screen, only works with NO_TRANSFER defined
|
// #define FAIL_TEST // to test the emergency screen, only works with NO_TRANSFER defined
|
||||||
|
|
||||||
// input / output paths
|
|
||||||
#define INPUT_PATH "0:/boot9strap"
|
|
||||||
|
|
||||||
// buffer area defines (big buffer for firm)
|
|
||||||
#define WORK_BUFFER ((u8*) 0x21000000)
|
|
||||||
#define WORK_BUFFER_SIZE (0x100000)
|
|
||||||
#define NAND_BUFFER ((u8*) 0x22000000)
|
|
||||||
#define NAND_BUFFER_SIZE (0x100000)
|
|
||||||
#define FIRM_BUFFER ((u8*) 0x23000000)
|
|
||||||
#define FIRM_BUFFER_SIZE (0x400000)
|
|
||||||
|
|
||||||
inline u32 strchrcount(const char* str, char symbol) {
|
inline u32 strchrcount(const char* str, char symbol) {
|
||||||
u32 count = 0;
|
u32 count = 0;
|
||||||
for (u32 i = 0; str[i] != '\0'; i++) {
|
for (u32 i = 0; str[i] != '\0'; i++) {
|
||||||
|
72
source/common/memcpy.s
Normal file
72
source/common/memcpy.s
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
@ memcpy_arm946e-s - hand written reimplementation of memcpy to be sequential
|
||||||
|
@ Written in 2019 by luigoalma <luigoalma at gmail dot com>
|
||||||
|
@ To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty.
|
||||||
|
@ For a copy of CC0 Public Domain Dedication, see <https://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
|
.cpu arm946e-s
|
||||||
|
.arch armv5te
|
||||||
|
.arm
|
||||||
|
.section .text.memcpy, "ax", %progbits
|
||||||
|
.align 2
|
||||||
|
.global memcpy
|
||||||
|
.syntax unified
|
||||||
|
.type memcpy, %function
|
||||||
|
memcpy:
|
||||||
|
@ r0 = dest
|
||||||
|
@ r1 = src
|
||||||
|
@ r2 = length
|
||||||
|
@ check if length 0 and return if so
|
||||||
|
cmp r2, #0
|
||||||
|
bxeq lr
|
||||||
|
push {r0,r4-r9,lr}
|
||||||
|
@ pre-fetch data
|
||||||
|
pld [r1]
|
||||||
|
@ alignment check with word size
|
||||||
|
@ if not aligned but both are in the same misalignment, fix it up
|
||||||
|
@ otherwise jump to basic loop
|
||||||
|
orr r12, r0, r1
|
||||||
|
ands r12, r12, #3
|
||||||
|
beq .L1
|
||||||
|
and r4, r0, #3
|
||||||
|
and r5, r1, #3
|
||||||
|
cmp r4, r5
|
||||||
|
bne .L6
|
||||||
|
rsb r4, r4, #4
|
||||||
|
.L0:
|
||||||
|
ldrb r3, [r1], #1
|
||||||
|
strb r3, [r0], #1
|
||||||
|
subs r2, r2, #1
|
||||||
|
popeq {r0,r4-r9,pc}
|
||||||
|
subs r4, r4, #1
|
||||||
|
bne .L0
|
||||||
|
.L1:
|
||||||
|
@ check if length higher than 32
|
||||||
|
@ if so, do the 32 byte block copy loop,
|
||||||
|
@ until there's nothing left or remainder to copy is less than 32
|
||||||
|
movs r3, r2, LSR#5
|
||||||
|
beq .L3
|
||||||
|
.L2:
|
||||||
|
ldm r1!, {r4-r9,r12,lr}
|
||||||
|
stm r0!, {r4-r9,r12,lr}
|
||||||
|
subs r3, r3, #1
|
||||||
|
bne .L2
|
||||||
|
ands r2, r2, #0x1F
|
||||||
|
popeq {r0,r4-r9,pc}
|
||||||
|
.L3:
|
||||||
|
@ copy in word size the remaining data,
|
||||||
|
@ and finish off with basic loop if can't copy all by word size.
|
||||||
|
movs r3, r2, LSR#2
|
||||||
|
beq .L6
|
||||||
|
.L4:
|
||||||
|
ldr r12, [r1], #4
|
||||||
|
str r12, [r0], #4
|
||||||
|
subs r3, r3, #1
|
||||||
|
bne .L4
|
||||||
|
ands r2, r2, #0x3
|
||||||
|
.L5: @ the basic loop
|
||||||
|
popeq {r0,r4-r9,pc}
|
||||||
|
.L6:
|
||||||
|
ldrb r3, [r1], #1
|
||||||
|
strb r3, [r0], #1
|
||||||
|
subs r2, r2, #1
|
||||||
|
b .L5
|
||||||
|
.size memcpy, .-memcpy
|
@ -52,8 +52,10 @@
|
|||||||
#define COLOR_STD_BG COLOR_BLACK
|
#define COLOR_STD_BG COLOR_BLACK
|
||||||
#define COLOR_STD_FONT COLOR_WHITE
|
#define COLOR_STD_FONT COLOR_WHITE
|
||||||
|
|
||||||
#define TOP_SCREEN (u8*)(*(u32*)0x23FFFE00)
|
#define TOP_SCREEN top_screen
|
||||||
#define BOT_SCREEN (u8*)(*(u32*)0x23FFFE08)
|
#define BOT_SCREEN bottom_screen
|
||||||
|
|
||||||
|
extern u8 *top_screen, *bottom_screen;
|
||||||
|
|
||||||
void ClearScreen(unsigned char *screen, int color);
|
void ClearScreen(unsigned char *screen, int color);
|
||||||
void ClearScreenF(bool clear_top, bool clear_bottom, int color);
|
void ClearScreenF(bool clear_top, bool clear_bottom, int color);
|
||||||
|
@ -10,11 +10,12 @@
|
|||||||
#define IS_DEVKIT ((*(vu8*) (0x01FFB800+0x19)) != 0x0)
|
#define IS_DEVKIT ((*(vu8*) (0x01FFB800+0x19)) != 0x0)
|
||||||
|
|
||||||
// see: https://3dbrew.org/wiki/CONFIG11_Registers
|
// see: https://3dbrew.org/wiki/CONFIG11_Registers
|
||||||
|
// (also returns true for sighaxed systems, maybe change the name later?)
|
||||||
#define IS_A9LH ((*(vu32*) 0x101401C0) == 0)
|
#define IS_A9LH ((*(vu32*) 0x101401C0) == 0)
|
||||||
|
|
||||||
// https://www.3dbrew.org/wiki/CONFIG9_Registers
|
// https://www.3dbrew.org/wiki/CONFIG9_Registers
|
||||||
// (actually checks for an unlocked OTP)
|
// (actually checks for an unlocked OTP)
|
||||||
#define IS_UNLOCKED (!((*(vu8*)0x10000000) & 0x2))
|
#define IS_UNLOCKED (!((*(vu8*)0x10000000) & 0x2))
|
||||||
|
|
||||||
// A9LH + unlocked = SigHax
|
// A9LH + unlocked == SigHax
|
||||||
#define IS_SIGHAX (IS_A9LH && IS_UNLOCKED)
|
#define IS_SIGHAX (IS_A9LH && IS_UNLOCKED)
|
||||||
|
@ -11,71 +11,34 @@
|
|||||||
#include "nand.h"
|
#include "nand.h"
|
||||||
#include "sdmmc.h"
|
#include "sdmmc.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define PART_INFO(pdrv) (DriveInfo + pdrv)
|
||||||
#define PART_TYPE(pdrv) (DriveInfo[pdrv].type)
|
#define PART_TYPE(pdrv) (DriveInfo[pdrv].type)
|
||||||
#define PART_SUBTYPE(pdrv) (DriveInfo[pdrv].subtype)
|
|
||||||
|
|
||||||
#define TYPE_NONE 0
|
#define TYPE_NONE 0
|
||||||
#define TYPE_SYSNAND NAND_SYSNAND
|
#define TYPE_SYSNAND (1UL<<0)
|
||||||
#define TYPE_SDCARD (1UL<<4)
|
#define TYPE_SDCARD (1UL<<4)
|
||||||
|
|
||||||
#define SUBTYPE_CTRN 0
|
#define SUBTYPE_CTRN 1
|
||||||
#define SUBTYPE_CTRN_N 1
|
#define SUBTYPE_TWLN 2
|
||||||
#define SUBTYPE_CTRN_NO 2
|
#define SUBTYPE_TWLP 3
|
||||||
#define SUBTYPE_TWLN 3
|
#define SUBTYPE_NONE 0
|
||||||
#define SUBTYPE_TWLP 4
|
|
||||||
#define SUBTYPE_NONE 5
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
BYTE type;
|
BYTE type;
|
||||||
BYTE subtype;
|
BYTE subtype;
|
||||||
} FATpartition;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
DWORD offset;
|
DWORD offset;
|
||||||
DWORD size;
|
DWORD size;
|
||||||
BYTE keyslot;
|
BYTE keyslot;
|
||||||
} SubtypeDesc;
|
} FATpartition;
|
||||||
|
|
||||||
FATpartition DriveInfo[4] = {
|
FATpartition DriveInfo[13] = {
|
||||||
{ TYPE_SDCARD, SUBTYPE_NONE }, // 0 - SDCARD
|
{ TYPE_SDCARD, SUBTYPE_NONE, 0, 0, 0xFF }, // 0 - SDCARD
|
||||||
{ TYPE_SYSNAND, SUBTYPE_CTRN }, // 1 - SYSNAND CTRNAND
|
{ TYPE_SYSNAND, SUBTYPE_CTRN, 0, 0, 0xFF }, // 1 - SYSNAND CTRNAND
|
||||||
{ TYPE_SYSNAND, SUBTYPE_TWLN }, // 2 - SYSNAND TWLN
|
{ TYPE_SYSNAND, SUBTYPE_TWLN, 0, 0, 0xFF }, // 2 - SYSNAND TWLN
|
||||||
{ TYPE_SYSNAND, SUBTYPE_TWLP }, // 3 - SYSNAND TWLP
|
{ TYPE_SYSNAND, SUBTYPE_TWLP, 0, 0, 0xFF }, // 3 - SYSNAND TWLP
|
||||||
};
|
};
|
||||||
|
|
||||||
SubtypeDesc SubTypes[5] = {
|
|
||||||
{ 0x05C980, 0x17AE80, 0x04 }, // O3DS CTRNAND
|
|
||||||
{ 0x05C980, 0x20F680, 0x05 }, // N3DS CTRNAND
|
|
||||||
{ 0x05C980, 0x20F680, 0x04 }, // N3DS CTRNAND (downgraded)
|
|
||||||
{ 0x000097, 0x047DA9, 0x03 }, // TWLN
|
|
||||||
{ 0x04808D, 0x0105B3, 0x03 } // TWLP
|
|
||||||
};
|
|
||||||
|
|
||||||
static BYTE nand_type_sys = 0;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------------*/
|
|
||||||
/* Get Drive Subtype helper */
|
|
||||||
/*-----------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
static inline SubtypeDesc* get_subtype_desc(
|
|
||||||
__attribute__((unused))
|
|
||||||
BYTE pdrv /* Physical drive number to identify the drive */
|
|
||||||
)
|
|
||||||
{
|
|
||||||
BYTE subtype = PART_SUBTYPE(pdrv);
|
|
||||||
|
|
||||||
if (subtype == SUBTYPE_NONE) {
|
|
||||||
return NULL;
|
|
||||||
} else if (subtype == SUBTYPE_CTRN) {
|
|
||||||
if (nand_type_sys != NAND_TYPE_O3DS)
|
|
||||||
subtype = (nand_type_sys == NAND_TYPE_N3DS) ? SUBTYPE_CTRN_N : SUBTYPE_CTRN_NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
return &(SubTypes[subtype]);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------------*/
|
/*-----------------------------------------------------------------------*/
|
||||||
@ -83,8 +46,8 @@ static inline SubtypeDesc* get_subtype_desc(
|
|||||||
/*-----------------------------------------------------------------------*/
|
/*-----------------------------------------------------------------------*/
|
||||||
|
|
||||||
DSTATUS disk_status (
|
DSTATUS disk_status (
|
||||||
__attribute__((unused))
|
__attribute__((unused))
|
||||||
BYTE pdrv /* Physical drive number to identify the drive */
|
BYTE pdrv /* Physical drive number to identify the drive */
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return RES_OK;
|
return RES_OK;
|
||||||
@ -97,17 +60,38 @@ DSTATUS disk_status (
|
|||||||
/*-----------------------------------------------------------------------*/
|
/*-----------------------------------------------------------------------*/
|
||||||
|
|
||||||
DSTATUS disk_initialize (
|
DSTATUS disk_initialize (
|
||||||
__attribute__((unused))
|
__attribute__((unused))
|
||||||
BYTE pdrv /* Physical drive number to identify the drive */
|
BYTE pdrv /* Physical drive number to identify the drive */
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (pdrv == 0) { // a mounted SD card is the preriquisite for everything else
|
FATpartition* fat_info = PART_INFO(pdrv);
|
||||||
|
BYTE type = PART_TYPE(pdrv);
|
||||||
|
|
||||||
|
fat_info->offset = fat_info->size = 0;
|
||||||
|
fat_info->keyslot = 0xFF;
|
||||||
|
|
||||||
|
if (type == TYPE_SDCARD) {
|
||||||
if (sdmmc_sdcard_init() != 0) return STA_NOINIT|STA_NODISK;
|
if (sdmmc_sdcard_init() != 0) return STA_NOINIT|STA_NODISK;
|
||||||
} else if (pdrv < 4) {
|
fat_info->size = getMMCDevice(1)->total_size;
|
||||||
nand_type_sys = CheckNandType();
|
} else if (type == TYPE_SYSNAND) {
|
||||||
if (!nand_type_sys) return STA_NOINIT|STA_NODISK;
|
NandPartitionInfo nprt_info;
|
||||||
|
if ((fat_info->subtype == SUBTYPE_CTRN) &&
|
||||||
|
(GetNandPartitionInfo(&nprt_info, NP_TYPE_STD, NP_SUBTYPE_CTR, 0) != 0) &&
|
||||||
|
(GetNandPartitionInfo(&nprt_info, NP_TYPE_STD, NP_SUBTYPE_CTR_N, 0) != 0)) {
|
||||||
|
return STA_NOINIT|STA_NODISK;
|
||||||
|
} else if ((fat_info->subtype == SUBTYPE_TWLN) &&
|
||||||
|
(GetNandPartitionInfo(&nprt_info, NP_TYPE_FAT, NP_SUBTYPE_TWL, 0) != 0)) {
|
||||||
|
return STA_NOINIT|STA_NODISK;
|
||||||
|
} else if ((fat_info->subtype == SUBTYPE_TWLP) &&
|
||||||
|
(GetNandPartitionInfo(&nprt_info, NP_TYPE_FAT, NP_SUBTYPE_TWL, 1) != 0)) {
|
||||||
|
return STA_NOINIT|STA_NODISK;
|
||||||
|
}
|
||||||
|
fat_info->offset = nprt_info.sector;
|
||||||
|
fat_info->size = nprt_info.count;
|
||||||
|
fat_info->keyslot = nprt_info.keyslot;
|
||||||
}
|
}
|
||||||
return RES_OK;
|
|
||||||
|
return RES_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -117,11 +101,11 @@ DSTATUS disk_initialize (
|
|||||||
/*-----------------------------------------------------------------------*/
|
/*-----------------------------------------------------------------------*/
|
||||||
|
|
||||||
DRESULT disk_read (
|
DRESULT disk_read (
|
||||||
__attribute__((unused))
|
__attribute__((unused))
|
||||||
BYTE pdrv, /* Physical drive number to identify the drive */
|
BYTE pdrv, /* Physical drive number to identify the drive */
|
||||||
BYTE *buff, /* Data buffer to store read data */
|
BYTE *buff, /* Data buffer to store read data */
|
||||||
DWORD sector, /* Sector address in LBA */
|
DWORD sector, /* Sector address in LBA */
|
||||||
UINT count /* Number of sectors to read */
|
UINT count /* Number of sectors to read */
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
BYTE type = PART_TYPE(pdrv);
|
BYTE type = PART_TYPE(pdrv);
|
||||||
@ -132,15 +116,12 @@ DRESULT disk_read (
|
|||||||
if (sdmmc_sdcard_readsectors(sector, count, buff))
|
if (sdmmc_sdcard_readsectors(sector, count, buff))
|
||||||
return RES_PARERR;
|
return RES_PARERR;
|
||||||
} else {
|
} else {
|
||||||
SubtypeDesc* subtype = get_subtype_desc(pdrv);
|
FATpartition* fat_info = PART_INFO(pdrv);
|
||||||
BYTE keyslot = subtype->keyslot;
|
if (ReadNandSectors(buff, fat_info->offset + sector, count, fat_info->keyslot))
|
||||||
DWORD isector = subtype->offset + sector;
|
|
||||||
|
|
||||||
if (ReadNandSectors(buff, isector, count, keyslot))
|
|
||||||
return RES_PARERR;
|
return RES_PARERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
return RES_OK;
|
return RES_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -151,11 +132,11 @@ DRESULT disk_read (
|
|||||||
|
|
||||||
#if _USE_WRITE
|
#if _USE_WRITE
|
||||||
DRESULT disk_write (
|
DRESULT disk_write (
|
||||||
__attribute__((unused))
|
__attribute__((unused))
|
||||||
BYTE pdrv, /* Physical drive number to identify the drive */
|
BYTE pdrv, /* Physical drive number to identify the drive */
|
||||||
const BYTE *buff, /* Data to be written */
|
const BYTE *buff, /* Data to be written */
|
||||||
DWORD sector, /* Sector address in LBA */
|
DWORD sector, /* Sector address in LBA */
|
||||||
UINT count /* Number of sectors to write */
|
UINT count /* Number of sectors to write */
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
BYTE type = PART_TYPE(pdrv);
|
BYTE type = PART_TYPE(pdrv);
|
||||||
@ -166,15 +147,12 @@ DRESULT disk_write (
|
|||||||
if (sdmmc_sdcard_writesectors(sector, count, (BYTE *)buff))
|
if (sdmmc_sdcard_writesectors(sector, count, (BYTE *)buff))
|
||||||
return RES_PARERR;
|
return RES_PARERR;
|
||||||
} else {
|
} else {
|
||||||
SubtypeDesc* subtype = get_subtype_desc(pdrv);
|
FATpartition* fat_info = PART_INFO(pdrv);
|
||||||
BYTE keyslot = subtype->keyslot;
|
if (WriteNandSectors(buff, fat_info->offset + sector, count, fat_info->keyslot))
|
||||||
DWORD isector = subtype->offset + sector;
|
|
||||||
|
|
||||||
if (WriteNandSectors(buff, isector, count, keyslot))
|
|
||||||
return RES_PARERR; // unstubbed!
|
return RES_PARERR; // unstubbed!
|
||||||
}
|
}
|
||||||
|
|
||||||
return RES_OK;
|
return RES_OK;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -186,26 +164,20 @@ DRESULT disk_write (
|
|||||||
|
|
||||||
#if _USE_IOCTL
|
#if _USE_IOCTL
|
||||||
DRESULT disk_ioctl (
|
DRESULT disk_ioctl (
|
||||||
__attribute__((unused))
|
__attribute__((unused))
|
||||||
BYTE pdrv, /* Physical drive number (0..) */
|
BYTE pdrv, /* Physical drive number (0..) */
|
||||||
__attribute__((unused))
|
__attribute__((unused))
|
||||||
BYTE cmd, /* Control code */
|
BYTE cmd, /* Control code */
|
||||||
__attribute__((unused))
|
__attribute__((unused))
|
||||||
void *buff /* Buffer to send/receive control data */
|
void *buff /* Buffer to send/receive control data */
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
BYTE type = PART_TYPE(pdrv);
|
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case GET_SECTOR_SIZE:
|
case GET_SECTOR_SIZE:
|
||||||
*((DWORD*) buff) = 0x200;
|
*((DWORD*) buff) = 0x200;
|
||||||
return RES_OK;
|
return RES_OK;
|
||||||
case GET_SECTOR_COUNT:
|
case GET_SECTOR_COUNT:
|
||||||
if (type == TYPE_SDCARD) { // SD card
|
*((DWORD*) buff) = PART_INFO(pdrv)->size;
|
||||||
*((DWORD*) buff) = getMMCDevice(1)->total_size;
|
|
||||||
} else if (type != TYPE_NONE) { // NAND
|
|
||||||
*((DWORD*) buff) = get_subtype_desc(pdrv)->size;
|
|
||||||
}
|
|
||||||
return RES_OK;
|
return RES_OK;
|
||||||
case GET_BLOCK_SIZE:
|
case GET_BLOCK_SIZE:
|
||||||
*((DWORD*) buff) = 0x2000;
|
*((DWORD*) buff) = 0x2000;
|
||||||
@ -215,6 +187,6 @@ DRESULT disk_ioctl (
|
|||||||
return RES_OK;
|
return RES_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
return RES_PARERR;
|
return RES_PARERR;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
32
source/fatfs/fatmbr.c
Normal file
32
source/fatfs/fatmbr.c
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#include "fatmbr.h"
|
||||||
|
|
||||||
|
u32 ValidateMbrHeader(MbrHeader* mbr) {
|
||||||
|
if (mbr->magic != FATMBR_MAGIC) return 1; // check magic
|
||||||
|
u32 sector = 1; // check partitions
|
||||||
|
for (u32 i = 0; i < 4; i++) {
|
||||||
|
MbrPartitionInfo* partition = mbr->partitions + i;
|
||||||
|
if (!partition->count && i) continue;
|
||||||
|
else if (!partition->count) return 1; // first partition can't be empty
|
||||||
|
if ((partition->type != 0x1) && (partition->type != 0x4) && (partition->type != 0x6) &&
|
||||||
|
(partition->type != 0xB) && (partition->type != 0xC) && (partition->type != 0xE))
|
||||||
|
return 1; // bad / unknown filesystem type
|
||||||
|
if (partition->sector < sector) return 1; // overlapping partitions
|
||||||
|
sector = partition->sector + partition->count;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 ValidateFatHeader(void* fat) {
|
||||||
|
if (getle16((u8*) fat + 0x1FE) != FATMBR_MAGIC) return 1; // check magic
|
||||||
|
Fat32Header* fat32 = (Fat32Header*) fat;
|
||||||
|
if (strncmp(fat32->fs_type, "FAT32 ", 8) == 0)
|
||||||
|
return 0; // is FAT32 header
|
||||||
|
Fat16Header* fat16 = (Fat16Header*) fat;
|
||||||
|
if ((strncmp(fat16->fs_type, "FAT16 ", 8) == 0) ||
|
||||||
|
(strncmp(fat16->fs_type, "FAT12 ", 8) == 0) ||
|
||||||
|
(strncmp(fat16->fs_type, "FAT ", 8) == 0))
|
||||||
|
return 0; // is FAT16 / FAT12 header
|
||||||
|
if ((getle64(fat16->fs_type) == 0) && (fat16->sct_size == 0x200))
|
||||||
|
return 0; // special case for public.sav
|
||||||
|
return 1; // failed, not a FAT header
|
||||||
|
}
|
90
source/fatfs/fatmbr.h
Normal file
90
source/fatfs/fatmbr.h
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#define FATMBR_MAGIC 0xAA55 // little endian!
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u8 status; // 0x80
|
||||||
|
u8 chs_start[3]; // 0x01 0x01 0x00
|
||||||
|
u8 type; // 0x0C
|
||||||
|
u8 chs_end[3]; // 0xFE 0xFF 0xFF
|
||||||
|
u32 sector; // 0x2000 (4MB offset, 512 byte sectors)
|
||||||
|
u32 count;
|
||||||
|
} __attribute__((packed)) MbrPartitionInfo;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char text[446];
|
||||||
|
MbrPartitionInfo partitions[4];
|
||||||
|
u16 magic; // 0xAA55
|
||||||
|
} __attribute__((packed)) MbrHeader;
|
||||||
|
|
||||||
|
typedef struct { // unused
|
||||||
|
u32 signature0; // 0x41615252
|
||||||
|
u8 reserved0[480];
|
||||||
|
u32 signature1; // 0x61417272
|
||||||
|
u32 clr_free; // 0xFFFFFFFF
|
||||||
|
u32 clr_next; // 0xFFFFFFFF
|
||||||
|
u8 reserved1[14];
|
||||||
|
u16 magic; // 0xAA55
|
||||||
|
} __attribute__((packed)) FileSystemInfo;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u8 jmp[3]; // 0x90 0x00 0xEB
|
||||||
|
char oemname[8]; // "anything"
|
||||||
|
u16 sct_size; // 0x0200
|
||||||
|
u8 clr_size; // 0x40 -> 32kB clusters with 512byte sectors
|
||||||
|
u16 sct_reserved; // 0x20
|
||||||
|
u8 fat_n; // 0x02
|
||||||
|
u16 reserved0; // root entry count in FAT16
|
||||||
|
u16 reserved1; // partition size when <= 32MB
|
||||||
|
u8 mediatype; // 0xF8
|
||||||
|
u16 reserved2; // FAT size in sectors in FAT16
|
||||||
|
u16 sct_track; // 0x3F
|
||||||
|
u16 sct_heads; // 0xFF
|
||||||
|
u32 sct_hidden; // same as partition offset in MBR
|
||||||
|
u32 sct_total; // same as partition size in MBR
|
||||||
|
u32 fat_size; // roundup((((sct_total - sct_reserved) / clr_size) * 4) / sct_size)
|
||||||
|
u16 flags; // 0x00
|
||||||
|
u16 version; // 0x00
|
||||||
|
u32 clr_root; // 0x02
|
||||||
|
u16 sct_fsinfo; // 0x01
|
||||||
|
u16 sct_backup; // 0x06
|
||||||
|
u8 reserved3[12];
|
||||||
|
u8 ndrive; // 0x80
|
||||||
|
u8 head_cur; // 0x00
|
||||||
|
u8 boot_sig; // 0x29
|
||||||
|
u32 vol_id; // volume id / 0x00
|
||||||
|
char vol_label[11]; // "anything "
|
||||||
|
char fs_type[8]; // "FAT32 "
|
||||||
|
u8 reserved4[420];
|
||||||
|
u16 magic; // 0xAA55
|
||||||
|
} __attribute__((packed)) Fat32Header;
|
||||||
|
|
||||||
|
typedef struct { // this struct is not tested enough!
|
||||||
|
u8 jmp[3]; // 0x90 0x00 0xEB
|
||||||
|
char oemname[8]; // "anything"
|
||||||
|
u16 sct_size; // 0x0200
|
||||||
|
u8 clr_size; // 0x20 (???) -> 16kB clusters with 512byte sectors
|
||||||
|
u16 sct_reserved; // 0x01
|
||||||
|
u8 fat_n; // 0x02
|
||||||
|
u16 root_n; // 0x0200
|
||||||
|
u16 reserved0; // partition size when <= 32MB
|
||||||
|
u8 mediatype; // 0xF8
|
||||||
|
u16 fat_size; // roundup((((sct_total - sct_reserved) / clr_size) * 2) / sct_size)
|
||||||
|
u16 sct_track; // 0x3F
|
||||||
|
u16 sct_heads; // 0xFF
|
||||||
|
u32 sct_hidden; // same as partition offset in MBR
|
||||||
|
u32 sct_total; // same as partition size in MBR
|
||||||
|
u8 ndrive; // 0x80
|
||||||
|
u8 head_cur; // 0x00
|
||||||
|
u8 boot_sig; // 0x29
|
||||||
|
u32 vol_id; // volume id / 0x00
|
||||||
|
char vol_label[11]; // "anything "
|
||||||
|
char fs_type[8]; // "FAT16 "
|
||||||
|
u8 reserved4[448];
|
||||||
|
u16 magic; // 0xAA55
|
||||||
|
} __attribute__((packed)) Fat16Header;
|
||||||
|
|
||||||
|
u32 ValidateMbrHeader(MbrHeader* mbr);
|
||||||
|
u32 ValidateFatHeader(void* fat);
|
@ -1,7 +1,9 @@
|
|||||||
#include "installer.h"
|
#include "installer.h"
|
||||||
#include "safewrite.h"
|
#include "safewrite.h"
|
||||||
#include "validator.h"
|
#include "validator.h"
|
||||||
|
#include "unittype.h"
|
||||||
#include "nand.h"
|
#include "nand.h"
|
||||||
|
#include "sdmmc.h"
|
||||||
#include "ui.h"
|
#include "ui.h"
|
||||||
#include "qff.h"
|
#include "qff.h"
|
||||||
#include "hid.h"
|
#include "hid.h"
|
||||||
@ -9,17 +11,14 @@
|
|||||||
#define COLOR_STATUS(s) ((s == STATUS_GREEN) ? COLOR_BRIGHTGREEN : (s == STATUS_YELLOW) ? COLOR_BRIGHTYELLOW : (s == STATUS_RED) ? COLOR_RED : COLOR_DARKGREY)
|
#define COLOR_STATUS(s) ((s == STATUS_GREEN) ? COLOR_BRIGHTGREEN : (s == STATUS_YELLOW) ? COLOR_BRIGHTYELLOW : (s == STATUS_RED) ? COLOR_RED : COLOR_DARKGREY)
|
||||||
|
|
||||||
#define MIN_SD_FREE (16 * 1024 * 1024) // 16MB
|
#define MIN_SD_FREE (16 * 1024 * 1024) // 16MB
|
||||||
#define FIRM_NAND_OFFSET 0x0B130000
|
|
||||||
#define FIRM_NAND_SIZE 0x800000
|
|
||||||
#define FIRM0_NAND_OFFSET FIRM_NAND_OFFSET
|
|
||||||
#define FIRM1_NAND_OFFSET (FIRM_NAND_OFFSET + (FIRM_NAND_SIZE/2))
|
|
||||||
|
|
||||||
#define NAME_SIGHAXFIRM (IS_DEVKIT ? INPUT_PATH "/boot9strap_dev.firm" : INPUT_PATH "/boot9strap.firm")
|
#define NAME_SIGHAXFIRM (IS_DEVKIT ? INPUT_PATH "/" NAME_FIRM "_dev.firm" : INPUT_PATH "/" NAME_FIRM ".firm")
|
||||||
#define NAME_SIGHAXFIRMSHA (IS_DEVKIT ? INPUT_PATH "/boot9strap_dev.firm.sha" : INPUT_PATH "/boot9strap.firm.sha")
|
#define NAME_SIGHAXFIRMSHA (IS_DEVKIT ? INPUT_PATH "/" NAME_FIRM "_dev.firm.sha" : INPUT_PATH "/" NAME_FIRM ".firm.sha")
|
||||||
#define NAME_SECTOR0x96 (IS_DEVKIT ? INPUT_PATH "/secret_sector_dev.bin" : INPUT_PATH "/secret_sector.bin")
|
#define NAME_SECTOR0x96 (IS_DEVKIT ? INPUT_PATH "/secret_sector_dev.bin" : INPUT_PATH "/secret_sector.bin")
|
||||||
#define NAME_FIRMBACKUP INPUT_PATH "/firm0firm1.bak"
|
#define NAME_FIRMBACKUP INPUT_PATH "/firm%lu_enc.bak"
|
||||||
#define NAME_SECTORBACKUP INPUT_PATH "/sector0x96.bak"
|
#define NAME_SECTORBACKUP INPUT_PATH "/sector0x96_enc.bak"
|
||||||
#define NAME_PAYLOAD INPUT_PATH "/payload.firm"
|
|
||||||
|
#define MAX_STAGE2_SIZE 0x89A00
|
||||||
|
|
||||||
#define STATUS_GREY -1
|
#define STATUS_GREY -1
|
||||||
#define STATUS_GREEN 0
|
#define STATUS_GREEN 0
|
||||||
@ -50,7 +49,9 @@ u32 ShowInstallerStatus(void) {
|
|||||||
const u32 pos_y0 = pos_yb + 50;
|
const u32 pos_y0 = pos_yb + 50;
|
||||||
const u32 stp = 14;
|
const u32 stp = 14;
|
||||||
|
|
||||||
DrawStringF(BOT_SCREEN, pos_xb, pos_yb, COLOR_STD_FONT, COLOR_STD_BG, "SafeB9SInstaller v" VERSION "\n" "-----------------------" "\n" "https://github.com/d0k3/SafeB9SInstaller");
|
// DrawStringF(BOT_SCREEN, pos_xb, pos_yb, COLOR_STD_FONT, COLOR_STD_BG, "SafeB9SInstaller v" VERSION "\n" "-----------------------" "\n" "https://github.com/d0k3/SafeB9SInstaller");
|
||||||
|
DrawStringF(BOT_SCREEN, pos_xb, pos_yb, COLOR_STD_FONT, COLOR_STD_BG, APP_TITLE "\n" "%.*s" "\n" APP_URL,
|
||||||
|
strnlen(APP_TITLE, 32), "--------------------------------");
|
||||||
|
|
||||||
DrawStringF(BOT_SCREEN, pos_x0, pos_y0 + (0*stp), COLOR_STD_FONT, COLOR_STD_BG, "ARM9LoaderHax -");
|
DrawStringF(BOT_SCREEN, pos_x0, pos_y0 + (0*stp), COLOR_STD_FONT, COLOR_STD_BG, "ARM9LoaderHax -");
|
||||||
DrawStringF(BOT_SCREEN, pos_x0, pos_y0 + (1*stp), COLOR_STD_FONT, COLOR_STD_BG, "MicroSD Card -");
|
DrawStringF(BOT_SCREEN, pos_x0, pos_y0 + (1*stp), COLOR_STD_FONT, COLOR_STD_BG, "MicroSD Card -");
|
||||||
@ -68,12 +69,13 @@ u32 ShowInstallerStatus(void) {
|
|||||||
DrawStringF(BOT_SCREEN, pos_x1, pos_y0 + (5*stp), COLOR_STATUS(statusBackup) , COLOR_STD_BG, "%-21.21s", msgBackup );
|
DrawStringF(BOT_SCREEN, pos_x1, pos_y0 + (5*stp), COLOR_STATUS(statusBackup) , COLOR_STD_BG, "%-21.21s", msgBackup );
|
||||||
DrawStringF(BOT_SCREEN, pos_x1, pos_y0 + (6*stp), COLOR_STATUS(statusInstall), COLOR_STD_BG, "%-21.21s", msgInstall);
|
DrawStringF(BOT_SCREEN, pos_x1, pos_y0 + (6*stp), COLOR_STATUS(statusInstall), COLOR_STD_BG, "%-21.21s", msgInstall);
|
||||||
|
|
||||||
DrawStringF(BOT_SCREEN, pos_xb, pos_yu - 10, COLOR_STD_FONT, COLOR_STD_BG, "Usage instructions: https://3ds.guide/");
|
DrawStringF(BOT_SCREEN, pos_xb, pos_yu - 10, COLOR_STD_FONT, COLOR_STD_BG, APP_USAGE);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 SafeB9SInstaller(void) {
|
u32 SafeB9SInstaller(void) {
|
||||||
UINT bt;
|
UINT bt;
|
||||||
|
u32 ret = 0;
|
||||||
|
|
||||||
// initialization
|
// initialization
|
||||||
ShowString("Initializing, please wait...");
|
ShowString("Initializing, please wait...");
|
||||||
@ -111,6 +113,7 @@ u32 SafeB9SInstaller(void) {
|
|||||||
ShowInstallerStatus();
|
ShowInstallerStatus();
|
||||||
u8 firm_sha[0x20];
|
u8 firm_sha[0x20];
|
||||||
UINT firm_size;
|
UINT firm_size;
|
||||||
|
bool unknown_payload = false;
|
||||||
if ((f_qread(NAME_SIGHAXFIRM, FIRM_BUFFER, 0, FIRM_BUFFER_SIZE, &firm_size) != FR_OK) ||
|
if ((f_qread(NAME_SIGHAXFIRM, FIRM_BUFFER, 0, FIRM_BUFFER_SIZE, &firm_size) != FR_OK) ||
|
||||||
(firm_size < 0x200)) {
|
(firm_size < 0x200)) {
|
||||||
snprintf(msgFirm, 64, "file not found");
|
snprintf(msgFirm, 64, "file not found");
|
||||||
@ -132,20 +135,22 @@ u32 SafeB9SInstaller(void) {
|
|||||||
statusFirm = STATUS_RED;
|
statusFirm = STATUS_RED;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (CheckBoot9Strap(FIRM_BUFFER) != 0) {
|
if (CheckFirmPayload(FIRM_BUFFER, msgFirm) != 0) {
|
||||||
snprintf(msgFirm, 64, "not boot9strap");
|
#ifndef OPEN_INSTALLER
|
||||||
statusFirm = STATUS_RED;
|
statusFirm = STATUS_RED;
|
||||||
return 1;
|
return 1;
|
||||||
|
#else
|
||||||
|
unknown_payload = true;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
snprintf(msgFirm, 64, "loaded & verified");
|
statusFirm = unknown_payload ? STATUS_YELLOW : STATUS_GREEN;
|
||||||
statusFirm = STATUS_GREEN;
|
|
||||||
ShowInstallerStatus();
|
ShowInstallerStatus();
|
||||||
// provided FIRM is okay!
|
// provided FIRM is okay!
|
||||||
|
|
||||||
|
|
||||||
// step #3 - check secret_sector.bin file
|
// step #3 - check secret_sector.bin file
|
||||||
u8 secret_sector[0x200] = { 0 };
|
u8 secret_sector[0x200] = { 0 };
|
||||||
if ((IS_A9LH && !IS_SIGHAX && !IS_O3DS)) {
|
if (IS_A9LH && !IS_SIGHAX && !IS_O3DS) {
|
||||||
snprintf(msgSector, 64, "checking...");
|
snprintf(msgSector, 64, "checking...");
|
||||||
statusSector = STATUS_YELLOW;
|
statusSector = STATUS_YELLOW;
|
||||||
ShowInstallerStatus();
|
ShowInstallerStatus();
|
||||||
@ -166,10 +171,24 @@ u32 SafeB9SInstaller(void) {
|
|||||||
// secret_sector.bin okay or not required!
|
// secret_sector.bin okay or not required!
|
||||||
|
|
||||||
|
|
||||||
// step #3 - check NAND crypto
|
// step #4 - check NAND crypto
|
||||||
snprintf(msgCrypto, 64, "checking...");
|
snprintf(msgCrypto, 64, "checking...");
|
||||||
statusCrypto = STATUS_YELLOW;
|
statusCrypto = STATUS_YELLOW;
|
||||||
ShowInstallerStatus();
|
ShowInstallerStatus();
|
||||||
|
u32 n_firms = 0;
|
||||||
|
for (; n_firms < 8; n_firms++) {
|
||||||
|
NandPartitionInfo np_info;
|
||||||
|
if (GetNandPartitionInfo(&np_info, NP_TYPE_FIRM, NP_SUBTYPE_CTR, n_firms) != 0) break;
|
||||||
|
if ((firm_size > np_info.count * 0x200) || (np_info.count * 0x200 > WORK_BUFFER_SIZE)) {
|
||||||
|
n_firms = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!n_firms) {
|
||||||
|
snprintf(msgCrypto, 64, "FIRM partition fail");
|
||||||
|
statusCrypto = STATUS_RED;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
if (!CheckFirmCrypto()) {
|
if (!CheckFirmCrypto()) {
|
||||||
snprintf(msgCrypto, 64, "FIRM crypto fail");
|
snprintf(msgCrypto, 64, "FIRM crypto fail");
|
||||||
statusCrypto = STATUS_RED;
|
statusCrypto = STATUS_RED;
|
||||||
@ -186,7 +205,9 @@ u32 SafeB9SInstaller(void) {
|
|||||||
|
|
||||||
|
|
||||||
// step #X - point of no return
|
// step #X - point of no return
|
||||||
if (!ShowUnlockSequence(1, "All input files verified.\n \nTo install FIRM, enter the sequence\nbelow or press B to cancel.")) {
|
if (!ShowUnlockSequence(unknown_payload ? 6 : 1, unknown_payload ?
|
||||||
|
"!!! FIRM NOT RECOGNIZED !!!\nProceeding may lead to a BRICK!\n \nTo proceed, enter the sequence\nbelow or press B to cancel." :
|
||||||
|
"All input files verified.\n \nTo install FIRM, enter the sequence\nbelow or press B to cancel.")) {
|
||||||
snprintf(msgBackup, 64, "cancelled by user");
|
snprintf(msgBackup, 64, "cancelled by user");
|
||||||
snprintf(msgInstall, 64, "cancelled by user");
|
snprintf(msgInstall, 64, "cancelled by user");
|
||||||
statusBackup = STATUS_YELLOW;
|
statusBackup = STATUS_YELLOW;
|
||||||
@ -199,25 +220,24 @@ u32 SafeB9SInstaller(void) {
|
|||||||
snprintf(msgBackup, 64, "FIRM backup...");
|
snprintf(msgBackup, 64, "FIRM backup...");
|
||||||
statusBackup = STATUS_YELLOW;
|
statusBackup = STATUS_YELLOW;
|
||||||
ShowInstallerStatus();
|
ShowInstallerStatus();
|
||||||
FIL fp;
|
|
||||||
u32 ret = 0;
|
|
||||||
if (f_open(&fp, NAME_FIRMBACKUP, FA_READ|FA_WRITE|FA_CREATE_ALWAYS) != FR_OK) {
|
|
||||||
snprintf(msgBackup, 64, "FIRM backup fail");
|
|
||||||
statusBackup = STATUS_RED;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
ShowProgress(0, 0, "FIRM backup");
|
ShowProgress(0, 0, "FIRM backup");
|
||||||
for (u32 pos = 0; (pos < FIRM_NAND_SIZE) && (ret == 0); pos += WORK_BUFFER_SIZE) {
|
for (u32 i = 0; i < n_firms; i++) {
|
||||||
UINT bytes = min(WORK_BUFFER_SIZE, FIRM_NAND_SIZE - pos);
|
NandPartitionInfo np_info;
|
||||||
snprintf(msgBackup, 64, "FIRM backup (%luMB/%luMB)",
|
ret = GetNandPartitionInfo(&np_info, NP_TYPE_FIRM, NP_SUBTYPE_CTR, i);
|
||||||
pos / (1024*1024), (u32) FIRM_NAND_SIZE / (1024 * 1024));
|
if (ret != 0) break;
|
||||||
|
u32 fsize = np_info.count * 0x200;
|
||||||
|
u32 foffset = np_info.sector * 0x200;
|
||||||
|
char bakname[64];
|
||||||
|
snprintf(bakname, 64, NAME_FIRMBACKUP, i);
|
||||||
|
snprintf(msgBackup, 64, "FIRM backup (%lu/%lu)", i, n_firms);
|
||||||
ShowInstallerStatus();
|
ShowInstallerStatus();
|
||||||
if ((ReadNandBytes(WORK_BUFFER, FIRM_NAND_OFFSET + pos, bytes, 0xFF) != 0) ||
|
if ((ReadNandBytes(WORK_BUFFER, foffset, fsize, 0xFF) != 0) ||
|
||||||
(SafeWriteFile(&fp, WORK_BUFFER, pos, bytes) != 0))
|
(SafeQWriteFile(bakname, WORK_BUFFER, fsize) != 0)) {
|
||||||
ret = 1;
|
ret = 1;
|
||||||
ShowProgress(pos + bytes, FIRM_NAND_SIZE, "FIRM backup");
|
break;
|
||||||
|
}
|
||||||
|
ShowProgress(i + 1, n_firms, "FIRM backup");
|
||||||
}
|
}
|
||||||
f_close(&fp);
|
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
snprintf(msgBackup, 64, "FIRM backup fail");
|
snprintf(msgBackup, 64, "FIRM backup fail");
|
||||||
statusBackup = STATUS_RED;
|
statusBackup = STATUS_RED;
|
||||||
@ -251,20 +271,26 @@ u32 SafeB9SInstaller(void) {
|
|||||||
#ifndef NO_WRITE
|
#ifndef NO_WRITE
|
||||||
ShowProgress(0, 0, "FIRM install");
|
ShowProgress(0, 0, "FIRM install");
|
||||||
do {
|
do {
|
||||||
ret = SafeWriteNand(FIRM_BUFFER, FIRM0_NAND_OFFSET, firm_size, 0x06);
|
for (u32 i = 0; i < n_firms; i++) {
|
||||||
|
NandPartitionInfo np_info;
|
||||||
|
ret = GetNandPartitionInfo(&np_info, NP_TYPE_FIRM, NP_SUBTYPE_CTR, i);
|
||||||
|
if (ret != 0) break;
|
||||||
|
ret = SafeWriteNand(FIRM_BUFFER, np_info.sector * 0x200, firm_size, np_info.keyslot);
|
||||||
|
if (ret != 0) break;
|
||||||
|
ShowProgress(i+1, n_firms, "FIRM install");
|
||||||
|
snprintf(msgInstall, 64, "FIRM install (%li/8)", i+1);
|
||||||
|
ShowInstallerStatus();
|
||||||
|
}
|
||||||
if (ret != 0) break;
|
if (ret != 0) break;
|
||||||
ShowProgress(1, 2, "FIRM install (1/2)");
|
uint8_t emptyStage2[MAX_STAGE2_SIZE]={0};
|
||||||
snprintf(msgInstall, 64, "FIRM install (1/2)");
|
// Uninstall a9lh stage 2 always if firm flashed ok
|
||||||
ShowInstallerStatus();
|
// This prevents false positives in the event of cfw uninstall
|
||||||
ret = SafeWriteNand(FIRM_BUFFER, FIRM1_NAND_OFFSET, firm_size, 0x06);
|
ret = sdmmc_nand_writesectors(0x5C000, MAX_STAGE2_SIZE / 0x200, emptyStage2);
|
||||||
if (ret != 0) break;
|
if (ret != 0) break;
|
||||||
ShowProgress(1, 2, "FIRM install (2/2)");
|
|
||||||
snprintf(msgInstall, 64, "FIRM install (2/2)");
|
|
||||||
ShowInstallerStatus();
|
|
||||||
if ((IS_A9LH && !IS_SIGHAX)) {
|
if ((IS_A9LH && !IS_SIGHAX)) {
|
||||||
snprintf(msgInstall, 64, "0x96 revert...");
|
snprintf(msgInstall, 64, "0x96 revert...");
|
||||||
ShowInstallerStatus();
|
ShowInstallerStatus();
|
||||||
ret = SafeWriteNand(secret_sector, 0x96 * 0x200, 0x200, IS_O3DS ? 0xFF : 0x11);
|
ret = SafeWriteNand(secret_sector, SECTOR_SECRET * 0x200, 0x200, IS_O3DS ? 0xFF : 0x11);
|
||||||
if (ret == 0) snprintf(msgA9lh, 64, "uninstalled");
|
if (ret == 0) snprintf(msgA9lh, 64, "uninstalled");
|
||||||
}
|
}
|
||||||
} while (false);
|
} while (false);
|
||||||
@ -282,41 +308,42 @@ u32 SafeB9SInstaller(void) {
|
|||||||
ShowInstallerStatus();
|
ShowInstallerStatus();
|
||||||
}
|
}
|
||||||
#elif !defined FAIL_TEST
|
#elif !defined FAIL_TEST
|
||||||
snprintf(msgInstall, 64, "test mode, not done");
|
snprintf(msgInstall, 64, "no write mode");
|
||||||
statusInstall = STATUS_YELLOW;
|
statusInstall = STATUS_YELLOW;
|
||||||
return 0;
|
return 0;
|
||||||
#else
|
#else
|
||||||
snprintf(msgInstall, 64, "fail test mode...");
|
snprintf(msgInstall, 64, "emergency mode");
|
||||||
statusInstall = STATUS_YELLOW;
|
statusInstall = STATUS_YELLOW;
|
||||||
ShowInstallerStatus();
|
ShowInstallerStatus();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// if we end up here: uhoh
|
// if we end up here: uhoh
|
||||||
ShowPrompt(false, "SafeB9SInstaller failed!\nThis really should not have happened :/.");
|
ShowPrompt(false, APP_TITLE " failed!\nThis really should not have happened :/.");
|
||||||
ShowPrompt(false, "Your system is now reverted to\nit's earlier state.\n \nDO NOT TURN OFF YOUR 3DS NOW!");
|
ShowPrompt(false, "Your system is now reverted to\nit's earlier state.\n \nDO NOT TURN OFF YOUR 3DS NOW!");
|
||||||
|
|
||||||
// try to revert to the earlier state
|
// try to revert to the earlier state
|
||||||
snprintf(msgBackup, 64, "FIRM restore...");
|
snprintf(msgBackup, 64, "FIRM restore...");
|
||||||
statusBackup = STATUS_YELLOW;
|
statusBackup = STATUS_YELLOW;
|
||||||
ShowInstallerStatus();
|
ShowInstallerStatus();
|
||||||
if (f_open(&fp, NAME_FIRMBACKUP, FA_READ|FA_OPEN_EXISTING) != FR_OK) {
|
|
||||||
snprintf(msgBackup, 64, "FIRM restore fail");
|
|
||||||
statusBackup = STATUS_RED;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
ShowProgress(0, 0, "FIRM restore");
|
ShowProgress(0, 0, "FIRM restore");
|
||||||
for (u32 pos = 0; (pos < FIRM_NAND_SIZE) && (ret == 0); pos += WORK_BUFFER_SIZE) {
|
for (u32 i = 0; i < n_firms; i++) {
|
||||||
UINT bytes = min(WORK_BUFFER_SIZE, FIRM_NAND_SIZE - pos);
|
NandPartitionInfo np_info;
|
||||||
snprintf(msgBackup, 64, "FIRM restore (%luMB/%luMB)",
|
ret = GetNandPartitionInfo(&np_info, NP_TYPE_FIRM, NP_SUBTYPE_CTR, i);
|
||||||
pos / (1024*1024), (u32) FIRM_NAND_SIZE / (1024 * 1024));
|
if (ret != 0) break;
|
||||||
|
u32 fsize = np_info.count * 0x200;
|
||||||
|
u32 foffset = np_info.sector * 0x200;
|
||||||
|
char bakname[64];
|
||||||
|
snprintf(bakname, 64, NAME_FIRMBACKUP, i);
|
||||||
|
snprintf(msgBackup, 64, "FIRM restore (%lu/%lu)", i, n_firms);
|
||||||
ShowInstallerStatus();
|
ShowInstallerStatus();
|
||||||
if ((f_read(&fp, WORK_BUFFER, bytes, &bt) != FR_OK) || (bt != bytes) ||
|
if ((f_qread(bakname, WORK_BUFFER, 0, fsize, &bt) != FR_OK) || (bt != fsize) ||
|
||||||
(WriteNandBytes(WORK_BUFFER, FIRM_NAND_OFFSET + pos, bytes, 0xFF) != 0))
|
(WriteNandBytes(WORK_BUFFER, foffset, fsize, 0xFF) != 0)) {
|
||||||
ret = 1;
|
ret = 1;
|
||||||
ShowProgress(pos + bytes, FIRM_NAND_SIZE, "FIRM restore");
|
break;
|
||||||
|
}
|
||||||
|
ShowProgress(i + 1, n_firms, "FIRM restore");
|
||||||
}
|
}
|
||||||
f_close(&fp);
|
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
snprintf(msgBackup, 64, "FIRM restore fail");
|
snprintf(msgBackup, 64, "FIRM restore fail");
|
||||||
statusBackup = STATUS_RED;
|
statusBackup = STATUS_RED;
|
||||||
@ -327,7 +354,7 @@ u32 SafeB9SInstaller(void) {
|
|||||||
ShowInstallerStatus();
|
ShowInstallerStatus();
|
||||||
u8 sector_backup[0x200];
|
u8 sector_backup[0x200];
|
||||||
if ((f_qread(NAME_SECTORBACKUP, sector_backup, 0, 0x200, &bt) != FR_OK) || (bt != 0x200) ||
|
if ((f_qread(NAME_SECTORBACKUP, sector_backup, 0, 0x200, &bt) != FR_OK) || (bt != 0x200) ||
|
||||||
(WriteNandSectors(sector_backup, 0x96, 1, 0xFF) != 0)) {
|
(WriteNandSectors(sector_backup, SECTOR_SECRET, 1, 0xFF) != 0)) {
|
||||||
snprintf(msgBackup, 64, "0x96 restore fail");
|
snprintf(msgBackup, 64, "0x96 restore fail");
|
||||||
statusBackup = STATUS_RED;
|
statusBackup = STATUS_RED;
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -11,15 +11,27 @@ void Reboot()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int main()
|
u8 *top_screen, *bottom_screen;
|
||||||
|
|
||||||
|
void main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
|
// Fetch the framebuffer addresses
|
||||||
|
if(argc >= 2) {
|
||||||
|
// newer entrypoints
|
||||||
|
u8 **fb = (u8 **)(void *)argv[1];
|
||||||
|
top_screen = fb[0];
|
||||||
|
bottom_screen = fb[2];
|
||||||
|
} else {
|
||||||
|
// outdated entrypoints
|
||||||
|
top_screen = (u8*)(*(u32*)0x23FFFE00);
|
||||||
|
bottom_screen = (u8*)(*(u32*)0x23FFFE08);
|
||||||
|
}
|
||||||
ClearScreenF(true, true, COLOR_STD_BG);
|
ClearScreenF(true, true, COLOR_STD_BG);
|
||||||
u32 ret = SafeB9SInstaller();
|
u32 ret = SafeB9SInstaller();
|
||||||
ShowInstallerStatus(); // update installer status one last time
|
ShowInstallerStatus(); // update installer status one last time
|
||||||
|
fs_deinit();
|
||||||
if (ret) ShowPrompt(false, "SigHaxed FIRM was not installed!\nCheck lower screen for info.");
|
if (ret) ShowPrompt(false, "SigHaxed FIRM was not installed!\nCheck lower screen for info.");
|
||||||
else ShowPrompt(false, "SigHaxed FIRM install success!");
|
else ShowPrompt(false, "SigHaxed FIRM install success!");
|
||||||
ClearScreenF(true, true, COLOR_STD_BG);
|
ClearScreenF(true, true, COLOR_STD_BG);
|
||||||
fs_deinit();
|
|
||||||
Reboot();
|
Reboot();
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
@ -2,14 +2,35 @@
|
|||||||
#include "keydb.h"
|
#include "keydb.h"
|
||||||
#include "aes.h"
|
#include "aes.h"
|
||||||
#include "sha.h"
|
#include "sha.h"
|
||||||
|
#include "fatmbr.h"
|
||||||
|
#include "unittype.h"
|
||||||
#include "sdmmc.h"
|
#include "sdmmc.h"
|
||||||
#include "qff.h"
|
|
||||||
|
|
||||||
|
#define KEY95_SHA256 ((IS_DEVKIT) ? slot0x11Key95dev_sha256 : slot0x11Key95_sha256)
|
||||||
|
|
||||||
|
// see: https://www.3dbrew.org/wiki/NCSD#NCSD_header
|
||||||
|
static const u32 np_keyslots[9][4] = { // [NP_TYPE][NP_SUBTYPE]
|
||||||
|
{ 0xFF, 0xFF, 0xFF, 0xFF }, // none
|
||||||
|
{ 0xFF, 0x03, 0x04, 0x05 }, // standard
|
||||||
|
{ 0xFF, 0x03, 0x04, 0x05 }, // FAT (custom, not in NCSD)
|
||||||
|
{ 0xFF, 0xFF, 0x06, 0xFF }, // FIRM
|
||||||
|
{ 0xFF, 0xFF, 0x07, 0xFF }, // AGBSAVE
|
||||||
|
{ 0xFF, 0xFF, 0xFF, 0xFF }, // NCSD (custom)
|
||||||
|
{ 0xFF, 0xFF, 0xFF, 0xFF }, // D0K3 (custom)
|
||||||
|
{ 0xFF, 0xFF, 0xFF, 0x11 }, // SECRET (custom)
|
||||||
|
{ 0xFF, 0xFF, 0xFF, 0xFF } // BONUS (custom)
|
||||||
|
};
|
||||||
|
|
||||||
static u8 slot0x05KeyY[0x10] = { 0x00 }; // need to load this from FIRM0 / external file
|
static u8 slot0x05KeyY[0x10] = { 0x00 }; // need to load this from FIRM0 / external file
|
||||||
static const u8 slot0x05KeyY_sha256[0x20] = { // hash for slot0x05KeyY (16 byte)
|
static const u8 slot0x05KeyY_sha256[0x20] = { // hash for slot0x05KeyY (16 byte)
|
||||||
0x98, 0x24, 0x27, 0x14, 0x22, 0xB0, 0x6B, 0xF2, 0x10, 0x96, 0x9C, 0x36, 0x42, 0x53, 0x7C, 0x86,
|
0x98, 0x24, 0x27, 0x14, 0x22, 0xB0, 0x6B, 0xF2, 0x10, 0x96, 0x9C, 0x36, 0x42, 0x53, 0x7C, 0x86,
|
||||||
0x62, 0x22, 0x5C, 0xFD, 0x6F, 0xAE, 0x9B, 0x0A, 0x85, 0xA5, 0xCE, 0x21, 0xAA, 0xB6, 0xC8, 0x4D
|
0x62, 0x22, 0x5C, 0xFD, 0x6F, 0xAE, 0x9B, 0x0A, 0x85, 0xA5, 0xCE, 0x21, 0xAA, 0xB6, 0xC8, 0x4D
|
||||||
};
|
};
|
||||||
|
static const u8 slot0x24KeyY_sha256[0x20] = { // hash for slot0x24KeyY (16 byte)
|
||||||
|
0x5F, 0x04, 0x01, 0x22, 0x95, 0xB2, 0x23, 0x70, 0x12, 0x40, 0x53, 0x30, 0xC0, 0xA7, 0xBF, 0x7C,
|
||||||
|
0xD4, 0x40, 0x92, 0x25, 0xD1, 0x9D, 0xA2, 0xDE, 0xCD, 0xC7, 0x12, 0x97, 0x08, 0x46, 0x54, 0xB7
|
||||||
|
};
|
||||||
|
|
||||||
static const u8 slot0x11Key95_sha256[0x20] = { // slot0x11Key95 hash (first 16 byte of sector0x96)
|
static const u8 slot0x11Key95_sha256[0x20] = { // slot0x11Key95 hash (first 16 byte of sector0x96)
|
||||||
0xBA, 0xC1, 0x40, 0x9C, 0x6E, 0xE4, 0x1F, 0x04, 0xAA, 0xC4, 0xE2, 0x09, 0x5C, 0xE9, 0x4F, 0x78,
|
0xBA, 0xC1, 0x40, 0x9C, 0x6E, 0xE4, 0x1F, 0x04, 0xAA, 0xC4, 0xE2, 0x09, 0x5C, 0xE9, 0x4F, 0x78,
|
||||||
@ -21,32 +42,6 @@ static const u8 slot0x11Key95dev_sha256[0x20] = { // slot0x11Key95 hash (first 1
|
|||||||
0xD7, 0x6C, 0xE9, 0xAD, 0xE7, 0xFE, 0x9A, 0x25, 0x4E, 0x4A, 0x0C, 0x82, 0x67, 0xB5, 0x4A, 0x7B
|
0xD7, 0x6C, 0xE9, 0xAD, 0xE7, 0xFE, 0x9A, 0x25, 0x4E, 0x4A, 0x0C, 0x82, 0x67, 0xB5, 0x4A, 0x7B
|
||||||
};
|
};
|
||||||
|
|
||||||
static const u8 nand_magic_n3ds[0x60] = { // NCSD NAND header N3DS magic
|
|
||||||
0x4E, 0x43, 0x53, 0x44, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x01, 0x04, 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x03, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x05, 0x00, 0x00, 0x88, 0x05, 0x00, 0x80, 0x01, 0x00, 0x00,
|
|
||||||
0x80, 0x89, 0x05, 0x00, 0x00, 0x20, 0x00, 0x00, 0x80, 0xA9, 0x05, 0x00, 0x00, 0x20, 0x00, 0x00,
|
|
||||||
0x80, 0xC9, 0x05, 0x00, 0x80, 0xF6, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
||||||
};
|
|
||||||
|
|
||||||
static const u8 nand_magic_o3ds[0x60] = { // NCSD NAND header O3DS magic
|
|
||||||
0x4E, 0x43, 0x53, 0x44, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x01, 0x04, 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x05, 0x00, 0x00, 0x88, 0x05, 0x00, 0x80, 0x01, 0x00, 0x00,
|
|
||||||
0x80, 0x89, 0x05, 0x00, 0x00, 0x20, 0x00, 0x00, 0x80, 0xA9, 0x05, 0x00, 0x00, 0x20, 0x00, 0x00,
|
|
||||||
0x80, 0xC9, 0x05, 0x00, 0x80, 0xAE, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
||||||
};
|
|
||||||
|
|
||||||
/*static const u8 twl_mbr[0x42] = { // encrypted version inside the NCSD NAND header (@0x1BE)
|
|
||||||
0x00, 0x04, 0x18, 0x00, 0x06, 0x01, 0xA0, 0x3F, 0x97, 0x00, 0x00, 0x00, 0xA9, 0x7D, 0x04, 0x00,
|
|
||||||
0x00, 0x04, 0x8E, 0x40, 0x06, 0x01, 0xA0, 0xC3, 0x8D, 0x80, 0x04, 0x00, 0xB3, 0x05, 0x01, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x55, 0xAA
|
|
||||||
};*/
|
|
||||||
|
|
||||||
static u8 CtrNandCtr[16];
|
static u8 CtrNandCtr[16];
|
||||||
static u8 TwlNandCtr[16];
|
static u8 TwlNandCtr[16];
|
||||||
static u8 OtpSha256[32] = { 0 };
|
static u8 OtpSha256[32] = { 0 };
|
||||||
@ -54,23 +49,24 @@ static u8 OtpSha256[32] = { 0 };
|
|||||||
|
|
||||||
u32 LoadKeyYFromP9(u8* key, const u8* keyhash, u32 offset, u32 keyslot)
|
u32 LoadKeyYFromP9(u8* key, const u8* keyhash, u32 offset, u32 keyslot)
|
||||||
{
|
{
|
||||||
static u32 offsetA9l = 0x066A00; // fixed offset, this only has to work for FIRM90 / FIRM81
|
static const u32 offsetA9l = 0x066A00; // fixed offset, this only has to work for FIRM90 / FIRM81
|
||||||
|
static const u32 sector_firm0 = 0x058980; // standard firm0 sector (this only has to work in A9LH anyways)
|
||||||
u8 ctr0x15[16] __attribute__((aligned(32)));
|
u8 ctr0x15[16] __attribute__((aligned(32)));
|
||||||
u8 keyY0x15[16] __attribute__((aligned(32)));
|
u8 keyY0x15[16] __attribute__((aligned(32)));
|
||||||
u8 keyY[16] __attribute__((aligned(32)));
|
u8 keyY[16] __attribute__((aligned(32)));
|
||||||
u8 header[0x200];
|
u8 header[0x200];
|
||||||
|
|
||||||
// check arm9loaderhax
|
// check arm9loaderhax
|
||||||
if (!IS_A9LH || (offset < (offsetA9l + 0x0800))) return 1;
|
if (!IS_A9LH || IS_SIGHAX || (offset < (offsetA9l + 0x0800))) return 1;
|
||||||
|
|
||||||
// section 2 (arm9loader) header of FIRM
|
// section 2 (arm9loader) header of FIRM
|
||||||
// this is @0x066A00 in FIRM90 & FIRM81
|
// this is @0x066A00 in FIRM90 & FIRM81
|
||||||
ReadNandBytes(header, (SECTOR_FIRM0 * 0x200) + offsetA9l, 0x200, 0x06);
|
ReadNandBytes(header, (sector_firm0 * 0x200) + offsetA9l, 0x200, 0x06);
|
||||||
memcpy(keyY0x15, header + 0x10, 0x10); // 0x15 keyY
|
memcpy(keyY0x15, header + 0x10, 0x10); // 0x15 keyY
|
||||||
memcpy(ctr0x15, header + 0x20, 0x10); // 0x15 counter
|
memcpy(ctr0x15, header + 0x20, 0x10); // 0x15 counter
|
||||||
|
|
||||||
// read and decrypt the encrypted keyY
|
// read and decrypt the encrypted keyY
|
||||||
ReadNandBytes(keyY, (SECTOR_FIRM0 * 0x200) + offset, 0x10, 0x06);
|
ReadNandBytes(keyY, (sector_firm0 * 0x200) + offset, 0x10, 0x06);
|
||||||
setup_aeskeyY(0x15, keyY0x15);
|
setup_aeskeyY(0x15, keyY0x15);
|
||||||
use_aeskey(0x15);
|
use_aeskey(0x15);
|
||||||
ctr_decrypt_byte(keyY, keyY, 0x10, offset - (offsetA9l + 0x800), AES_CNT_CTRNAND_MODE, ctr0x15);
|
ctr_decrypt_byte(keyY, keyY, 0x10, offset - (offsetA9l + 0x800), AES_CNT_CTRNAND_MODE, ctr0x15);
|
||||||
@ -92,21 +88,25 @@ bool InitNandCrypto(void)
|
|||||||
{
|
{
|
||||||
// part #0: KeyX / KeyY for secret sector 0x96
|
// part #0: KeyX / KeyY for secret sector 0x96
|
||||||
// on a9lh this MUST be run before accessing the SHA register in any other way
|
// on a9lh this MUST be run before accessing the SHA register in any other way
|
||||||
if (IS_A9LH) { // for a9lh
|
if (IS_UNLOCKED) { // if OTP is unlocked
|
||||||
|
// see: https://www.3dbrew.org/wiki/OTP_Registers
|
||||||
|
sha_quick(OtpSha256, (u8*) 0x10012000, 0x90, SHA256_MODE);
|
||||||
|
} else if (IS_A9LH) { // for a9lh
|
||||||
// store the current SHA256 from register
|
// store the current SHA256 from register
|
||||||
memcpy(OtpSha256, (void*) REG_SHAHASH, 32);
|
memcpy(OtpSha256, (void*) REG_SHAHASH, 32);
|
||||||
if (!CheckSector0x96Crypto()) { // 3dssafe users be damned (no offense meant to mashers)
|
}
|
||||||
u8 __attribute__((aligned(32))) otp0x90[0x90];
|
if (!CheckSector0x96Crypto()) { // if all else fails...
|
||||||
u8 __attribute__((aligned(32))) otp_key[0x10];
|
u8 __attribute__((aligned(32))) otp0x90[0x90];
|
||||||
u8 __attribute__((aligned(32))) otp_iv[0x10];
|
u8 __attribute__((aligned(32))) otp_key[0x10];
|
||||||
memcpy(otp0x90, (u8*) 0x01FFB800, 0x90);
|
u8 __attribute__((aligned(32))) otp_iv[0x10];
|
||||||
if ((LoadKeyFromFile(otp_key, 0x11, 'N', "OTP") == 0) &&
|
memcpy(otp0x90, (u8*) 0x01FFB800, 0x90);
|
||||||
(LoadKeyFromFile(otp_iv, 0x11, 'I', "IVOTP") == 0)) {
|
if ((LoadKeyFromFile(otp_key, 0x11, 'N', "OTP") == 0) &&
|
||||||
setup_aeskey(0x11, otp_key);
|
((LoadKeyFromFile(otp_iv, 0x11, 'I', "IVOTP") == 0) ||
|
||||||
use_aeskey(0x11);
|
(LoadKeyFromFile(otp_iv, 0x11, 'I', "OTP") == 0))) {
|
||||||
cbc_encrypt(otp0x90, otp0x90, 0x90 / 0x10, AES_CNT_TITLEKEY_ENCRYPT_MODE, otp_iv);
|
setup_aeskey(0x11, otp_key);
|
||||||
sha_quick(OtpSha256, otp0x90, 0x90, SHA256_MODE);
|
use_aeskey(0x11);
|
||||||
}
|
cbc_encrypt(otp0x90, otp0x90, 0x90 / 0x10, AES_CNT_TITLEKEY_ENCRYPT_MODE, otp_iv);
|
||||||
|
sha_quick(OtpSha256, otp0x90, 0x90, SHA256_MODE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,7 +124,7 @@ bool InitNandCrypto(void)
|
|||||||
|
|
||||||
// part #2: TWL KEY
|
// part #2: TWL KEY
|
||||||
// see: https://www.3dbrew.org/wiki/Memory_layout#ARM9_ITCM
|
// see: https://www.3dbrew.org/wiki/Memory_layout#ARM9_ITCM
|
||||||
if (IS_A9LH) { // only for a9lh
|
if (IS_A9LH) { // only for a9lh (and sighax, for now)
|
||||||
u32* TwlCustId = (u32*) (0x01FFB808);
|
u32* TwlCustId = (u32*) (0x01FFB808);
|
||||||
u8 TwlKeyX[16] __attribute__((aligned(32)));
|
u8 TwlKeyX[16] __attribute__((aligned(32)));
|
||||||
u8 TwlKeyY[16] __attribute__((aligned(32)));
|
u8 TwlKeyY[16] __attribute__((aligned(32)));
|
||||||
@ -148,14 +148,21 @@ bool InitNandCrypto(void)
|
|||||||
use_aeskey(0x03);
|
use_aeskey(0x03);
|
||||||
}
|
}
|
||||||
|
|
||||||
// part #3: CTRNAND N3DS KEY
|
// part #3: CTRNAND N3DS KEY / AGBSAVE CMAC KEY
|
||||||
// thanks AuroraWright and Gelex for advice on this
|
// thanks AuroraWright and Gelex for advice on this
|
||||||
// see: https://github.com/AuroraWright/Luma3DS/blob/master/source/crypto.c#L347
|
// see: https://github.com/AuroraWright/Luma3DS/blob/master/source/crypto.c#L347
|
||||||
if (IS_A9LH) { // only for a9lh
|
if (IS_A9LH && !IS_SIGHAX) { // only on A9LH, not required on sighax
|
||||||
// keyY 0x05 is encrypted @0x0EB014 in the FIRM90
|
// keyY 0x05 is encrypted @0x0EB014 in the FIRM90
|
||||||
// keyY 0x05 is encrypted @0x0EB24C in the FIRM81
|
// keyY 0x05 is encrypted @0x0EB24C in the FIRM81
|
||||||
if ((LoadKeyYFromP9(slot0x05KeyY, slot0x05KeyY_sha256, 0x0EB014, 0x05) != 0) &&
|
if ((LoadKeyYFromP9(slot0x05KeyY, slot0x05KeyY_sha256, 0x0EB014, 0x05) != 0) &&
|
||||||
(LoadKeyYFromP9(slot0x05KeyY, slot0x05KeyY_sha256, 0x0EB24C, 0x05) != 0)) {};
|
(LoadKeyYFromP9(slot0x05KeyY, slot0x05KeyY_sha256, 0x0EB24C, 0x05) != 0))
|
||||||
|
LoadKeyFromFile(slot0x05KeyY, 0x05, 'Y', NULL);
|
||||||
|
|
||||||
|
// keyY 0x24 is encrypted @0x0E62DC in the FIRM90
|
||||||
|
// keyY 0x24 is encrypted @0x0E6514 in the FIRM81
|
||||||
|
if ((LoadKeyYFromP9(NULL, slot0x24KeyY_sha256, 0x0E62DC, 0x24) != 0) &&
|
||||||
|
(LoadKeyYFromP9(NULL, slot0x24KeyY_sha256, 0x0E6514, 0x24) != 0))
|
||||||
|
LoadKeyFromFile(NULL, 0x24, 'Y', NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -164,17 +171,11 @@ bool InitNandCrypto(void)
|
|||||||
bool CheckSlot0x05Crypto(void)
|
bool CheckSlot0x05Crypto(void)
|
||||||
{
|
{
|
||||||
// step #1 - check the slot0x05KeyY SHA-256
|
// step #1 - check the slot0x05KeyY SHA-256
|
||||||
u8 shasum[32];
|
if (sha_cmp(slot0x05KeyY_sha256, slot0x05KeyY, 16, SHA256_MODE) == 0)
|
||||||
sha_quick(shasum, slot0x05KeyY, 16, SHA256_MODE);
|
|
||||||
if (memcmp(shasum, slot0x05KeyY_sha256, 32) == 0)
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// step #2 - check actual CTRNAND magic
|
// step #2 - check actual presence of CTRNAND FAT
|
||||||
const u8 magic[8] = {0xE9, 0x00, 0x00, 0x43, 0x54, 0x52, 0x20, 0x20};
|
if (GetNandPartitionInfo(NULL, NP_TYPE_STD, NP_SUBTYPE_CTR_N, 0) == 0)
|
||||||
const u32 sector = 0x05CAD7;
|
|
||||||
u8 buffer[0x200];
|
|
||||||
ReadNandSectors(buffer, sector, 1, 0x05);
|
|
||||||
if (memcmp(buffer, magic, 8) == 0)
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// failed if we arrive here
|
// failed if we arrive here
|
||||||
@ -184,38 +185,39 @@ bool CheckSlot0x05Crypto(void)
|
|||||||
bool CheckSector0x96Crypto(void)
|
bool CheckSector0x96Crypto(void)
|
||||||
{
|
{
|
||||||
u8 buffer[0x200];
|
u8 buffer[0x200];
|
||||||
ReadNandSectors(buffer, 0x96, 1, 0x11);
|
ReadNandSectors(buffer, SECTOR_SECRET, 1, 0x11);
|
||||||
return (sha_cmp((IS_DEVKIT) ? slot0x11Key95dev_sha256 : slot0x11Key95_sha256, buffer, 16, SHA256_MODE) == 0);
|
return (sha_cmp(KEY95_SHA256, buffer, 16, SHA256_MODE) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CheckFirmCrypto(void)
|
bool CheckFirmCrypto(void)
|
||||||
{
|
{
|
||||||
// check the FIRM magic
|
// check the FIRM magic
|
||||||
const u8 magic[8] = {'F', 'I', 'R', 'M'};
|
const u8 magic[8] = {'F', 'I', 'R', 'M'};
|
||||||
const u32 sectors[] = { SECTOR_FIRM0, SECTOR_FIRM1 };
|
for (u32 i = 0; i < 8; i++) {
|
||||||
u8 buffer[0x200];
|
NandPartitionInfo np_info;
|
||||||
for (u32 i = 0; i < sizeof(sectors) / sizeof(u32); i++) {
|
u8 buffer[0x200];
|
||||||
ReadNandSectors(buffer, sectors[i], 1, 0x06);
|
if ((GetNandPartitionInfo(&np_info, NP_TYPE_FIRM, NP_SUBTYPE_CTR, i) != 0) ||
|
||||||
if (memcmp(buffer, magic, sizeof(magic)) != 0) return false;
|
(ReadNandSectors(buffer, np_info.sector, 1, np_info.keyslot) != 0)) break;
|
||||||
|
if (memcmp(buffer, magic, sizeof(magic)) == 0) return true; // success
|
||||||
}
|
}
|
||||||
|
|
||||||
// success if we arrive here
|
// failed if we arrive here
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CryptNand(void* buffer, u32 sector, u32 count, u32 keyslot)
|
void CryptNand(void* buffer, u32 sector, u32 count, u32 keyslot)
|
||||||
{
|
{
|
||||||
u32 mode = (sector >= SECTOR_TWL + SIZE_TWL) ? AES_CNT_CTRNAND_MODE : AES_CNT_TWLNAND_MODE;
|
u32 mode = (keyslot != 0x03) ? AES_CNT_CTRNAND_MODE : AES_CNT_TWLNAND_MODE; // somewhat hacky
|
||||||
u8 ctr[16] __attribute__((aligned(32)));
|
u8 ctr[16] __attribute__((aligned(32)));
|
||||||
u32 blocks = count * (0x200 / 0x10);
|
u32 blocks = count * (0x200 / 0x10);
|
||||||
|
|
||||||
// copy NAND CTR and increment it
|
// copy NAND CTR and increment it
|
||||||
memcpy(ctr, (sector >= SECTOR_TWL + SIZE_TWL) ? CtrNandCtr : TwlNandCtr, 16);
|
memcpy(ctr, (keyslot != 0x03) ? CtrNandCtr : TwlNandCtr, 16); // hacky again
|
||||||
add_ctr(ctr, sector * (0x200 / 0x10));
|
add_ctr(ctr, sector * (0x200 / 0x10));
|
||||||
|
|
||||||
// decrypt the data
|
// decrypt the data
|
||||||
use_aeskey(keyslot);
|
use_aeskey(keyslot);
|
||||||
ctr_decrypt(buffer, buffer, blocks, mode, ctr);
|
ctr_decrypt((void*) buffer, (void*) buffer, blocks, mode, ctr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CryptSector0x96(void* buffer, bool encrypt)
|
void CryptSector0x96(void* buffer, bool encrypt)
|
||||||
@ -228,16 +230,16 @@ void CryptSector0x96(void* buffer, bool encrypt)
|
|||||||
|
|
||||||
// decrypt the sector
|
// decrypt the sector
|
||||||
use_aeskey(0x11);
|
use_aeskey(0x11);
|
||||||
ecb_decrypt(buffer, buffer, 0x200 / AES_BLOCK_SIZE, mode);
|
ecb_decrypt((void*) buffer, (void*) buffer, 0x200 / AES_BLOCK_SIZE, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ReadNandBytes(void* buffer, u32 offset, u32 count, u32 keyslot)
|
int ReadNandBytes(void* buffer, u64 offset, u64 count, u32 keyslot)
|
||||||
{
|
{
|
||||||
u8* buffer8 = (u8*) buffer;
|
|
||||||
if (!(offset % 0x200) && !(count % 0x200)) { // aligned data -> simple case
|
if (!(offset % 0x200) && !(count % 0x200)) { // aligned data -> simple case
|
||||||
// simple wrapper function for ReadNandSectors(...)
|
// simple wrapper function for ReadNandSectors(...)
|
||||||
return ReadNandSectors(buffer8, offset / 0x200, count / 0x200, keyslot);
|
return ReadNandSectors(buffer, offset / 0x200, count / 0x200, keyslot);
|
||||||
} else { // misaligned data -> -___-
|
} else { // misaligned data -> -___-
|
||||||
|
u8* buffer8 = (u8*) buffer;
|
||||||
u8 l_buffer[0x200];
|
u8 l_buffer[0x200];
|
||||||
int errorcode = 0;
|
int errorcode = 0;
|
||||||
if (offset % 0x200) { // handle misaligned offset
|
if (offset % 0x200) { // handle misaligned offset
|
||||||
@ -264,13 +266,13 @@ int ReadNandBytes(void* buffer, u32 offset, u32 count, u32 keyslot)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int WriteNandBytes(const void* buffer, u32 offset, u32 count, u32 keyslot)
|
int WriteNandBytes(const void* buffer, u64 offset, u64 count, u32 keyslot)
|
||||||
{
|
{
|
||||||
u8* buffer8 = (u8*) buffer;
|
|
||||||
if (!(offset % 0x200) && !(count % 0x200)) { // aligned data -> simple case
|
if (!(offset % 0x200) && !(count % 0x200)) { // aligned data -> simple case
|
||||||
// simple wrapper function for WriteNandSectors(...)
|
// simple wrapper function for WriteNandSectors(...)
|
||||||
return WriteNandSectors(buffer8, offset / 0x200, count / 0x200, keyslot);
|
return WriteNandSectors(buffer, offset / 0x200, count / 0x200, keyslot);
|
||||||
} else { // misaligned data -> -___-
|
} else { // misaligned data -> -___-
|
||||||
|
u8* buffer8 = (u8*) buffer8;
|
||||||
u8 l_buffer[0x200];
|
u8 l_buffer[0x200];
|
||||||
int errorcode = 0;
|
int errorcode = 0;
|
||||||
if (offset % 0x200) { // handle misaligned offset
|
if (offset % 0x200) { // handle misaligned offset
|
||||||
@ -303,23 +305,24 @@ int WriteNandBytes(const void* buffer, u32 offset, u32 count, u32 keyslot)
|
|||||||
|
|
||||||
int ReadNandSectors(void* buffer, u32 sector, u32 count, u32 keyslot)
|
int ReadNandSectors(void* buffer, u32 sector, u32 count, u32 keyslot)
|
||||||
{
|
{
|
||||||
|
u8* buffer8 = (u8*) buffer;
|
||||||
if (!count) return 0; // <--- just to be safe
|
if (!count) return 0; // <--- just to be safe
|
||||||
int errorcode = sdmmc_nand_readsectors(sector, count, buffer);
|
int errorcode = sdmmc_nand_readsectors(sector, count, buffer8);
|
||||||
if (errorcode) return errorcode;
|
if (errorcode) return errorcode;
|
||||||
if ((keyslot == 0x11) && (sector == 0x96)) CryptSector0x96(buffer, false);
|
if ((keyslot == 0x11) && (sector == SECTOR_SECRET)) CryptSector0x96(buffer8, false);
|
||||||
else if (keyslot < 0x40) CryptNand(buffer, sector, count, keyslot);
|
else if (keyslot < 0x40) CryptNand(buffer8, sector, count, keyslot);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int WriteNandSectors(const void* buffer, u32 sector, u32 count, u32 keyslot)
|
int WriteNandSectors(const void* buffer, u32 sector, u32 count, u32 keyslot)
|
||||||
{
|
{
|
||||||
|
u8* buffer8 = (u8*) buffer;
|
||||||
// buffer must not be changed, so this is a little complicated
|
// buffer must not be changed, so this is a little complicated
|
||||||
const u8* buffer8 = (u8*) buffer;
|
|
||||||
for (u32 s = 0; s < count; s += (NAND_BUFFER_SIZE / 0x200)) {
|
for (u32 s = 0; s < count; s += (NAND_BUFFER_SIZE / 0x200)) {
|
||||||
u32 pcount = min((NAND_BUFFER_SIZE/0x200), (count - s));
|
u32 pcount = min((NAND_BUFFER_SIZE/0x200), (count - s));
|
||||||
memcpy(NAND_BUFFER, buffer8 + (s*0x200), pcount * 0x200);
|
memcpy(NAND_BUFFER, buffer8 + (s*0x200), pcount * 0x200);
|
||||||
if ((keyslot == 0x11) && (sector == 0x96)) CryptSector0x96(NAND_BUFFER, true);
|
if ((keyslot == 0x11) && (sector == SECTOR_SECRET)) CryptSector0x96(NAND_BUFFER, true);
|
||||||
else if (keyslot < 0x40) CryptNand(NAND_BUFFER, sector + s, pcount, keyslot);
|
else if (keyslot < 0x40) CryptNand(NAND_BUFFER, sector + s, pcount, keyslot);
|
||||||
int errorcode = sdmmc_nand_writesectors(sector + s, pcount, NAND_BUFFER);
|
int errorcode = sdmmc_nand_writesectors(sector + s, pcount, NAND_BUFFER);
|
||||||
if (errorcode) return errorcode;
|
if (errorcode) return errorcode;
|
||||||
@ -328,43 +331,132 @@ int WriteNandSectors(const void* buffer, u32 sector, u32 count, u32 keyslot)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 CheckNandHeader(void* header)
|
// shamelessly stolen from myself
|
||||||
|
// see: https://github.com/d0k3/GodMode9/blob/master/source/game/ncsd.c#L4
|
||||||
|
u32 ValidateNandNcsdHeader(NandNcsdHeader* header)
|
||||||
{
|
{
|
||||||
// TWL MBR check - ignored
|
u8 zeroes[16] = { 0 };
|
||||||
/*u8 header_dec[0x200];
|
if ((memcmp(header->magic, "NCSD", 4) != 0) || // check magic number
|
||||||
memcpy(header_dec, header, 0x200);
|
(memcmp(header->partitions_fs_type, zeroes, 8) == 0) || header->mediaId) // prevent detection of cart NCSD images
|
||||||
CryptNand(header_dec, 0, 1, 0x03);
|
return 1;
|
||||||
if (memcmp(header_dec + 0x1BE, twl_mbr, sizeof(twl_mbr)) != 0)
|
|
||||||
return 0; // header does not belong to console */
|
|
||||||
|
|
||||||
// header type check
|
u32 data_units = 0;
|
||||||
u8* header_enc = header;
|
u32 firm_count = 0;
|
||||||
if (memcmp(header_enc + 0x100, nand_magic_n3ds, sizeof(nand_magic_n3ds) == 0) == 0)
|
for (u32 i = 0; i < 8; i++) {
|
||||||
return (IS_O3DS) ? 0 : NAND_TYPE_N3DS;
|
NandNcsdPartition* partition = header->partitions + i;
|
||||||
else if (memcmp(header_enc + 0x100, nand_magic_o3ds, sizeof(nand_magic_o3ds) == 0) == 0)
|
u8 np_type = header->partitions_fs_type[i];
|
||||||
return NAND_TYPE_O3DS;
|
if ((i == 0) && !partition->size) return 1; // first content must be there
|
||||||
|
else if (!partition->size) continue;
|
||||||
|
if (!np_type) return 1; // partition must have a type
|
||||||
|
if (partition->offset < data_units)
|
||||||
|
return 1; // overlapping partitions, failed
|
||||||
|
data_units = partition->offset + partition->size;
|
||||||
|
if (np_type == NP_TYPE_FIRM) firm_count++; // count firms
|
||||||
|
}
|
||||||
|
if (data_units > header->size) return 1;
|
||||||
|
if (!firm_count) return 1; // at least one firm is required
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 CheckNandType(void)
|
u32 GetNandNcsdMinSizeSectors(NandNcsdHeader* ncsd)
|
||||||
{
|
{
|
||||||
if (ReadNandSectors(NAND_BUFFER, 0, 1, 0xFF) != 0)
|
u32 nand_minsize = 0;
|
||||||
|
for (u32 prt_idx = 0; prt_idx < 8; prt_idx++) {
|
||||||
|
u32 prt_end = ncsd->partitions[prt_idx].offset + ncsd->partitions[prt_idx].size;
|
||||||
|
if (prt_end > nand_minsize) nand_minsize = prt_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nand_minsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetNandMinSizeSectors(void)
|
||||||
|
{
|
||||||
|
NandNcsdHeader ncsd;
|
||||||
|
if ((ReadNandSectors((u8*) &ncsd, 0, 1, 0xFF) != 0) ||
|
||||||
|
(ValidateNandNcsdHeader(&ncsd) != 0)) return 0;
|
||||||
|
|
||||||
|
return GetNandNcsdMinSizeSectors(&ncsd);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetNandSizeSectors(void)
|
||||||
|
{
|
||||||
|
return getMMCDevice(0)->total_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetNandNcsdPartitionInfo(NandPartitionInfo* info, u32 type, u32 subtype, u32 index, NandNcsdHeader* ncsd)
|
||||||
|
{
|
||||||
|
// safety / set keyslot
|
||||||
|
if ((type == NP_TYPE_FAT) || (type > NP_TYPE_BONUS) || (subtype > NP_SUBTYPE_CTR_N)) return 1;
|
||||||
|
info->keyslot = np_keyslots[type][subtype];
|
||||||
|
|
||||||
|
// full (minimum) NAND "partition"
|
||||||
|
if (type == NP_TYPE_NONE) {
|
||||||
|
info->sector = 0x00;
|
||||||
|
info->count = GetNandNcsdMinSizeSectors(ncsd);
|
||||||
return 0;
|
return 0;
|
||||||
if (memcmp(NAND_BUFFER + 0x100, nand_magic_n3ds, 0x60) == 0) {
|
}
|
||||||
return (IS_O3DS) ? 0 : NAND_TYPE_N3DS;
|
|
||||||
} else if (memcmp(NAND_BUFFER + 0x100, nand_magic_o3ds, 0x60) == 0) {
|
// special, custom partition types, not in NCSD
|
||||||
u8 magic[8] = {0xE9, 0x00, 0x00, 0x43, 0x54, 0x52, 0x20, 0x20};
|
if (type >= NP_TYPE_NCSD) {
|
||||||
if (ReadNandSectors(NAND_BUFFER, 0x5CAE5, 1, 0x04) != 0)
|
if (type == NP_TYPE_NCSD) {
|
||||||
return 0;
|
info->sector = 0x00; // hardcoded
|
||||||
return ((IS_O3DS) || (memcmp(magic, NAND_BUFFER, 8) == 0)) ?
|
info->count = 0x01;
|
||||||
NAND_TYPE_O3DS : NAND_TYPE_NO3DS;
|
} else if (type == NP_TYPE_D0K3) {
|
||||||
|
info->sector = SECTOR_D0K3; // hardcoded
|
||||||
|
info->count = SECTOR_SECRET - info->sector;
|
||||||
|
} else if (type == NP_TYPE_SECRET) {
|
||||||
|
info->sector = SECTOR_SECRET;
|
||||||
|
info->count = 0x01;
|
||||||
|
} else if (type == NP_TYPE_BONUS) {
|
||||||
|
info->sector = GetNandNcsdMinSizeSectors(ncsd);
|
||||||
|
info->count = 0x00; // placeholder, actual size needs info from NAND chip
|
||||||
|
} else return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 prt_idx = 8;
|
||||||
|
for (prt_idx = 0; prt_idx < 8; prt_idx++) {
|
||||||
|
if ((ncsd->partitions_fs_type[prt_idx] != type) ||
|
||||||
|
(ncsd->partitions_crypto_type[prt_idx] != subtype)) continue;
|
||||||
|
if (index == 0) break;
|
||||||
|
index--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prt_idx >= 8) return 1; // not found
|
||||||
|
info->sector = ncsd->partitions[prt_idx].offset;
|
||||||
|
info->count = ncsd->partitions[prt_idx].size;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetNandPartitionInfo(NandPartitionInfo* info, u32 type, u32 subtype, u32 index)
|
||||||
|
{
|
||||||
|
// workaround for info == NULL
|
||||||
|
NandPartitionInfo dummy;
|
||||||
|
if (!info) info = &dummy;
|
||||||
|
|
||||||
|
// find type & subtype in NCSD header
|
||||||
|
u8 header[0x200];
|
||||||
|
ReadNandSectors(header, 0x00, 1, 0xFF);
|
||||||
|
NandNcsdHeader* ncsd = (NandNcsdHeader*) header;
|
||||||
|
if ((ValidateNandNcsdHeader(ncsd) != 0) ||
|
||||||
|
((type == NP_TYPE_FAT) && (GetNandNcsdPartitionInfo(info, NP_TYPE_STD, subtype, 0, ncsd) != 0)) ||
|
||||||
|
((type != NP_TYPE_FAT) && (GetNandNcsdPartitionInfo(info, type, subtype, index, ncsd) != 0)))
|
||||||
|
return 1; // not found
|
||||||
|
|
||||||
|
if (type == NP_TYPE_BONUS) { // size of bonus partition
|
||||||
|
info->count = GetNandSizeSectors() - info->sector;
|
||||||
|
} else if (type == NP_TYPE_FAT) { // FAT type specific stuff
|
||||||
|
ReadNandSectors(header, info->sector, 1, info->keyslot);
|
||||||
|
MbrHeader* mbr = (MbrHeader*) header;
|
||||||
|
if ((ValidateMbrHeader(mbr) != 0) || (index >= 4) ||
|
||||||
|
(mbr->partitions[index].sector == 0) || (mbr->partitions[index].count == 0) ||
|
||||||
|
(mbr->partitions[index].sector + mbr->partitions[index].count > info->count))
|
||||||
|
return 1;
|
||||||
|
info->sector += mbr->partitions[index].sector;
|
||||||
|
info->count = mbr->partitions[index].count;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 GetNandSizeSectors(void)
|
|
||||||
{
|
|
||||||
return getMMCDevice(0)->total_size;
|
|
||||||
}
|
|
||||||
|
@ -1,39 +1,59 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "unittype.h"
|
|
||||||
|
|
||||||
#define NAND_MIN_SECTORS ((!IS_O3DS) ? NAND_MIN_SECTORS_N3DS : NAND_MIN_SECTORS_O3DS)
|
// hardcoded start sectors
|
||||||
|
#define SECTOR_D0K3 0x000001
|
||||||
#define NAND_SYSNAND (1<<0)
|
|
||||||
#define NAND_ZERONAND (1<<3)
|
|
||||||
#define NAND_TYPE_O3DS (1<<4)
|
|
||||||
#define NAND_TYPE_N3DS (1<<5)
|
|
||||||
#define NAND_TYPE_NO3DS (1<<6)
|
|
||||||
|
|
||||||
// minimum size of NAND memory
|
|
||||||
#define NAND_MIN_SECTORS_O3DS 0x1D7800
|
|
||||||
#define NAND_MIN_SECTORS_N3DS 0x26C000
|
|
||||||
|
|
||||||
// start sectors of partitions
|
|
||||||
#define SECTOR_TWL 0x000000
|
|
||||||
#define SECTOR_SECRET 0x000096
|
#define SECTOR_SECRET 0x000096
|
||||||
#define SECTOR_TWLN 0x000097
|
|
||||||
#define SECTOR_TWLP 0x04808D
|
|
||||||
#define SECTOR_AGBSAVE 0x058800
|
|
||||||
#define SECTOR_FIRM0 0x058980
|
|
||||||
#define SECTOR_FIRM1 0x05A980
|
|
||||||
#define SECTOR_CTR 0x05C980
|
|
||||||
|
|
||||||
// sizes of partitions (in sectors)
|
// filenames for sector 0x96
|
||||||
#define SIZE_TWL 0x058800
|
#define SECTOR_NAME "sector0x96.bin"
|
||||||
#define SIZE_TWLN 0x047DA9
|
#define SECRET_NAME "secret_sector.bin"
|
||||||
#define SIZE_TWLP 0x0105B3
|
|
||||||
#define SIZE_AGBSAVE 0x000180
|
// 0x110...0x118 in the NAND NCSD header
|
||||||
#define SIZE_FIRM0 0x002000
|
// see: https://www.3dbrew.org/wiki/NCSD#NCSD_header
|
||||||
#define SIZE_FIRM1 0x002000
|
#define NP_TYPE_NONE 0
|
||||||
#define SIZE_CTR_O3DS 0x17AE80
|
#define NP_TYPE_STD 1
|
||||||
#define SIZE_CTR_N3DS 0x20F680
|
#define NP_TYPE_FAT 2 // this is of our own making
|
||||||
|
#define NP_TYPE_FIRM 3
|
||||||
|
#define NP_TYPE_AGB 4
|
||||||
|
#define NP_TYPE_NCSD 5 // this is of our own making
|
||||||
|
#define NP_TYPE_D0K3 6 // my own partition ^_^
|
||||||
|
#define NP_TYPE_SECRET 7 // this is of our own making
|
||||||
|
#define NP_TYPE_BONUS 8 // this is of our own making
|
||||||
|
|
||||||
|
// 0x118...0x120 in the NAND NCSD header
|
||||||
|
// see: https://www.3dbrew.org/wiki/NCSD#NCSD_header
|
||||||
|
#define NP_SUBTYPE_NONE 0
|
||||||
|
#define NP_SUBTYPE_TWL 1
|
||||||
|
#define NP_SUBTYPE_CTR 2
|
||||||
|
#define NP_SUBTYPE_CTR_N 3
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u32 sector;
|
||||||
|
u32 count;
|
||||||
|
u32 keyslot;
|
||||||
|
} __attribute__((packed)) NandPartitionInfo;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u32 offset;
|
||||||
|
u32 size;
|
||||||
|
} __attribute__((packed)) NandNcsdPartition;
|
||||||
|
|
||||||
|
// see: https://www.3dbrew.org/wiki/NCSD#NCSD_header
|
||||||
|
typedef struct {
|
||||||
|
u8 signature[0x100];
|
||||||
|
u8 magic[4];
|
||||||
|
u32 size;
|
||||||
|
u64 mediaId; // this is zero
|
||||||
|
u8 partitions_fs_type[8];
|
||||||
|
u8 partitions_crypto_type[8];
|
||||||
|
NandNcsdPartition partitions[8];
|
||||||
|
u8 unknown[0x5E];
|
||||||
|
u8 twl_mbr[0x42];
|
||||||
|
} __attribute__((packed)) NandNcsdHeader;
|
||||||
|
|
||||||
|
|
||||||
bool InitNandCrypto(void);
|
bool InitNandCrypto(void);
|
||||||
bool CheckSlot0x05Crypto(void);
|
bool CheckSlot0x05Crypto(void);
|
||||||
@ -42,11 +62,14 @@ bool CheckFirmCrypto(void);
|
|||||||
|
|
||||||
void CryptNand(void* buffer, u32 sector, u32 count, u32 keyslot);
|
void CryptNand(void* buffer, u32 sector, u32 count, u32 keyslot);
|
||||||
void CryptSector0x96(void* buffer, bool encrypt);
|
void CryptSector0x96(void* buffer, bool encrypt);
|
||||||
int ReadNandBytes(void* buffer, u32 offset, u32 count, u32 keyslot);
|
int ReadNandBytes(void* buffer, u64 offset, u64 count, u32 keyslot);
|
||||||
int WriteNandBytes(const void* buffer, u32 offset, u32 count, u32 keyslot);
|
int WriteNandBytes(const void* buffer, u64 offset, u64 count, u32 keyslot);
|
||||||
int ReadNandSectors(void* buffer, u32 sector, u32 count, u32 keyslot);
|
int ReadNandSectors(void* buffer, u32 sector, u32 count, u32 keyslot);
|
||||||
int WriteNandSectors(const void* buffer, u32 sector, u32 count, u32 keyslot);
|
int WriteNandSectors(const void* buffer, u32 sector, u32 count, u32 keyslot);
|
||||||
|
|
||||||
u64 GetNandSizeSectors(void);
|
u32 ValidateNandNcsdHeader(NandNcsdHeader* header);
|
||||||
u32 CheckNandHeader(void* header);
|
u32 GetNandNcsdMinSizeSectors(NandNcsdHeader* ncsd);
|
||||||
u32 CheckNandType(void);
|
u32 GetNandMinSizeSectors(void);
|
||||||
|
u32 GetNandSizeSectors(void);
|
||||||
|
u32 GetNandNcsdPartitionInfo(NandPartitionInfo* info, u32 type, u32 subtype, u32 index, NandNcsdHeader* ncsd);
|
||||||
|
u32 GetNandPartitionInfo(NandPartitionInfo* info, u32 type, u32 subtype, u32 index);
|
||||||
|
@ -18,6 +18,18 @@ u32 SafeWriteFile(FIL* file, void* buff, FSIZE_t ofs, UINT btw) {
|
|||||||
return (sha_cmp(sha_in, buff, btw, SHA256_MODE) == 0) ? 0 : 1;
|
return (sha_cmp(sha_in, buff, btw, SHA256_MODE) == 0) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// safe file quick writer function
|
||||||
|
// same as SafeWriteFile(), but also handles filec reate, too, same warnings apply
|
||||||
|
u32 SafeQWriteFile(const TCHAR* path, void* buff, UINT btw) {
|
||||||
|
FIL fp;
|
||||||
|
u32 ret = 0;
|
||||||
|
if (f_open(&fp, path, FA_READ|FA_WRITE|FA_CREATE_ALWAYS) != FR_OK)
|
||||||
|
return 1;
|
||||||
|
ret = SafeWriteFile(&fp, buff, 0, btw);
|
||||||
|
f_close(&fp);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
// safe NAND writer function, warnings:
|
// safe NAND writer function, warnings:
|
||||||
// (1) contents of buffer may change on corruption
|
// (1) contents of buffer may change on corruption
|
||||||
// (2) uses SHA register
|
// (2) uses SHA register
|
||||||
|
@ -4,4 +4,5 @@
|
|||||||
#include "ff.h"
|
#include "ff.h"
|
||||||
|
|
||||||
u32 SafeWriteFile(FIL* file, void* buff, FSIZE_t ofs, UINT btw);
|
u32 SafeWriteFile(FIL* file, void* buff, FSIZE_t ofs, UINT btw);
|
||||||
|
u32 SafeQWriteFile(const TCHAR* path, void* buff, UINT btw);
|
||||||
u32 SafeWriteNand(void* buff, u32 ofs, u32 btw, u32 keyslot);
|
u32 SafeWriteNand(void* buff, u32 ofs, u32 btw, u32 keyslot);
|
||||||
|
@ -8,6 +8,9 @@
|
|||||||
#define B9S_MAGIC "B9S"
|
#define B9S_MAGIC "B9S"
|
||||||
#define B9S_OFFSET (0x40 - strnlen(B9S_MAGIC, 0x10))
|
#define B9S_OFFSET (0x40 - strnlen(B9S_MAGIC, 0x10))
|
||||||
|
|
||||||
|
#define FB3_MAGIC "FASTBOOT 3DS "
|
||||||
|
#define FB3_OFFSET 0x200 // this is not actually used
|
||||||
|
|
||||||
// see: https://www.3dbrew.org/wiki/FIRM#Firmware_Section_Headers
|
// see: https://www.3dbrew.org/wiki/FIRM#Firmware_Section_Headers
|
||||||
typedef struct {
|
typedef struct {
|
||||||
u32 offset;
|
u32 offset;
|
||||||
@ -52,6 +55,12 @@ const u8 sighaxHash_dev[0x20] = {
|
|||||||
0x96, 0xD3, 0x78, 0x6E, 0xDF, 0x50, 0x3D, 0x11, 0x86, 0x84, 0x01, 0x59, 0x97, 0x50, 0x42, 0x26
|
0x96, 0xD3, 0x78, 0x6E, 0xDF, 0x50, 0x3D, 0x11, 0x86, 0x84, 0x01, 0x59, 0x97, 0x50, 0x42, 0x26
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// see: http://www.sighax.com/
|
||||||
|
const u8 sighaxHash_alt[0x20] = {
|
||||||
|
0xAD, 0xB7, 0x3A, 0xBC, 0x35, 0x70, 0x8E, 0xF1, 0xDF, 0xE9, 0xEF, 0x9C, 0xA5, 0xFA, 0xC8, 0xBF,
|
||||||
|
0xC2, 0xDF, 0x91, 0x6B, 0xB2, 0xE3, 0x81, 0x01, 0x85, 0x84, 0x82, 0x40, 0x9F, 0x0D, 0x45, 0x0A
|
||||||
|
};
|
||||||
|
|
||||||
u32 ValidateFirmHeader(FirmHeader* header, u32 data_size) {
|
u32 ValidateFirmHeader(FirmHeader* header, u32 data_size) {
|
||||||
u8 magic[] = { FIRM_MAGIC };
|
u8 magic[] = { FIRM_MAGIC };
|
||||||
if (memcmp(header->magic, magic, sizeof(magic)) != 0)
|
if (memcmp(header->magic, magic, sizeof(magic)) != 0)
|
||||||
@ -117,9 +126,35 @@ u32 ValidateSector(void* sector) {
|
|||||||
|
|
||||||
u32 CheckFirmSigHax(void* firm) {
|
u32 CheckFirmSigHax(void* firm) {
|
||||||
FirmHeader* header = (FirmHeader*) firm;
|
FirmHeader* header = (FirmHeader*) firm;
|
||||||
return (sha_cmp((IS_DEVKIT) ? sighaxHash_dev : sighaxHash, header->signature, 0x100, SHA256_MODE) == 0) ? 0 : 1;
|
return ((sha_cmp((IS_DEVKIT) ? sighaxHash_dev : sighaxHash, header->signature, 0x100, SHA256_MODE) == 0) ||
|
||||||
|
(!IS_DEVKIT && (sha_cmp(sighaxHash_alt, header->signature, 0x100, SHA256_MODE) == 0))) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 CheckBoot9Strap(void* firm) {
|
u32 CheckBoot9Strap(void* firm) {
|
||||||
return (memcmp(((u8*) firm) + B9S_OFFSET, B9S_MAGIC, strnlen(B9S_MAGIC, 0x10)) == 0) ? 0 : 1;
|
return (memcmp(((u8*) firm) + B9S_OFFSET, B9S_MAGIC, strnlen(B9S_MAGIC, 0x10)) == 0) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 CheckFastBoot3DS(void* firm) {
|
||||||
|
FirmHeader* header = (FirmHeader*) firm;
|
||||||
|
u32 offset = 0;
|
||||||
|
for (u32 i = 0; (i < 4) && !offset; i++) { // find ARM9 section
|
||||||
|
FirmSectionHeader* section = header->sections + i;
|
||||||
|
if (section->size && (section->type == 0))
|
||||||
|
offset = section->offset;
|
||||||
|
}
|
||||||
|
return (offset && (memcmp(((u8*) firm) + offset, FB3_MAGIC, strnlen(FB3_MAGIC, 0x10)) == 0)) ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 CheckFirmPayload(void* firm, char* result) {
|
||||||
|
if (CheckBoot9Strap(firm) == 0) {
|
||||||
|
if (result) snprintf(result, 32, "boot9strap firm");
|
||||||
|
return 0;
|
||||||
|
#ifdef OPEN_INSTALLER
|
||||||
|
} else if (CheckFastBoot3DS(firm) == 0) {
|
||||||
|
if (result) snprintf(result, 32, "fastboot3ds firm");
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if (result) snprintf(result, 32, "unknown firm");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
@ -5,4 +5,4 @@
|
|||||||
u32 ValidateFirm(void* firm, u8* firm_sha, u32 firm_size, char* output);
|
u32 ValidateFirm(void* firm, u8* firm_sha, u32 firm_size, char* output);
|
||||||
u32 ValidateSector(void* sector);
|
u32 ValidateSector(void* sector);
|
||||||
u32 CheckFirmSigHax(void* firm);
|
u32 CheckFirmSigHax(void* firm);
|
||||||
u32 CheckBoot9Strap(void* firm);
|
u32 CheckFirmPayload(void* firm, char* result);
|
||||||
|
@ -65,6 +65,13 @@ _start_gw:
|
|||||||
stmia r0, {r1,r2,r3}
|
stmia r0, {r1,r2,r3}
|
||||||
@ framebuffers properly set
|
@ framebuffers properly set
|
||||||
|
|
||||||
|
ldr r3, =0xFFFF0830 @ flush (clean & invalidate) entire dcache b9 func
|
||||||
|
blx r3
|
||||||
|
|
||||||
|
mov r3, #0
|
||||||
|
mcr p15, 0, r3, c7, c5, 0 @ invalidate I-cache
|
||||||
|
|
||||||
|
mov r2, #0
|
||||||
ldr r3, .entry
|
ldr r3, .entry
|
||||||
bx r3
|
bx r3
|
||||||
|
|
||||||
@ -74,13 +81,42 @@ _start_gw:
|
|||||||
.entry: .word 0x23F00000
|
.entry: .word 0x23F00000
|
||||||
|
|
||||||
_skip_gw:
|
_skip_gw:
|
||||||
|
mov r9, r0 @ argc
|
||||||
|
mov r10, r1 @ argv
|
||||||
|
|
||||||
|
ldr r4, =0xBEEF
|
||||||
|
lsl r2, #16
|
||||||
|
lsr r2, #16
|
||||||
|
cmp r2, r4 @ magic word
|
||||||
|
movne r9, #0
|
||||||
|
|
||||||
@ Disable caches / mpu
|
@ Disable caches / mpu
|
||||||
mrc p15, 0, r4, c1, c0, 0 @ read control register
|
mrc p15, 0, r4, c1, c0, 0 @ read control register
|
||||||
|
bic r4, #(1<<16) @ - dtcm disable (mandated by the docs, before you change the dtcm's address)
|
||||||
bic r4, #(1<<12) @ - instruction cache disable
|
bic r4, #(1<<12) @ - instruction cache disable
|
||||||
bic r4, #(1<<2) @ - data cache disable
|
bic r4, #(1<<2) @ - data cache disable
|
||||||
bic r4, #(1<<0) @ - mpu disable
|
bic r4, #(1<<0) @ - mpu disable
|
||||||
mcr p15, 0, r4, c1, c0, 0 @ write control register
|
mcr p15, 0, r4, c1, c0, 0 @ write control register
|
||||||
|
|
||||||
|
@ Disable FIQs and IRQs
|
||||||
|
msr cpsr_cxsf, #0xD3 @ PSR_SVC_MODE | PSR_I | PSR_F
|
||||||
|
|
||||||
|
@ Disable and acknowledge interrupts
|
||||||
|
mov r2, #0x10000000
|
||||||
|
add r2, r2, #0x1000
|
||||||
|
mov r0, #0
|
||||||
|
mvn r1, #0 @ 0xFFFFFFFF
|
||||||
|
strd r0, r1, [r2] @ REG_IE/IF
|
||||||
|
|
||||||
|
@ Clear NDMA registers
|
||||||
|
add r2, r2, #0x1000 @ NDMA_GLOBAL_CNT, 0x10002000 = 0x10001000 + 0x1000
|
||||||
|
add r1, r2, #0xFC
|
||||||
|
add r2, r2, #0x1C
|
||||||
|
dma_clear_loop:
|
||||||
|
str r0, [r2], #0x1C
|
||||||
|
cmp r1, r2
|
||||||
|
bne dma_clear_loop
|
||||||
|
|
||||||
@ Clear bss
|
@ Clear bss
|
||||||
ldr r0, =__bss_start
|
ldr r0, =__bss_start
|
||||||
ldr r1, =__bss_end
|
ldr r1, =__bss_end
|
||||||
@ -91,10 +127,10 @@ _skip_gw:
|
|||||||
strlt r2, [r0], #4
|
strlt r2, [r0], #4
|
||||||
blt .bss_clr
|
blt .bss_clr
|
||||||
|
|
||||||
@ Flush caches
|
@ Invalidate caches
|
||||||
mov r5, #0
|
mov r5, #0
|
||||||
mcr p15, 0, r5, c7, c5, 0 @ flush I-cache
|
mcr p15, 0, r5, c7, c5, 0 @ invalidate I-cache
|
||||||
mcr p15, 0, r5, c7, c6, 0 @ flush D-cache
|
mcr p15, 0, r5, c7, c6, 0 @ invalidate D-cache
|
||||||
mcr p15, 0, r5, c7, c10, 4 @ drain write buffer
|
mcr p15, 0, r5, c7, c10, 4 @ drain write buffer
|
||||||
|
|
||||||
@ Give read/write access to all the memory regions
|
@ Give read/write access to all the memory regions
|
||||||
@ -142,9 +178,10 @@ _skip_gw:
|
|||||||
mov r1, #0x340
|
mov r1, #0x340
|
||||||
str r1, [r0]
|
str r1, [r0]
|
||||||
|
|
||||||
mov sp, #0x27000000
|
ldr sp, =0x23F00000
|
||||||
|
|
||||||
blx main
|
mov r0, r9
|
||||||
b _start
|
mov r1, r10
|
||||||
|
b main
|
||||||
|
|
||||||
.pool
|
.pool
|
||||||
|
Loading…
x
Reference in New Issue
Block a user