diff --git a/include/arm9/main.h b/include/arm9/main.h index 0901d53..e736639 100644 --- a/include/arm9/main.h +++ b/include/arm9/main.h @@ -1,47 +1,5 @@ #pragma once -enum menu_state_type { - STATE_MAIN = 0, - STATE_NAND_MENU, - STATE_NAND_BACKUP, - STATE_NAND_RESTORE, - STATE_OPTIONS_MENU, - STATE_FIRM_LAUNCH, -}; - -typedef struct { - const char *name; - const enum menu_state_type state; -} named_state; - -typedef struct { - const u8 count; // holds the number of available options - const named_state options[]; -} menu_state_options; - -const menu_state_options menu_main = { - 3, - { - {"Launch FIRM", STATE_FIRM_LAUNCH}, - {"NAND tools...", STATE_NAND_MENU}, - {"Options...", STATE_OPTIONS_MENU} - } -}; - -const menu_state_options menu_nand = { - 2, - { - {"Backup NAND", STATE_NAND_BACKUP}, - {"Restore NAND", STATE_NAND_RESTORE} - } -}; - -// state -> menu_state_options instance -const menu_state_options *options_lookup[] = { - &menu_main, // STATE_MAIN - &menu_nand // STATE_NAND_MENU -}; - // Shut up gcc warning /*typedef struct config_format { u8 dev_id; @@ -50,7 +8,17 @@ extern bool unit_is_new3ds; +// PrintConsole for each screen +extern PrintConsole con_top, con_bottom; +// SD card FAT fs instance +extern FATFS sd_fs; +// same for all NAND filesystems +extern FATFS nand_twlnfs, nand_twlpfs, nand_fs; +void heap_init(); void devs_init(); +void devs_close(); void mount_fs(); +void unmount_fs(); void unit_detect(); +u8 rng_get_byte(); diff --git a/include/arm9/menu.h b/include/arm9/menu.h new file mode 100644 index 0000000..4947973 --- /dev/null +++ b/include/arm9/menu.h @@ -0,0 +1,49 @@ +#pragma once + +enum menu_state_type { + STATE_MAIN = 0, + STATE_NAND_MENU, + STATE_NAND_BACKUP, + STATE_NAND_RESTORE, + STATE_OPTIONS_MENU, + STATE_FIRM_LAUNCH, +}; + +typedef struct { + const char *name; + const enum menu_state_type state; +} named_state; + +typedef struct { + const u8 count; // holds the number of available options + const named_state options[]; +} menu_state_options; + +const menu_state_options menu_main = { + 3, + { + {"Launch FIRM", STATE_FIRM_LAUNCH}, + {"NAND tools...", STATE_NAND_MENU}, + {"Options...", STATE_OPTIONS_MENU} + } +}; + +const menu_state_options menu_nand = { + 2, + { + {"Backup NAND", STATE_NAND_BACKUP}, + {"Restore NAND", STATE_NAND_RESTORE} + } +}; + +// menu_state_type -> menu_state_options instance +const menu_state_options *options_lookup[] = { + &menu_main, // STATE_MAIN + &menu_nand // STATE_NAND_MENU +}; + + +// PrintConsole for each screen +extern PrintConsole con_top, con_bottom; + +int enter_menu(void); diff --git a/include/arm9/util_nand.h b/include/arm9/util_nand.h new file mode 100644 index 0000000..52f2561 --- /dev/null +++ b/include/arm9/util_nand.h @@ -0,0 +1,6 @@ +#pragma once + +extern PrintConsole con_top; +extern PrintConsole con_bottom; + +bool dumpNand(const char *filepath); diff --git a/source/arm9/main.c b/source/arm9/main.c index 66de457..7df9d08 100644 --- a/source/arm9/main.c +++ b/source/arm9/main.c @@ -15,43 +15,23 @@ // PrintConsole for each screen PrintConsole con_top, con_bottom; +u8 color; // SD card FAT fs instance FATFS sd_fs; // same for all NAND fss FATFS nand_twlnfs, nand_twlpfs, nand_fs; -// -enum menu_state_type menu_state; - -enum menu_state_type menu_previous_states[8]; -int previous_states_count; bool unit_is_new3ds; -void heap_init(); - -static void menu_main_draw_top() -{ - const char *sd_res[2] = {"\x1B[31mNo ", "\x1B[32mYes"}; - const char *nand_res[2] = {"\x1B[31mError ", "\x1B[32mOK "}; - bool sd_status, nand_status; - - sd_status = dev_sdcard->is_active(); - nand_status = dev_rawnand->is_active(); - - consoleSelect(&con_top); - printf("\n\t\t\t\t\t3DS Bootloader v0.1\n\n\n\n"); - printf(" SD card inserted: %s\e[0m\n", sd_res[sd_status]); - printf(" NAND status: %s\e[0m\n", nand_res[nand_status]); - printf(" Wifi flash status: %s\e[0m", nand_res[spiflash_get_status()]); -} int main(void) { u32 keys; //Initialize console for both screen using the two different PrintConsole we have defined - consoleInit(1, &con_top); - consoleInit(0, &con_bottom); + consoleInit(SCREEN_TOP, &con_top); + consoleSetWindow(&con_top, 1, 1, con_top.windowWidth - 2, con_top.windowHeight - 2); + consoleInit(SCREEN_LOW, &con_bottom); consoleSelect(&con_top); @@ -63,93 +43,13 @@ devs_init(); + printf("Mounting filesystems...\n"); + mount_fs(); consoleClear(); - previous_states_count = 0; - - int cursor_pos = 0; - - // Menu main loop - for(;;) - { - hidScanInput(); - keys = hidKeysDown(); - - // get ready to repaint - consoleSelect(&con_top); - printf("\033[0;0H"); // set cursor to the upper left corner - consoleSelect(&con_bottom); - printf("\033[0;0H\n\n\n\n"); - - const menu_state_options *cur_options = options_lookup[menu_state]; - - switch(menu_state) - { - - case STATE_MAIN: - case STATE_NAND_MENU: - - menu_main_draw_top(); - - // print all the options of the current state - consoleSelect(&con_bottom); - for(int i=0; i < cur_options->count; i++) - printf("\t\t\t%s\e[0m %s\n", cursor_pos == i ? "\x1B[33m*" : " ", cur_options->options[i].name); - - if(keys & KEY_DUP) - { - if(cursor_pos == 0) cursor_pos = cur_options->count - 1; // jump to the last option - else cursor_pos--; - } - else if(keys & KEY_DDOWN) - { - if(cursor_pos == cur_options->count - 1) cursor_pos = 0; // jump to the first option - else cursor_pos++; - } - else if(keys & KEY_A) // select option - { - menu_previous_states[previous_states_count] = menu_state; - previous_states_count++; - menu_state = cur_options->options[cursor_pos].state; - consoleClear(); - cursor_pos = 0; - } - else if(keys & KEY_B) // go back - { - if(previous_states_count != 0) - { - menu_state = menu_previous_states[previous_states_count-1]; - previous_states_count--; - consoleClear(); - cursor_pos = 0; - } - } - break; - - case STATE_FIRM_LAUNCH: - consoleClear(); - consoleSelect(&con_top); - consoleClear(); - if(!firm_load_verify()) - { - consoleSelect(&con_bottom); - printf("Press B to return to Main Menu"); - do { - hidScanInput(); - keys = hidKeysDown(); - } while(!(keys & KEY_B)); - menu_state = STATE_MAIN; - } - else firm_launch(NULL); - break; - - default: printf("OOPS!\n"); break; - } - - - } + enter_menu(); return 0; } @@ -190,6 +90,14 @@ if(!res) sleep_wait(0x8000000); // mmc or wififlash init fail } +void devs_close() +{ + dev_sdcard->close(); + dev_decnand->close(); + dev_rawnand->close(); + dev_flash->close(); +} + void mount_fs() { FRESULT res; @@ -214,6 +122,15 @@ res = f_mount(&nand_fs, "nand:", 1); if(res == FR_OK) printf("%s\e[0m\n", res_str[1]); else printf("%s ERROR 0x%X\e[0m\n", res_str[0], res); + sleep_wait(0x8000000); +} + +void unmount_fs() +{ + f_mount(NULL, "sdmc:", 1); + f_mount(NULL, "twln:", 1); + f_mount(NULL, "twlp:", 1); + f_mount(NULL, "nand:", 1); } void unit_detect() @@ -226,6 +143,19 @@ printf("%s3DS detected!\n", unit_is_new3ds ? "New " : ""); } +u8 rng_get_byte() +{ + u8 number; // uninitialized stack byte + + for(int i=0; i<0x100; i++) + { + // grab a byte from the uninitialized memory. + number += *(u8 *)(A9_RAM_BASE + 0x1000 + i); + } + + return number; +} + static void loadFirmNand(void) { memset((u8*)FCRAM_BASE, 0x00, 0x200); diff --git a/source/arm9/menu.c b/source/arm9/menu.c new file mode 100644 index 0000000..6f49e18 --- /dev/null +++ b/source/arm9/menu.c @@ -0,0 +1,168 @@ +#include +#include +#include "types.h" +#include "mem_map.h" +#include "IO.h" +#include "util.h" +#include "console.h" +#include "dev.h" +#include "fatfs/ff.h" +#include "fatfs/diskio.h" +#include "hid.h" +#include "main.h" +#include "util_nand.h" +#include "menu.h" + +u8 color; +enum menu_state_type menu_state; + +enum menu_state_type menu_previous_states[8]; +int previous_states_count; + +bool unit_is_new3ds; + +void heap_init(); + +static void menu_main_draw_top() +{ + const char *sd_res[2] = {"\x1B[31mNo ", "\x1B[32mYes"}; + const char *nand_res[2] = {"\x1B[31mError ", "\x1B[32mOK "}; + bool sd_status, nand_status, wififlash_status; + + sd_status = dev_sdcard->is_active(); + nand_status = dev_rawnand->is_active(); + wififlash_status = dev_flash->is_active(); + + consoleSelect(&con_top); + drawConsoleWindow(&con_top, 2, color); + printf("\n\t\t\t\t3DS Bootloader v0.1\n\n\n\n"); + printf(" SD card inserted: %s\e[0m\n", sd_res[sd_status]); + printf(" NAND status: %s\e[0m\n", nand_res[nand_status]); + printf(" Wifi flash status: %s\e[0m", nand_res[wififlash_status]); +} + +void rewindConsole() +{ + // get ready to repaint + consoleSelect(&con_top); + printf("\033[0;0H"); // set cursor to the upper left corner + consoleSelect(&con_bottom); + printf("\033[0;0H\n\n\n\n"); +} + +void clearConsoles() +{ + consoleSelect(&con_bottom); + consoleClear(); + consoleSelect(&con_top); + consoleClear(); +} + +int enter_menu(void) +{ + u32 keys; + int cursor_pos; + + // randomize color + color = (rng_get_byte() % 9) + 7; + + previous_states_count = 0; + + cursor_pos = 0; + + // Menu main loop + for(;;) + { + hidScanInput(); + keys = hidKeysDown(); + + rewindConsole(); + + const menu_state_options *cur_options = options_lookup[menu_state]; + + switch(menu_state) + { + + case STATE_MAIN: + case STATE_NAND_MENU: + + menu_main_draw_top(); + + // print all the options of the current state + consoleSelect(&con_bottom); + for(int i=0; i < cur_options->count; i++) + printf("\t\t\t%s\e[0m %s\n", cursor_pos == i ? "\x1B[33m*" : " ", cur_options->options[i].name); + + if(keys & KEY_DUP) + { + if(cursor_pos == 0) cursor_pos = cur_options->count - 1; // jump to the last option + else cursor_pos--; + } + else if(keys & KEY_DDOWN) + { + if(cursor_pos == cur_options->count - 1) cursor_pos = 0; // jump to the first option + else cursor_pos++; + } + else if(keys & KEY_A) // select option + { + menu_previous_states[previous_states_count] = menu_state; + previous_states_count++; + menu_state = cur_options->options[cursor_pos].state; + consoleClear(); + cursor_pos = 0; + } + else if(keys & KEY_B) // go back + { + if(previous_states_count != 0) + { + menu_state = menu_previous_states[previous_states_count-1]; + previous_states_count--; + consoleClear(); + cursor_pos = 0; + } + } + break; + + case STATE_NAND_BACKUP: + consoleSelect(&con_top); + consoleClear(); + dumpNand("sdmc:/nand.bin"); + clearConsoles(); + menu_state = menu_previous_states[previous_states_count-1]; + previous_states_count--; + break; + + case STATE_NAND_RESTORE: + consoleSelect(&con_top); + consoleClear(); + restoreNand("sdmc:/nand.bin"); + clearConsoles(); + menu_state = menu_previous_states[previous_states_count-1]; + previous_states_count--; + break; + + case STATE_FIRM_LAUNCH: + consoleClear(); + consoleSelect(&con_top); + consoleClear(); + if(!firm_load_verify()) + { + consoleSelect(&con_bottom); + printf("Press B to return to Main Menu"); + do { + hidScanInput(); + keys = hidKeysDown(); + } while(!(keys & KEY_B)); + menu_state = STATE_MAIN; + } + else firm_launch(NULL); + break; + + default: printf("OOPS!\n"); break; + } + + + } + + return 0; +} diff --git a/source/arm9/util_nand.c b/source/arm9/util_nand.c index 77e2d2a..3eb7bbe 100644 --- a/source/arm9/util_nand.c +++ b/source/arm9/util_nand.c @@ -1,14 +1,13 @@ #include +#include #include "types.h" #include "mem_map.h" -#include "arm9/console.h" -#include "arm9/dev.h" -#include "arm9/fatfs/ff.h" -#include "arm9/firm.h" -#include "arm9/sdmmc.h" -#include "arm9/crypto.h" +#include "console.h" +#include "dev.h" +#include "fatfs/ff.h" #include "hid.h" -#include "arm9/ndma.h" +#include "util.h" +#include "util_nand.h" @@ -27,102 +26,159 @@ bool dumpNand(const char *filePath) { - u32 nandSectorCnt = getMMCDevice(0)->total_size; - u32 sectorBlkSize = 0x4000, curSector = 0; - u8 *buf = (u8*)FCRAM_BASE; + const u32 nand_size = dev_rawnand->get_size(); + const u32 chunk_size = 0x80000; + u32 cur_size; + u32 cur_offset = 0; FIL file; - UINT bytesWritten; - u32 blockSize; - bool res = true; + FRESULT fres; + UINT bytes_written; + + consoleSelect(&con_bottom); + + printf("\n\t\tPress B to cancel"); + + consoleSelect(&con_top); + + u8 *buf = (u8 *) malloc(chunk_size); + if(!buf) + { + printf("Not enough memory!\n"); + return false; + } - - if(getFreeSpace("sdmc:") < nandSectorCnt<<9) + if(getFreeSpace("sdmc:") < nand_size) { printf("Not enough space on the SD card!\n"); - return false; + goto fail; } - if(f_open(&file, filePath, FA_CREATE_ALWAYS | FA_WRITE) != FR_OK) + + if((fres = f_open(&file, filePath, FA_CREATE_ALWAYS | FA_WRITE)) != FR_OK) { - printf("Failed to create '%s'!\n", filePath); - return false; + printf("Failed to create '%s'! Error: %x\n", filePath, fres); + f_close(&file); + goto fail; } - while(curSector < nandSectorCnt) + while(cur_offset < nand_size) { - blockSize = ((nandSectorCnt - curSector < sectorBlkSize) ? nandSectorCnt - curSector : sectorBlkSize); + if(cur_offset + chunk_size < nand_size) cur_size = chunk_size; + else cur_size = nand_size - cur_offset; - if(!dev_rawnand->read(curSector<<9, blockSize<<9, buf)) + if(!dev_rawnand->read(cur_offset, cur_size, buf)) { - printf("\nFailed to read sector 0x%X!\n", (unsigned int)curSector); - res = false; - break; + printf("\nFailed to read sector 0x%X!\n", (unsigned int)cur_offset>>9); + f_close(&file); + goto fail; } - if(f_write(&file, buf, blockSize<<9, &bytesWritten) != FR_OK || bytesWritten>>9 != blockSize) + if((f_write(&file, buf, chunk_size, &bytes_written) != FR_OK) || (bytes_written != chunk_size)) { printf("\nFailed to write to file!\n"); - res = false; - break; + f_close(&file); + goto fail; } - - curSector += blockSize; - printf("\r%u%% (Sector 0x%X/0x%X)", (unsigned int)(curSector * 100 / nandSectorCnt), (unsigned int)curSector, (unsigned int)nandSectorCnt); + + hidScanInput(); + + if(hidKeysDown() & KEY_B) + { + printf("\n...canceled!\n"); + f_close(&file); + goto fail; + } + + cur_offset += cur_size; + printf("\r%d/%d MB (Sector 0x%X/0x%X)", (unsigned int)cur_offset>>20, (unsigned int)nand_size>>20, + (unsigned int)cur_offset>>9, (unsigned int)nand_size>>9); } f_close(&file); - return res; + free(buf); + return true; + +fail: + free(buf); + sleep_wait(0x8000000); + return false; } bool restoreNand(const char *filePath) { FIL file; UINT bytesRead; - u32 blockSize; - bool res = true; - u32 offset = 0; - u32 bufSize = 0x800000; - u8 *buf = (u8*)FCRAM_BASE; - + u32 cur_size; + u32 cur_offset = 0; + const u32 nand_size = dev_rawnand->get_size(); + const u32 chunk_size = 0x80000; + u8 *buf; + + consoleSelect(&con_bottom); + + printf("\n\t\tPress B to cancel"); + + consoleSelect(&con_top); + + buf = (u8 *) malloc(chunk_size); + if(!buf) + { + printf("Not enough memory!\n"); + goto fail; + } FILINFO fileStat; if(f_stat(filePath, &fileStat) != FR_OK) { printf("Failed to get file status!\n"); - return false; + goto fail; } - if(fileStat.fsize > (getMMCDevice(0)->total_size * 512)) + if(fileStat.fsize > nand_size) { printf("NAND file is bigger than NAND!\n"); - return false; + goto fail; + } + if(fileStat.fsize < 0x200) + { + printf("NAND file is too small!\n"); + goto fail; } if(f_open(&file, filePath, FA_READ) != FR_OK) { printf("Failed to open '%s'!\n", filePath); - return false; + f_close(&file); + goto fail; } u32 size = fileStat.fsize; - while(offset < size) + while(cur_offset < size) { - blockSize = ((size - offset < bufSize) ? size - offset : bufSize); + if(cur_offset + chunk_size < nand_size) cur_size = chunk_size; + else cur_size = nand_size - cur_offset; - if(f_read(&file, buf, blockSize, &bytesRead) != FR_OK || bytesRead != blockSize) + if(f_read(&file, buf, cur_size, &bytesRead) != FR_OK || bytesRead != cur_size) { printf("\nFailed to read from file!\n"); - res = false; - break; + f_close(&file); + goto fail; } - if(!dev_rawnand->write(offset, blockSize, buf)) + if(!dev_rawnand->write(cur_offset, cur_size, buf)) { - printf("\nFailed to write sector 0x%X!\n", (unsigned int)offset>>9); - res = false; - break; + printf("\nFailed to write sector 0x%X!\n", (unsigned int)cur_offset>>9); + f_close(&file); + goto fail; } - offset += blockSize; - printf("\r%d%% (Sector 0x%X/0x%X)", (unsigned int)((u64)offset * 100 / size), (unsigned int)offset>>9, (unsigned int)size>>9); + cur_offset += cur_size; + printf("\r%d/%d MB (Sector 0x%X/0x%X)", (unsigned int)cur_offset>>20, (unsigned int)nand_size>>20, + (unsigned int)cur_offset>>9, (unsigned int)nand_size>>9); } f_close(&file); - return res; + free(buf); + return true; + +fail: + free(buf); + sleep_wait(0x8000000); + return false; }