+// This is a tiny chainloader. It expects r0 to be the
+// code to copy, and r1 to be the code's size.
+
+// As long as GCC follows standard calling conventions, you
+// can call it from C once in memory like:
+
+// void copy(uint8_t* data, uint32_t size)
+
+// This means NO need to use fatfs with the chainloader since the
+// caller itself handles the disk read.
+
+// The code below is also all PC-relative, meaning you can actually
+// run the chainloader from anywhere (as long as it is aligned to
+// instruction boundaries/the chainloader isn't overwritten/the
+// code isn't copied wrong over itself)
+
.syntax unified
.section .text
.global copy
-copy: // void copy(uint8_t* data, uint32_t size)
+copy:
ldr r3, value
add r1, r0, r1
--- /dev/null
+#include "common.h"
+#include "firm/firm.h"
+#include "firm/headers.h"
+
+uint32_t current_chain_index = 0;
+
+struct options_s *chains = (struct options_s*)FCRAM_CHAIN_LOC;
+
+int show_menu(struct options_s *options, uint8_t *toggles);
+
+// TODO - The near same function is called in different places. It would
+// be better to have a recursive listing that calls a function for
+// each entry (it would cut code density)
+
+void chainload_file(char* chain_file_data) {
+ // We copy because it's possible the payload will overwrite us in memory.
+ char chain_file[256];
+ strncpy(chain_file, chain_file_data, 255);
+
+ char code_file[] = PATH_BITS "/chain.bin";
+
+ uint8_t* bootstrap = (uint8_t*)0x24F00000;
+ uint32_t size = 0;
+ uint8_t* chain_data;
+
+ FILE* f = fopen(code_file, "r");
+ if (!f) {
+ // File missing.
+ abort("Missing chainloader.\n");
+ }
+
+ size = fsize(f);
+ fread(bootstrap, 1, size, f);
+ fclose(f);
+
+ chain_data = bootstrap + size;
+
+ f = fopen(chain_file, "r");
+ if (!f) {
+ // File missing.
+ abort("Missing program to chainload?\n");
+ }
+
+ size = fsize(f);
+ fread(chain_data, 1, size, f);
+ fclose(f);
+
+ fprintf(stderr, "Chaining to copy payload.\n");
+
+ ((void(*)())0x24F00000)(chain_data, size);
+}
+
+// This function is based on PathDeleteWorker from GodMode9.
+// It was easier to just import it.
+int
+list_chain_build_back(char *fpath)
+{
+ FILINFO fno = {.lfname = NULL };
+
+ // this code handles directory content deletion
+ if (f_stat(fpath, &fno) != FR_OK)
+ return 1; // fpath does not exist
+
+ if (fno.fattrib & AM_DIR) { // process folder contents
+ DIR pdir;
+ char *fname = fpath + strnlen(fpath, 255);
+ if (f_opendir(&pdir, fpath) != FR_OK)
+ return 1;
+
+ *(fname++) = '/';
+ fno.lfname = fname;
+ fno.lfsize = fpath + 255 - fname;
+
+ while (f_readdir(&pdir, &fno) == FR_OK) {
+ if ((strncmp(fno.fname, ".", 2) == 0) || (strncmp(fno.fname, "..", 3) == 0))
+ continue; // filter out virtual entries
+ if (fname[0] == 0)
+ strncpy(fname, fno.fname, fpath + 255 - fname);
+ if (fno.fname[0] == 0)
+ break;
+ else // return value won't matter
+ list_chain_build_back(fpath);
+ }
+
+ f_closedir(&pdir);
+ *(--fname) = '\0';
+ } else {
+ char* basename = &fpath[strlen(fpath) - 1];
+ while(basename[0] != '/') basename--;
+ basename++;
+
+ strncpy(chains[current_chain_index].name, basename, 64);
+ strncpy(chains[current_chain_index].desc, fpath, 255);
+
+ chains[current_chain_index].index = 0;
+ chains[current_chain_index].allowed = call_fun;
+ chains[current_chain_index].a = (uint32_t) chainload_file;
+ chains[current_chain_index].b = (uint32_t) chains[current_chain_index].desc;
+
+ current_chain_index++;
+ }
+
+ return 0;
+}
+
+// This is dual purpose. When we actually list
+// patches to build the cache - desc_is_fname
+// will be set to 1.
+
+void
+list_chain_build(char *name)
+{
+ current_chain_index = 0;
+
+ strncpy(chains[0].name, "\x1b[40;32mChainloader Payloads\x1b[0m", 64);
+ strncpy(chains[0].desc, "", 255);
+ chains[0].index = 0;
+ chains[0].allowed = not_option;
+ chains[0].a = 0;
+ chains[0].b = 0;
+
+ current_chain_index += 1;
+
+ char fpath[256];
+ strncpy(fpath, name, 256);
+ list_chain_build_back(fpath);
+ chains[current_chain_index].index = -1;
+
+ if (chains[1].index == -1)
+ chains[0].index = -1; // No chainloadable files.
+}
+
+void chainload_menu() {
+ list_chain_build(PATH_CHAINS);
+
+ show_menu(chains, NULL);
+}
f_mkdir(PATH_PATCHES);
f_mkdir(PATH_FIRMWARES);
f_mkdir(PATH_MODULES);
- f_mkdir(PATH_SVC);
f_mkdir(PATH_KEYS);
f_mkdir(PATH_EXEFS);
f_mkdir(PATH_EXEFS_TEXT);
break_menu = 6
};
-typedef void (*func_call_t)(void);
+typedef void (*func_call_t)(uint32_t data);
struct range_str
{
// This isn't supported by ANY tools like D9 at the moment
// (Though I hope they'll consider it -
// there's only benefits to users with multiple EmuNANDs)
-#define OPTION_EMUNAND_REVERSE 22
+
+// Disable Reverse. We're going to implement an actual filesystem.
+// #define OPTION_EMUNAND_REVERSE 22
// Save log files during boot and from loader.
// This will slow things down a bit.
else
toggles[options[cursor_y].index]++;
} else if (options[cursor_y].allowed == call_fun) {
- ((func_call_t)(options[cursor_y].a))(); // Call 'a' as a function.
+ ((func_call_t)(options[cursor_y].a))(options[cursor_y].b); // Call 'a' as a function.
} else if (options[cursor_y].allowed == break_menu) {
exit = 1;
clear_screen(TOP_SCREEN);
// Path that the font will be loaded at.
#define FCRAM_FONT_LOC (FCRAM_PATCHLIST_LOC + (FCRAM_SPACING / 2))
+// Path that the menu for chains will be at
+#define FCRAM_CHAIN_LOC (FCRAM_FONT_LOC + FCRAM_SPACING)
+
#endif
{ OPTION_EMUNAND, "Use EmuNAND", "Redirects NAND write/read to the SD. This supports both Gateway and redirected layouts.", boolean_val, 0, 0 },
{ OPTION_EMUNAND_INDEX, " Index", "Which EmuNAND to use. If you only have one, you want 0. Currently the maximum supported is 10 (0-9), but this is arbitrary.", ranged_val, 0, 0x9 },
- { OPTION_EMUNAND_REVERSE, " Reverse layout", "(Warning - Experimental!) Calculate EmuNAND sector from the end of the disk, not the start. This isn't supported by tools like Decrypt9, but has some advantages.", boolean_val, 0, 0x9 },
+// { OPTION_EMUNAND_REVERSE, " Reverse layout", "(Warning - Experimental!) Calculate EmuNAND sector from the end of the disk, not the start. This isn't supported by tools like Decrypt9, but has some advantages.", boolean_val, 0, 0x9 },
{ OPTION_AUTOBOOT, "Autoboot", "Boot the system automatically, unless the R key is held while booting.", boolean_val, 0, 0 },
{ OPTION_SILENCE, " Silent mode", "Suppress all debug output during autoboot. You'll see the screen turn on and then off once.", boolean_val, 0, 0 },
;
}
+void chainload_menu();
+
static struct options_s main_s[] = {
{ 0, "Options", "Internal options for the CFW. These are part of Corbenik itself.", call_fun, (uint32_t)menu_options, 0 },
{ 0, "Patches", "External bytecode patches found in `" PATH_PATCHES "`. You can choose which to enable.", call_fun, (uint32_t)menu_patches, 0 },
{ 0, "Reboot", "Reboots the console.", call_fun, (uint32_t)reset, 0 },
{ 0, "Power off", "Powers off the console.", call_fun, (uint32_t)poweroff, 0 },
{ 0, "Save Configuration", "Save the configuration. You must do this prior to booting, otherwise nothing is done.", call_fun, (uint32_t)save_config, 0 },
+ { 0, "Chainload", "Boot another ARM9 payload file.", call_fun, (uint32_t)chainload_menu, 0 },
{ 0, "Boot Firmware", "Generates caches, patches the firmware, and boots it.", break_menu, 0, 0 },
// Sentinel.
uint32_t offset;
if (nandSize > 0x200000)
- offset = 0x400000;
+ offset = 0x400000 * index;
else
- offset = 0x200000;
-
- if (config.options[OPTION_EMUNAND_REVERSE]) {
- // Subtract offset from back of disk.
- offset = (getMMCDevice(1)->total_size - 1) - (offset * (index + 1));
- } else {
- offset = offset * index;
- }
+ offset = 0x200000 * index;
// Check for RedNAND/Normal physical layout on SD
if (!sdmmc_sdcard_readsectors(offset + 1, 1, emunand_temp) && *(uint32_t *)(emunand_temp + 0x100) == NCSD_MAGIC) {
#define PATH_PATCHES PATH_CFW "/patch" // Patch binary folder.
#define PATH_FIRMWARES PATH_CFW "/firmware" // Firmware folder.
#define PATH_MODULES PATH_CFW "/module" // Sysmodule location
-#define PATH_SVC PATH_CFW "/svc" // Svc code location.
+
+#define PATH_CHAINS PATH_CFW "/chain"
#define PATH_TEMP PATH_CFW "/cache" // Files that are transient and used to speed operation
#define PATH_LOADER_CACHE PATH_TEMP "/loader" // Cached patch bytecode for loader.