diff --git a/fusee/fusee-secondary/src/fuse.c b/fusee/fusee-secondary/src/fuse.c index 9a64ce0be..efcbdba06 100644 --- a/fusee/fusee-secondary/src/fuse.c +++ b/fusee/fusee-secondary/src/fuse.c @@ -14,6 +14,7 @@ * along with this program. If not, see . */ +#include #include #include #include @@ -344,3 +345,43 @@ uint32_t fuse_get_regulator(void) { return 0; /* Regulator_Erista_Max77621 */ } } + + +static const uint32_t fuse_version_increment_firmwares[] = { + ATMOSPHERE_TARGET_FIRMWARE_11_0_0, + ATMOSPHERE_TARGET_FIRMWARE_10_0_0, + ATMOSPHERE_TARGET_FIRMWARE_9_1_0, + ATMOSPHERE_TARGET_FIRMWARE_9_0_0, + ATMOSPHERE_TARGET_FIRMWARE_8_1_0, + ATMOSPHERE_TARGET_FIRMWARE_7_0_0, + ATMOSPHERE_TARGET_FIRMWARE_6_2_0, + ATMOSPHERE_TARGET_FIRMWARE_6_0_0, + ATMOSPHERE_TARGET_FIRMWARE_5_0_0, + ATMOSPHERE_TARGET_FIRMWARE_4_0_0, + ATMOSPHERE_TARGET_FIRMWARE_3_0_2, + ATMOSPHERE_TARGET_FIRMWARE_3_0_0, + ATMOSPHERE_TARGET_FIRMWARE_2_0_0, + ATMOSPHERE_TARGET_FIRMWARE_1_0_0, +}; + +static const uint32_t num_fuse_increments = sizeof(fuse_version_increment_firmwares) / sizeof(fuse_version_increment_firmwares[0]); + +uint32_t fuse_get_expected_fuse_count(uint32_t target_firmware) { + for (uint32_t i = 0; i < num_fuse_increments; ++i) { + if (target_firmware >= fuse_version_increment_firmwares[i]) { + return num_fuse_increments - i; + } + } + return 0; +} + +uint32_t fuse_get_burnt_fuse_count(void) { + const uint32_t val = fuse_get_reserved_odm(7); + uint32_t count = 0; + for (uint32_t i = 0; i < sizeof(uint32_t) * CHAR_BIT; ++i) { + if (((val >> i) & 1) != 0) { + ++count; + } + } + return count; +} diff --git a/fusee/fusee-secondary/src/fuse.h b/fusee/fusee-secondary/src/fuse.h index fe17746cc..afd5cf60e 100644 --- a/fusee/fusee-secondary/src/fuse.h +++ b/fusee/fusee-secondary/src/fuse.h @@ -477,6 +477,9 @@ uint32_t fuse_get_device_unique_key_generation(void); uint32_t fuse_get_soc_type(void); uint32_t fuse_get_regulator(void); +uint32_t fuse_get_expected_fuse_count(uint32_t target_firmware); +uint32_t fuse_get_burnt_fuse_count(void); + uint32_t fuse_hw_read(uint32_t addr); void fuse_hw_write(uint32_t value, uint32_t addr); void fuse_hw_sense(void); diff --git a/fusee/fusee-secondary/src/nxboot.c b/fusee/fusee-secondary/src/nxboot.c index e23007b77..16acacdb8 100644 --- a/fusee/fusee-secondary/src/nxboot.c +++ b/fusee/fusee-secondary/src/nxboot.c @@ -19,6 +19,8 @@ #include #include #include +#include + #include "utils.h" #include "fs_utils.h" #include "nxboot.h" @@ -675,6 +677,10 @@ static bool get_and_clear_has_run_sept(void) { return has_run_sept; } +static void get_mariko_warmboot_path(char *dst, size_t dst_size, uint32_t version) { + snprintf(dst, dst_size, "warmboot_mariko/wb_%02" PRIx32 ".bin", version); +} + /* This is the main function responsible for booting Horizon. */ static nx_keyblob_t __attribute__((aligned(16))) g_keyblobs[32]; uint32_t nxboot_main(void) { @@ -948,26 +954,73 @@ uint32_t nxboot_main(void) { } /* Read the warmboot firmware from a file, otherwise from Atmosphere's implementation (Erista only) or from cache (Mariko only). */ - if (loader_ctx->warmboot_path[0] != '\0') { - warmboot_fw_size = get_file_size(loader_ctx->warmboot_path); - if (warmboot_fw_size == 0) { - fatal_error("[NXBOOT] Could not read the warmboot firmware from %s!\n", loader_ctx->warmboot_path); + uint32_t warmboot_fuses = 0; + if (is_mariko) { + /* Get warmboot firmware from package1. */ + const package1_header_t *pk11_header = (const package1_header_t *)((uintptr_t)package1loader + (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_6_2_0 ? 0x7000 : 0x4000)); + + warmboot_fw = package1_get_warmboot_fw(pk11_header); + warmboot_fw_size = *(const uint32_t *)warmboot_fw; + + if (!(0x800 <= warmboot_fw_size && warmboot_fw_size < 0x1000)) { + fatal_error("[NXBOOT] Warmboot firmware seems invalid!\n"); } - /* Allocate memory for the warmboot firmware. */ - warmboot_fw = malloc(warmboot_fw_size); + const uint32_t expected_fuses = fuse_get_expected_fuse_count(target_firmware); + const uint32_t burnt_fuses = fuse_get_burnt_fuse_count(); - if (warmboot_fw == NULL) { - fatal_error("[NXBOOT] Out of memory!\n"); + warmboot_fuses = expected_fuses; + + char warmboot_fn[0x80]; + get_mariko_warmboot_path(warmboot_fn, sizeof(warmboot_fn), expected_fuses); + + if (!is_valid_file(warmboot_fn)) { + dump_to_file(warmboot_fw, warmboot_fw_size, warmboot_fn); } - if (read_from_file(warmboot_fw, warmboot_fw_size, loader_ctx->warmboot_path) != warmboot_fw_size) { - fatal_error("[NXBOOT] Could not read the warmboot firmware from %s!\n", loader_ctx->warmboot_path); - } - } else { - if (is_mariko) { - /* TODO */ + + if (burnt_fuses > expected_fuses) { warmboot_fw = NULL; warmboot_fw_size = 0; + for (uint32_t attempt = burnt_fuses; attempt <= 32; ++attempt) { + get_mariko_warmboot_path(warmboot_fn, sizeof(warmboot_fn), attempt); + if (is_valid_file(warmboot_fn)) { + warmboot_fw_size = get_file_size(warmboot_fn); + + /* Allocate memory for the warmboot firmware. */ + warmboot_fw = malloc(warmboot_fw_size); + + if (warmboot_fw == NULL) { + fatal_error("[NXBOOT] Out of memory!\n"); + } + if (read_from_file(warmboot_fw, warmboot_fw_size, warmboot_fn) != warmboot_fw_size) { + fatal_error("[NXBOOT] Could not read the warmboot firmware from %s!\n", warmboot_fn); + } + + warmboot_fuses = attempt; + break; + } + } + } + + if (warmboot_fw == NULL) { + fatal_error("[NXBOOT] Failed to determine mariko warmboot firmware\n"); + } + } else { + if (loader_ctx->warmboot_path[0] != '\0') { + warmboot_fw_size = get_file_size(loader_ctx->warmboot_path); + if (warmboot_fw_size == 0) { + fatal_error("[NXBOOT] Could not read the warmboot firmware from %s!\n", loader_ctx->warmboot_path); + } + + /* Allocate memory for the warmboot firmware. */ + warmboot_fw = malloc(warmboot_fw_size); + + if (warmboot_fw == NULL) { + fatal_error("[NXBOOT] Out of memory!\n"); + } + if (read_from_file(warmboot_fw, warmboot_fw_size, loader_ctx->warmboot_path) != warmboot_fw_size) { + fatal_error("[NXBOOT] Could not read the warmboot firmware from %s!\n", loader_ctx->warmboot_path); + } } else { /* Use Atmosphere's warmboot firmware implementation. */ warmboot_fw_size = warmboot_bin_size; @@ -1013,16 +1066,25 @@ uint32_t nxboot_main(void) { /* Handle warmboot security check. */ if (is_mariko) { - /* TODO */ + switch (warmboot_fuses) { + case 7: + pmc->secure_scratch32 = 0x87; + break; + case 8: + pmc->secure_scratch32 = 0xA8; + break; + default: + pmc->secure_scratch32 = (0x108 + 0x21 * (warmboot_fuses - 8)); + break; + } + + pmc->sec_disable3 |= BIT(16); } else { /* Set 3.0.0/3.0.1/3.0.2 warmboot security check. */ if (target_firmware == ATMOSPHERE_TARGET_FIRMWARE_3_0_0) { - const package1loader_header_t *package1loader_header = (const package1loader_header_t *)package1loader; - if (!strcmp(package1loader_header->build_timestamp, "20170519101410")) { - pmc->secure_scratch32 = 0xE3; /* Warmboot 3.0.0 security check.*/ - } else if (!strcmp(package1loader_header->build_timestamp, "20170710161758")) { - pmc->secure_scratch32 = 0x104; /* Warmboot 3.0.1/3.0.2 security check. */ - } + pmc->secure_scratch32 = 0xE3; /* Warmboot 3.0.0 security check.*/ + } else if (target_firmware == ATMOSPHERE_TARGET_FIRMWARE_3_0_1 || target_firmware == ATMOSPHERE_TARGET_FIRMWARE_3_0_2) { + pmc->secure_scratch32 = 0x104; /* Warmboot 3.0.1/3.0.2 security check. */ } }