ams: replace sept with tsec firmware (#1594)

* ams: replace sept with tsec firmware

This replaces sept with a custom tsec key derivation firmware.

NOTE: This does not use any TSEC exploits whatsoever; it is a well-signed
TSEC binary assembled with envyas and signed with the real cauth key.

For more details, contact SciresM#0524.

* fusee: only set SBK if it's readable
This commit is contained in:
SciresM 2021-08-20 13:13:29 -07:00 committed by GitHub
parent f175802136
commit 17ca463c3f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
137 changed files with 149 additions and 28364 deletions

View file

@ -17,8 +17,6 @@
#include "../../../fusee/fusee-primary/fusee-primary-main/src/fs_utils.h"
#elif defined(FUSEE_STAGE2_SRC)
#include "../../../fusee/fusee-secondary/src/device_partition.h"
#elif defined(SEPT_STAGE2_SRC)
#include "../../../sept/sept-secondary/src/fs_utils.h"
#endif
#ifdef FUSEE_STAGE2_SRC

View file

@ -30,8 +30,6 @@
#include "../../../fusee/fusee-primary/fusee-primary-main/src/timers.h"
#elif defined(FUSEE_STAGE2_SRC)
#include "../../../fusee/fusee-secondary/src/timers.h"
#elif defined(SEPT_STAGE2_SRC)
#include "../../../sept/sept-secondary/src/timers.h"
#endif
#define UNSTUFF_BITS(resp,start,size) \
@ -102,7 +100,7 @@ static int sdmmc_device_send_r1_cmd(sdmmc_device_t *device, uint32_t opcode, uin
if (resp_mask) {
resp &= ~(resp_mask);
}
/* We got an error state. */
if (is_sdmmc_device_r1_error(resp)) {
return 0;
@ -355,7 +353,7 @@ static int sdmmc_sd_decode_scr(sdmmc_device_t *device, uint8_t *scr) {
if (device->scr.sda_spec3) {
device->scr.cmds = UNSTUFF_BITS(resp, 32, 2);
}
/* Unknown SCR structure version. */
if (UNSTUFF_BITS(resp, 60, 4)) {
return 0;
@ -465,7 +463,7 @@ static int sdmmc_sd_send_op_cond(sdmmc_device_t *device, bool is_sd_ver2, bool i
if (is_uhs_en) {
arg |= SD_OCR_S18R;
}
cmd.opcode = SD_APP_OP_COND;
cmd.arg = arg;
cmd.flags = SDMMC_RSP_R3;
@ -916,7 +914,7 @@ int sdmmc_device_sd_init(sdmmc_device_t *device, sdmmc_t *sdmmc, SdmmcBusWidth b
if (!sdmmc_sd_decode_csd(device, csd)) {
sdmmc_warn(sdmmc, "Got unknown CSD structure (0x%08x)!", device->csd.structure);
}
/* If we never switched to 1.8V, change the bus speed mode. */
if (!device->is_180v) {
/* Reconfigure the internal clock. */
@ -1155,7 +1153,7 @@ static int sdmmc_mmc_send_op_cond(sdmmc_device_t *device, SdmmcBusVoltage bus_vo
if (resp & SD_OCR_CCS) {
device->is_block_sdhc = true;
}
return 1;
}
@ -1439,7 +1437,7 @@ int sdmmc_device_mmc_init(sdmmc_device_t *device, sdmmc_t *sdmmc, SdmmcBusWidth
if (!sdmmc_mmc_decode_csd(device, csd)) {
sdmmc_warn(sdmmc, "Got unknown CSD structure (0x%08x)!", device->csd.structure);
}
/* Reconfigure the internal clock. */
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_MMC_LEGACY)) {
sdmmc_error(sdmmc, "Failed to apply the correct bus speed!");
@ -1498,7 +1496,7 @@ int sdmmc_device_mmc_init(sdmmc_device_t *device, sdmmc_t *sdmmc, SdmmcBusWidth
} else {
sdmmc_info(sdmmc, "BKOPS is disabled!");
}
/* Switch to high speed mode. */
if (!sdmmc_mmc_select_timing(device, bus_speed)) {
sdmmc_error(sdmmc, "Failed to switch to high speed mode!");

View file

@ -41,15 +41,6 @@
#include "../../../fusee/fusee-secondary/src/gpio.h"
#include "../../../fusee/fusee-secondary/src/pmc.h"
#include "../../../fusee/fusee-secondary/src/max7762x.h"
#elif defined(SEPT_STAGE2_SRC)
#include "../../../sept/sept-secondary/src/car.h"
#include "../../../sept/sept-secondary/src/fuse.h"
#include "../../../sept/sept-secondary/src/pinmux.h"
#include "../../../sept/sept-secondary/src/timers.h"
#include "../../../sept/sept-secondary/src/apb_misc.h"
#include "../../../sept/sept-secondary/src/gpio.h"
#include "../../../sept/sept-secondary/src/pmc.h"
#include "../../../sept/sept-secondary/src/max7762x.h"
#endif
#include "../log.h"

View file

@ -158,7 +158,12 @@ static void config_se_brom(void) {
/* Bootrom part we skipped. */
uint32_t sbk[4] = {fuse_chip->FUSE_PRIVATE_KEY[0], fuse_chip->FUSE_PRIVATE_KEY[1], fuse_chip->FUSE_PRIVATE_KEY[2], fuse_chip->FUSE_PRIVATE_KEY[3]};
set_aes_keyslot(0xE, sbk, 0x10);
for (int i = 0; i < 4; ++i) {
if (sbk[i] != 0xFFFFFFFF) {
set_aes_keyslot(0xE, sbk, 0x10);
break;
}
}
/* Lock SBK from being read. */
se->SE_CRYPTO_KEYTABLE_ACCESS[0xE] = 0x7E;
@ -247,7 +252,7 @@ void nx_hwinit(bool enable_log) {
} else {
uint8_t val = 0x40;
i2c_send(I2C_5, MAX77620_PWR_I2C_ADDR, MAX77620_REG_CNFGBBC, &val, 1);
val = 0x60;
val = 0x58;
i2c_send(I2C_5, MAX77620_PWR_I2C_ADDR, MAX77620_REG_ONOFFCNFG1, &val, 1);
val = 0x38;
i2c_send(I2C_5, MAX77620_PWR_I2C_ADDR, MAX77620_REG_FPS_CFG0, &val, 1);

View file

@ -92,8 +92,8 @@ export KIPDIRS := $(AMS)/stratosphere/loader $(AMS)/stratosphere/ncm $(AMS)/stra
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir)) \
$(AMS)/exosphere $(AMS)/exosphere/warmboot $(AMS)/exosphere/program/rebootstub \
$(AMS)/thermosphere $(AMS)/fusee/fusee-primary $(AMS)/sept/sept-primary \
$(AMS)/sept/sept-secondary $(AMS)/emummc $(AMS)/mesosphere $(AMS)/mesosphere/kernel_ldr $(KIPDIRS)
$(AMS)/thermosphere $(AMS)/fusee/fusee-primary $(AMS)/emummc $(AMS)/mesosphere \
$(KIPDIRS)
export DEPSDIR := $(CURDIR)/$(BUILD)
@ -103,9 +103,7 @@ SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
KIPFILES := loader.kip ncm.kip pm.kip sm.kip ams_mitm.kip spl.kip boot.kip
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) fusee-primary.bin \
exosphere.bin warmboot.bin rebootstub.bin thermosphere.bin splash_screen.bin \
sept-primary.bin sept-secondary_00.enc sept-secondary_01.enc emummc.kip \
sept-secondary_dev_00.enc sept-secondary_dev_01.enc mesosphere.bin kernel_ldr.bin \
mariko_fatal.bin $(KIPFILES)
emummc.kip mesosphere.bin mariko_fatal.bin $(KIPFILES)
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
@ -133,7 +131,7 @@ export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
.PHONY: $(BUILD) clean all
.PHONY: check_fusee_primary check_exosphere check_sept check_emummc check_thermosphere check_stratosphere check_libraries
.PHONY: check_fusee_primary check_exosphere check_emummc check_thermosphere check_stratosphere check_libraries
#---------------------------------------------------------------------------------
all: $(BUILD)
@ -144,9 +142,6 @@ check_fusee_primary:
check_exosphere:
@$(MAKE) -C $(AMS)/exosphere all
check_sept:
@$(MAKE) -C $(AMS)/sept all
check_emummc:
@$(MAKE) -C $(AMS)/emummc EMUMMCDIR=$(AMS)/emummc all
@ -163,7 +158,7 @@ check_libraries:
@$(MAKE) -C $(AMS)/libraries all
$(BUILD): check_fusee_primary check_exosphere check_sept check_emummc check_thermosphere check_libraries check_stratosphere check_mesosphere
$(BUILD): check_fusee_primary check_exosphere check_emummc check_thermosphere check_libraries check_stratosphere check_mesosphere
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
@ -176,7 +171,6 @@ clean:
@$(MAKE) -C $(AMS)/libraries clean
@$(MAKE) -C $(AMS)/mesosphere clean
@$(MAKE) -C $(AMS)/stratosphere clean
@$(MAKE) -C $(AMS)/sept clean
@$(MAKE) -C $(AMS)/emummc clean
@rm -fr $(BUILD) $(TARGET).bin $(TARGET).elf
@ -216,31 +210,6 @@ fusee_primary.bin.o fusee_primary_bin.h: fusee-primary.bin
@echo $(notdir $<)
@$(_bin2o)
sept_primary.bin.o sept_primary_bin.h: sept-primary.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(_bin2o)
sept_secondary_00.enc.o sept_secondary_00_enc.h: sept-secondary_00.enc
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(_bin2o)
sept_secondary_01.enc.o sept_secondary_01_enc.h: sept-secondary_01.enc
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(_bin2o)
sept_secondary_dev_00.enc.o sept_secondary_dev_00_enc.h: sept-secondary_dev_00.enc
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(_bin2o)
sept_secondary_dev_01.enc.o sept_secondary_dev_01_enc.h: sept-secondary_dev_01.enc
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(_bin2o)
%.bin.o %_bin.h: %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)

Binary file not shown.

View file

@ -244,12 +244,6 @@ SECTIONS
PROVIDE(__pm_kip_size__ = pm_kip_end - pm_kip);
PROVIDE(__rebootstub_bin_start__ = rebootstub_bin - __start__);
PROVIDE(__rebootstub_bin_size__ = rebootstub_bin_end - rebootstub_bin);
PROVIDE(__sept_primary_bin_start__ = sept_primary_bin - __start__);
PROVIDE(__sept_primary_bin_size__ = sept_primary_bin_end - sept_primary_bin);
PROVIDE(__sept_secondary_00_enc_start__ = sept_secondary_00_enc - __start__);
PROVIDE(__sept_secondary_00_enc_size__ = sept_secondary_00_enc_end - sept_secondary_00_enc);
PROVIDE(__sept_secondary_01_enc_start__ = sept_secondary_01_enc - __start__);
PROVIDE(__sept_secondary_01_enc_size__ = sept_secondary_01_enc_end - sept_secondary_01_enc);
PROVIDE(__sm_kip_start__ = sm_kip - __start__);
PROVIDE(__sm_kip_size__ = sm_kip_end - sm_kip);
PROVIDE(__spl_kip_start__ = spl_kip - __start__);
@ -266,4 +260,6 @@ SECTIONS
PROVIDE(__mesosphere_bin_size__ = mesosphere_bin_end - mesosphere_bin);
PROVIDE(__mariko_fatal_bin_start__ = mariko_fatal_bin - __start__);
PROVIDE(__mariko_fatal_bin_size__ = mariko_fatal_bin_end - mariko_fatal_bin);
PROVIDE(__tsec_keygen_bin_start__ = tsec_keygen_bin - __start__);
PROVIDE(__tsec_keygen_bin_size__ = tsec_keygen_bin_end - tsec_keygen_bin);
}

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FUSEE_CAR_H
#define FUSEE_CAR_H
@ -51,7 +51,7 @@ typedef enum {
CARDEVICE_USB2 = ((1 << 5) | 0x1A),
CARDEVICE_CORESIGHT = ((2 << 5) | 0x9),
CARDEVICE_TSEC = ((2 << 5) | 0x13),
CARDEVICE_MSELECT = ((3 << 5) | 0x8),
CARDEVICE_MSELECT = ((3 << 5) | 0x3),
CARDEVICE_ACTMON = ((3 << 5) | 0x17),
CARDEVICE_TZRAM = ((3 << 5) | 0x1E),
CARDEVICE_SE = ((3 << 5) | 0x1F),
@ -489,7 +489,7 @@ typedef struct {
uint32_t sdmmc4_pllc4_out0_shaper_ctrl; /* _SDMMC4_PLLC4_OUT0_SHAPER_CTRL_0, 0x738 */
uint32_t sdmmc4_pllc4_out1_shaper_ctrl; /* _SDMMC4_PLLC4_OUT1_SHAPER_CTRL_0, 0x73c */
uint32_t sdmmc4_pllc4_out2_shaper_ctrl; /* _SDMMC4_PLLC4_OUT2_SHAPER_CTRL_0, 0x740 */
uint32_t sdmmc4_div_clk_shaper_ctrl; /* _SDMMC4_DIV_CLK_SHAPER_CTRL_0, 0x744 */
uint32_t sdmmc4_div_clk_shaper_ctrl; /* _SDMMC4_DIV_CLK_SHAPER_CTRL_0, 0x744 */
} tegra_car_t;
static inline volatile tegra_car_t *car_get_regs(void) {

View file

@ -21,12 +21,6 @@
#include "kernel_patches.h"
#include "ips.h"
#define u8 uint8_t
#define u32 uint32_t
#include "kernel_ldr_bin.h"
#undef u8
#undef u32
#define MAKE_BRANCH(a, o) 0x14000000 | ((((o) - (a)) >> 2) & 0x3FFFFFF)
#define MAKE_NOP 0xD503201F

View file

@ -26,19 +26,6 @@
#define AL16 ALIGN(16)
static const uint8_t AL16 keyblob_seeds[MASTERKEY_REVISION_MAX][0x10] = {
{0xDF, 0x20, 0x6F, 0x59, 0x44, 0x54, 0xEF, 0xDC, 0x70, 0x74, 0x48, 0x3B, 0x0D, 0xED, 0x9F, 0xD3}, /* Keyblob seed 00. */
{0x0C, 0x25, 0x61, 0x5D, 0x68, 0x4C, 0xEB, 0x42, 0x1C, 0x23, 0x79, 0xEA, 0x82, 0x25, 0x12, 0xAC}, /* Keyblob seed 01. */
{0x33, 0x76, 0x85, 0xEE, 0x88, 0x4A, 0xAE, 0x0A, 0xC2, 0x8A, 0xFD, 0x7D, 0x63, 0xC0, 0x43, 0x3B}, /* Keyblob seed 02. */
{0x2D, 0x1F, 0x48, 0x80, 0xED, 0xEC, 0xED, 0x3E, 0x3C, 0xF2, 0x48, 0xB5, 0x65, 0x7D, 0xF7, 0xBE}, /* Keyblob seed 03. */
{0xBB, 0x5A, 0x01, 0xF9, 0x88, 0xAF, 0xF5, 0xFC, 0x6C, 0xFF, 0x07, 0x9E, 0x13, 0x3C, 0x39, 0x80}, /* Keyblob seed 04. */
{0xD8, 0xCC, 0xE1, 0x26, 0x6A, 0x35, 0x3F, 0xCC, 0x20, 0xF3, 0x2D, 0x3B, 0x51, 0x7D, 0xE9, 0xC0} /* Keyblob seed 05. */
};
static const uint8_t AL16 keyblob_mac_seed[0x10] = {
0x59, 0xC7, 0xFB, 0x6F, 0xBE, 0x9B, 0xBE, 0x87, 0x65, 0x6B, 0x15, 0xC0, 0x53, 0x73, 0x36, 0xA5
};
static const uint8_t AL16 masterkey_seed[0x10] = {
0xD8, 0xA2, 0x41, 0x0A, 0xC6, 0xC5, 0x90, 0x01, 0xC6, 0x1D, 0x6A, 0x26, 0x7C, 0x51, 0x3F, 0x3C
};
@ -55,164 +42,51 @@ static const uint8_t AL16 masterkey_4x_seed[0x10] = {
0x2D, 0xC1, 0xF4, 0x8D, 0xF3, 0x5B, 0x69, 0x33, 0x42, 0x10, 0xAC, 0x65, 0xDA, 0x90, 0x46, 0x66
};
/* TODO: Bother adding 8.1.0 here? We'll never call into here... */
static const uint8_t AL16 new_master_kek_seeds[MASTERKEY_REVISION_700_800 - MASTERKEY_REVISION_600_610][0x10] = {
{0x37, 0x4B, 0x77, 0x29, 0x59, 0xB4, 0x04, 0x30, 0x81, 0xF6, 0xE5, 0x8C, 0x6D, 0x36, 0x17, 0x9A}, /* MasterKek seed 06. */
{0x9A, 0x3E, 0xA9, 0xAB, 0xFD, 0x56, 0x46, 0x1C, 0x9B, 0xF6, 0x48, 0x7F, 0x5C, 0xFA, 0x09, 0x5C}, /* MasterKek seed 07. */
static const uint8_t AL16 keyblob_seed_00[0x10] = {
0xDF, 0x20, 0x6F, 0x59, 0x44, 0x54, 0xEF, 0xDC, 0x70, 0x74, 0x48, 0x3B, 0x0D, 0xED, 0x9F, 0xD3
};
static const uint8_t AL16 master_kek_seed_erista[0x10] = { /* TODO: Update on next change of keys. */
0x84, 0x67, 0xB6, 0x7F, 0x13, 0x11, 0xAE, 0xE6, 0x58, 0x9B, 0x19, 0xAF, 0x13, 0x6C, 0x80, 0x7A /* Erista MasterKek seed 0B. */
};
static const uint8_t AL16 master_devkey_seed_erista[0x10] = {
0xAA, 0xFD, 0xBC, 0xBB, 0x25, 0xC3, 0xA4, 0xEF, 0xE3, 0xEE, 0x58, 0x53, 0xB7, 0xF8, 0xDD, 0xD6
};
static const uint8_t AL16 master_kek_seed_mariko[0x10] = { /* TODO: Update on next change of keys. */
0xE5, 0x41, 0xAC, 0xEC, 0xD1, 0xA7, 0xD1, 0xAB, 0xED, 0x03, 0x77, 0xF1, 0x27, 0xCA, 0xF8, 0xF1, /* Mariko MasterKek seed 0B. */
};
static nx_dec_keyblob_t AL16 g_dec_keyblobs[32];
static int get_keyblob(nx_keyblob_t *dst, uint32_t revision, const nx_keyblob_t *keyblobs, uint32_t available_revision) {
if (revision >= 0x20) {
return -1;
/* TODO: what should we do? */
}
if (keyblobs != NULL) {
*dst = keyblobs[revision];
} else {
return -1;
/* TODO: what should we do? */
}
return 0;
}
static bool safe_memcmp(uint8_t *a, uint8_t *b, size_t sz) {
uint8_t different = 0;
for (unsigned int i = 0; i < sz; i++) {
different |= a[i] ^ b[i];
}
return different != 0;
}
static int decrypt_keyblob(const nx_keyblob_t *keyblobs, uint32_t revision, uint32_t available_revision) {
nx_keyblob_t AL16 keyblob;
uint8_t AL16 work_buffer[0x10];
unsigned int keyslot = revision == MASTERKEY_REVISION_100_230 ? 0xF : KEYSLOT_SWITCH_TEMPKEY;
if (get_keyblob(&keyblob, revision, keyblobs, available_revision) != 0) {
return -1;
}
se_aes_ecb_decrypt_block(0xD, work_buffer, 0x10, keyblob_seeds[revision], 0x10);
decrypt_data_into_keyslot(keyslot, 0xE, work_buffer, 0x10);
decrypt_data_into_keyslot(0xB, keyslot, keyblob_mac_seed, 0x10);
/* Validate keyblob. */
se_compute_aes_128_cmac(0xB, work_buffer, 0x10, keyblob.mac + sizeof(keyblob.mac), sizeof(keyblob) - sizeof(keyblob.mac));
if (safe_memcmp(keyblob.mac, work_buffer, 0x10)) {
return -1;
}
/* Decrypt keyblob. */
se_aes_ctr_crypt(keyslot, &g_dec_keyblobs[revision], sizeof(g_dec_keyblobs[revision]), keyblob.data, sizeof(keyblob.data), keyblob.ctr, sizeof(keyblob.ctr));
return 0;
}
int load_package1_key(uint32_t revision) {
if (revision > MASTERKEY_REVISION_600_610) {
return -1;
}
set_aes_keyslot(0xB, g_dec_keyblobs[revision].package1_key, 0x10);
return 0;
}
/* Derive all Switch keys. */
int derive_nx_keydata_erista(uint32_t target_firmware, const nx_keyblob_t *keyblobs, uint32_t available_revision, const void *tsec_key, void *tsec_root_keys, unsigned int *out_keygen_type) {
int derive_nx_keydata_erista(uint32_t target_firmware) {
uint8_t AL16 work_buffer[0x10];
uint8_t AL16 zeroes[0x10] = {0};
/* Initialize keygen type. */
*out_keygen_type = 0;
/* Get whether we're using dev keys. */
const bool is_retail = fuse_get_hardware_state() != 0;
/* TODO: Set keyslot flags properly in preparation of derivation. */
set_aes_keyslot_flags(0xE, 0x15);
set_aes_keyslot_flags(0xD, 0x15);
/* Derive Keyblob Key 00. */
se_aes_ecb_decrypt_block(0xC, work_buffer, 0x10, keyblob_seed_00, 0x10);
decrypt_data_into_keyslot(0xF, 0xE, work_buffer, 0x10);
/* Set the TSEC key. */
set_aes_keyslot(0xD, tsec_key, 0x10);
/* Derive master kek. */
decrypt_data_into_keyslot(0xE, is_retail ? 0xD : 0xB, master_kek_seed_erista, 0x10);
/* Decrypt all keyblobs, setting keyslot 0xF correctly. */
for (unsigned int rev = 0; rev <= MASTERKEY_REVISION_600_610; rev++) {
int ret = decrypt_keyblob(keyblobs, rev, available_revision);
if (ret) {
return ret;
}
}
/* Derive master key, device master key. */
decrypt_data_into_keyslot(0xD, 0xE, masterkey_seed, 0x10);
decrypt_data_into_keyslot(0xE, 0xE, masterkey_4x_seed, 0x10);
/* Do 6.2.0+ keygen. */
if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_6_2_0) {
uint32_t desired_keyblob;
if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_8_1_0) {
/* NOTE: We load in the current key for all >= 8.1.0 firmwares to reduce sept binaries. */
desired_keyblob = MASTERKEY_REVISION_C10_CURRENT;
} else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_7_0_0) {
desired_keyblob = MASTERKEY_REVISION_700_800;
} else {
desired_keyblob = MASTERKEY_REVISION_620;
}
/* Try emulation result. */
for (unsigned int rev = MASTERKEY_REVISION_620; rev < MASTERKEY_REVISION_MAX; rev++) {
void *tsec_root_key = (void *)((uintptr_t)tsec_root_keys + 0x10 * (rev - MASTERKEY_REVISION_620));
if (memcmp(tsec_root_key, zeroes, 0x10) != 0) {
/* We got a valid key from emulation. */
set_aes_keyslot(0xD, tsec_root_key, 0x10);
se_aes_ecb_decrypt_block(0xD, work_buffer, 0x10, new_master_kek_seeds[rev - MASTERKEY_REVISION_620], 0x10);
memcpy(g_dec_keyblobs[rev].master_kek, work_buffer, 0x10);
}
}
if (memcmp(g_dec_keyblobs[desired_keyblob].master_kek, zeroes, 0x10) == 0) {
/* Try reading the keys from a file. */
const char *keyfile = fuse_get_hardware_state() != 0 ? "atmosphere/prod.keys" : "atmosphere/dev.keys";
FILE *extkey_file = fopen(keyfile, "r");
AL16 fusee_extkeys_t extkeys = {0};
if (extkey_file == NULL) {
fatal_error("Error: failed to read %s, needed for 6.2.0+ key derivation!", keyfile);
}
extkeys_initialize_keyset(&extkeys, extkey_file);
fclose(extkey_file);
for (unsigned int rev = MASTERKEY_REVISION_620; rev < MASTERKEY_REVISION_MAX; rev++) {
if (memcmp(extkeys.tsec_root_keys[rev - MASTERKEY_REVISION_620], zeroes, 0x10) != 0) {
set_aes_keyslot(0xD, extkeys.tsec_root_keys[rev - MASTERKEY_REVISION_620], 0x10);
se_aes_ecb_decrypt_block(0xD, work_buffer, 0x10, new_master_kek_seeds[rev - MASTERKEY_REVISION_620], 0x10);
memcpy(g_dec_keyblobs[rev].master_kek, work_buffer, 0x10);
} else {
memcpy(g_dec_keyblobs[rev].master_kek, extkeys.master_keks[rev], 0x10);
}
}
}
if (memcmp(g_dec_keyblobs[available_revision].master_kek, zeroes, 0x10) == 0) {
fatal_error("Error: failed to derive master_kek_%02x!", available_revision);
}
}
/* Clear the SBK. */
clear_aes_keyslot(0xE);
/* Get needed data. */
set_aes_keyslot(0xD, g_dec_keyblobs[available_revision].master_kek, 0x10);
/* Also set the Package1 key for the revision that is stored on the eMMC boot0 partition. */
if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_6_2_0) {
load_package1_key(available_revision);
}
/* Derive keys for Exosphere, lock critical keyslots. */
/* Derive device keys. */
decrypt_data_into_keyslot(0xA, 0xF, devicekey_4x_seed, 0x10);
decrypt_data_into_keyslot(0xF, 0xF, devicekey_seed, 0x10);
decrypt_data_into_keyslot(0xC, 0xD, masterkey_4x_seed, 0x10);
decrypt_data_into_keyslot(0xD, 0xD, masterkey_seed, 0x10);
decrypt_data_into_keyslot(0xF, 0xF, devicekey_seed, 0x10);
/* Derive firmware specific device key. */
se_aes_ecb_decrypt_block(0xA, work_buffer, 0x10, master_devkey_seed_erista, 0x10);
decrypt_data_into_keyslot(0xC, 0xE, work_buffer, 0x10);
/* Clear keyslots 0xB/0xE. */
clear_aes_keyslot(0xB);
clear_aes_keyslot(0xE);
/* Setup master key revision, derive older master keys for use. */
return mkey_detect_revision(fuse_get_hardware_state() != 0);

View file

@ -27,29 +27,8 @@ typedef enum BisPartition {
BisPartition_UserSystem = 2,
} BisPartition;
typedef struct {
union {
uint8_t keys[9][0x10];
struct {
uint8_t master_kek[0x10];
uint8_t _keys[7][0x10];
uint8_t package1_key[0x10];
};
};
} nx_dec_keyblob_t;
typedef struct nx_keyblob_t {
uint8_t mac[0x10];
uint8_t ctr[0x10];
union {
uint8_t data[0x90];
nx_dec_keyblob_t dec_blob;
};
} nx_keyblob_t;
int derive_nx_keydata_erista(uint32_t target_firmware, const nx_keyblob_t *keyblobs, uint32_t available_revision, const void *tsec_key, void *tsec_root_key, unsigned int *out_keygen_type);
int derive_nx_keydata_erista(uint32_t target_firmware);
int derive_nx_keydata_mariko(uint32_t target_firmware);
int load_package1_key(uint32_t revision);
void derive_bis_key(void *dst, BisPartition partition_id, uint32_t target_firmware);
#endif

View file

@ -185,8 +185,8 @@ void derive_new_device_keys(bool is_retail, unsigned int keygen_keyslot, unsigne
if (relative_revision > mkey_get_revision()) {
break;
} else if (relative_revision == mkey_get_revision()) {
/* On 7.0.0 erista, sept will have derived this key for us already. */
if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_7_0_0 || is_mariko) {
/* On Erista, this will already be derived. */
if (is_mariko) {
decrypt_data_into_keyslot(is_mariko ? KEYSLOT_SWITCH_DEVICEKEY_MARIKO : KEYSLOT_SWITCH_DEVICEKEY, KEYSLOT_SWITCH_TEMPKEY, work_buffer, 0x10);
}
} else {

View file

@ -44,7 +44,6 @@
#include "masterkey.h"
#include "package1.h"
#include "package2.h"
#include "smmu.h"
#include "tsec.h"
#include "lp0.h"
#include "loader.h"
@ -59,11 +58,8 @@
#include "exosphere_bin.h"
#include "mariko_fatal_bin.h"
#include "mesosphere_bin.h"
#include "sept_secondary_00_enc.h"
#include "sept_secondary_01_enc.h"
#include "sept_secondary_dev_00_enc.h"
#include "sept_secondary_dev_01_enc.h"
#include "warmboot_bin.h"
#include "tsec_keygen_bin.h"
#include "emummc_kip.h"
#undef u8
#undef u32
@ -724,18 +720,11 @@ static void nxboot_move_bootconfig() {
free(bootconfig);
}
static bool get_and_clear_has_run_sept(void) {
bool has_run_sept = (MAKE_EMC_REG(EMC_SCRATCH0) & 0x80000000) != 0;
MAKE_EMC_REG(EMC_SCRATCH0) &= ~0x80000000;
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) {
volatile tegra_pmc_t *pmc = pmc_get_regs();
loader_ctx_t *loader_ctx = get_loader_ctx();
@ -745,8 +734,6 @@ uint32_t nxboot_main(void) {
size_t package2_size;
void *tsec_fw;
size_t tsec_fw_size;
const void *sept_secondary_enc = NULL;
size_t sept_secondary_enc_size = 0;
void *warmboot_fw;
size_t warmboot_fw_size;
void *warmboot_memaddr;
@ -756,7 +743,6 @@ uint32_t nxboot_main(void) {
size_t mesosphere_size;
void *emummc;
size_t emummc_size;
uint32_t available_revision;
FILE *boot0, *pk2file;
void *exosphere_memaddr;
exo_emummc_config_t exo_emummc_cfg;
@ -844,7 +830,7 @@ uint32_t nxboot_main(void) {
fatal_error("[NXBOOT] Couldn't parse boot0: %s!\n", strerror(errno));
}
} else {
if (package1_read_and_parse_boot0_erista(&package1loader, &package1loader_size, g_keyblobs, &available_revision, boot0) == -1) {
if (package1_read_and_parse_boot0_erista(&package1loader, &package1loader_size, boot0) == -1) {
fatal_error("[NXBOOT] Couldn't parse boot0: %s!\n", strerror(errno));
}
}
@ -860,103 +846,26 @@ uint32_t nxboot_main(void) {
}
/* Handle TSEC and Sept (Erista only). */
uint8_t tsec_key[0x10] = {0};
uint8_t tsec_root_keys[0x20][0x10] = {0};
if (!is_mariko) {
/* Read the TSEC firmware from a file, otherwise from PK1L. */
if (loader_ctx->tsecfw_path[0] != '\0') {
tsec_fw_size = get_file_size(loader_ctx->tsecfw_path);
if ((tsec_fw_size != 0) && (tsec_fw_size != 0xF00 && tsec_fw_size != 0x2900 && tsec_fw_size != 0x3000 && tsec_fw_size != 0x3300)) {
fatal_error("[NXBOOT] TSEC firmware from %s has a wrong size!\n", loader_ctx->tsecfw_path);
} else if (tsec_fw_size == 0) {
fatal_error("[NXBOOT] Could not read the TSEC firmware from %s!\n", loader_ctx->tsecfw_path);
}
/* Use Atmosphere's tsec_keygen implementation. */
tsec_fw_size = tsec_keygen_bin_size;
tsec_fw = memalign(0x100, tsec_fw_size);
/* Allocate memory for the TSEC firmware. */
tsec_fw = memalign(0x100, tsec_fw_size);
if (tsec_fw == NULL) {
fatal_error("[NXBOOT] Out of memory!\n");
}
if (read_from_file(tsec_fw, tsec_fw_size, loader_ctx->tsecfw_path) != tsec_fw_size) {
fatal_error("[NXBOOT] Could not read the TSEC firmware from %s!\n", loader_ctx->tsecfw_path);
}
if (tsec_fw_size == 0x3000) {
if (fuse_get_hardware_state() != 0) {
sept_secondary_enc = sept_secondary_00_enc;
sept_secondary_enc_size = sept_secondary_00_enc_size;
} else {
sept_secondary_enc = sept_secondary_dev_00_enc;
sept_secondary_enc_size = sept_secondary_dev_00_enc_size;
}
} else if (tsec_fw_size == 0x3300) {
if (fuse_get_hardware_state() != 0) {
sept_secondary_enc = sept_secondary_01_enc;
sept_secondary_enc_size = sept_secondary_01_enc_size;
} else {
sept_secondary_enc = sept_secondary_dev_01_enc;
sept_secondary_enc_size = sept_secondary_dev_01_enc_size;
}
} else {
fatal_error("[NXBOOT] Unable to identify sept revision to run.");
}
} else {
if (!package1_get_tsec_fw(&tsec_fw, package1loader, package1loader_size)) {
fatal_error("[NXBOOT] Failed to read the TSEC firmware from Package1loader!\n");
}
if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_8_1_0) {
if (fuse_get_hardware_state() != 0) {
sept_secondary_enc = sept_secondary_01_enc;
sept_secondary_enc_size = sept_secondary_01_enc_size;
} else {
sept_secondary_enc = sept_secondary_dev_01_enc;
sept_secondary_enc_size = sept_secondary_dev_01_enc_size;
}
tsec_fw_size = 0x3300;
} else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_7_0_0) {
if (fuse_get_hardware_state() != 0) {
sept_secondary_enc = sept_secondary_00_enc;
sept_secondary_enc_size = sept_secondary_00_enc_size;
} else {
sept_secondary_enc = sept_secondary_dev_00_enc;
sept_secondary_enc_size = sept_secondary_dev_00_enc_size;
}
tsec_fw_size = 0x3000;
} else if (target_firmware == ATMOSPHERE_TARGET_FIRMWARE_6_2_0) {
tsec_fw_size = 0x2900;
} else {
tsec_fw_size = 0xF00;
}
if (tsec_fw == NULL) {
fatal_error("[NXBOOT] Out of memory!\n");
}
print(SCREEN_LOG_LEVEL_INFO, "[NXBOOT] Loaded firmware from eMMC...\n");
memcpy(tsec_fw, tsec_keygen_bin, tsec_fw_size);
/* Get the TSEC keys. */
if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_7_0_0) {
/* Detect whether we need to run sept-secondary in order to derive keys. */
if (!get_and_clear_has_run_sept()) {
reboot_to_sept(tsec_fw, tsec_fw_size, sept_secondary_enc, sept_secondary_enc_size);
} else {
if (mkey_detect_revision(fuse_get_hardware_state() != 0) != 0) {
fatal_error("[NXBOOT] Sept derived incorrect keys!\n");
}
}
get_and_clear_has_run_sept();
} else if (target_firmware == ATMOSPHERE_TARGET_FIRMWARE_6_2_0) {
uint8_t tsec_keys[0x20] = {0};
if (tsec_fw_size == 0) {
fatal_error("[NXBOOT] Could not read the warmboot firmware from Package1!\n");
}
/* Emulate the TSEC payload on 6.2.0+. */
smmu_emulate_tsec((void *)tsec_keys, package1loader, package1loader_size, package1loader);
/* Copy back the keys. */
memcpy((void *)tsec_key, (void *)tsec_keys, 0x10);
memcpy((void *)tsec_root_keys, (void *)tsec_keys + 0x10, 0x10);
} else {
/* Run the TSEC payload and get the key. */
if (tsec_get_key(tsec_key, 1, tsec_fw, tsec_fw_size) != 0) {
fatal_error("[NXBOOT] Failed to get TSEC key!\n");
}
/* Get the TSEC keys into the security engine. */
int tsec_res = tsec_run_fw(tsec_fw, tsec_fw_size);
if (tsec_res != 0) {
volatile tegra_tsec_t *tsec = tsec_get_regs();
fatal_error("[NXBOOT] Failed to run TSEC firmware %d %08x %08x!\n", tsec_res, tsec->TSEC_FALCON_MAILBOX0, tsec->TSEC_FALCON_MAILBOX1);
}
}
@ -969,8 +878,8 @@ uint32_t nxboot_main(void) {
if (derive_nx_keydata_mariko(target_firmware) != 0) {
fatal_error("[NXBOOT] Mariko key derivation failed!\n");
}
} else if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_7_0_0) { /* If on 7.0.0+, sept has already derived keys for us (Erista only). */
if (derive_nx_keydata_erista(target_firmware, g_keyblobs, available_revision, tsec_key, tsec_root_keys, &keygen_type) != 0) {
} else {
if (derive_nx_keydata_erista(target_firmware) != 0) {
fatal_error("[NXBOOT] Erista key derivation failed!\n");
}
}
@ -1230,9 +1139,6 @@ uint32_t nxboot_main(void) {
/* Clean up. */
free(package1loader);
if (loader_ctx->tsecfw_path[0] != '\0') {
free(tsec_fw);
}
if (loader_ctx->warmboot_path[0] != '\0') {
free(warmboot_fw);
}

View file

@ -24,18 +24,10 @@
#include "mc.h"
#include "nxboot.h"
#include "se.h"
#include "smmu.h"
#include "timers.h"
#include "sysreg.h"
/* Determine the current SoC for Mariko specific code. */
static bool is_soc_mariko() {
return (fuse_get_soc_type() == 1);
}
void nxboot_finish(uint32_t boot_memaddr) {
bool is_mariko = is_soc_mariko();
/* Boot up Exosphère. */
MAILBOX_NX_BOOTLOADER_IS_SECMON_AWAKE = 0;
MAILBOX_NX_BOOTLOADER_SETUP_STATE = NX_BOOTLOADER_STATE_DRAM_INITIALIZED_4X;
@ -43,26 +35,8 @@ void nxboot_finish(uint32_t boot_memaddr) {
/* Terminate the display. */
display_end();
if (is_mariko) {
/* Boot CPU0. */
cluster_boot_cpu0(boot_memaddr);
} else {
/* Check if SMMU emulation has been used. */
uint32_t smmu_magic = *(uint32_t *)(SMMU_AARCH64_PAYLOAD_ADDR + 0xFC);
if (smmu_magic == 0xDEADC0DE) {
/* Clear the magic. */
*(uint32_t *)(SMMU_AARCH64_PAYLOAD_ADDR + 0xFC) = 0;
/* Pass the boot address to the already running payload. */
*(uint32_t *)(SMMU_AARCH64_PAYLOAD_ADDR + 0xF0) = boot_memaddr;
/* Wait a while. */
mdelay(500);
} else {
/* Boot CPU0. */
cluster_boot_cpu0(boot_memaddr);
}
}
/* Boot CPU0. */
cluster_boot_cpu0(boot_memaddr);
/* Wait for Exosphère to wake up. */
while (MAILBOX_NX_BOOTLOADER_IS_SECMON_AWAKE == 0) {

View file

@ -58,16 +58,12 @@ bool package1_is_custom_public_key(const void *bct, bool mariko) {
}
}
int package1_read_and_parse_boot0_erista(void **package1loader, size_t *package1loader_size, nx_keyblob_t *keyblobs, uint32_t *revision, FILE *boot0) {
int package1_read_and_parse_boot0_erista(void **package1loader, size_t *package1loader_size, FILE *boot0) {
nvboot_config_table *bct; /* Normal firmware BCT, primary. TODO: check? */
nv_bootloader_info *pk1l_info; /* TODO: check? */
size_t fpos, pk1l_offset;
union {
nx_keyblob_t keyblob;
uint8_t sector[0x200];
} d;
if (package1loader == NULL || package1loader_size == NULL || keyblobs == NULL || revision == NULL || boot0 == NULL) {
if (package1loader == NULL || package1loader_size == NULL || boot0 == NULL) {
errno = EINVAL;
return -1;
}
@ -105,7 +101,6 @@ int package1_read_and_parse_boot0_erista(void **package1loader, size_t *package1
return -1;
}
*revision = pk1l_info->version - 1;
*package1loader_size = pk1l_info->length;
pk1l_offset = 0x4000 * pk1l_info->start_blk + 0x200 * pk1l_info->start_page;
@ -128,14 +123,6 @@ int package1_read_and_parse_boot0_erista(void **package1loader, size_t *package1
return -1;
}
/* Read the full keyblob area.*/
for (size_t i = 0; i < 32; i++) {
if (!fread(d.sector, 0x200, 1, boot0)) {
return -1;
}
keyblobs[i] = d.keyblob;
}
return 0;
}
@ -211,24 +198,6 @@ bool package1_get_tsec_fw(void **tsec_fw, const void *package1loader, size_t pac
return false;
}
size_t package1_get_encrypted_package1(package1_header_t **package1, uint8_t *ctr, const void *package1loader, size_t package1loader_size) {
const uint8_t *crypt_hdr = (const uint8_t *)package1loader + 0x4000 - 0x20;
if (package1loader_size < 0x4000) {
return 0; /* Shouldn't happen, ever. */
}
memcpy(ctr, crypt_hdr + 0x10, 0x10);
(*package1) = (package1_header_t *)(crypt_hdr + 0x20);
return *(uint32_t *)crypt_hdr;
}
bool package1_decrypt(package1_header_t *package1, size_t package1_size, const uint8_t *ctr) {
uint8_t __attribute__((aligned(16))) ctrbuf[16];
memcpy(ctrbuf, ctr, 16);
se_aes_ctr_crypt(0xB, package1, package1_size, package1, package1_size, ctrbuf, 16);
return memcmp(package1->magic, "PK11", 4) == 0;
}
void *package1_get_warmboot_fw(const package1_header_t *package1) {
/*
The layout of pk1 changes between versions.

View file

@ -59,14 +59,10 @@ typedef struct {
bool package1_is_custom_public_key(const void *bct, bool mariko);
int package1_read_and_parse_boot0_erista(void **package1loader, size_t *package1loader_size, nx_keyblob_t *keyblobs, uint32_t *revision, FILE *boot0);
int package1_read_and_parse_boot0_erista(void **package1loader, size_t *package1loader_size, FILE *boot0);
int package1_read_and_parse_boot0_mariko(void **package1loader, size_t *package1loader_size, FILE *boot0);
bool package1_get_tsec_fw(void **tsec_fw, const void *package1loader, size_t package1loader_size);
size_t package1_get_encrypted_package1(package1_header_t **package1, uint8_t *ctr, const void *package1loader, size_t package1loader_size);
/* Must be aligned to 16 bytes. */
bool package1_decrypt(package1_header_t *package1, size_t package1_size, const uint8_t *ctr);
void *package1_get_warmboot_fw(const package1_header_t *package1);
#endif

View file

@ -1,284 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "smmu.h"
#include "cluster.h"
#include "mc.h"
#include "timers.h"
#include "tsec.h"
#define TSEC_KEYGEN_MAX_RETRIES 25
void *smmu_heap = (void *)SMMU_HEAP_BASE_ADDR;
static void safe_memcpy(void *dst, void *src, uint32_t sz) {
/* Aligned memcpy to read MMIO correctly. */
for (size_t i = 0; i < (sz/4); i++) {
((volatile uint32_t *)dst)[i] = ((volatile uint32_t *)src)[i];
}
}
static void smmu_flush_ppsb() {
/* Read-back barrier for interactions between the PPSB and the APB/AHB. */
(void)MAKE_MC_REG(MC_SMMU_TLB_CONFIG);
}
static void smmu_flush_regs() {
/* Flush all TLB and PTC entries. */
MAKE_MC_REG(MC_SMMU_PTC_FLUSH) = 0;
smmu_flush_ppsb();
MAKE_MC_REG(MC_SMMU_TLB_FLUSH) = 0;
smmu_flush_ppsb();
}
static void *smmu_alloc_page(uint32_t page_count) {
void *cur_page = smmu_heap;
smmu_heap += (page_count * SMMU_PAGE_SIZE);
memset(cur_page, 0, (page_count * SMMU_PAGE_SIZE));
return cur_page;
}
static uint32_t *smmu_alloc_pdir() {
uint32_t *pdir = (uint32_t *)smmu_alloc_page(1);
for (int pdn = 0; pdn < SMMU_PDIR_COUNT; pdn++) {
pdir[pdn] = _PDE_VACANT(pdn);
}
return pdir;
}
static uint32_t *smmu_locate_pte(uint32_t *pdir_page, uint32_t iova) {
uint32_t ptn = SMMU_ADDR_TO_PFN(iova);
uint32_t pdn = SMMU_ADDR_TO_PDN(iova);
uint32_t *pdir = pdir_page;
uint32_t *ptbl;
if (pdir[pdn] != _PDE_VACANT(pdn)) {
/* Mapped entry table already exists. */
ptbl = (uint32_t *)SMMU_EX_PTBL_PAGE(pdir[pdn]);
} else {
/* Allocate page table. */
ptbl = (uint32_t *)smmu_alloc_page(1);
uint32_t addr = SMMU_PDN_TO_ADDR(pdn);
for (int pn = 0; pn < SMMU_PTBL_COUNT; pn++, addr += SMMU_PAGE_SIZE) {
ptbl[pn] = _PTE_VACANT(addr);
}
pdir[pdn] = SMMU_MK_PDE((uint32_t)ptbl, _PDE_ATTR | _PDE_NEXT);
smmu_flush_regs();
}
return &ptbl[ptn % SMMU_PTBL_COUNT];
}
static void smmu_map(uint32_t *pdir, uint32_t addr, uint32_t ptpage, int pcount, uint32_t pte_attr) {
for (int i = 0; i < pcount; i++) {
uint32_t *pte = smmu_locate_pte(pdir, addr);
*pte = SMMU_PFN_TO_PTE(SMMU_ADDR_TO_PFN(ptpage), pte_attr);
addr += SMMU_PAGE_SIZE;
ptpage += SMMU_PAGE_SIZE;
}
smmu_flush_regs();
}
static uint32_t *smmu_setup_tsec_as(uint32_t asid) {
/* Allocate the page directory. */
uint32_t *pdir_page = smmu_alloc_pdir();
/* Set the PTB ASID and point it to the PDIR. */
MAKE_MC_REG(MC_SMMU_PTB_ASID) = asid;
MAKE_MC_REG(MC_SMMU_PTB_DATA) = SMMU_MK_PDIR((uint32_t)pdir_page, _PDIR_ATTR);
smmu_flush_ppsb();
/* Assign the ASID to TSEC. */
MAKE_MC_REG(MC_SMMU_TSEC_ASID) = SMMU_ASID_ENABLE((asid << 24) | (asid << 16) | (asid << 8) | asid);
smmu_flush_ppsb();
return pdir_page;
}
static void smmu_clear_tsec_as(uint32_t asid) {
/* Set the PTB ASID and clear it's data. */
MAKE_MC_REG(MC_SMMU_PTB_ASID) = asid;
MAKE_MC_REG(MC_SMMU_PTB_DATA) = 0;
/* Clear the ASID from TSEC. */
MAKE_MC_REG(MC_SMMU_TSEC_ASID) = SMMU_ASID_DISABLE;
smmu_flush_ppsb();
}
static void smmu_enable() {
/* AARCH64 payload for enabling the SMMU. */
/* Write 1 to MC_SMMU_CONFIG, read back and write the result to 0x40003F80. */
/* This will leave the CPU waiting until 0x40003FF0 is set to Exosphère's address. */
static const uint32_t aarch64_payload[20] = {
0x52800020, 0x58000162, 0x58000183, 0xB9000040,
0xB9400041, 0xB9000061, 0x58000142, 0xF9400040,
0xF100001F, 0x54FFFFA0, 0xD61F0000, 0x00000000,
0x70019010, 0x00000000, 0x40003F80, 0x00000000,
0x40003FF0, 0x00000000, 0x00000000, 0x00000000
};
/* Reset Translation Enable Registers. */
MAKE_MC_REG(MC_SMMU_TRANSLATION_ENABLE_0) = 0xFFFFFFFF;
MAKE_MC_REG(MC_SMMU_TRANSLATION_ENABLE_1) = 0xFFFFFFFF;
MAKE_MC_REG(MC_SMMU_TRANSLATION_ENABLE_2) = 0xFFFFFFFF;
MAKE_MC_REG(MC_SMMU_TRANSLATION_ENABLE_3) = 0xFFFFFFFF;
MAKE_MC_REG(MC_SMMU_TRANSLATION_ENABLE_4) = 0xFFFFFFFF;
/* Setup initial TLB and PTC configuration. */
MAKE_MC_REG(MC_SMMU_PTB_ASID) = 0;
MAKE_MC_REG(MC_SMMU_PTB_DATA) = 0;
MAKE_MC_REG(MC_SMMU_TLB_CONFIG) = 0x30000030;
MAKE_MC_REG(MC_SMMU_PTC_CONFIG) = 0x2800003F;
smmu_flush_regs();
/* Power on the CCPLEX to enable the SMMU globally (requires a secure write). */
volatile uint32_t *aarch64_payload_res = (volatile uint32_t *)(SMMU_AARCH64_PAYLOAD_ADDR + 0x80);
memset((void *)SMMU_AARCH64_PAYLOAD_ADDR, 0, 0x100);
memcpy((void *)SMMU_AARCH64_PAYLOAD_ADDR, aarch64_payload, 20 * 4);
cluster_boot_cpu0(SMMU_AARCH64_PAYLOAD_ADDR);
mdelay(500);
if (*aarch64_payload_res != 1) {
fatal_error("[SMMU]: Failed to enable SMMU!\n");
}
/* Write magic for nxboot. */
*(uint32_t *)(SMMU_AARCH64_PAYLOAD_ADDR + 0xFC) = 0xDEADC0DE;
/* Flush TLB and PTC entries. */
smmu_flush_regs();
}
void smmu_emulate_tsec(void *tsec_keys, const void *package1, size_t package1_size, void *package1_dec) {
volatile tegra_tsec_t *tsec = tsec_get_regs();
/* Backup IRAM to DRAM. */
memcpy((void *)SMMU_IRAM_BACKUP_ADDR, (void *)0x40010000, 0x30000);
/* Copy package1 into IRAM. */
memcpy((void *)0x40010000, package1, package1_size);
/* Setup TSEC's address space. */
uint32_t *pdir = smmu_setup_tsec_as(1);
/* Allocate pages for MMIO and IRAM. */
volatile uint32_t *car_page = smmu_alloc_page(1);
volatile uint32_t *fuse_page = smmu_alloc_page(1);
volatile uint32_t *pmc_page = smmu_alloc_page(1);
volatile uint32_t *flow_page = smmu_alloc_page(1);
volatile uint32_t *se_page = smmu_alloc_page(1);
volatile uint32_t *mc_page = smmu_alloc_page(1);
volatile uint32_t *iram_pages = smmu_alloc_page(48);
volatile uint32_t *expv_page = smmu_alloc_page(1);
/* Map all necessary pages. */
smmu_map(pdir, 0x60006000, (uint32_t)car_page, 1, _READABLE | _WRITABLE | _NONSECURE);
smmu_map(pdir, 0x7000F000, (uint32_t)fuse_page, 1, _READABLE | _NONSECURE);
smmu_map(pdir, 0x7000E000, (uint32_t)pmc_page, 1, _READABLE | _NONSECURE);
smmu_map(pdir, 0x60007000, (uint32_t)flow_page, 1, _WRITABLE | _NONSECURE);
smmu_map(pdir, 0x70012000, (uint32_t)se_page, 1, _READABLE | _WRITABLE | _NONSECURE);
smmu_map(pdir, 0x70019000, (uint32_t)mc_page, 1, _READABLE | _NONSECURE);
smmu_map(pdir, 0x40010000, (uint32_t)iram_pages, 48, _READABLE | _WRITABLE | _NONSECURE);
smmu_map(pdir, 0x6000F000, (uint32_t)expv_page, 1, _READABLE | _WRITABLE | _NONSECURE);
/* Enable the SMMU. */
smmu_enable();
/* Loop retrying TSEC firmware execution, in case we lose the SE keydata race. */
uint32_t key_buf[0x20/4] = {0};
unsigned int retries = 0;
while (true) {
if (retries++ > TSEC_KEYGEN_MAX_RETRIES) {
fatal_error("[SMMU] TSEC key generation race was lost too many times!");
}
/* Load the TSEC firmware from IRAM. */
if (tsec_load_fw((void *)(0x40010000 + 0xE00), 0x2900) < 0) {
fatal_error("[SMMU]: Failed to load TSEC firmware!\n");
}
/* Disable the aperture since it has precedence over the SMMU. */
mc_disable_ahb_redirect();
/* Clear all pages. */
memset((void *)car_page, 0, SMMU_PAGE_SIZE);
memset((void *)fuse_page, 0, SMMU_PAGE_SIZE);
memset((void *)pmc_page, 0, SMMU_PAGE_SIZE);
memset((void *)flow_page, 0, SMMU_PAGE_SIZE);
memset((void *)se_page, 0, SMMU_PAGE_SIZE);
memset((void *)mc_page, 0, SMMU_PAGE_SIZE);
memset((void *)iram_pages, 0, 48 * SMMU_PAGE_SIZE);
memset((void *)expv_page, 0, SMMU_PAGE_SIZE);
/* Copy CAR, MC and FUSE. */
safe_memcpy((void *)car_page, (void *)0x60006000, 0x1000);
safe_memcpy((void *)mc_page, (void *)0x70019000, 0x1000);
safe_memcpy((void *)&fuse_page[0x800/4], (void *)0x7000F800, 0x400);
/* Copy IRAM. */
memcpy((void *)iram_pages, (void *)0x40010000, 0x30000);
/* TSEC wants CLK_RST_CONTROLLER_CLK_SOURCE_TSEC_0 to be equal to 2. */
car_page[0x1F4/4] = 2;
/* TSEC wants the aperture fully open. */
mc_page[0x65C/4] = 0;
mc_page[0x660/4] = 0x80000000;
/* Run the TSEC firmware. */
tsec_run_fw();
/* Extract the keys from SE. */
volatile uint32_t *key_data = (volatile uint32_t *)((void *)se_page + 0x320);
uint32_t old_key_data = *key_data;
uint32_t buf_counter = 0;
while (!(tsec->TSEC_FALCON_CPUCTL & 0x10)) {
const uint32_t new_key_data = *key_data;
if (new_key_data != old_key_data) {
old_key_data = new_key_data;
key_buf[buf_counter] = new_key_data;
buf_counter++;
}
}
/* Enable back the aperture. */
mc_enable_ahb_redirect();
if (buf_counter == 8) {
break;
}
}
/* Check if the TSEC firmware wrote over the exception vectors. */
volatile uint32_t *tsec_done_check = (volatile uint32_t *)((void *)expv_page + 0x200);
if (!(*tsec_done_check)) {
fatal_error("[SMMU]: Failed to emulate the TSEC firmware!\n");
}
/* Copy back the extracted keys. */
memcpy((void *)tsec_keys, (void *)key_buf, 0x20);
/* Manually disable TSEC clocks. */
tsec_disable_clkrst();
/* Clear TSEC's address space. */
smmu_clear_tsec_as(1);
/* Return the decrypted package1 from emulated IRAM. */
memcpy(package1_dec, (void *)iram_pages, package1_size);
/* Restore IRAM from DRAM. */
memcpy((void *)0x40010000, (void *)SMMU_IRAM_BACKUP_ADDR, 0x30000);
}

View file

@ -1,63 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FUSEE_SMMU_H_
#define FUSEE_SMMU_H_
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#define SMMU_HEAP_BASE_ADDR 0x81000000
#define SMMU_IRAM_BACKUP_ADDR 0x82000000
#define SMMU_AARCH64_PAYLOAD_ADDR 0x40003F00
#define SMMU_PAGE_SHIFT 12
#define SMMU_PAGE_SIZE (1 << SMMU_PAGE_SHIFT)
#define SMMU_PDIR_COUNT 1024
#define SMMU_PDIR_SIZE (sizeof(uint32_t) * SMMU_PDIR_COUNT)
#define SMMU_PTBL_COUNT 1024
#define SMMU_PTBL_SIZE (sizeof(uint32_t) * SMMU_PTBL_COUNT)
#define SMMU_PDIR_SHIFT 12
#define SMMU_PDE_SHIFT 12
#define SMMU_PTE_SHIFT 12
#define SMMU_PFN_MASK 0x000fffff
#define SMMU_PDE_NEXT_SHIFT 28
#define SMMU_ADDR_TO_PFN(addr) ((addr) >> 12)
#define SMMU_ADDR_TO_PDN(addr) ((addr) >> 22)
#define SMMU_PDN_TO_ADDR(pdn) ((pdn) << 22)
#define _READABLE (1 << 31)
#define _WRITABLE (1 << 30)
#define _NONSECURE (1 << 29)
#define _PDE_NEXT (1 << SMMU_PDE_NEXT_SHIFT)
#define _MASK_ATTR (_READABLE | _WRITABLE | _NONSECURE)
#define _PDIR_ATTR (_READABLE | _WRITABLE | _NONSECURE)
#define _PDE_ATTR (_READABLE | _WRITABLE | _NONSECURE)
#define _PDE_ATTR_N (_PDE_ATTR | _PDE_NEXT)
#define _PDE_VACANT(pdn) (((pdn) << 10) | _PDE_ATTR)
#define _PTE_ATTR (_READABLE | _WRITABLE | _NONSECURE)
#define _PTE_VACANT(addr) (((addr) >> SMMU_PAGE_SHIFT) | _PTE_ATTR)
#define SMMU_MK_PDIR(page, attr) (((page) >> SMMU_PDIR_SHIFT) | (attr))
#define SMMU_MK_PDE(page, attr) (((page) >> SMMU_PDE_SHIFT) | (attr))
#define SMMU_EX_PTBL_PAGE(pde) (((pde) & SMMU_PFN_MASK) << SMMU_PDIR_SHIFT)
#define SMMU_PFN_TO_PTE(pfn, attr) ((pfn) | (attr))
#define SMMU_ASID_ENABLE(asid) ((asid) | (1 << 31))
#define SMMU_ASID_DISABLE 0
#define SMMU_ASID_ASID(n) ((n) & ~SMMU_ASID_ENABLE(0))
void smmu_emulate_tsec(void *tsec_keys, const void *package1, size_t package1_size, void *package1_dec);
#endif

View file

@ -108,6 +108,7 @@ _metadata:
#define CONTENT_TYPE_KLD 9
#define CONTENT_TYPE_KRN 10
#define CONTENT_TYPE_EXF 11
#define CONTENT_TYPE_TKG 12
#define CONTENT_FLAG_NONE (0 << 0)
@ -213,39 +214,6 @@ _content_headers:
.asciz "rebootstub"
.align 5
/* sept_primary content header */
.word __sept_primary_bin_start__
.word __sept_primary_bin_size__
.byte CONTENT_TYPE_SP1
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word 0xCCCCCCCC
.asciz "sept_primary"
.align 5
/* sept_secondary 00 content header */
.word __sept_secondary_00_enc_start__
.word __sept_secondary_00_enc_size__
.byte CONTENT_TYPE_SP2
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word 0xCCCCCCCC
.asciz "septsecondary00"
.align 5
/* sept_secondary 01 content header */
.word __sept_secondary_01_enc_start__
.word __sept_secondary_01_enc_size__
.byte CONTENT_TYPE_SP2
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word 0xCCCCCCCC
.asciz "septsecondary01"
.align 5
/* sm content header */
.word __sm_kip_start__
.word __sm_kip_size__
@ -301,6 +269,17 @@ _content_headers:
.asciz "exosphere_fatal"
.align 5
/* tsec_keygen content header */
.word __tsec_keygen_bin_start__
.word __tsec_keygen_bin_size__
.byte CONTENT_TYPE_TKG
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word 0xCCCCCCCC
.asciz "tsec_keygen"
.align 5
_content_headers_end:
/* No need to include this in normal programs: */

View file

@ -14,15 +14,16 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "tsec.h"
#include "di.h"
#include "timers.h"
#include "car.h"
#include "mc.h"
static int tsec_dma_wait_idle()
{
volatile tegra_tsec_t *tsec = tsec_get_regs();
volatile tegra_tsec_t *tsec = tsec_get_regs();
uint32_t timeout = (get_time_ms() + 10000);
while (!(tsec->TSEC_FALCON_DMATRFCMD & 2))
@ -30,13 +31,13 @@ static int tsec_dma_wait_idle()
if (get_time_ms() > timeout)
return 0;
}
return 1;
}
static int tsec_dma_phys_to_flcn(bool is_imem, uint32_t flcn_offset, uint32_t phys_offset)
{
volatile tegra_tsec_t *tsec = tsec_get_regs();
volatile tegra_tsec_t *tsec = tsec_get_regs();
uint32_t cmd = 0;
if (!is_imem)
@ -52,16 +53,16 @@ static int tsec_dma_phys_to_flcn(bool is_imem, uint32_t flcn_offset, uint32_t ph
}
static int tsec_kfuse_wait_ready()
{
{
uint32_t timeout = (get_time_ms() + 10000);
/* Wait for STATE_DONE. */
while (!(KFUSE_STATE & 0x10000))
{
if (get_time_ms() > timeout)
return 0;
}
/* Check for STATE_CRCPASS. */
if (!(KFUSE_STATE & 0x20000))
return 0;
@ -91,19 +92,19 @@ void tsec_disable_clkrst()
clkrst_disable(CARDEVICE_HOST1X);
}
int tsec_get_key(uint8_t *key, uint32_t rev, const void *tsec_fw, size_t tsec_fw_size)
static int tsec_run_fw_impl(const void *tsec_fw, size_t tsec_fw_size)
{
volatile tegra_tsec_t *tsec = tsec_get_regs();
/* Enable clocks. */
tsec_enable_clkrst();
/* Make sure KFUSE is ready. */
if (!tsec_kfuse_wait_ready())
{
/* Disable clocks. */
tsec_disable_clkrst();
return -1;
}
@ -112,16 +113,16 @@ int tsec_get_key(uint8_t *key, uint32_t rev, const void *tsec_fw, size_t tsec_fw
tsec->TSEC_FALCON_IRQMSET = 0xFFF2;
tsec->TSEC_FALCON_IRQDEST = 0xFFF0;
tsec->TSEC_FALCON_ITFEN = 3;
/* Make sure the DMA block is idle. */
if (!tsec_dma_wait_idle())
{
/* Disable clocks. */
tsec_disable_clkrst();
return -2;
}
/* Load firmware. */
tsec->TSEC_FALCON_DMATRFBASE = (uint32_t)tsec_fw >> 8;
for (uint32_t addr = 0; addr < tsec_fw_size; addr += 0x100)
@ -130,128 +131,71 @@ int tsec_get_key(uint8_t *key, uint32_t rev, const void *tsec_fw, size_t tsec_fw
{
/* Disable clocks. */
tsec_disable_clkrst();
return -3;
}
}
/* Write magic value to HOST1X scratch register. */
MAKE_HOST1X_REG(0x3300) = 0x34C2E1DA;
/* Execute firmware. */
tsec->TSEC_FALCON_MAILBOX1 = 0;
tsec->TSEC_FALCON_MAILBOX0 = rev;
tsec->TSEC_FALCON_MAILBOX0 = 1;
tsec->TSEC_FALCON_BOOTVEC = 0;
tsec->TSEC_FALCON_CPUCTL = 2;
/* Make sure the DMA block is idle. */
if (!tsec_dma_wait_idle())
{
/* Disable clocks. */
tsec_disable_clkrst();
return -4;
}
uint32_t timeout = (get_time_ms() + 2000);
while (!tsec->TSEC_FALCON_MAILBOX1)
{
if (get_time_ms() > timeout)
{
uint32_t timeout = (get_time_ms() + 4000);
while (!(tsec->TSEC_FALCON_CPUCTL & 0x10)) {
if (get_time_ms() > timeout) {
/* Disable clocks. */
tsec_disable_clkrst();
return -5;
}
}
if (tsec->TSEC_FALCON_MAILBOX1 != 0xB0B0B0B0)
{
/* Disable clocks. */
tsec_disable_clkrst();
return -6;
}
/* Clear magic value from HOST1X scratch register. */
MAKE_HOST1X_REG(0x3300) = 0;
/* Fetch result from SOR1. */
uint32_t tmp[0x4] = {0};
tmp[0] = SOR1_DP_HDCP_BKSV_LSB;
tmp[1] = SOR1_TMDS_HDCP_BKSV_LSB;
tmp[2] = SOR1_TMDS_HDCP_CN_MSB;
tmp[3] = SOR1_TMDS_HDCP_CN_LSB;
/* Clear SOR1 registers. */
SOR1_DP_HDCP_BKSV_LSB = 0;
SOR1_TMDS_HDCP_BKSV_LSB = 0;
SOR1_TMDS_HDCP_CN_MSB = 0;
SOR1_TMDS_HDCP_CN_LSB = 0;
/* Copy back the key. */
memcpy(key, &tmp, 0x10);
return 0;
}
int tsec_load_fw(const void *tsec_fw, size_t tsec_fw_size)
{
volatile tegra_tsec_t *tsec = tsec_get_regs();
int tsec_run_fw(const void *tsec_fw, size_t tsec_fw_size) {
/* Ensure that the ahb redirect is enabled. */
mc_enable_ahb_redirect();
/* Enable clocks. */
tsec_enable_clkrst();
/* Make sure KFUSE is ready. */
if (!tsec_kfuse_wait_ready())
{
/* Disable clocks. */
tsec_disable_clkrst();
return -1;
}
/* Get bom/tom */
uint32_t bom = MAKE_MC_REG(MC_IRAM_BOM);
uint32_t tom = MAKE_MC_REG(MC_IRAM_TOM);
/* Configure Falcon. */
tsec->TSEC_FALCON_DMACTL = 0;
tsec->TSEC_FALCON_IRQMSET = 0xFFF2;
tsec->TSEC_FALCON_IRQDEST = 0xFFF0;
tsec->TSEC_FALCON_ITFEN = 3;
/* Make sure the DMA block is idle. */
if (!tsec_dma_wait_idle())
{
/* Disable clocks. */
tsec_disable_clkrst();
return -2;
}
/* Load firmware. */
tsec->TSEC_FALCON_DMATRFBASE = (uint32_t)tsec_fw >> 8;
for (uint32_t addr = 0; addr < tsec_fw_size; addr += 0x100)
{
if (!tsec_dma_phys_to_flcn(true, addr, addr))
{
/* Disable clocks. */
tsec_disable_clkrst();
return -3;
}
}
/* Override the ahb redirect extents. */
MAKE_MC_REG(MC_IRAM_BOM) = 0x40000000;
MAKE_MC_REG(MC_IRAM_TOM) = 0x80000000;
return 0;
}
/* Run the fw. */
int res = tsec_run_fw_impl(tsec_fw, tsec_fw_size);
void tsec_run_fw()
{
volatile tegra_tsec_t *tsec = tsec_get_regs();
/* Write magic value to HOST1X scratch register. */
MAKE_HOST1X_REG(0x3300) = 0x34C2E1DA;
/* Execute firmware. */
tsec->TSEC_FALCON_MAILBOX1 = 0;
tsec->TSEC_FALCON_MAILBOX0 = 1;
tsec->TSEC_FALCON_BOOTVEC = 0;
tsec->TSEC_FALCON_CPUCTL = 2;
/* Reset the ahb redirect extents. */
MAKE_MC_REG(MC_IRAM_BOM) = bom;
MAKE_MC_REG(MC_IRAM_TOM) = tom;
return res;
}

View file

@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FUSEE_TSEC_H_
#define FUSEE_TSEC_H_
@ -306,8 +306,7 @@ static inline volatile tegra_tsec_t *tsec_get_regs(void)
void tsec_enable_clkrst();
void tsec_disable_clkrst();
int tsec_get_key(uint8_t *key, uint32_t rev, const void *tsec_fw, size_t tsec_fw_size);
int tsec_load_fw(const void *tsec_fw, size_t tsec_fw_size);
void tsec_run_fw();
int tsec_run_fw(const void *tsec_fw, size_t tsec_fw_size);
#endif

View file

@ -34,7 +34,6 @@
#define u8 uint8_t
#define u32 uint32_t
#include "fusee_primary_bin.h"
#include "sept_primary_bin.h"
#include "rebootstub_bin.h"
#undef u8
#undef u32
@ -131,42 +130,8 @@ __attribute__((noreturn)) void reboot_to_fusee_primary(void) {
reboot_to_payload();
}
__attribute__((noreturn)) void reboot_to_sept(const void *tsec_fw, size_t tsec_fw_length, const void *stage2, size_t stage2_size) {
if (is_soc_mariko()) {
/* Reboot to sept isn't possible on mariko, so just do normal reboot. */
shutdown_system(true);
} else {
/* Copy tsec firmware. */
for (size_t i = 0; i < tsec_fw_length; i += sizeof(uint32_t)) {
write32le((void *)0x40010F00, i, read32le(tsec_fw, i));
}
MAKE_REG32(0x40010EFC) = tsec_fw_length;
/* Copy stage 2. */
for (size_t i = 0; i < stage2_size; i += sizeof(uint32_t)) {
write32le((void *)0x40016FE0, i, read32le(stage2, i));
}
/* Copy sept into IRAM low. */
for (size_t i = 0; i < sept_primary_bin_size; i += sizeof(uint32_t)) {
write32le((void *)0x4003F000, i, read32le(sept_primary_bin, i));
}
/* Patch SDRAM init to perform an SVC immediately after second write */
APBDEV_PMC_SCRATCH45_0 = 0x2E38DFFF;
APBDEV_PMC_SCRATCH46_0 = 0x6001DC28;
/* Set SVC handler to jump to reboot stub in IRAM. */
APBDEV_PMC_SCRATCH33_0 = 0x4003F000;
APBDEV_PMC_SCRATCH40_0 = 0x6000F208;
/* Trigger warm reboot. */
pmc_reboot(1 << 0);
while (true) { }
}
}
__attribute__((noreturn)) void reboot_to_iram_payload(void *payload, size_t payload_size) {
/* Copy sept into IRAM low. */
/* Copy payload into IRAM low. */
for (size_t i = 0; i < payload_size; i += sizeof(uint32_t)) {
write32le((void *)0x40010000, i, read32le(payload, i));
}

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FUSEE_UTILS_H
#define FUSEE_UTILS_H
@ -121,7 +121,6 @@ static inline bool check_32bit_address_range_in_program(uintptr_t addr, size_t s
__attribute__((noreturn)) void watchdog_reboot(void);
__attribute__((noreturn)) void pmc_reboot(uint32_t scratch0);
__attribute__((noreturn)) void reboot_to_fusee_primary(void);
__attribute__((noreturn)) void reboot_to_sept(const void *tsec_fw, size_t tsec_fw_length, const void *stage2, size_t stage2_size);
__attribute__((noreturn)) void reboot_to_iram_payload(void *payload, size_t payload_size);
__attribute__((noreturn)) void wait_for_button_and_reboot(void);
void wait_for_button(void);