diff --git a/include/arm9/config.h b/include/arm9/config.h index 7d9a18f..29ee3ae 100644 --- a/include/arm9/config.h +++ b/include/arm9/config.h @@ -23,6 +23,7 @@ #define numKeys KLast +bool loadConfigFile(); void *configCopyText(int key); const void *configGetData(int key); const char *configGetKeyText(int key); diff --git a/include/arm9/firm.h b/include/arm9/firm.h index e17ed25..a422865 100644 --- a/include/arm9/firm.h +++ b/include/arm9/firm.h @@ -42,5 +42,5 @@ */ -bool firm_load_verify(u32 firmSize); +bool firm_verify(u32 firmSize); noreturn void firm_launch(void); diff --git a/include/arm9/main.h b/include/arm9/main.h index 8ac28d0..b5c185b 100644 --- a/include/arm9/main.h +++ b/include/arm9/main.h @@ -17,22 +17,20 @@ bootInfoStruct bootInfo; -/* -typedef struct { - -} configFileStruct; - -configFileStruct configFile; -*/ - // PrintConsole for each screen PrintConsole con_top, con_bottom; + // SD card FAT fs instance FATFS sd_fs; + // same for all NAND filesystems FATFS nand_twlnfs, nand_twlpfs, nand_fs; void devs_close(); +bool remount_nand_fs(); void unmount_fs(); +void unmount_nand_fs(); u8 rng_get_byte(); void clearConsoles(); +bool tryLoadFirmware(const char *filepath); +void power_off_safe(); diff --git a/include/arm9/menu.h b/include/arm9/menu.h index 5d548eb..ced43ce 100644 --- a/include/arm9/menu.h +++ b/include/arm9/menu.h @@ -1,14 +1,16 @@ #pragma once enum menu_state_type { - STATE_MAIN = 0, - STATE_NAND_MENU, - STATE_NAND_BACKUP, - STATE_NAND_RESTORE, - STATE_OPTIONS_MENU, - STATE_FIRM_LAUNCH, - STATE_TEST_MENU, - STATE_TEST_CONFIG + MENU_STATE_MAIN = 0, + MENU_STATE_NAND_MENU, + MENU_STATE_NAND_BACKUP, + MENU_STATE_NAND_RESTORE, + MENU_STATE_OPTIONS_MENU, + MENU_STATE_FIRM_LAUNCH, + MENU_STATE_BROWSER, + MENU_STATE_TEST_CONFIG, + + STATE_PREVIOUS // pseudo state for menuSetReturnToState() }; typedef struct { @@ -21,41 +23,34 @@ const named_state options[]; } menu_state_options; -const menu_state_options menu_main = { - 5, - { - {"Launch FIRM", STATE_FIRM_LAUNCH}, - {"NAND tools...", STATE_NAND_MENU}, - {"Options...", STATE_OPTIONS_MENU}, - {"DEBUG TEST", STATE_TEST_MENU}, - {"CONFIG TEST", STATE_TEST_CONFIG} - } -}; - -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 -}; - enum menu_events { MENU_EVENT_NONE = 0, - MENU_EVENT_HOME_PRESSED + MENU_EVENT_HOME_PRESSED, + MENU_EVENT_POWER_PRESSED, + MENU_EVENT_SD_CARD_INSERTED, + MENU_EVENT_SD_CARD_REMOVED, + MENU_EVENT_STATE_CHANGE }; +extern enum menu_state_type menu_state; +extern enum menu_state_type menu_next_state; -// PrintConsole for each screen -extern PrintConsole con_top, con_bottom; +extern enum menu_state_type menu_previous_states[8]; +extern int menu_previous_states_count; -int enter_menu(void); +extern int menu_event_state; +int enter_menu(int initial_state); + +// Use this to change to another sub-menu. +// Call menuUpdateGlobalState and menuActState afterwards. +void menuSetReturnToState(int state); +// Use this to change back to a previous sub-menu. +// Call menuUpdateGlobalState and menuActState afterwards. +void menuSetEnterNextState(int state); +// This must be called once in each (sub-) menu iteration. +// Returns an event code. int menuUpdateGlobalState(void); - +// This must be called once in each (sub-) menu iteration, +// after menuUpdateGlobalState has been called. +void menuActState(void); diff --git a/include/arm9/util_nand.h b/include/arm9/util_nand.h index 52f2561..a7c73e8 100644 --- a/include/arm9/util_nand.h +++ b/include/arm9/util_nand.h @@ -1,6 +1,5 @@ #pragma once -extern PrintConsole con_top; -extern PrintConsole con_bottom; +#include "main.h" bool dumpNand(const char *filepath); diff --git a/include/gfx.h b/include/gfx.h index b149b4d..f193909 100644 --- a/include/gfx.h +++ b/include/gfx.h @@ -21,7 +21,7 @@ void gfx_init(); void gfx_set_framebufs(u8 r, u8 g, u8 b, u8 a); void gfx_set_black_sub(); -void gfx_draw_ppm(); +void gfx_clear_framebufs(); inline u32 *gfx_get_framebuf_ptr(int top_screen, u16 x, u16 y) { diff --git a/include/hid.h b/include/hid.h index bc41230..2c292d5 100644 --- a/include/hid.h +++ b/include/hid.h @@ -21,6 +21,8 @@ KEY_Y = (1<<11), }; +#define HID_KEY_MASK_ALL (KEY_Y << 1) - 1 + void hidScanInput(void); u32 hidKeysHeld(void); diff --git a/include/pxi.h b/include/pxi.h index 73bd396..474dcfe 100644 --- a/include/pxi.h +++ b/include/pxi.h @@ -22,10 +22,15 @@ #define PXI_ENABLE_SEND_RECV_FIFO (1u<<15) // Custom PXI Command/Reply Definitions +#define PXI_CMD_ENABLE_LCDS 0x4C434453 +#define PXI_CMD_POWER_OFF 0x504F4646 +#define PXI_CMD_ALLOW_POWER_OFF 0x504F4F4B +#define PXI_CMD_FORBID_POWER_OFF 0x504F4E4F #define PXI_CMD_FIRM_LAUNCH 0x544F4F42 #define PXI_RPL_FIRM_LAUNCH_READY 0x4F4B6666 #define PXI_RPL_OK 0x4F4B4F4B -#define PXI_RPL_HOME_PRESSED 0x4F4B4F4B +#define PXI_RPL_HOME_PRESSED 0x484F4D45 +#define PXI_RPL_POWER_PRESSED 0x504F5752 void PXI_init(void); diff --git a/source/arm11/gfx.c b/source/arm11/gfx.c index fdfb521..dd13c15 100644 --- a/source/arm11/gfx.c +++ b/source/arm11/gfx.c @@ -104,7 +104,7 @@ REG_GPU_EXT_CNT |= 0x300; //memset(VRAM_BASE, 0xFF, VRAM_SIZE); - gfx_set_framebufs(0x00, 0x00, 0x00, 0x00); + //gfx_set_framebufs(0x00, 0x00, 0x00, 0x00); gfx_setup_framebuf_top(); gfx_setup_framebuf_low(); @@ -229,6 +229,13 @@ void gfx_init() { + static bool gfxInitDone; + + if(gfxInitDone) + return; + + gfxInitDone = true; + gfx_init_step1(); gfx_init_step2(); gfx_init_step3(); @@ -260,21 +267,8 @@ REG_LCD_COLORFILL_SUB |= 1<<24; } -void gfx_draw_ppm() +void gfx_clear_framebufs() { - /* - u8 *framebuf = (u8 *) FRAMEBUF_TOP_A_1; - u8 *imagedata = (u8 *) (test2_ppm_bin+0x26); // skip ppm header - for(u32 x=0; x<400; x++) - { - for(u32 y=240; y>=1; y--) - { - - *framebuf++ = 0; - *framebuf++ = imagedata[(400*(y-1)+x)*3+2]; - *framebuf++ = imagedata[(400*(y-1)+x)*3+1]; - *framebuf++ = imagedata[(400*(y-1)+x)*3+0]; - } - } - */ + gfx_set_framebufs(0x00, 0x00, 0x00, 0x00); } + diff --git a/source/arm11/main.c b/source/arm11/main.c index 2ced004..d252dd7 100644 --- a/source/arm11/main.c +++ b/source/arm11/main.c @@ -11,7 +11,6 @@ void turn_off(void) { - //printf("Attempting to turn off...\n"); i2cmcu_lcd_poweroff(); i2cmcu_lcd_backlight_poweroff(); for(;;) @@ -42,6 +41,9 @@ int main(void) { + void (*stub)(void) = (void (*)(void))A11_STUB_ENTRY; + bool poweroff_allowed = false; + // Relocate ARM11 stub for(u32 i = 0; i < 0x200>>2; i++) { @@ -49,7 +51,8 @@ } PXI_init(); - gfx_init(); + + gfx_clear_framebufs(); for(;;) { @@ -62,6 +65,19 @@ switch(cmdCode) { + case PXI_CMD_ENABLE_LCDS: + gfx_init(); + PXI_sendWord(PXI_RPL_OK); + break; + case PXI_CMD_ALLOW_POWER_OFF: + poweroff_allowed = true; + break; + case PXI_CMD_FORBID_POWER_OFF: + poweroff_allowed = false; + break; + case PXI_CMD_POWER_OFF: + turn_off(); + break; case PXI_CMD_FIRM_LAUNCH: goto start_firmlaunch; default: @@ -73,7 +89,16 @@ u8 hidstate = i2cmcu_readreg_hid(); if(hidstate & MCU_HID_POWER_BUTTON_PRESSED) - turn_off(); + { + if(poweroff_allowed) // direct power off allowed? + turn_off(); + PXI_trySendWord(PXI_RPL_POWER_PRESSED); + } + + if(hidstate & MCU_HID_HOME_BUTTON_PRESSED) + PXI_trySendWord(PXI_RPL_HOME_PRESSED); + + // handle shell state if(hidstate & MCU_HID_SHELL_GOT_CLOSED) i2cmcu_lcd_poweroff(); else if(hidstate & MCU_HID_SHELL_GOT_OPENED) @@ -81,18 +106,19 @@ i2cmcu_lcd_poweron(); i2cmcu_lcd_backlight_poweron(); } - if(hidstate & MCU_HID_HOME_BUTTON_PRESSED) - PXI_trySendWord(PXI_RPL_HOME_PRESSED); + + // wait a bit, don't spam the i2c bus wait(0x8000); } start_firmlaunch: +/* // Turn off LCDs and backlight before FIRM launch. i2cmcu_lcd_poweroff(); i2cmcu_lcd_backlight_poweroff(); +*/ - void (*stub)(void) = (void (*)(void))A11_STUB_ENTRY; disableCaches(); stub(); diff --git a/source/arm9/config_parser.c b/source/arm9/config_parser.c index b3cfade..d439c6a 100644 --- a/source/arm9/config_parser.c +++ b/source/arm9/config_parser.c @@ -179,7 +179,6 @@ AttributeEntryType *curAttr; u32 len; char *text; - bool ret; // pass #1: look for definitions and populate our lookup table. for(u32 i=0; itextLength, curAttr->textData); - ret = keyFunctions[i].parse(curAttr); - //if(ret) printf("success!\n"); + keyFunctions[i].parse(curAttr); } } @@ -290,7 +288,7 @@ { if(strnicmp(textData, convTable[i], strlen(convTable[i])) == 0) { - padValue |= 1 << i; + padValue |= (u32) 1 << i; break; } } @@ -309,7 +307,6 @@ static bool parseBootMode(AttributeEntryType *attr) { - char c; char *textData = attr->textData; u32 i; diff --git a/source/arm9/debug.c b/source/arm9/debug.c index 49a9439..7ee5d54 100644 --- a/source/arm9/debug.c +++ b/source/arm9/debug.c @@ -2,9 +2,11 @@ #include #include "types.h" #include "mem_map.h" +#include "pxi.h" #include "arm9/console.h" #include "arm9/fatfs/ff.h" - +#include "arm9/main.h" +#include "arm9/interrupt.h" static u32 debugHash = 0; @@ -29,12 +31,14 @@ consoleInit(0, NULL); - printf("\x1b[41m\x1b[0J\x1b[9CGPANIC!!!\n\nlr = 0x%08"PRIX32"\n", lr); + printf("\x1b[41m\x1b[0J\x1b[9C****PANIC!!!****\n\nlr = 0x%08"PRIX32"\n", lr); unmount_fs(); devs_close(); - while(1); + PXI_sendWord(PXI_CMD_ALLOW_POWER_OFF); + + for(;;) waitForIrq(); } // Expects the registers in the exception stack to be in the following order: @@ -95,7 +99,9 @@ unmount_fs(); devs_close(); - while(1); + PXI_sendWord(PXI_CMD_ALLOW_POWER_OFF); + + for(;;) waitForIrq(); } void dumpMem(u8 *mem, u32 size, char *filepath) diff --git a/source/arm9/dev.c b/source/arm9/dev.c index aa2e810..e287dfd 100644 --- a/source/arm9/dev.c +++ b/source/arm9/dev.c @@ -11,6 +11,7 @@ #include "arm9/dev.h" +extern void panic(); // SD card device bool sdmmc_sd_init(void); @@ -122,8 +123,10 @@ // -------------------------------- sd card glue functions -------------------------------- bool sdmmc_sd_init(void) { - if(!dev_rnand.initialized && !dev_sd.initialized && !dev_dnand.dev.initialized) - sdmmc_init(); + //printf("sdmmc_sd_init\n"); + //if(!dev_rnand.initialized && !dev_sd.initialized && !dev_dnand.dev.initialized) + sdmmc_init(); + dev_rnand.initialized = dev_sd.initialized = dev_dnand.dev.initialized = false; if(!dev_sd.initialized) { @@ -132,17 +135,23 @@ *((u16*)0x10000020) &= ~0x1; //If set while bitmask 0x200 is set, a sdbus command timeout error will occur during sdbus init. if(SD_Init()) return false; dev_sd.initialized = true; + //printf("sd init: success!\n"); } + //else printf("sd init: nothing to do!\n"); return true; } bool sdmmc_sd_read_sector(u32 sector, u32 count, void *buf) { + if(!dev_sd.initialized) + return false; return !sdmmc_sdcard_readsectors(sector, count, buf); } bool sdmmc_sd_write_sector(u32 sector, u32 count, const void *buf) { + if(!dev_sd.initialized) + return false; return !sdmmc_sdcard_writesectors(sector, count, buf); } @@ -178,11 +187,15 @@ bool sdmmc_rnand_read_sector(u32 sector, u32 count, void *buf) { + if(!dev_rnand.initialized && !sdmmc_rnand_init()) + return false; return !sdmmc_nand_readsectors(sector, count, buf); } bool sdmmc_rnand_write_sector(u32 sector, u32 count, const void *buf) { + if(!dev_rnand.initialized && !sdmmc_rnand_init()) + return false; return !sdmmc_nand_writesectors(sector, count, buf); } @@ -304,7 +317,8 @@ bool sdmmc_dnand_read_sector(u32 sector, u32 count, void *buf) { - if(!dev_dnand.dev.initialized) return false; + if(!dev_dnand.dev.initialized && !sdmmc_dnand_init()) + return false; nand_partition_struct *partition = find_partition(sector, count); @@ -333,10 +347,12 @@ bool sdmmc_dnand_write_sector(UNUSED u32 sector, UNUSED u32 count, UNUSED const void *buf) { - if(!dev_dnand.dev.initialized) return false; + if(!dev_dnand.dev.initialized && !sdmmc_dnand_init()) + return false; //return !sdmmc_nand_writesectors(sector, count, buf); printf("Decnand write not implemented!\n"); + panic(); return false; } diff --git a/source/arm9/filebrowse.c b/source/arm9/filebrowse.c index d9b3898..74f9e11 100644 --- a/source/arm9/filebrowse.c +++ b/source/arm9/filebrowse.c @@ -6,6 +6,7 @@ #include "arm9/fatfs/ff.h" #include "arm9/console.h" #include "arm9/main.h" +#include "arm9/menu.h" #include "arm9/interrupt.h" #include "util.h" #include "hid.h" @@ -112,7 +113,7 @@ } } -static void updateCursor(u32 cursor_pos, u32 old_cursor_pos, u32 maxEntries) +static void updateCursor(int cursor_pos, int old_cursor_pos, int maxEntries) { // clear old '*' char consoleSetCursor(&con_bottom, 0, (old_cursor_pos % (maxEntries)) + 1); @@ -122,13 +123,13 @@ printf("*"); } -static void updateScreen(u32 cursor_pos, bool dirChange) +static void updateScreen(int cursor_pos, bool dirChange) { // we don't want ugly line breaks in our list const int maxLen = con_bottom.windowWidth - 1; const int maxEntries = con_bottom.windowHeight - 1; - static u32 old_cursor_pos; + static int old_cursor_pos; consoleSelect(&con_bottom); @@ -157,14 +158,14 @@ } else { - start = cursor_pos - (u32) maxEntries + 1; + start = cursor_pos - maxEntries + 1; end = min(cursor_pos + 1, curEntriesCount); } for(unsigned i = start; i < end; i++) { formatEntry(entry, dirEntries[i].name, (u32) maxLen - 2, true); - printf("\n%s %s", i == cursor_pos ? "*" : " ", entry); + printf("\n%s %s", i == (unsigned) cursor_pos ? "*" : " ", entry); } } @@ -173,12 +174,19 @@ FRESULT res; u32 keys; u32 cursor_pos = 0; + bool fileSelected = false; curEntriesCount = 0; indexStackPtr = indexStack; dirEntries = (EntryType *) malloc(MAX_CACHED_ENTRIES); - if(!dirEntries) return NULL; + if(!dirEntries) + { + menuSetReturnToState(STATE_PREVIOUS); + menuUpdateGlobalState(); + menuActState(); + return NULL; + } if(basePath) strncpy_s(curPath, basePath, sizeof(curPath) - 1, sizeof(curPath)); @@ -189,13 +197,16 @@ if (res != FR_OK) { free(dirEntries); + menuSetReturnToState(STATE_PREVIOUS); + menuUpdateGlobalState(); + menuActState(); return NULL; } // do an initial scan scanDirectory(); - updateScreen(cursor_pos, true); + updateScreen((int)cursor_pos, true); for(;;) { @@ -209,7 +220,7 @@ if(cursor_pos != 0) { cursor_pos -= keys & KEY_DLEFT ? min(cursor_pos, 8) : 1; - updateScreen(cursor_pos, false); + updateScreen((int)cursor_pos, false); } } @@ -227,11 +238,11 @@ else cursor_pos = curEntriesCount - 1; if(cursor_pos != old_pos) - updateScreen(cursor_pos, false); + updateScreen((int)cursor_pos, false); } else { - updateScreen(cursor_pos, false); + updateScreen((int)cursor_pos, false); } } @@ -251,7 +262,7 @@ curEntriesCount = 0; scanDirectory(); cursor_pos = 0; - updateScreen(cursor_pos, true); + updateScreen((int)cursor_pos, true); } } } @@ -261,7 +272,8 @@ goto fail; // TODO? Path too long. strcat(curPath, "\\"); strcat(curPath, dirEntries[cursor_pos].name); - break; + fileSelected = true; + menuSetReturnToState(STATE_PREVIOUS); } } } @@ -282,22 +294,44 @@ goto fail; } while(curEntriesCount <= cursor_pos); - updateScreen(cursor_pos, true); + updateScreen((int)cursor_pos, true); } - else goto fail; + else + menuSetReturnToState(STATE_PREVIOUS); } - waitForIrq(); - REG_IRQ_IF = (u32)IRQ_TIMER_0; + switch(menuUpdateGlobalState()) + { + case MENU_EVENT_HOME_PRESSED: + case MENU_EVENT_POWER_PRESSED: + case MENU_EVENT_SD_CARD_REMOVED: + goto fail; + case MENU_EVENT_STATE_CHANGE: + if(!fileSelected) goto fail; + else goto done; + default: + break; + } + + menuActState(); + } +done: + f_closedir(&curDirectory); free(dirEntries); - + + menuActState(); + return curPath; fail: + f_closedir(&curDirectory); free(dirEntries); + + menuActState(); + return NULL; } diff --git a/source/arm9/firm.c b/source/arm9/firm.c index d65adc1..f8f1649 100644 --- a/source/arm9/firm.c +++ b/source/arm9/firm.c @@ -49,6 +49,7 @@ } }; +// NOTE: Do not call any functions here! void NAKED firmLaunchStub(void) { firm_header *firm_hdr = (firm_header*)FIRM_LOAD_ADDR; @@ -93,7 +94,7 @@ __asm__("mov lr, %[in]\nbx %[in2]\n" : : [in] "r" (ret), [in2] "r" (entry9)); } -bool firm_load_verify(u32 fwSize) +bool firm_verify(u32 fwSize) { firm_header *firm_hdr = (firm_header*)FIRM_LOAD_ADDR; const char *res[2] = {"\x1B[31mBAD", "\x1B[32mGOOD"}; @@ -107,7 +108,7 @@ printf("ARM9 entry: 0x%"PRIX32"\n", firm_hdr->entrypointarm9); printf("ARM11 entry: 0x%"PRIX32"\n", firm_hdr->entrypointarm11); - for(int i=0; i<4; i++) + for(u32 i=0; i<4; i++) { firm_sectionheader *section = &firm_hdr->section[i]; @@ -131,11 +132,11 @@ // check for bad sections const u32 numEntries = sizeof(firmProtectedAreas)/sizeof(firmProtectedArea); - for(i=0; iaddress; @@ -169,16 +170,16 @@ noreturn void firm_launch(void) { - printf("Sending PXI_CMD_FIRM_LAUNCH\n"); + //printf("Sending PXI_CMD_FIRM_LAUNCH\n"); PXI_sendWord(PXI_CMD_FIRM_LAUNCH); - printf("Waiting for ARM11...\n"); + //printf("Waiting for ARM11...\n"); while(PXI_recvWord() != PXI_RPL_OK); //printf("Relocating FIRM launch stub...\n"); NDMA_copy((u32*)A9_STUB_ENTRY, (u32*)firmLaunchStub, A9_STUB_SIZE>>2); - //printf("Starting firm launch...\n"); + printf("Starting firm launch...\n"); void (*stub)(void) = (void (*)(void))A9_STUB_ENTRY; stub(); while(1); diff --git a/source/arm9/main.c b/source/arm9/main.c index f4bb94e..f3b5005 100644 --- a/source/arm9/main.c +++ b/source/arm9/main.c @@ -1,89 +1,135 @@ #include #include #include -#include "types.h" -#include "arm9/main.h" #include "util.h" -#include "io.h" +#include "pxi.h" +#include "hid.h" #include "arm9/fatfs/ff.h" #include "arm9/dev.h" #include "arm9/firm.h" #include "arm9/config.h" +#include "arm9/timer.h" +#include "arm9/menu.h" +#include "arm9/main.h" -static void devs_init(); -static void mount_fs(); +static bool devs_init(); +static bool mount_fs(); +static void screen_init(); static void unit_detect(); static void boot_env_detect(); static void fw_detect(); -static void loadSettings(); +static bool loadSettings(int *mode); +bool tryLoadFirmwareFromSettings(); +u32 loadFirmSd(const char *filePath); + +// TODO: remove after debugging +extern void panic(void); +extern void hashCodeRoData(void); int main(void) { + int mode; + bool firmLoaded = false; + hashCodeRoData(); // TODO: remove after debugging - //Initialize console for both screen using the two different PrintConsole we have defined + PXI_sendWord(PXI_CMD_FORBID_POWER_OFF); + + //Initialize console for both screens using the two different PrintConsole we have defined 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); - printf("\x1B[32mGood morning, hello!\e[0m\n\n"); - - printf("Detecting unit...\n"); - - unit_detect(); - - printf("Detecting boot environment...\n"); - - boot_env_detect(); + printf("\x1B[32mGood morning\nHello !\e[0m\n\n"); printf("Initializing devices...\n"); - - devs_init(); - - printf("Mounting filesystems...\n"); - - mount_fs(); - - printf("Detecting firmware...\n"); - - fw_detect(); - - printf("Loading settings...\n"); - - loadSettings(); - - wait(0x8000000); - consoleClear(); + if(!devs_init()) screen_init(); + + printf("Mounting filesystems...\n"); + + if(!mount_fs()) screen_init(); + + printf("Loading settings...\n"); + + if(!loadSettings(&mode)) screen_init(); + + switch(mode) + { + case BootModeQuick: + screen_init(); + case BootModeQuiet: + firmLoaded = tryLoadFirmwareFromSettings(); + if(firmLoaded) + break; + case BootModeNormal: + screen_init(); + break; + default: + panic(); + } + + if(!firmLoaded) + { + printf("Detecting unit...\n"); + + unit_detect(); + + printf("Detecting boot environment...\n"); + + boot_env_detect(); + + printf("Detecting firmware...\n"); + + fw_detect(); + + printf("Entering menu...\n"); + + timerSleep(2000); + + consoleClear(); + + enter_menu(MENU_STATE_MAIN); + } - enter_menu(); + printf("Unmounting FS...\n"); unmount_fs(); + printf("Closing devices...\n"); + devs_close(); return 0; } -static void devs_init() +static bool devs_init() { - bool res; const char *res_str[2] = {"\x1B[31mFailed!", "\x1B[32mOK!"}; + bool sdRes, nandRes, wifiRes; printf(" Initializing SD card... "); - printf("%s\e[0m\n", res_str[dev_sdcard->init()]); + printf("%s\e[0m\n", res_str[sdRes = dev_sdcard->init()]); printf(" Initializing raw NAND... "); - printf("%s\e[0m\n", res_str[dev_rawnand->init()]); + printf("%s\e[0m\n", res_str[nandRes = dev_rawnand->init()]); printf(" Initializing Wifi flash... "); - printf("%s\e[0m\n", res_str[(res = dev_flash->init())]); + printf("%s\e[0m\n", res_str[wifiRes = dev_flash->init()]); - bootInfo.sd_status = dev_sdcard->is_active(); - bootInfo.nand_status = dev_rawnand->is_active(); - bootInfo.wififlash_status = dev_flash->is_active(); + bootInfo.sd_status = sdRes; + bootInfo.nand_status = nandRes; + bootInfo.wififlash_status = wifiRes; - if(!res) wait(0x8000000); // mmc or wififlash init fail + // TODO: avoid this somehow... + if(sdRes) + { + while(!dev_sdcard->is_active()); + } + + // atm only those boot options are possible, so if the init + // for both failed, this is a fatal error. + return sdRes && nandRes; } void devs_close() @@ -94,30 +140,65 @@ dev_flash->close(); } -static void mount_fs() +bool remount_nand_fs() { - FRESULT res; + bool res = true; + + res &= f_mount(&nand_twlnfs, "twln:", 1); + res &= f_mount(&nand_twlpfs, "twlp:", 1); + res &= f_mount(&nand_fs, "nand:", 1); + + return res; +} + +void unmount_nand_fs() +{ + f_mount(NULL, "twln:", 1); + f_mount(NULL, "twlp:", 1); + f_mount(NULL, "nand:", 1); +} + +static bool mount_fs() +{ + FRESULT res[4]; + bool finalRes = true; const char *res_str[2] = {"\x1B[31mFailed!", "\x1B[32mOK!"}; - printf("Mounting SD card FAT FS... "); - res = f_mount(&sd_fs, "sdmc:", 1); - if(res == FR_OK) printf("%s\e[0m\n", res_str[1]); - else printf("%s ERROR 0x%d\e[0m\n", res_str[0], res); + if(bootInfo.sd_status) + { + printf("Mounting SD card FAT FS... "); + res[0] = f_mount(&sd_fs, "sdmc:", 1); + if(res[0] == FR_OK) printf("%s\e[0m\n", res_str[1]); + else printf("%s ERROR 0x%d\e[0m\n", res_str[0], res[0]); + finalRes &= res[0] == FR_OK; + } + else finalRes = false; - printf("Mounting twln FS... "); - res = f_mount(&nand_twlnfs, "twln:", 1); - if(res == FR_OK) printf("%s\e[0m\n", res_str[1]); - else printf("%s ERROR 0x%d\e[0m\n", res_str[0], res); + if(bootInfo.nand_status) + { + printf("Mounting twln FS... "); + res[1] = f_mount(&nand_twlnfs, "twln:", 1); + if(res[1] == FR_OK) printf("%s\e[0m\n", res_str[1]); + else printf("%s ERROR 0x%d\e[0m\n", res_str[0], res[1]); - printf("Mounting twlp FS... "); - res = f_mount(&nand_twlpfs, "twlp:", 1); - if(res == FR_OK) printf("%s\e[0m\n", res_str[1]); - else printf("%s ERROR 0x%d\e[0m\n", res_str[0], res); + printf("Mounting twlp FS... "); + res[2] = f_mount(&nand_twlpfs, "twlp:", 1); + if(res[2] == FR_OK) printf("%s\e[0m\n", res_str[1]); + else printf("%s ERROR 0x%d\e[0m\n", res_str[0], res[2]); - printf("Mounting CTR NAND FAT FS... "); - res = f_mount(&nand_fs, "nand:", 1); - if(res == FR_OK) printf("%s\e[0m\n", res_str[1]); - else printf("%s ERROR 0x%d\e[0m\n", res_str[0], res); + printf("Mounting CTR NAND FAT FS... "); + res[3] = f_mount(&nand_fs, "nand:", 1); + if(res[3] == FR_OK) printf("%s\e[0m\n", res_str[1]); + else printf("%s ERROR 0x%d\e[0m\n", res_str[0], res[3]); + + finalRes &= res[3] == res[2] == res[1] == FR_OK; + } + else finalRes = false; + + if(!finalRes) + timerSleep(2000); + + return finalRes; } void unmount_fs() @@ -128,6 +209,17 @@ f_mount(NULL, "nand:", 1); } +static void screen_init() +{ + static bool screenInitDone; + if(!screenInitDone) + { + screenInitDone = true; + PXI_sendWord(PXI_CMD_ENABLE_LCDS); + while(PXI_recvWord() != PXI_RPL_OK); + } +} + static void unit_detect() { bootInfo.unit_is_new3ds = REG_PDN_MPCORE_CLKCNT != 0; @@ -170,12 +262,25 @@ free(nand_sector); } -static void loadSettings() +static bool loadSettings(int *mode) { const char *res_str[2] = {"\x1B[31mFailed!", "\x1B[32mOK!"}; bool success = loadConfigFile(); - printf(res_str[success]); + printf("%s\e[0m\n", res_str[success]); + + if(success) + { + const int *mode_ptr = (const int *)configGetData(KBootMode); + if(mode_ptr != NULL) + *mode = *mode_ptr; + else *mode = BootModeNormal; + return true; + } + + *mode = BootModeNormal; + + return false; } u8 rng_get_byte() @@ -188,31 +293,119 @@ return (u8)tmp; } +bool tryLoadFirmwareFromSettings(void) +{ + const char *path; + int keyBootOption, keyPad; + int i; + u32 padValue, expectedPadValue; + + consoleSelect(&con_top); + + printf("\n\nAttempting to load FIRM from settings...\n"); + + keyBootOption = KBootOption1; + keyPad = KBootOption1Buttons; + + for(i=0; i<3; i++, keyBootOption++, keyPad++) + { + path = (const char *)configGetData(keyBootOption); + if(path) + { + printf("Boot Option #%i:\n", i + 1); + // check pad value + const u32 *temp; + temp = (const u32 *)configGetData(keyPad); + if(temp) + { + expectedPadValue = *temp; + hidScanInput(); + padValue = HID_KEY_MASK_ALL & hidKeysHeld(); + if(padValue != expectedPadValue) + { + printf("Skipping, right buttons are not pressed.\n"); + printf("%x %x\n", padValue, expectedPadValue); + continue; + } + } + + if(tryLoadFirmware(path)) + break; + } + + // ... we failed, try next one + } + + if(i >= 3) + return false; + + return true; +} + +bool tryLoadFirmware(const char *filepath) +{ + u32 fw_size; + + if(!filepath) + return false; + + printf("Loading firmware:\n%s\n", filepath); + + if(strncmp(filepath, "sdmc:", 5) == 0) + fw_size = loadFirmSd(filepath); + else + return false; // TODO: Support more devices + + if(fw_size == 0) + return false; + + return firm_verify(fw_size); +} + static void loadFirmNand(void) { dev_decnand->read_sector(0x0005A980, 0x00002000, (u8*)FIRM_LOAD_ADDR); } -bool loadFirmSd(const char *filePath) +u32 loadFirmSd(const char *filePath) { FIL file; + u32 fileSize; UINT bytesRead = 0; - bool res = true; + FILINFO fileStat; + if(f_stat(filePath, &fileStat) != FR_OK) + { + printf("Failed to get file status!\n"); + return 0; + } + + fileSize = fileStat.fsize; if(f_open(&file, filePath, FA_READ) != FR_OK) { printf("Failed to open '%s'!\n", filePath); - return false; + return 0; } - if(f_read(&file, (u8*)FIRM_LOAD_ADDR, FIRM_MAX_SIZE, &bytesRead) != FR_OK) + + if(fileSize > FIRM_MAX_SIZE) + { + f_close(&file); + return 0; + } + + if(f_read(&file, (u8*)FIRM_LOAD_ADDR, fileSize, &bytesRead) != FR_OK) { printf("Failed to read from file!\n"); - res = false; + fileSize = 0; } + + if(bytesRead != fileSize) + fileSize = 0; + f_close(&file); - return res; + return fileSize; } void clearConsoles() @@ -222,3 +415,17 @@ consoleSelect(&con_top); consoleClear(); } + +void power_off_safe() +{ + clearConsoles(); + + printf("Attempting to turn off...\n"); + + unmount_fs(); + devs_close(); + // tell the arm11 we're done + PXI_sendWord(PXI_CMD_POWER_OFF); + + for(;;); +} diff --git a/source/arm9/menu.c b/source/arm9/menu.c index 27cdfe6..d3bfb77 100644 --- a/source/arm9/menu.c +++ b/source/arm9/menu.c @@ -18,23 +18,50 @@ #include "arm9/interrupt.h" #include "arm9/config.h" -u8 color; +// PrintConsole for each screen +extern PrintConsole con_top, con_bottom; + +const menu_state_options menu_main = { + 5, + { + {"Launch FIRM", MENU_STATE_FIRM_LAUNCH}, + {"NAND tools...", MENU_STATE_NAND_MENU}, + {"Options...", MENU_STATE_OPTIONS_MENU}, + {"Browse FIRM...", MENU_STATE_BROWSER}, + {"CONFIG TEST", MENU_STATE_TEST_CONFIG} + } +}; + +const menu_state_options menu_nand = { + 2, + { + {"Backup NAND", MENU_STATE_NAND_BACKUP}, + {"Restore NAND", MENU_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 +}; + enum menu_state_type menu_state; +enum menu_state_type menu_next_state; enum menu_state_type menu_previous_states[8]; -int previous_states_count; +int menu_previous_states_count; -void heap_init(); +int menu_event_state; + + +u8 color; 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; - - sd_status = dev_sdcard->is_active(); - consoleSelect(&con_top); drawConsoleWindow(&con_top, 2, color); printf("\n\t\t\t\t\t3DS Bootloader v0.1\n\n\n\n"); @@ -42,7 +69,7 @@ printf(" Model: %s\n", bootInfo.model); printf(" \x1B[33m%s\e[0m\n", bootInfo.boot_env); printf(" \x1B[32m(%s Mode)\e[0m\n\n", bootInfo.mode); - printf(" SD card inserted: %s\e[0m\n", sd_res[sd_status]); + printf(" SD card inserted: %s\e[0m\n", sd_res[bootInfo.sd_status]); printf(" NAND status: %s\e[0m\n", nand_res[bootInfo.nand_status]); printf(" Wifi flash status: %s\e[0m", nand_res[bootInfo.wififlash_status]); } @@ -56,20 +83,28 @@ printf("\033[0;0H\n\n\n\n"); } -int enter_menu(void) +int enter_menu(int initial_state) { u32 keys; int cursor_pos; + menu_event_state = MENU_EVENT_NONE; + // randomize color color = (rng_get_byte() % 6) + 1; - previous_states_count = 0; - cursor_pos = 0; startTimer(TIMER_0, TIMER_PRESCALER_64, TIMER_FREQ_64(60.0f), true); + // caller requested to enter a submenu? + if(initial_state != MENU_STATE_MAIN) + { + menuSetEnterNextState(initial_state); + menuUpdateGlobalState(); + menuActState(); + } + // Menu main loop for(;;) { @@ -83,8 +118,8 @@ switch(menu_state) { - case STATE_MAIN: - case STATE_NAND_MENU: + case MENU_STATE_MAIN: + case MENU_STATE_NAND_MENU: menu_main_draw_top(); @@ -105,61 +140,39 @@ } 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(); + menuSetEnterNextState(cur_options->options[cursor_pos].state); 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; - } + menuSetReturnToState(STATE_PREVIOUS); + cursor_pos = 0; } break; - case STATE_NAND_BACKUP: + case MENU_STATE_NAND_BACKUP: consoleSelect(&con_top); consoleClear(); dumpNand("sdmc:/nand.bin"); - clearConsoles(); - menu_state = menu_previous_states[previous_states_count-1]; - previous_states_count--; + menuSetReturnToState(STATE_PREVIOUS); break; - case STATE_NAND_RESTORE: + case MENU_STATE_NAND_RESTORE: consoleSelect(&con_top); consoleClear(); restoreNand("sdmc:/nand.bin"); - clearConsoles(); - menu_state = menu_previous_states[previous_states_count-1]; - previous_states_count--; + menuSetReturnToState(STATE_PREVIOUS); break; - case STATE_FIRM_LAUNCH: + case MENU_STATE_FIRM_LAUNCH: consoleClear(); consoleSelect(&con_top); consoleClear(); - if(!firm_load_verify(0x400000)) - { - consoleSelect(&con_bottom); - printf("Press B to return to Main Menu"); - do { - hidScanInput(); - keys = hidKeysDown(); - } while(!(keys & KEY_B)); - menu_state = STATE_MAIN; - } - else goto exitAndLaunchFirm; + printf("OOPS!\n"); + for(;;); break; - // test - case STATE_TEST_MENU: + case MENU_STATE_BROWSER: consoleClear(); consoleSelect(&con_top); consoleClear(); @@ -167,14 +180,11 @@ consoleClear(); printf("selected file:\n%s\n", path); wait(0x8000000); - printf("loading firm...\n"); - loadFirmSd(path); - if(!firm_load_verify(0x400000)) printf("bad firm\n"); + if(!tryLoadFirmware(path)) printf("bad firm\n"); else goto exitAndLaunchFirm; - menu_state = STATE_MAIN; break; - case STATE_TEST_CONFIG: + case MENU_STATE_TEST_CONFIG: consoleClear(); consoleSelect(&con_top); consoleClear(); @@ -204,7 +214,8 @@ } else printf(""); } - for(;;); + timerSleep(6000); + menuSetReturnToState(STATE_PREVIOUS); break; default: printf("OOPS!\n"); break; @@ -213,21 +224,13 @@ switch(menuUpdateGlobalState()) { case MENU_EVENT_HOME_PRESSED: - menu_state = STATE_MAIN; - previous_states_count = 0; - consoleClear(); - cursor_pos = 0; break; default: break; } - // Later if PXI interrupts are implemented we need an - // interrupt handler here which can tell the timer and - // PXI interrupts apart. - waitForIrq(); - REG_IRQ_IF = (u32)IRQ_TIMER_0; + menuActState(); } exitAndLaunchFirm: @@ -236,11 +239,62 @@ return 0; } +void menuSetReturnToState(int state) +{ + int i; + + if(state == STATE_PREVIOUS) + { + if(menu_previous_states_count == 0) + return; + state = menu_previous_states[menu_previous_states_count - 1]; + menu_previous_states_count--; + } + else + { + for(i=menu_previous_states_count; i>0; i--) + { + if(menu_previous_states[i] == state) + { + menu_previous_states_count = i; + } + } + + if(i == 0) + menu_previous_states_count = 0; + } + + menu_next_state = state; + // emit event + menu_event_state = MENU_EVENT_STATE_CHANGE; +} + +void menuSetEnterNextState(int state) +{ + if(menu_previous_states_count >= 8) + panic(); + + menu_previous_states[menu_previous_states_count] = menu_state; + menu_previous_states_count++; + + menu_next_state = state; + // emit event + menu_event_state = MENU_EVENT_STATE_CHANGE; +} + int menuUpdateGlobalState(void) { int retcode = MENU_EVENT_NONE; + // Later if PXI interrupts are implemented we need an + // interrupt handler here which can tell the timer and + // PXI interrupts apart. + waitForIrq(); + REG_IRQ_IF = (u32)IRQ_TIMER_0; + + /* Check PXI Response register */ + bool successFlag; u32 replyCode = PXI_tryRecvWord(&successFlag); @@ -251,6 +305,9 @@ case PXI_RPL_HOME_PRESSED: retcode = MENU_EVENT_HOME_PRESSED; break; + case PXI_RPL_POWER_PRESSED: + retcode = MENU_EVENT_POWER_PRESSED; + break; default: panic(); } @@ -258,5 +315,59 @@ replyCode = PXI_tryRecvWord(&successFlag); } + + /* Check for HW changes */ + + bool sd_status; + + sd_status = dev_sdcard->is_active(); + if(sd_status != bootInfo.sd_status) + { + retcode = sd_status ? MENU_EVENT_SD_CARD_INSERTED : + MENU_EVENT_SD_CARD_REMOVED; + bootInfo.sd_status = sd_status; // update bootInfo status + } + + + // Submenu wants to call a new menu? + if(menu_state != menu_next_state) + retcode = MENU_EVENT_STATE_CHANGE; + + // did anything happen? + if(retcode != MENU_EVENT_NONE) + menu_event_state = retcode; + return retcode; } + +void menuActState(void) +{ + switch(menu_event_state) + { + case MENU_EVENT_NONE: + break; // nothing to do, boring. + case MENU_EVENT_POWER_PRESSED: + power_off_safe(); + break; + case MENU_EVENT_HOME_PRESSED: + menuSetReturnToState(MENU_STATE_MAIN); + break; + case MENU_EVENT_STATE_CHANGE: + clearConsoles(); + menu_state = menu_next_state; + break; + case MENU_EVENT_SD_CARD_INSERTED: + dev_sdcard->init(); // try to initialize sd card + f_mount(&sd_fs, "sdmc:", 1); + break; + case MENU_EVENT_SD_CARD_REMOVED: + // what else to do here? + dev_sdcard->close(); + f_mount(NULL, "sdmc:", 1); + break; + default: + break; + } + + menu_event_state = MENU_EVENT_NONE; +} diff --git a/source/arm9/util_nand.c b/source/arm9/util_nand.c index 375a70f..abc1963 100644 --- a/source/arm9/util_nand.c +++ b/source/arm9/util_nand.c @@ -7,10 +7,10 @@ #include "arm9/fatfs/ff.h" #include "hid.h" #include "util.h" +#include "arm9/menu.h" #include "arm9/util_nand.h" - u64 getFreeSpace(const char *drive) { FATFS *fs; @@ -39,10 +39,7 @@ u8 *buf = (u8*)malloc(sectorBlkSize<<9); if(!buf) - { - printf("Not enough memory!\n"); - return false; - } + panic(); // this should never happen. if(getFreeSpace("sdmc:") < sectorCount<<9) { @@ -57,6 +54,9 @@ goto fail; } + + /* Main loop */ + u32 curSector = 0; u32 curSectorBlkSize; while(curSector < sectorCount) @@ -86,8 +86,25 @@ } curSector += curSectorBlkSize; + + // print current progress information printf("\r%"PRId32"/%"PRId32" MB (Sector 0x%"PRIX32"/0x%"PRIX32")", curSector>>11, sectorCount>>11, curSector, sectorCount); + + + switch(menuUpdateGlobalState()) + { + case MENU_EVENT_HOME_PRESSED: + case MENU_EVENT_POWER_PRESSED: + f_close(&file); + case MENU_EVENT_SD_CARD_REMOVED: + menuActState(); + goto fail; + default: + break; + } + + menuActState(); } f_close(&file); @@ -106,17 +123,13 @@ FIL file; UINT bytesRead; - consoleSelect(&con_bottom); printf("\n\t\tPress B to cancel"); consoleSelect(&con_top); u8 *buf = (u8*)malloc(sectorBlkSize<<9); if(!buf) - { - printf("Not enough memory!\n"); - goto fail; - } + panic(); // this should never happen. FILINFO fileStat; if(f_stat(filePath, &fileStat) != FR_OK) @@ -143,6 +156,10 @@ } + unmount_nand_fs(); + + /* Main loop */ + const u32 sectorCount = fileStat.fsize>>9; u32 curSector = 0; u32 curSectorBlkSize; @@ -167,14 +184,30 @@ curSector += curSectorBlkSize; printf("\r%"PRId32"/%"PRId32" MB (Sector 0x%"PRIX32"/0x%"PRIX32")", curSector>>11, sectorCount>>11, curSector, sectorCount); + + switch(menuUpdateGlobalState()) + { + case MENU_EVENT_HOME_PRESSED: + case MENU_EVENT_POWER_PRESSED: + f_close(&file); + case MENU_EVENT_SD_CARD_REMOVED: + menuActState(); + goto fail; + default: + break; + } + + menuActState(); } f_close(&file); free(buf); + remount_nand_fs(); return true; fail: free(buf); + remount_nand_fs(); wait(0x8000000); return false; }