Enable handling of downgraded (2.x) N3DS NANDs

This commit is contained in:
d0k3 2016-03-15 16:25:27 +01:00
parent 1debcaeecc
commit 38fccfb28b
3 changed files with 98 additions and 20 deletions

View File

@ -8,7 +8,6 @@
/*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/
#include "diskio.h" /* FatFs lower layer API */ #include "diskio.h" /* FatFs lower layer API */
#include "platform.h"
#include "nand.h" #include "nand.h"
#include "sdmmc.h" #include "sdmmc.h"
@ -18,11 +17,10 @@
#define SUBTYPE_CTRN 0 #define SUBTYPE_CTRN 0
#define SUBTYPE_CTRN_N 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 4 #define SUBTYPE_TWLP 4
#define SUBTYPE_NONE 5
#define SUBTYPE(pd) ((mode_n3ds && (DriveInfo[pd].subtype == SUBTYPE_CTRN)) ? SUBTYPE_CTRN_N : DriveInfo[pd].subtype)
typedef struct { typedef struct {
BYTE type; BYTE type;
@ -45,14 +43,42 @@ FATpartition DriveInfo[7] = {
{ TYPE_EMUNAND, SUBTYPE_TWLP }, // 6 - EMUNAND TWLP { TYPE_EMUNAND, SUBTYPE_TWLP }, // 6 - EMUNAND TWLP
}; };
SubtypeDesc SubTypes[4] = { SubtypeDesc SubTypes[5] = {
{ 0x05CAE5, 0x179F1B, 0x4 }, // O3DS CTRNAND { 0x05CAE5, 0x179F1B, 0x4 }, // O3DS CTRNAND
{ 0x05CAD7, 0x20E969, 0x5 }, // N3DS CTRNAND { 0x05CAD7, 0x20E969, 0x5 }, // N3DS CTRNAND
{ 0x05CAD7, 0x20E969, 0x4 }, // N3DS CTRNAND (downgraded)
{ 0x000097, 0x047DA9, 0x3 }, // TWLN { 0x000097, 0x047DA9, 0x3 }, // TWLN
{ 0x04808D, 0x0105B3, 0x3 } // TWLP { 0x04808D, 0x0105B3, 0x3 } // TWLP
}; };
static bool mode_n3ds = false; static BYTE nand_type_sys = NAND_TYPE_UNK;
static BYTE nand_type_emu = NAND_TYPE_UNK;
/*-----------------------------------------------------------------------*/
/* Get Drive Subtype helper */
/*-----------------------------------------------------------------------*/
SubtypeDesc* get_subtype_desc(
__attribute__((unused))
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
BYTE type = DriveInfo[pdrv].type;
BYTE subtype = DriveInfo[pdrv].subtype;
if (type == TYPE_SDCARD) {
return NULL;
} else if (subtype == SUBTYPE_CTRN) {
BYTE nand_type = (type == TYPE_SYSNAND) ? nand_type_sys : nand_type_emu;
if (nand_type != NAND_TYPE_O3DS)
subtype = (nand_type == NAND_TYPE_N3DS) ? SUBTYPE_CTRN_N : SUBTYPE_CTRN_NO;
}
return &(SubTypes[subtype]);
}
/*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/
@ -79,8 +105,11 @@ DSTATUS disk_initialize (
) )
{ {
if (pdrv == 0) { // a mounted SD card is the preriquisite for everything else if (pdrv == 0) { // a mounted SD card is the preriquisite for everything else
mode_n3ds = (GetUnitPlatform() == PLATFORM_N3DS);
sdmmc_sdcard_init(); sdmmc_sdcard_init();
} else if (pdrv < 4) {
nand_type_sys = CheckNandType(false);
} else if (pdrv < 7) {
nand_type_emu = CheckNandType(true);
} }
return RES_OK; return RES_OK;
} }
@ -106,9 +135,9 @@ DRESULT disk_read (
return RES_PARERR; return RES_PARERR;
} }
} else { } else {
BYTE subtype = SUBTYPE(pdrv); SubtypeDesc* subtype = get_subtype_desc(pdrv);
BYTE keyslot = SubTypes[subtype].keyslot; BYTE keyslot = subtype->keyslot;
DWORD isector = SubTypes[subtype].offset + sector; DWORD isector = subtype->offset + sector;
if (ReadNandSectors(buff, isector, count, keyslot, type == TYPE_EMUNAND)) if (ReadNandSectors(buff, isector, count, keyslot, type == TYPE_EMUNAND))
return RES_PARERR; return RES_PARERR;
@ -139,9 +168,9 @@ DRESULT disk_write (
return RES_PARERR; return RES_PARERR;
} }
} else { } else {
BYTE subtype = SUBTYPE(pdrv); SubtypeDesc* subtype = get_subtype_desc(pdrv);
BYTE keyslot = SubTypes[subtype].keyslot; BYTE keyslot = subtype->keyslot;
DWORD isector = SubTypes[subtype].offset + sector; DWORD isector = subtype->offset + sector;
if (WriteNandSectors(buff, isector, count, keyslot, type == TYPE_EMUNAND)) if (WriteNandSectors(buff, isector, count, keyslot, type == TYPE_EMUNAND))
return RES_PARERR; // unstubbed! return RES_PARERR; // unstubbed!
@ -175,7 +204,7 @@ DRESULT disk_ioctl (
if (DriveInfo[pdrv].type == TYPE_SDCARD) { if (DriveInfo[pdrv].type == TYPE_SDCARD) {
*((DWORD*) buff) = getMMCDevice(1)->total_size; *((DWORD*) buff) = getMMCDevice(1)->total_size;
} else { } else {
*((DWORD*) buff) = SubTypes[SUBTYPE(pdrv)].size; *((DWORD*) buff) = get_subtype_desc(pdrv)->size;
} }
return RES_OK; return RES_OK;
case GET_BLOCK_SIZE: case GET_BLOCK_SIZE:

View File

@ -10,6 +10,24 @@
#define NAND_BUFFER ((u8*)0x21100000) #define NAND_BUFFER ((u8*)0x21100000)
#define NAND_BUFFER_SIZE (0x100000) // must be multiple of 0x200 #define NAND_BUFFER_SIZE (0x100000) // must be multiple of 0x200
static 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 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 u8 CtrNandCtr[16]; static u8 CtrNandCtr[16];
static u8 TwlNandCtr[16]; static u8 TwlNandCtr[16];
@ -93,13 +111,20 @@ void CryptNand(u8* buffer, u32 sector, u32 count, u32 keyslot)
int ReadNandSectors(u8* buffer, u32 sector, u32 count, u32 keyslot, bool read_emunand) int ReadNandSectors(u8* buffer, u32 sector, u32 count, u32 keyslot, bool read_emunand)
{ {
if (read_emunand) { if (read_emunand) {
int errorcode = sdmmc_sdcard_readsectors(emunand_base_sector + sector, count, buffer); int errorcode = 0;
if ((sector == 0) && (emunand_base_sector % 0x200000 == 0)) { // GW EmuNAND header handling
errorcode = sdmmc_sdcard_readsectors(emunand_base_sector + getMMCDevice(0)->total_size, 1, buffer);
sector = 1;
count--;
buffer += 0x200;
}
errorcode = (!errorcode && count) ? sdmmc_sdcard_readsectors(emunand_base_sector + sector, count, buffer) : errorcode;
if (errorcode) return errorcode; if (errorcode) return errorcode;
} else { } else {
int errorcode = sdmmc_nand_readsectors(sector, count, buffer); int errorcode = sdmmc_nand_readsectors(sector, count, buffer);
if (errorcode) return errorcode; if (errorcode) return errorcode;
} }
CryptNand(buffer, sector, count, keyslot); if (keyslot < 0x40) CryptNand(buffer, sector, count, keyslot);
return 0; return 0;
} }
@ -110,9 +135,13 @@ int WriteNandSectors(const u8* buffer, u32 sector, u32 count, u32 keyslot, bool
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, buffer + (s*0x200), pcount * 0x200); memcpy(NAND_BUFFER, buffer + (s*0x200), pcount * 0x200);
CryptNand(NAND_BUFFER, sector + s, pcount, keyslot); if (keyslot < 0x40) CryptNand(NAND_BUFFER, sector + s, pcount, keyslot);
if (write_emunand) { if (write_emunand) {
int errorcode = sdmmc_sdcard_writesectors(emunand_base_sector + sector + s, pcount, NAND_BUFFER); int errorcode = 0;
if ((sector + s == 0) && (emunand_base_sector % 0x200000 == 0)) { // GW EmuNAND header handling
errorcode = sdmmc_sdcard_writesectors(emunand_base_sector + getMMCDevice(0)->total_size, 1, NAND_BUFFER);
errorcode = (!errorcode && (pcount > 1)) ? sdmmc_sdcard_writesectors(emunand_base_sector + 1, pcount - 1, NAND_BUFFER + 0x200) : errorcode;
} else errorcode = sdmmc_sdcard_writesectors(emunand_base_sector + sector + s, pcount, NAND_BUFFER);
if (errorcode) return errorcode; if (errorcode) return errorcode;
} else { } else {
int errorcode = sdmmc_nand_writesectors(sector + s, pcount, NAND_BUFFER); int errorcode = sdmmc_nand_writesectors(sector + s, pcount, NAND_BUFFER);
@ -123,6 +152,19 @@ int WriteNandSectors(const u8* buffer, u32 sector, u32 count, u32 keyslot, bool
return 0; return 0;
} }
u8 CheckNandType(bool check_emunand)
{
if (ReadNandSectors(NAND_BUFFER, 0, 1, 0xFF, check_emunand) != 0)
return NAND_TYPE_UNK;
if (memcmp(NAND_BUFFER + 0x100, nand_magic_n3ds, 0x60) == 0) {
return NAND_TYPE_N3DS;
} else if (memcmp(NAND_BUFFER + 0x100, nand_magic_o3ds, 0x60) == 0) {
return (GetUnitPlatform() == PLATFORM_3DS) ? NAND_TYPE_O3DS : NAND_TYPE_NO3DS;
}
return NAND_TYPE_UNK;
}
u32 GetEmuNandBase(void) u32 GetEmuNandBase(void)
{ {
return emunand_base_sector; return emunand_base_sector;

View File

@ -2,12 +2,19 @@
#include "common.h" #include "common.h"
#define NAND_TYPE_UNK 0
#define NAND_TYPE_O3DS 1
#define NAND_TYPE_N3DS 2
#define NAND_TYPE_NO3DS 3
bool InitNandCrypto(void); bool InitNandCrypto(void);
void CryptNand(u8* buffer, u32 sector, u32 count, u32 keyslot); void CryptNand(u8* buffer, u32 sector, u32 count, u32 keyslot);
int ReadNandSectors(u8* buffer, u32 sector, u32 count, u32 keyslot, bool read_emunand); int ReadNandSectors(u8* buffer, u32 sector, u32 count, u32 keyslot, bool read_emunand);
int WriteNandSectors(const u8* buffer, u32 sector, u32 count, u32 keyslot, bool write_emunand); int WriteNandSectors(const u8* buffer, u32 sector, u32 count, u32 keyslot, bool write_emunand);
u8 CheckNandType(bool check_emunand);
u32 GetEmuNandBase(void); u32 GetEmuNandBase(void);
u32 SwitchEmuNandBase(int start_sector); u32 SwitchEmuNandBase(int start_sector);