forked from Mirror/SafeB9SInstaller
Fix NAND offset hardcoding
... SafeB9SInstaller now works with custom NCSDs
This commit is contained in:
parent
73bcc07349
commit
88bc7955d8
@ -55,11 +55,11 @@
|
|||||||
|
|
||||||
// buffer area defines (big buffer for firm)
|
// buffer area defines (big buffer for firm)
|
||||||
#define WORK_BUFFER ((u8*) 0x21000000)
|
#define WORK_BUFFER ((u8*) 0x21000000)
|
||||||
#define WORK_BUFFER_SIZE (0x100000)
|
#define WORK_BUFFER_SIZE (0x400000)
|
||||||
#define NAND_BUFFER ((u8*) 0x22000000)
|
#define FIRM_BUFFER ((u8*) 0x21400000)
|
||||||
#define NAND_BUFFER_SIZE (0x100000)
|
|
||||||
#define FIRM_BUFFER ((u8*) 0x23000000)
|
|
||||||
#define FIRM_BUFFER_SIZE (0x400000)
|
#define FIRM_BUFFER_SIZE (0x400000)
|
||||||
|
#define NAND_BUFFER ((u8*) 0x21800000)
|
||||||
|
#define NAND_BUFFER_SIZE (0x100000)
|
||||||
|
|
||||||
inline u32 strchrcount(const char* str, char symbol) {
|
inline u32 strchrcount(const char* str, char symbol) {
|
||||||
u32 count = 0;
|
u32 count = 0;
|
||||||
|
@ -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]);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------------------*/
|
/*-----------------------------------------------------------------------*/
|
||||||
@ -101,12 +64,33 @@ DSTATUS disk_initialize (
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,11 +116,8 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,11 +147,8 @@ 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!
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,18 +172,12 @@ DRESULT disk_ioctl (
|
|||||||
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;
|
||||||
|
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,6 +1,7 @@
|
|||||||
#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 "ui.h"
|
#include "ui.h"
|
||||||
#include "qff.h"
|
#include "qff.h"
|
||||||
@ -9,16 +10,12 @@
|
|||||||
#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 "/" NAME_FIRM "_dev.firm" : INPUT_PATH "/" NAME_FIRM ".firm")
|
#define NAME_SIGHAXFIRM (IS_DEVKIT ? INPUT_PATH "/" NAME_FIRM "_dev.firm" : INPUT_PATH "/" NAME_FIRM ".firm")
|
||||||
#define NAME_SIGHAXFIRMSHA (IS_DEVKIT ? INPUT_PATH "/" NAME_FIRM "_dev.firm.sha" : INPUT_PATH "/" NAME_FIRM ".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 STATUS_GREY -1
|
#define STATUS_GREY -1
|
||||||
#define STATUS_GREEN 0
|
#define STATUS_GREEN 0
|
||||||
@ -73,6 +70,7 @@ u32 ShowInstallerStatus(void) {
|
|||||||
|
|
||||||
u32 SafeB9SInstaller(void) {
|
u32 SafeB9SInstaller(void) {
|
||||||
UINT bt;
|
UINT bt;
|
||||||
|
u32 ret = 0;
|
||||||
|
|
||||||
// initialization
|
// initialization
|
||||||
ShowString("Initializing, please wait...");
|
ShowString("Initializing, please wait...");
|
||||||
@ -169,6 +167,20 @@ u32 SafeB9SInstaller(void) {
|
|||||||
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;
|
||||||
@ -198,25 +210,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;
|
||||||
@ -250,20 +261,21 @@ 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;
|
if (ret != 0) break;
|
||||||
ShowProgress(1, 2, "FIRM install (1/2)");
|
ret = SafeWriteNand(FIRM_BUFFER, np_info.sector * 0x200, firm_size, np_info.keyslot);
|
||||||
snprintf(msgInstall, 64, "FIRM install (1/2)");
|
|
||||||
ShowInstallerStatus();
|
|
||||||
ret = SafeWriteNand(FIRM_BUFFER, FIRM1_NAND_OFFSET, firm_size, 0x06);
|
|
||||||
if (ret != 0) break;
|
if (ret != 0) break;
|
||||||
ShowProgress(1, 2, "FIRM install (2/2)");
|
ShowProgress(i+1, n_firms, "FIRM install");
|
||||||
snprintf(msgInstall, 64, "FIRM install (2/2)");
|
snprintf(msgInstall, 64, "FIRM install (%li/8)", i+1);
|
||||||
ShowInstallerStatus();
|
ShowInstallerStatus();
|
||||||
|
}
|
||||||
|
if (ret != 0) break;
|
||||||
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);
|
||||||
@ -281,11 +293,11 @@ 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
|
||||||
@ -299,23 +311,24 @@ u32 SafeB9SInstaller(void) {
|
|||||||
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;
|
||||||
@ -326,7 +339,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;
|
||||||
|
@ -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,23 +88,27 @@ 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)
|
}
|
||||||
|
if (!CheckSector0x96Crypto()) { // if all else fails...
|
||||||
u8 __attribute__((aligned(32))) otp0x90[0x90];
|
u8 __attribute__((aligned(32))) otp0x90[0x90];
|
||||||
u8 __attribute__((aligned(32))) otp_key[0x10];
|
u8 __attribute__((aligned(32))) otp_key[0x10];
|
||||||
u8 __attribute__((aligned(32))) otp_iv[0x10];
|
u8 __attribute__((aligned(32))) otp_iv[0x10];
|
||||||
memcpy(otp0x90, (u8*) 0x01FFB800, 0x90);
|
memcpy(otp0x90, (u8*) 0x01FFB800, 0x90);
|
||||||
if ((LoadKeyFromFile(otp_key, 0x11, 'N', "OTP") == 0) &&
|
if ((LoadKeyFromFile(otp_key, 0x11, 'N', "OTP") == 0) &&
|
||||||
(LoadKeyFromFile(otp_iv, 0x11, 'I', "IVOTP") == 0)) {
|
((LoadKeyFromFile(otp_iv, 0x11, 'I', "IVOTP") == 0) ||
|
||||||
|
(LoadKeyFromFile(otp_iv, 0x11, 'I', "OTP") == 0))) {
|
||||||
setup_aeskey(0x11, otp_key);
|
setup_aeskey(0x11, otp_key);
|
||||||
use_aeskey(0x11);
|
use_aeskey(0x11);
|
||||||
cbc_encrypt(otp0x90, otp0x90, 0x90 / 0x10, AES_CNT_TITLEKEY_ENCRYPT_MODE, otp_iv);
|
cbc_encrypt(otp0x90, otp0x90, 0x90 / 0x10, AES_CNT_TITLEKEY_ENCRYPT_MODE, otp_iv);
|
||||||
sha_quick(OtpSha256, otp0x90, 0x90, SHA256_MODE);
|
sha_quick(OtpSha256, otp0x90, 0x90, SHA256_MODE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// part #1: Get NAND CID, set up TWL/CTR counter
|
// part #1: Get NAND CID, set up TWL/CTR counter
|
||||||
u32 NandCid[4];
|
u32 NandCid[4];
|
||||||
@ -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,19 +185,20 @@ 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++) {
|
||||||
|
NandPartitionInfo np_info;
|
||||||
u8 buffer[0x200];
|
u8 buffer[0x200];
|
||||||
for (u32 i = 0; i < sizeof(sectors) / sizeof(u32); i++) {
|
if ((GetNandPartitionInfo(&np_info, NP_TYPE_FIRM, NP_SUBTYPE_CTR, i) != 0) ||
|
||||||
ReadNandSectors(buffer, sectors[i], 1, 0x06);
|
(ReadNandSectors(buffer, np_info.sector, 1, np_info.keyslot) != 0)) break;
|
||||||
if (memcmp(buffer, magic, sizeof(magic)) == 0) return true;
|
if (memcmp(buffer, magic, sizeof(magic)) == 0) return true; // success
|
||||||
}
|
}
|
||||||
|
|
||||||
// failed if we arrive here
|
// failed if we arrive here
|
||||||
@ -205,17 +207,17 @@ bool CheckFirmCrypto(void)
|
|||||||
|
|
||||||
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;
|
||||||
return 0;
|
for (u32 prt_idx = 0; prt_idx < 8; prt_idx++) {
|
||||||
if (memcmp(NAND_BUFFER + 0x100, nand_magic_n3ds, 0x60) == 0) {
|
u32 prt_end = ncsd->partitions[prt_idx].offset + ncsd->partitions[prt_idx].size;
|
||||||
return (IS_O3DS) ? 0 : NAND_TYPE_N3DS;
|
if (prt_end > nand_minsize) nand_minsize = prt_end;
|
||||||
} else if (memcmp(NAND_BUFFER + 0x100, nand_magic_o3ds, 0x60) == 0) {
|
|
||||||
u8 magic[8] = {0xE9, 0x00, 0x00, 0x43, 0x54, 0x52, 0x20, 0x20};
|
|
||||||
if (ReadNandSectors(NAND_BUFFER, 0x5CAE5, 1, 0x04) != 0)
|
|
||||||
return 0;
|
|
||||||
return ((IS_O3DS) || (memcmp(magic, NAND_BUFFER, 8) == 0)) ?
|
|
||||||
NAND_TYPE_O3DS : NAND_TYPE_NO3DS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return nand_minsize;
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 GetNandSizeSectors(void)
|
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;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
@ -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);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user