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. */
}
}