Compare commits

..

18 Commits

Author SHA1 Message Date
lifehackerhansol
107675e006
Fix bugs and compilations from toolchain / b9s upgrade (#56)
* Fix date parsing in Makefile

* crt0: clear more interrupts and registers

After boot9strap 1.4 update, without these SDMMC dies.
Also inadvertently fixes a screen init issue that happens from the
aforementioned boot9strap update.

Co-authored-by: profi200 <fd3194@gmx.de>

* Add memcpy reimplementation

* Change link to guide

Plailect restrctured the domains a few years ago; 3ds.guide is now
3ds.hacks.guide, and devkit guide is panda.hacks.guide.

---------

Co-authored-by: profi200 <fd3194@gmx.de>
Co-authored-by: luigoalma <luigoalma@hotmail.com>
2025-03-29 13:11:55 +01:00
Luis Mayo Valbuena
67c4eeaf1b
Updated brahmaloader dependency to fix cloning (#52) 2022-07-22 19:15:57 +02:00
MechanicalDragon
07207a2fb6
Clear a9lh stage2 section & fix compile (#39)
Fixes #40 and #41
2020-05-06 17:52:23 +02:00
d0k3
2d6def3c08 Fixed a typo 2017-12-05 18:55:16 +01:00
d0k3
30c728b4a8 Deinit filesystem before showing the last prompt 2017-12-05 18:43:07 +01:00
d0k3
6c493ac132 Open installer can install any sighaxed FIRM 2017-06-05 14:26:40 +02:00
d0k3
49b5b55221 Allow compile as open installer 2017-06-03 13:50:01 +02:00
d0k3
8282c8e769 Update BrahmaLoader submodule (thanks @AuroraWright) 2017-06-02 17:24:55 +02:00
d0k3
88bc7955d8 Fix NAND offset hardcoding
... SafeB9SInstaller now works with custom NCSDs
2017-06-02 17:24:55 +02:00
d0k3
73bcc07349 Merge branch 'master' of https://github.com/d0k3/SafeB9SInstaller 2017-06-02 15:04:41 +02:00
Bailey Fox
3135962edb Display the correct link on devkits 2017-06-02 15:04:19 +02:00
d0k3
0369452723 Add support for upcoming Luma and B9S versions 2017-06-02 15:04:19 +02:00
Bailey Fox
bb00614532 Display the correct link on devkits 2017-06-01 12:04:11 +02:00
d0k3
40d01c3930 Add support for upcoming Luma and B9S versions 2017-06-01 01:44:44 +02:00
d0k3
57afda82ad Less strict check for valid FIRM crypto
... only one partition has to match
2017-05-29 01:43:18 +02:00
d0k3
1f55ec9ca3 Source code reorganizations 2017-05-24 12:19:14 +02:00
d0k3
098a64d3cd Support alternative retail signature 2017-05-24 12:16:49 +02:00
d0k3
58433ba99c Made firm the default build goal 2017-05-24 12:15:25 +02:00
20 changed files with 765 additions and 343 deletions

4
.gitmodules vendored
View File

@ -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

View File

@ -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 +0,0 @@
Subproject commit 375e66c946968c8d39258b01038817a628b88e89

View File

@ -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
View 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

View File

@ -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);

View File

@ -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)

View File

@ -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
View 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
View 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);

View File

@ -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;

View File

@ -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;
} }

View File

@ -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,
@ -20,32 +41,6 @@ static const u8 slot0x11Key95dev_sha256[0x20] = { // slot0x11Key95 hash (first 1
0x97, 0x0E, 0x52, 0x29, 0x63, 0x19, 0x47, 0x51, 0x15, 0xD8, 0x02, 0x7A, 0x22, 0x0F, 0x58, 0x15, 0x97, 0x0E, 0x52, 0x29, 0x63, 0x19, 0x47, 0x51, 0x15, 0xD8, 0x02, 0x7A, 0x22, 0x0F, 0x58, 0x15,
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];
@ -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
@ -302,24 +304,25 @@ 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;
}
u32 GetNandNcsdMinSizeSectors(NandNcsdHeader* ncsd)
{
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;
}
// special, custom partition types, not in NCSD
if (type >= NP_TYPE_NCSD) {
if (type == NP_TYPE_NCSD) {
info->sector = 0x00; // hardcoded
info->count = 0x01;
} 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; return 0;
} }
u32 CheckNandType(void) u32 GetNandPartitionInfo(NandPartitionInfo* info, u32 type, u32 subtype, u32 index)
{ {
if (ReadNandSectors(NAND_BUFFER, 0, 1, 0xFF) != 0) // workaround for info == NULL
return 0; NandPartitionInfo dummy;
if (memcmp(NAND_BUFFER + 0x100, nand_magic_n3ds, 0x60) == 0) { if (!info) info = &dummy;
return (IS_O3DS) ? 0 : NAND_TYPE_N3DS;
} else if (memcmp(NAND_BUFFER + 0x100, nand_magic_o3ds, 0x60) == 0) { // find type & subtype in NCSD header
u8 magic[8] = {0xE9, 0x00, 0x00, 0x43, 0x54, 0x52, 0x20, 0x20}; u8 header[0x200];
if (ReadNandSectors(NAND_BUFFER, 0x5CAE5, 1, 0x04) != 0) ReadNandSectors(header, 0x00, 1, 0xFF);
return 0; NandNcsdHeader* ncsd = (NandNcsdHeader*) header;
return ((IS_O3DS) || (memcmp(magic, NAND_BUFFER, 8) == 0)) ? if ((ValidateNandNcsdHeader(ncsd) != 0) ||
NAND_TYPE_O3DS : NAND_TYPE_NO3DS; ((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;
}

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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