diff --git a/include/arm9/firm.h b/include/arm9/firm.h index db88362..ba33ad0 100644 --- a/include/arm9/firm.h +++ b/include/arm9/firm.h @@ -38,6 +38,6 @@ }; */ - +bool firm_size(size_t *size); bool firm_verify(u32 fwSize, bool skipHashCheck, bool printInfo); noreturn void firm_launch(void); diff --git a/source/arm9/dev.c b/source/arm9/dev.c index 339d969..6cbad09 100644 --- a/source/arm9/dev.c +++ b/source/arm9/dev.c @@ -10,6 +10,7 @@ #include "arm9/ndma.h" #include "arm9/timer.h" #include "arm9/dev.h" +#include "arm9/partitions.h" // SD card device @@ -56,19 +57,11 @@ // Decrypted NAND device typedef struct { - u32 sector; - u32 count; - u8 type; - u8 keyslot; -} nand_partition_struct; - -typedef struct { dev_struct dev; u32 twlCounter[4]; u32 ctrCounter[4]; AES_ctx twlAesCtx; AES_ctx ctrAesCtx; - nand_partition_struct partitions[8]; } dev_dnand_struct; bool sdmmc_dnand_init(void); @@ -93,7 +86,6 @@ {0}, {0}, {0}, - {0}, {0} }; const dev_struct *dev_decnand = &dev_dnand.dev; @@ -225,6 +217,7 @@ bool sdmmc_dnand_init(void) { NCSD_header header; + size_t temp; extern u32 ctr_nand_sector; if(!dev_rnand.initialized && !dev_sd.initialized && !dev_dnand.dev.initialized) @@ -245,32 +238,49 @@ if(header.magic != 0x4453434E) return false; // Collect partition infos... - for(int i = 0; i < 8; i++) + for(int i = 0; i < MAX_PARTITIONS; i++) { - dev_dnand.partitions[i].sector = header.partitions[i].mediaOffset; - dev_dnand.partitions[i].count = header.partitions[i].mediaSize; - dev_dnand.partitions[i].type = header.partFsType[i]; + u8 type = header.partFsType[i]; + size_t sector = header.partitions[i].mediaOffset; + + size_t index = partitionAdd(sector, header.partitions[i].mediaSize, type); - switch(dev_dnand.partitions[i].type) + switch(type) { case 1: - if(i == 0) dev_dnand.partitions[i].keyslot = 0x03; // TWL NAND partition - if(i == 4) // CTR NAND partition + if(i == 0) { - if(bootInfo.unit_is_new3ds) dev_dnand.partitions[i].keyslot = 0x05; // TODO: Load N3DS keyY - else dev_dnand.partitions[i].keyslot = 0x04; + partitionSetKeyslot(index, 0x03); // TWL NAND partition + partitionSetName(index, "twln"); + } + else if(i == 4) // CTR NAND partition + { + if(bootInfo.unit_is_new3ds) + partitionSetKeyslot(index, 0x05); // TODO: Load N3DS keyY + else + partitionSetKeyslot(index, 0x04); + + partitionSetName(index, "nand"); + // Set CTR NAND partition offset for diskio.c - ctr_nand_sector = header.partitions[i].mediaOffset; + ctr_nand_sector = sector; } break; case 3: // firmX - dev_dnand.partitions[i].keyslot = 0x06; + /* NOTE: This assumes there's not more than two firmware partitions! */ + partitionSetKeyslot(index, 0x06); + if(partitionGetIndex("firm0", &temp)) + partitionSetName(index, "firm0"); + else + partitionSetName(index, "firm1"); break; case 4: // AGB_FIRM savegame - dev_dnand.partitions[i].keyslot = 0x07; + partitionSetKeyslot(index, 0x07); + partitionSetName(index, "agb"); break; default: // Unused - dev_dnand.partitions[i].keyslot = 0xFF; + partitionSetKeyslot(index, 0xFF); + partitionSetName(index, "invalid"); } } @@ -307,33 +317,25 @@ return true; } -static nand_partition_struct *find_partition(u32 sector, u32 count) -{ - for(u32 i = 0; i < 8; i++) - { - nand_partition_struct *partition = &dev_dnand.partitions[i]; - if((partition->sector <= sector) && (partition->count >= count) - && (partition->sector + partition->count >= sector + count)) - return partition; - } - - return NULL; -} - bool sdmmc_dnand_read_sector(u32 sector, u32 count, void *buf) { + size_t index; + u8 keyslot; + if(!dev_dnand.dev.initialized && !sdmmc_dnand_init()) return false; - nand_partition_struct *partition = find_partition(sector, count); + if(!partitionFind(sector, count, &index)) + return false; + + partitionGetKeyslot(index, &keyslot); - - if(!partition) return false; - if(partition->keyslot == 0xFF) return false; // unknown partition type + if(keyslot == 0xFF) + return false; // unknown partition type AES_ctx *ctx; - AES_selectKeyslot(partition->keyslot, true); - if(partition->keyslot == 0x03) + AES_selectKeyslot(keyslot, true); + if(keyslot == 0x03) { ctx = &dev_dnand.twlAesCtx; AES_setCtrIvNonce(ctx, dev_dnand.twlCounter, AES_INPUT_LITTLE | AES_INPUT_REVERSED | AES_MODE_CTR, sector<<9); diff --git a/source/arm9/firm.c b/source/arm9/firm.c index 8c798df..18a1f43 100644 --- a/source/arm9/firm.c +++ b/source/arm9/firm.c @@ -50,6 +50,42 @@ } }; +/* Calculates the actual firm partition size by using its header */ +bool firm_size(size_t *size) +{ + firm_header *firm_hdr = (firm_header*)FIRM_LOAD_ADDR; + u32 curLen = sizeof(firm_header); + u32 curOffset = 0; + *size = 0; + + /* scan sections in reverse order */ + for(int i=3; i>=0; i--) + { + firm_sectionheader *section = &firm_hdr->section[i]; + + if(section->size == 0) + continue; + + if(section->offset <= curOffset) + continue; + + curOffset = section->offset; + + if(section->size > FIRM_MAX_SIZE || curOffset >= FIRM_MAX_SIZE) + return false; + + if(curLen < curOffset + section->size) + curLen = curOffset + section->size; + + if(curLen > FIRM_MAX_SIZE) + return false; + } + + *size = curLen; + + return true; +} + // NOTE: Do not call any functions here! void NAKED firmLaunchStub(void) { @@ -146,7 +182,7 @@ printf("Section %i:\noffset: 0x%"PRIX32", addr: 0x%"PRIX32", size: 0x%"PRIX32"\n", (int)i, section->offset, section->address, section->size); - if(section->offset >= fwSize) + if(section->offset >= fwSize || section->offset < sizeof(firm_header)) { if(printInfo) printf("\x1B[31mBad section offset!\e[0m\n"); diff --git a/source/arm9/firmwriter.c b/source/arm9/firmwriter.c index dcb4be8..0824bd5 100644 --- a/source/arm9/firmwriter.c +++ b/source/arm9/firmwriter.c @@ -1,12 +1,13 @@ #include #include +#include #include "types.h" #include "mem_map.h" -#include "arm9/console.h" #include "arm9/dev.h" #include "fatfs/ff.h" #include "hid.h" #include "util.h" +#include "arm9/firmwriter.h" #include "arm9/main.h" #include "arm9/menu.h" #include "arm9/timer.h" @@ -45,7 +46,7 @@ } /* read back data and compare */ - if(!dev_rawnand->read_sector(sector, sizeof blockData, blockData) || + if(!dev_rawnand->read_sector(curSector, sizeof blockData, blockData) || memcmp(sourceBuf, blockData, sizeof blockData) != 0) { failure = true; @@ -82,11 +83,11 @@ return false; } - memcpy(header, firmBuf, 0x100); - firmBuf = (void *) header; + memcpy(&header, firmBuf, 0x100); + firmBuf = (void *) &header; } - if(!dev_rawnand->write_sector(sector, 0x200, firmBuf); + if(!dev_rawnand->write_sector(sector, 0x200, firmBuf)) { failure = true; return false; diff --git a/source/arm9/menu_update.c b/source/arm9/menu_update.c index cc5848d..bd8d76d 100644 --- a/source/arm9/menu_update.c +++ b/source/arm9/menu_update.c @@ -5,10 +5,12 @@ #include "mem_map.h" #include "arm9/console.h" #include "arm9/dev.h" +#include "arm9/partitions.h" #include "fatfs/ff.h" #include "hid.h" #include "util.h" #include "version.h" +#include "arm9/firmwriter.h" #include "arm9/main.h" #include "arm9/menu.h" #include "arm9/timer.h" @@ -16,13 +18,14 @@ #define MIN_UPDATE_SIZE 0x1000 static const char *updateFilePath = "sdmc:\\fastboot3ds.bin"; -static const char *installPath = "firm0" +static const char *installPath = "firm0"; bool menuUpdateLoader() { u8 *updateBuffer = (u8 *) FIRM_LOAD_ADDR; FILINFO fileStat; size_t fwSize; + size_t index; size_t sector; uiClearConsoles(); @@ -45,11 +48,9 @@ goto fail; } - // XXX - fwSize = calcFirmwareSize(); - - if(fwSize < MIN_UPDATE_SIZE || fwSize % 0x200) // this would be really odd. + if(!firm_size(&fwSize) || fwSize < MIN_UPDATE_SIZE || fwSize % 0x200) { + // this would be really odd. uiPrintError("Invalid update file!\n"); goto fail; } @@ -83,7 +84,10 @@ /* NOTE: We assume sighax is installed on firm0 */ - sector = partitionGetSector(installPath); + if(!partitionGetIndex(installPath, &index)) + panic(); + + partitionGetSectorOffset(index, §or); uiPrintTextAt(0, 4, "Updating...\n"); @@ -91,31 +95,27 @@ for(size_t i=0; i