From: chaoskagami Date: Tue, 26 Apr 2016 09:12:55 +0000 (-0400) Subject: Import cakes' FIRM decryption with heavy alterations, start on menuing X-Git-Tag: stable-1~88 X-Git-Url: https://chaos.moe/g/?a=commitdiff_plain;h=604d8880228ccfaa25c3ead43734c24bdc8a9097;p=corbenik%2Fcorbenik.git Import cakes' FIRM decryption with heavy alterations, start on menuing --- diff --git a/source/common.h b/source/common.h index 2861a82..f603257 100644 --- a/source/common.h +++ b/source/common.h @@ -7,8 +7,11 @@ #include "std/fs.h" #include "std/memory.h" -#include "crypto.h" -#include "headers.h" +#include "firm/fcram.h" +#include "firm/crypto.h" +#include "firm/headers.h" + #include "patch_format.h" +#include "config.h" #endif diff --git a/source/config.c b/source/config.c new file mode 100644 index 0000000..161d77b --- /dev/null +++ b/source/config.c @@ -0,0 +1,63 @@ +#include "common.h" + +FILE conf_handle; + +void regenerate_config() { + f_mkdir(PATH_CFW); + f_mkdir(PATH_FIRMWARES); + f_mkdir(PATH_PATCHES); + f_mkdir(PATH_LOCEMU); + f_mkdir(PATH_TEMP); + f_mkdir(PATH_KEYS); + f_mkdir(PATH_EXEFS); + + cprintf(BOTTOM_SCREEN, "Created directory structure.\n"); + + memset(&config, 0, sizeof(config)); + memcpy(&(config.magic), CONFIG_MAGIC, 4); + config.config_ver = config_version; + + fopen(&conf_handle, PATH_CONFIG, "w"); + fwrite(&config, 1, sizeof(config), &conf_handle); + fclose(&conf_handle); + + cprintf(BOTTOM_SCREEN, "Config file written.\n"); +} + +void load_config() { + // Zero on success. + if (fopen(&conf_handle, PATH_CONFIG, "r")) { + cprintf(BOTTOM_SCREEN, "Config file is missing:\n" + " %s\n" + "Will create it with defaults.\n", + PATH_CONFIG); + regenerate_config(); + } else { + fread(&config, 1, sizeof(config), &conf_handle); + fclose(&conf_handle); + + if (memcmp(&(config.magic), CONFIG_MAGIC, 4)) { + cprintf(BOTTOM_SCREEN, "Config file at:\n" + " %s\n" + "has incorrect magic:\n" + " '%c%c%c%c'\n" + "Regenerating with defaults.\n", + PATH_CONFIG, + config.magic[0], config.magic[1], config.magic[2], config.magic[3]); + f_unlink(PATH_CONFIG); + regenerate_config(); + } + + if (config.config_ver < config_version) { + cprintf(BOTTOM_SCREEN, "Config file has outdated version:\n" + " %s\n" + "Regenerating with defaults.\n", + PATH_CONFIG); + f_unlink(PATH_CONFIG); + regenerate_config(); + } + } + + cprintf(BOTTOM_SCREEN, "Config file loaded.\n"); +} + diff --git a/source/config.h b/source/config.h new file mode 100644 index 0000000..59bd197 --- /dev/null +++ b/source/config.h @@ -0,0 +1,37 @@ +#ifndef __CONFIG_H +#define __CONFIG_H + +static unsigned int config_version = 1; + +#define CONFIG_MAGIC "OVAN" + +// Structure of config file +struct config_file { + char magic[4]; // "OVAN" for shits and giggles again. + uint32_t config_ver; // Config file version. + + uint8_t options[256]; // Options in the menu - deliberately large to avoid config version bumps. + + uint64_t patch_ids[256]; // What patches are enabled by UUID. 256 is an arbitrary limit - contact me if you hit it. +}__attribute__((packed)); + +static struct config_file config; + +#define OPTION_AUTOBOOT 0 +#define OPTION_SILENCE 1 +#define OPTION_TRACE 2 + +void load_config(); + +/* +[CORBENIK] +version=1 +option_= +... +[PATCHES] +=<1|0> +... +*/ + + +#endif diff --git a/source/crypto.c b/source/firm/crypto.c similarity index 99% rename from source/crypto.c rename to source/firm/crypto.c index 29b6dfc..ee929ae 100644 --- a/source/crypto.c +++ b/source/firm/crypto.c @@ -3,7 +3,7 @@ #include "crypto.h" #include -#include "std/memory.h" +#include "../std/memory.h" /* original version by megazig */ #ifndef __thumb__ diff --git a/source/crypto.h b/source/firm/crypto.h similarity index 98% rename from source/crypto.h rename to source/firm/crypto.h index c9176a0..ba490f9 100644 --- a/source/crypto.h +++ b/source/firm/crypto.h @@ -1,6 +1,7 @@ // From http://github.com/b1l1s/ctr -#pragma once +#ifndef __CRYPTO_H +#define __CRYPTO_H #include #include "headers.h" @@ -132,3 +133,5 @@ typedef enum } ctr_ncchtypes; void ncch_getctr(const ncch_h* ncch, uint8_t* ctr, uint8_t type); + +#endif diff --git a/source/firm/fcram.c b/source/firm/fcram.c new file mode 100644 index 0000000..6ab1f67 --- /dev/null +++ b/source/firm/fcram.c @@ -0,0 +1,3 @@ +#include "fcram.h" + +void *fcram_temp = (void *)0x23000000; diff --git a/source/firm/fcram.h b/source/firm/fcram.h new file mode 100644 index 0000000..cfc5ca9 --- /dev/null +++ b/source/firm/fcram.h @@ -0,0 +1,28 @@ +#ifndef __FCRAM_H +#define __FCRAM_H + +// File to keep track of all the fcram offsets in use. +// It provides an easy overview of all that is used. + +#include + +extern void *fcram_temp; + +// Space between most of the locations +#define FCRAM_SPACING 0x100000 + +// Start of the space we use +#define FCRAM_START 0x24000000 + +// firm.c +#define FCRAM_FIRM_LOC FCRAM_START +#define FCRAM_TWL_FIRM_LOC (FCRAM_START + FCRAM_SPACING) // Double size +#define FCRAM_AGB_FIRM_LOC (FCRAM_START + FCRAM_SPACING * 3) + +// patch.c +#define FCRAM_MEMORY_LOC (FCRAM_START + FCRAM_SPACING * 4) + +static uint8_t *memory_loc = (uint8_t *)FCRAM_MEMORY_LOC; +static void *current_memory_loc; + +#endif diff --git a/source/firm/firm.c b/source/firm/firm.c new file mode 100644 index 0000000..2f885d8 --- /dev/null +++ b/source/firm/firm.c @@ -0,0 +1,398 @@ +#include "firm.h" + +#include +#include + +#include "../common.h" + +firm_h *firm_loc = (firm_h *)FCRAM_FIRM_LOC; +static uint32_t firm_size = FCRAM_SPACING; + +firm_h *twl_firm_loc = (firm_h *)FCRAM_TWL_FIRM_LOC; +static uint32_t twl_firm_size = FCRAM_SPACING * 2; + +firm_h *agb_firm_loc = (firm_h *)FCRAM_AGB_FIRM_LOC; +static uint32_t agb_firm_size = FCRAM_SPACING; + +static int update_96_keys = 0; +static int save_firm = 0; + +static volatile uint32_t *const a11_entry = (volatile uint32_t *)0x1FFFFFF8; + +void slot0x11key96_init() +{ + // 9.6 crypto may need us to get the key from somewhere else. + // Unless the console already has the key initialized, that is. + uint8_t key[AES_BLOCK_SIZE]; + if (read_file(key, PATH_SLOT0X11KEY96, AES_BLOCK_SIZE) || + read_file(key, PATH_ALT_SLOT0X11KEY96, AES_BLOCK_SIZE)) { + // If we can't read the key, we assume it's not needed, and the firmware is the right version. + // Otherwise, we make sure the error message for decrypting arm9bin mentions this. + aes_setkey(0x11, key, AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL); + + // Tell boot_firm it needs to regenerate the keys. + update_96_keys = 1; + } +} + +int decrypt_cetk_key(void *key, const void *cetk) +{ + // This function only decrypts the NATIVE_FIRM CETK. + // I don't need it for anything else atm. + // Either way, this is the reason for the two checks here at the top. + + static int common_key_y_init = 0; + uint8_t iv[AES_BLOCK_SIZE] = {0}; + + uint32_t sigtype = __builtin_bswap32(*(uint32_t *)cetk); + if (sigtype != SIG_TYPE_RSA2048_SHA256) return 1; + + ticket_h *ticket = (ticket_h *)(cetk + sizeof(sigtype) + 0x13C); + if (ticket->ticketCommonKeyYIndex != 1) return 1; + + if (!common_key_y_init) { + uint8_t common_key_y[AES_BLOCK_SIZE] = {0}; + uint8_t *p9_base = (uint8_t *)0x08028000; + uint8_t *i; + for (i = p9_base + 0x70000 - AES_BLOCK_SIZE; i >= p9_base; i--) { + if (i[0] == 0xD0 && i[4] == 0x9C && i[8] == 0x32 && i[12] == 0x23) { + // At i, there's 7 keys with 4 bytes padding between them. + // We only need the 2nd. + memcpy(common_key_y, i + AES_BLOCK_SIZE + 4, sizeof(common_key_y)); + cprintf(BOTTOM_SCREEN, "Found the common key Y\n"); + + break; + } + } + if (i < p9_base) return 1; + + aes_setkey(0x3D, common_key_y, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL); + common_key_y_init = 1; + } + + aes_use_keyslot(0x3D); + + memcpy(iv, ticket->titleID, sizeof(ticket->titleID)); + + cprintf(BOTTOM_SCREEN, "Decrypting key\n"); + memcpy(key, ticket->titleKey, sizeof(ticket->titleKey)); + aes(key, key, 1, iv, AES_CBC_DECRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL); + + return 0; +} + +int decrypt_firm_title(firm_h *dest, ncch_h *ncch, uint32_t *size, void *key) +{ + uint8_t firm_iv[16] = {0}; + uint8_t exefs_key[16] = {0}; + uint8_t exefs_iv[16] = {0}; + + cprintf(BOTTOM_SCREEN, "Decrypting the NCCH\n"); + aes_setkey(0x16, key, AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL); + aes_use_keyslot(0x16); + aes(ncch, ncch, *size / AES_BLOCK_SIZE, firm_iv, AES_CBC_DECRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL); + + if (ncch->magic != NCCH_MAGIC) + return 1; + + memcpy(exefs_key, ncch, 16); + ncch_getctr(ncch, exefs_iv, NCCHTYPE_EXEFS); + + // Get the exefs offset and size from the NCCH + exefs_h *exefs = (exefs_h *)((void *)ncch + ncch->exeFSOffset * MEDIA_UNITS); + uint32_t exefs_size = ncch->exeFSSize * MEDIA_UNITS; + + cprintf(BOTTOM_SCREEN, "Decrypting the exefs\n"); + aes_setkey(0x2C, exefs_key, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL); + aes_use_keyslot(0x2C); + aes(exefs, exefs, exefs_size / AES_BLOCK_SIZE, exefs_iv, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL); + + // Get the decrypted FIRM + // We assume the firm.bin is always the first file + firm_h *firm = (firm_h *)&exefs[1]; // The offset right behind the exefs header; the first file. + *size = exefs->fileHeaders[0].size; + + if (firm->magic != FIRM_MAGIC) + return 1; + + memcpy(dest, firm, *size); + + return 0; +} + +int decrypt_arm9bin(arm9bin_h *header, uint64_t firm_title) { + uint8_t slot = 0x15; + + cprintf(BOTTOM_SCREEN, "Decrypting ARM9 FIRM binary\n"); + +// if (firm_title == NATIVE_FIRM_TITLEID && version > 0x0F) { + if (firm_title == NATIVE_FIRM_TITLEID) { + uint8_t decrypted_keyx[AES_BLOCK_SIZE]; + + slot0x11key96_init(); + slot = 0x16; + + aes_use_keyslot(0x11); + aes(decrypted_keyx, header->slot0x16keyX, 1, NULL, AES_ECB_DECRYPT_MODE, 0); + aes_setkey(slot, decrypted_keyx, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL); + } + + aes_setkey(slot, header->keyy, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL); + aes_setiv(header->ctr, AES_INPUT_BE | AES_INPUT_NORMAL); + + void *arm9bin = (uint8_t *)header + 0x800; + int size = atoi(header->size); + + aes_use_keyslot(slot); + aes(arm9bin, arm9bin, size / AES_BLOCK_SIZE, header->ctr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL); + + if (firm_title == NATIVE_FIRM_TITLEID) + return *(uint32_t *)arm9bin != ARM9BIN_MAGIC; + else if (firm_title == AGB_FIRM_TITLEID || firm_title == TWL_FIRM_TITLEID) + return *(uint32_t *)arm9bin != LGY_ARM9BIN_MAGIC; + else return 0; +} + +int decrypt_firm(firm_h *dest, char *path_firmkey, char *path_cetk, uint32_t *size, uint64_t firm_title) { + uint8_t firm_key[AES_BLOCK_SIZE]; + + // Firmware is likely encrypted. Decrypt. + if (!read_file(firm_key, path_firmkey, AES_BLOCK_SIZE)) { + cprintf(BOTTOM_SCREEN, "Failed to load FIRM key,\n" + " Attempting to generate with CETK.\n"); + + if (!read_file(fcram_temp, path_cetk, FCRAM_SPACING)) { + cprintf(BOTTOM_SCREEN, "Failed to load CETK\n"); + return 1; + } + cprintf(BOTTOM_SCREEN, "Loaded CETK\n"); + + if (decrypt_cetk_key(firm_key, fcram_temp) != 0) { + cprintf(BOTTOM_SCREEN, "Failed to decrypt the CETK\n"); + return 1; + } + cprintf(BOTTOM_SCREEN, "Saving FIRM key for future use\n"); + write_file(firm_key, path_firmkey, AES_BLOCK_SIZE); + } else { + cprintf(BOTTOM_SCREEN, "Loaded FIRM key\n"); + } + + cprintf(BOTTOM_SCREEN, "Decrypting FIRM\n"); + if (decrypt_firm_title(dest, (void *)dest, size, firm_key) != 0) { + cprintf(BOTTOM_SCREEN, "Failed to decrypt the firmware\n"); + return 1; + } + return 0; +} + +int load_firm(firm_h *dest, char *path, char *path_firmkey, char *path_cetk, uint32_t *size, uint64_t firm_title) { + int status = 0; + int firmware_changed = 0; + + if (read_file(dest, path, *size)) { + cprintf(BOTTOM_SCREEN, "Failed to read FIRM from SD\n"); + + // Only whine about this if it's NATIVE_FIRM, which is important. + if (firm_title == NATIVE_FIRM_TITLEID) { + cprintf(BOTTOM_SCREEN, "Failed to load NATIVE_FIRM from:\n" + " " PATH_NATIVE_F "\n" + "This is fatal. Aborting.\n"); + } + + status = 1; + goto exit_error; + } + + // Check and decrypt FIRM if it is encrypted. + if (dest->magic != FIRM_MAGIC) { + status = decrypt_firm(dest, path_firmkey, path_cetk, size, firm_title); + if (status != 0) { + if (firm_title == NATIVE_FIRM_TITLEID) { + cprintf(BOTTOM_SCREEN, "Failed to decrypt firmware.\n" + "This is fatal. Aborting.\n"); + } + goto exit_error; + } + firmware_changed = 1; // Decryption performed. + } else { + cprintf(BOTTOM_SCREEN, "FIRM not encrypted\n"); + } + + // The N3DS firm has an additional encryption layer for ARM9 + // Only run if n3ds. + { + // Look for the arm9 section + for (firm_section_h *section = dest->section; + section < dest->section + 4; section++) { + if (section->type == FIRM_TYPE_ARM9) { + // Check whether the arm9bin is encrypted. + int arm9bin_iscrypt = 0; + uint32_t magic = *(uint32_t*)((uintptr_t)dest + section->offset + 0x800); + if (firm_title == NATIVE_FIRM_TITLEID) + arm9bin_iscrypt = (magic != ARM9BIN_MAGIC); + else if (firm_title == AGB_FIRM_TITLEID || firm_title == TWL_FIRM_TITLEID) + arm9bin_iscrypt = (magic != LGY_ARM9BIN_MAGIC); + + if (arm9bin_iscrypt) { + // Decrypt the arm9bin. + if (decrypt_arm9bin((arm9bin_h *)((uintptr_t)dest + section->offset), firm_title)) { + cprintf(BOTTOM_SCREEN, "Couldn't decrypt ARM9 FIRM binary.\n" + "Check if you have the needed key at:\n" + " " PATH_SLOT0X11KEY96 "\n"); + status = 1; + goto exit_error; + } + firmware_changed = 1; // Decryption of arm9bin performed. + } else { + cprintf(BOTTOM_SCREEN, "ARM9 FIRM binary not encrypted\n"); +// if (firm_type == NATIVE_FIRM && firm_current->version > 0x0F) { + slot0x11key96_init(); // This has to be loaded regardless, otherwise boot will fail. +// } + } + + // We assume there's only one section to decrypt. + break; + } + } + } + + // Save firmware.bin if decryption was done. + if (firmware_changed) { + cprintf(BOTTOM_SCREEN, "Saving decrypted FIRM\n"); + write_file(dest, path, *size); + } + + //if (firm_current->console == console_n3ds) + { + cprintf(BOTTOM_SCREEN, "Fixing arm9 entrypoint\n"); + + // Patch the entrypoint to skip arm9loader + if (firm_title == NATIVE_FIRM_TITLEID) { + dest->a9Entry = 0x0801B01C; + } else if (firm_title == AGB_FIRM_TITLEID || + firm_title == TWL_FIRM_TITLEID) { + dest->a9Entry = 0x0801301C; + } + // The entrypoints seem to be the same across different FIRM versions, + // so we don't change them. + } + + return 0; + +exit_error: + + return status; +} + +void __attribute__((naked)) disable_lcds() { + *a11_entry = 0; // Don't wait for us + + *(volatile uint32_t *)0x10202A44 = 0; + *(volatile uint32_t *)0x10202244 = 0; + *(volatile uint32_t *)0x1020200C = 0; + *(volatile uint32_t *)0x10202014 = 0; + + while (!*a11_entry); + ((void (*)())*a11_entry)(); +} + +void boot_firm() { + // Set up the keys needed to boot a few firmwares, due to them being unset, depending on which firmware you're booting from. + // TODO: Don't use the hardcoded offset. + if (update_96_keys) { + void *keydata = (void *)((uintptr_t)firm_loc + firm_loc->section[2].offset + 0x89814); + + aes_use_keyslot(0x11); + uint8_t keyx[AES_BLOCK_SIZE]; + for (int slot = 0x19; slot < 0x20; slot++) { + aes(keyx, keydata, 1, NULL, AES_ECB_DECRYPT_MODE, 0); + aes_setkey(slot, keyx, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL); + *(uint8_t *)(keydata + 0xF) += 1; + } + + cprintf(BOTTOM_SCREEN, "Updated keyX keyslots\n"); + } + + struct memory_header *memory = (void *)(memory_loc + 1); + while ((uintptr_t)memory < (uintptr_t)memory_loc + *memory_loc) { + memcpy((void *)memory->location, memory + 1, memory->size); + memory = (void *)((uintptr_t)(memory + 1) + memory->size); + } + cprintf(BOTTOM_SCREEN, "Copied memory\n"); + + for (firm_section_h *section = firm_loc->section; + section < firm_loc->section + 4 && section->address != 0; section++) { + memcpy((void *)section->address, (void *)firm_loc + section->offset, section->size); + } + cprintf(BOTTOM_SCREEN, "Copied FIRM\n"); + + *a11_entry = (uint32_t)disable_lcds; + while (*a11_entry); // Make sure it jumped there correctly before changing it. + *a11_entry = (uint32_t)firm_loc->a11Entry; + + cprintf(BOTTOM_SCREEN, "Prepared arm11 entry, jumping to FIRM\n"); + + ((void (*)())firm_loc->a9Entry)(); +} + +int load_firms() { + cprintf(TOP_SCREEN, "[Loading FIRM]"); + + cprintf(BOTTOM_SCREEN, "Loading NATIVE_FIRM\n"); + if (load_firm(firm_loc, PATH_NATIVE_F, PATH_NATIVE_FIRMKEY, PATH_NATIVE_CETK, &firm_size, NATIVE_FIRM_TITLEID) != 0) + return 1; + + cprintf(BOTTOM_SCREEN, "Loading TWL_FIRM\n"); + if(load_firm(twl_firm_loc, PATH_TWL_F, PATH_TWL_FIRMKEY, PATH_TWL_CETK, &twl_firm_size, TWL_FIRM_TITLEID)) + cprintf(BOTTOM_SCREEN, "TWL_FIRM failed to load.\n"); + + cprintf(BOTTOM_SCREEN, "Loading AGB_FIRM\n"); + if(load_firm(agb_firm_loc, PATH_AGB_F, PATH_AGB_FIRMKEY, PATH_AGB_CETK, &agb_firm_size, AGB_FIRM_TITLEID)) + cprintf(BOTTOM_SCREEN, "AGB_FIRM failed to load.\n"); + + return 0; +} + +void boot_cfw() { + cprintf(TOP_SCREEN, "[Patching]"); +// if (patch_firm_all() != 0) +// return; + + // Only save the firm if that option is required (or it's needed for autoboot), + // and either the patches have been modified, or the file doesn't exist. + if ((save_firm || config.options[OPTION_AUTOBOOT]) && + f_stat(PATH_NATIVE_P, NULL) != 0) { + cprintf(BOTTOM_SCREEN, "Saving patched NATIVE_FIRM\n"); + if (write_file(firm_loc, PATH_NATIVE_P, firm_size) != firm_size) { + cprintf(BOTTOM_SCREEN, "%pFailed to save patched FIRM.\nWriting SD failed.\nThis is fatal.\n", COLOR(RED, BLACK)); + return; + } + } + + if ((save_firm || config.options[OPTION_AUTOBOOT]) && + f_stat(PATH_MEMBIN, NULL) != 0) { + cprintf(BOTTOM_SCREEN, "Saving patched memory\n"); + if (write_file(memory_loc, PATH_MEMBIN, *memory_loc) != *memory_loc) { + cprintf(BOTTOM_SCREEN, "%pFailed to save the patched FIRM\nWriting SD failed.\n", COLOR(RED, BLACK)); + return; + } + } + + if (f_stat(PATH_TWL_P, NULL) != 0) { + cprintf(BOTTOM_SCREEN, "Saving patched TWL_FIRM\n"); + if (write_file(twl_firm_loc, PATH_TWL_P, twl_firm_size) != twl_firm_size) { + cprintf(BOTTOM_SCREEN, "%pFailed to save the patched FIRM\nWriting SD failed.\n", COLOR(RED, BLACK)); + return; + } + } + + if (f_stat(PATH_AGB_P, NULL) != 0) { + cprintf(BOTTOM_SCREEN, "Saving patched AGB_FIRM\n"); + if (write_file(agb_firm_loc, PATH_AGB_P, agb_firm_size) != agb_firm_size) { + cprintf(BOTTOM_SCREEN, "%pFailed to save the patched FIRM\nWriting SD failed.\n", COLOR(RED, BLACK)); + return; + } + } + +// boot_firm(); +} diff --git a/source/firm/firm.h b/source/firm/firm.h new file mode 100644 index 0000000..20eb89b --- /dev/null +++ b/source/firm/firm.h @@ -0,0 +1,22 @@ +#ifndef __FIRM_H + +#include +#include "headers.h" + +extern firm_h *firm_loc; +extern struct firm_signature *current_firm; + +extern firm_h *twl_firm_loc; +extern struct firm_signature *current_twl_firm; + +extern firm_h *agb_firm_loc; +extern struct firm_signature *current_agb_firm; + +struct firm_signature *get_firm_info(firm_h *firm); +void slot0x11key96_init(); + +int load_firms(); +void boot_firm(); +void boot_cfw(); + +#endif diff --git a/source/headers.h b/source/firm/headers.h similarity index 98% rename from source/headers.h rename to source/firm/headers.h index 5110cdc..8ea3556 100644 --- a/source/headers.h +++ b/source/firm/headers.h @@ -17,6 +17,11 @@ #define ARM9BIN_MAGIC (0x47704770) #define LGY_ARM9BIN_MAGIC (0xB0862000) +struct memory_header { + uint32_t location; + uint32_t size; +} memory_header_t; + typedef struct firm_section_h { uint32_t offset; uint32_t address; diff --git a/source/main.c b/source/main.c index 42053ce..099f7bd 100644 --- a/source/main.c +++ b/source/main.c @@ -1,12 +1,70 @@ #include "common.h" -void load_options() { - FILE config; -} - void init_system() {} +#define MENU_BOOTME -1 +#define MENU_MAIN 1 +#define MENU_OPTIONS 2 +#define MENU_PATCHES 3 +#define MENU_INFO 4 +#define MENU_RESET 5 +#define MENU_POWER 6 + +static int cursor_y = 0; +static int which_menu = 1; + +int menu_options() { return MENU_MAIN; } +int menu_patches() { return MENU_MAIN; } +int menu_info() { return MENU_MAIN; } + +int menu_main() { + set_cursor(TOP_SCREEN, 0, 0); + + const char *list[] = { + "Options", + "Patches", + "Info", + "Boot firmware", + "Reset", + "Power off" + }; + + cprintf(TOP_SCREEN, "%p[Corbenik - The Rebirth]\n", COLOR(CYAN, BLACK)); + + for(int i=0; i < 6; i++) { + if (cursor_y == i) + cprintf(TOP_SCREEN, "%p-> ", COLOR(GREEN, BLACK)); + else + cprintf(TOP_SCREEN, " "); + cprintf(TOP_SCREEN, "%s\n", list[i]); + } + + return 0; +} + int menu_handler() { + int to_menu = 0; + switch(which_menu) { + case MENU_MAIN: + to_menu = menu_main(); + break; + case MENU_OPTIONS: + to_menu = menu_options(); + break; + case MENU_PATCHES: + to_menu = menu_patches(); + break; + case MENU_INFO: + to_menu = menu_info(); + break; + case MENU_BOOTME: + default: + return 0; + } + + if (to_menu != 0) + which_menu = to_menu; + return 1; } @@ -18,13 +76,9 @@ void main() { cprintf(BOTTOM_SCREEN, "Mounted SD card.\n"); } - for (int i = 0; i < 200; i++) { - cprintf(TOP_SCREEN, "%d\n", i); - } - - cprintf(TOP_SCREEN, "Done printing."); + load_config(); // Load configuration. - load_options(); // Load configuration. + load_firms(); int in_menu = 1; diff --git a/source/patch_format.h b/source/patch_format.h index 3b7ff95..38879af 100644 --- a/source/patch_format.h +++ b/source/patch_format.h @@ -13,6 +13,43 @@ #define LOADER_TITLEID 0x0004013000001302llu // Loader is handled specially. +#define PATCH_MANDATORY (1 << 0) // Patch must be applied for successful boot. +#define PATCH_FAIL_ABORT (1 << 1) // If patch fails to apply, abort and show an error. +#define PATCH_DISABLED (1 << 2) // Do not allow changing this patch's status. With PATCH_MANDATORY, this prevents disabling it. + +#define PATH_CFW "/corbenik" // CFW root directory. +#define PATH_CONFIG PATH_CFW "/main.conf" // Config file. +#define PATH_LOCEMU PATH_CFW "/locale.conf" // Locale emulation config +#define PATH_CPU_CFG PATH_CFW "/cpu.conf" // CPU settings config + +#define PATH_FIRMWARES PATH_CFW "/firmware" // Firmware folder. +#define PATH_PATCHES PATH_CFW "/patches" // Patches folder. +#define PATH_TEMP PATH_CFW "/temp" // Files that are transient (user can delete them and they will be regenerated) +#define PATH_KEYS PATH_CFW "/keys" // Keyfiles will be loaded from this dir, and additionally the root if not found. +#define PATH_EXEFS PATH_CFW "/exefs" // ExeFS overrides, named like '.exefs' + +#define PATH_NATIVE_F PATH_FIRMWARES "/native" +#define PATH_AGB_F PATH_FIRMWARES "/agb" +#define PATH_TWL_F PATH_FIRMWARES "/twl" + +#define PATH_NATIVE_CETK PATH_KEYS "/native.cetk" +#define PATH_NATIVE_FIRMKEY PATH_KEYS "/native.key" + +#define PATH_TWL_CETK PATH_KEYS "/twl.cetk" +#define PATH_TWL_FIRMKEY PATH_KEYS "/twl.key" + +#define PATH_AGB_CETK PATH_KEYS "/agb.cetk" +#define PATH_AGB_FIRMKEY PATH_KEYS "/agb.key" + +#define PATH_SLOT0X11KEY96 PATH_KEYS "/0x11.key" + +#define PATH_ALT_SLOT0X11KEY96 "/slot0x11key96.bin" // Hey, your perrogative, buddy. I like cleaned up paths. + +#define PATH_MEMBIN PATH_TEMP "/memory_p" // Memory binary +#define PATH_NATIVE_P PATH_TEMP "/native_p" // Native FIRM patched +#define PATH_AGB_P PATH_TEMP "/agb_firm_p" // AGB FIRM patched +#define PATH_TWL_P PATH_TEMP "/twl_firm_p" // TWL FIRM patched + // Structure of a patch file. struct system_patch { char magic[4]; // "AIDA" for shits and giggles and because we like .hack. @@ -24,11 +61,29 @@ struct system_patch { uint64_t patch_id; // Unique ID for patch. Each unique patch should provide a unique ID. uint64_t tid; // What title this patch is intended for. Certain values are specially handled. - uint64_t depends[64]; // What patches need to be applied for this patch to be applied; as unique IDs + + uint8_t extra_flags; // Extra flags for patch. + + uint64_t depends[16]; // What patches need to be applied for this patch to be applied; as unique IDs uint32_t patch_size; // Size of the following patch data. uint8_t patch_data[]; // The data for the patch. This is a sort of interpreted code...see below. } __attribute__((packed)); +/* +[PATCH] +version=1 +cfw_version=1 + +name=Signature Check +desc=Disables firmware signature checking. +uuid=1 +title_id=0004013800000002 +[DEPENDS] +none +[DATA] + +*/ + #endif diff --git a/source/std/draw.c b/source/std/draw.c index 0a01481..e722559 100644 --- a/source/std/draw.c +++ b/source/std/draw.c @@ -17,20 +17,20 @@ static char color_buffer_bottom [TEXT_BOTTOM_SIZE]; static uint32_t colors[16] = { 0x000000, // Black - 0x0000aa, // Blue + 0xaa0000, // Blue 0x00aa00, // Green - 0x00aaaa, // Cyan - 0xaa0000, // Red + 0xaaaa00, // Cyan + 0x0000aa, // Red 0xaa00aa, // Magenta - 0xaa5500, // Brown + 0x0055aa, // Brown 0xaaaaaa, // Gray 0x555555, // Dark gray - 0x5555ff, // Bright blue + 0xff5555, // Bright blue 0x55ff55, // Bright green - 0x55ffff, // Bright cyan - 0xff5555, // Bright red + 0xffff55, // Bright cyan + 0x5555ff, // Bright red 0xff55ff, // Bright megenta - 0xffff55, // Yellow + 0x55ffff, // Yellow 0xffffff // White }; @@ -43,10 +43,14 @@ void clear_screen(uint8_t* screen) { size = SCREEN_TOP_SIZE; buffer = text_buffer_top; buffer_size = TEXT_TOP_SIZE; + top_cursor_x = 0; + top_cursor_y = 0; } else if(screen == framebuffers->bottom) { size = SCREEN_BOTTOM_SIZE; buffer = text_buffer_bottom; buffer_size = TEXT_BOTTOM_SIZE; + bottom_cursor_x = 0; + bottom_cursor_y = 0; } else { return; // Invalid buffer. } @@ -55,13 +59,22 @@ void clear_screen(uint8_t* screen) { memset(buffer, 0, buffer_size); } +void set_cursor(int channel, unsigned int x, unsigned int y) { + switch(channel) { + case TOP_SCREEN: + top_cursor_x = x; + top_cursor_y = y; + break; + case BOTTOM_SCREEN: + bottom_cursor_x = x; + bottom_cursor_y = y; + break; + } +} + void clear_screens() { clear_screen(framebuffers->top_left); clear_screen(framebuffers->bottom); - top_cursor_x = 0; - top_cursor_y = 0; - bottom_cursor_x = 0; - bottom_cursor_y = 0; } void draw_character(uint8_t* screen, const char character, const unsigned int buf_x, const unsigned int buf_y, const uint32_t color_fg, const uint32_t color_bg) { @@ -103,7 +116,7 @@ void draw_character(uint8_t* screen, const char character, const unsigned int bu } } -void putc(int buf, unsigned char color, const char c) { +void putc(int buf, unsigned char color, const int c) { unsigned int width = 0; unsigned int height = 0; unsigned int size = 0; diff --git a/source/std/draw.h b/source/std/draw.h index 26b5831..a79eb0b 100644 --- a/source/std/draw.h +++ b/source/std/draw.h @@ -51,13 +51,15 @@ void draw_character(uint8_t* screen, const char character, const unsigned int po #define TOP_SCREEN 0 #define BOTTOM_SCREEN 1 -void putc(int buf, unsigned char color, const char c); +void putc(int buf, unsigned char color, const int c); void puts(int buf, unsigned char color, const char *string); void cflush(int channel); void put_int(int channel, unsigned char color, int n); void put_uint(int channel, unsigned char color, unsigned int n); +void set_cursor(int channel, unsigned int x, unsigned int y); + // Like printf. Supports the following format specifiers: // %s %c %d %u // The following non-standard formats are also supported (but are subject to replacement) diff --git a/source/std/fs.c b/source/std/fs.c index f222913..e25a7a3 100644 --- a/source/std/fs.c +++ b/source/std/fs.c @@ -21,9 +21,7 @@ int fopen(FILE* fp, const char *filename, const char *mode) { fp->mode = (*mode == 'r' ? FA_READ : (FA_WRITE | FA_OPEN_ALWAYS)); - f_open(&(fp->handle), filename, fp->mode); - - return 0; + return f_open(&(fp->handle), filename, fp->mode); } void fclose(FILE* fp) { @@ -61,16 +59,38 @@ size_t fsize(FILE* fp) { return f_size(&(fp->handle)); } -size_t fwrite(const char *buffer, size_t elementSize, size_t elementCnt, FILE* fp) { - uint32_t br; +size_t fwrite(const void *buffer, size_t elementSize, size_t elementCnt, FILE* fp) { + UINT br; if(f_write(&(fp->handle), buffer, elementSize*elementCnt, &br)) return 0; if (br == elementSize*elementCnt) br /= elementSize; else return 0; return br; } -size_t fread(char *buffer, size_t elementSize, size_t elementCnt, FILE* fp) { - uint32_t br; - if(f_read(&(fp->handle), buffer, elementSize*elementCnt, &br)) return 0; - if (br == elementSize*elementCnt) br /= elementSize; else return 0; +size_t fread(void *buffer, size_t elementSize, size_t elementCnt, FILE* fp) { + UINT br; + if(f_read(&(fp->handle), buffer, elementSize*elementCnt, &br)) + return 0; + if (br == elementSize*elementCnt) + br /= elementSize; + else + return 0; return br; } + +size_t write_file(void* data, char* path, size_t size) { + FILE temp; + fopen(&temp, path, "w"); + size_t written = fwrite(data, 1, size, &temp); + fclose(&temp); + return written; +} + +size_t read_file(void* data, char* path, size_t size) { + FILE temp; + fopen(&temp, path, "r"); + + size_t read = fread(data, 1, size, &temp); + fclose(&temp); + + return read; +} diff --git a/source/std/fs.h b/source/std/fs.h index 53f9761..16f6f47 100644 --- a/source/std/fs.h +++ b/source/std/fs.h @@ -31,7 +31,11 @@ int feof (FILE* fp); size_t fsize (FILE* fp); -size_t fwrite (const char *buffer, size_t elementSize, size_t elementCnt, FILE* fp); -size_t fread (char *buffer, size_t elementSize, size_t elementCnt, FILE* fp); +size_t fwrite (const void *buffer, size_t elementSize, size_t elementCnt, FILE* fp); +size_t fread (void *buffer, size_t elementSize, size_t elementCnt, FILE* fp); + + +size_t write_file(void* data, char* path, size_t size); +size_t read_file(void* data, char* path, size_t size); #endif