mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-11-22 20:06:40 +00:00
fusee: Add full 6.2.0 support via SMMU virtualization.
This commit is contained in:
parent
e321f0ac04
commit
ed37706915
11 changed files with 495 additions and 74 deletions
|
@ -36,8 +36,10 @@
|
|||
#define MC_SMMU_PTB_DATA 0x20
|
||||
#define MC_SMMU_TLB_FLUSH 0x30
|
||||
#define MC_SMMU_PTC_FLUSH 0x34
|
||||
#define MC_SMMU_ASID_SECURITY 0x38
|
||||
#define MC_SMMU_AFI_ASID 0x238
|
||||
#define MC_SMMU_AVPC_ASID 0x23c
|
||||
#define MC_SMMU_TSEC_ASID 0x294
|
||||
#define MC_SMMU_PPCS1_ASID 0x298
|
||||
#define MC_SMMU_TRANSLATION_ENABLE_0 0x228
|
||||
#define MC_SMMU_TRANSLATION_ENABLE_1 0x22c
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
#include "se.h"
|
||||
#include "exocfg.h"
|
||||
#include "fuse.h"
|
||||
#include "tsec.h"
|
||||
#include "extkeys.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
@ -61,10 +60,6 @@ static const uint8_t AL16 new_master_kek_seeds[1][0x10] = {
|
|||
|
||||
static nx_dec_keyblob_t AL16 g_dec_keyblobs[32];
|
||||
|
||||
static int get_tsec_key(void *dst, const void *tsec_fw, size_t tsec_fw_size, uint32_t tsec_key_id) {
|
||||
return tsec_get_key(dst, tsec_key_id, tsec_fw, tsec_fw_size);
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -123,20 +118,18 @@ int load_package1_key(uint32_t revision) {
|
|||
}
|
||||
|
||||
/* Derive all Switch keys. */
|
||||
int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, uint32_t available_revision, const void *tsec_fw, size_t tsec_fw_size, unsigned int *out_keygen_type) {
|
||||
uint8_t AL16 tsec_key[0x10];
|
||||
int derive_nx_keydata(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) {
|
||||
uint8_t AL16 work_buffer[0x10];
|
||||
uint8_t AL16 zeroes[0x10] = {0};
|
||||
|
||||
/* Initialize keygen type. */
|
||||
*out_keygen_type = 0;
|
||||
|
||||
/* TODO: Set keyslot flags properly in preparation of derivation. */
|
||||
set_aes_keyslot_flags(0xE, 0x15);
|
||||
set_aes_keyslot_flags(0xD, 0x15);
|
||||
|
||||
/* Set TSEC key. */
|
||||
if (get_tsec_key(tsec_key, tsec_fw, tsec_fw_size, 1) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set the TSEC key. */
|
||||
set_aes_keyslot(0xD, tsec_key, 0x10);
|
||||
|
||||
/* Decrypt all keyblobs, setting keyslot 0xF correctly. */
|
||||
|
@ -146,29 +139,37 @@ int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, ui
|
|||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* TODO: Eventually do 6.2.0+ keygen properly? */
|
||||
*out_keygen_type = 0;
|
||||
|
||||
/* Do 6.2.0+ keygen. */
|
||||
if (target_firmware >= EXOSPHERE_TARGET_FIRMWARE_620) {
|
||||
const char *keyfile = fuse_get_retail_type() != 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);
|
||||
|
||||
if (memcmp(extkeys.tsec_root_key, zeroes, 0x10) != 0) {
|
||||
set_aes_keyslot(0xC, extkeys.tsec_root_key, 0x10);
|
||||
if (memcmp(tsec_root_key, zeroes, 0x10) != 0) {
|
||||
/* We got a valid key from emulation. */
|
||||
set_aes_keyslot(0xC, tsec_root_key, 0x10);
|
||||
for (unsigned int rev = MASTERKEY_REVISION_620_CURRENT; rev < MASTERKEY_REVISION_MAX; rev++) {
|
||||
se_aes_ecb_decrypt_block(0xC, work_buffer, 0x10, new_master_kek_seeds[rev - MASTERKEY_REVISION_620_CURRENT], 0x10);
|
||||
memcpy(g_dec_keyblobs[rev].master_kek, work_buffer, 0x10);
|
||||
}
|
||||
} else {
|
||||
for (unsigned int rev = MASTERKEY_REVISION_620_CURRENT; rev < MASTERKEY_REVISION_MAX; rev++) {
|
||||
memcpy(g_dec_keyblobs[rev].master_kek, extkeys.master_keks[rev], 0x10);
|
||||
/* Try reading the keys from a file. */
|
||||
const char *keyfile = fuse_get_retail_type() != 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);
|
||||
|
||||
if (memcmp(extkeys.tsec_root_key, zeroes, 0x10) != 0) {
|
||||
set_aes_keyslot(0xC, extkeys.tsec_root_key, 0x10);
|
||||
for (unsigned int rev = MASTERKEY_REVISION_620_CURRENT; rev < MASTERKEY_REVISION_MAX; rev++) {
|
||||
se_aes_ecb_decrypt_block(0xC, work_buffer, 0x10, new_master_kek_seeds[rev - MASTERKEY_REVISION_620_CURRENT], 0x10);
|
||||
memcpy(g_dec_keyblobs[rev].master_kek, work_buffer, 0x10);
|
||||
}
|
||||
} else {
|
||||
for (unsigned int rev = MASTERKEY_REVISION_620_CURRENT; rev < MASTERKEY_REVISION_MAX; rev++) {
|
||||
memcpy(g_dec_keyblobs[rev].master_kek, extkeys.master_keks[rev], 0x10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ typedef struct nx_keyblob_t {
|
|||
};
|
||||
} nx_keyblob_t;
|
||||
|
||||
int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, uint32_t available_revision, const void *tsec_fw, size_t tsec_fw_size, unsigned int *out_keygen_type);
|
||||
int derive_nx_keydata(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 load_package1_key(uint32_t revision);
|
||||
void finalize_nx_keydata(uint32_t target_firmware);
|
||||
void derive_bis_key(void *dst, BisPartition partition_id, uint32_t target_firmware);
|
||||
|
|
|
@ -36,8 +36,10 @@
|
|||
#define MC_SMMU_PTB_DATA 0x20
|
||||
#define MC_SMMU_TLB_FLUSH 0x30
|
||||
#define MC_SMMU_PTC_FLUSH 0x34
|
||||
#define MC_SMMU_ASID_SECURITY 0x38
|
||||
#define MC_SMMU_AFI_ASID 0x238
|
||||
#define MC_SMMU_AVPC_ASID 0x23c
|
||||
#define MC_SMMU_TSEC_ASID 0x294
|
||||
#define MC_SMMU_PPCS1_ASID 0x298
|
||||
#define MC_SMMU_TRANSLATION_ENABLE_0 0x228
|
||||
#define MC_SMMU_TRANSLATION_ENABLE_1 0x22c
|
||||
|
|
|
@ -36,6 +36,8 @@
|
|||
#include "key_derivation.h"
|
||||
#include "package1.h"
|
||||
#include "package2.h"
|
||||
#include "smmu.h"
|
||||
#include "tsec.h"
|
||||
#include "loader.h"
|
||||
#include "splash_screen.h"
|
||||
#include "exocfg.h"
|
||||
|
@ -311,9 +313,28 @@ uint32_t nxboot_main(void) {
|
|||
|
||||
print(SCREEN_LOG_LEVEL_MANDATORY, "[NXBOOT]: Loaded firmware from eMMC...\n");
|
||||
|
||||
/* Get the TSEC keys. */
|
||||
uint8_t tsec_key[0x10] = {0};
|
||||
uint8_t tsec_root_key[0x10] = {0};
|
||||
if (target_firmware >= EXOSPHERE_TARGET_FIRMWARE_620) {
|
||||
uint8_t tsec_keys[0x20] = {0};
|
||||
|
||||
/* 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_key, (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");
|
||||
}
|
||||
}
|
||||
|
||||
/* Derive keydata. */
|
||||
unsigned int keygen_type = 0;
|
||||
if (derive_nx_keydata(target_firmware, g_keyblobs, available_revision, tsec_fw, tsec_fw_size, &keygen_type) != 0) {
|
||||
if (derive_nx_keydata(target_firmware, g_keyblobs, available_revision, tsec_key, tsec_root_key, &keygen_type) != 0) {
|
||||
fatal_error("[NXBOOT]: Key derivation failed!\n");
|
||||
}
|
||||
|
||||
|
@ -343,16 +364,26 @@ uint32_t nxboot_main(void) {
|
|||
fatal_error("[NXBOOT]: Could not read the warmboot firmware from %s!\n", loader_ctx->warmboot_path);
|
||||
}
|
||||
} else {
|
||||
uint8_t ctr[16];
|
||||
package1_size = package1_get_encrypted_package1(&package1, ctr, package1loader, package1loader_size);
|
||||
if (package1_decrypt(package1, package1_size, ctr)) {
|
||||
if (target_firmware >= EXOSPHERE_TARGET_FIRMWARE_620) {
|
||||
/* Package1 was decrypted during TSEC emulation. */
|
||||
const uint8_t *package1_hdr = (const uint8_t *)package1loader + 0x7000 - 0x20;
|
||||
package1 = (package1_header_t *)(package1_hdr + 0x20);
|
||||
package1_size = *(uint32_t *)package1_hdr;
|
||||
warmboot_fw = package1_get_warmboot_fw(package1);
|
||||
warmboot_fw_size = package1->warmboot_size;
|
||||
} else {
|
||||
warmboot_fw = NULL;
|
||||
warmboot_fw_size = 0;
|
||||
/* Decrypt package1 and extract the warmboot firmware. */
|
||||
uint8_t ctr[16];
|
||||
package1_size = package1_get_encrypted_package1(&package1, ctr, package1loader, package1loader_size);
|
||||
if (package1_decrypt(package1, package1_size, ctr)) {
|
||||
warmboot_fw = package1_get_warmboot_fw(package1);
|
||||
warmboot_fw_size = package1->warmboot_size;
|
||||
} else {
|
||||
warmboot_fw = NULL;
|
||||
warmboot_fw_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (warmboot_fw_size == 0) {
|
||||
fatal_error("[NXBOOT]: Could not read the warmboot firmware from Package1!\n");
|
||||
}
|
||||
|
|
|
@ -23,7 +23,9 @@
|
|||
#include "mc.h"
|
||||
#include "nxboot.h"
|
||||
#include "se.h"
|
||||
#include "smmu.h"
|
||||
#include "timers.h"
|
||||
#include "sysreg.h"
|
||||
|
||||
void nxboot_finish(uint32_t boot_memaddr) {
|
||||
volatile tegra_se_t *se = se_get_regs();
|
||||
|
@ -69,8 +71,21 @@ void nxboot_finish(uint32_t boot_memaddr) {
|
|||
/* Terminate the display. */
|
||||
display_end();
|
||||
|
||||
/* Boot CPU0. */
|
||||
cluster_boot_cpu0(boot_memaddr);
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* Wait for Exosphère to wake up. */
|
||||
while (MAILBOX_NX_BOOTLOADER_IS_SECMON_AWAKE == 0) {
|
||||
|
|
|
@ -131,7 +131,7 @@ void *package1_get_warmboot_fw(const package1_header_t *package1) {
|
|||
https://github.com/ARM-software/arm-trusted-firmware/blob/master/plat/nvidia/tegra/common/aarch64/tegra_helpers.S#L312
|
||||
and thus by 0xD5034FDF.
|
||||
|
||||
Nx-bootloader seems to always start by 0xE328F0C0 (msr cpsr_f, 0xc0).
|
||||
Nx-bootloader starts by 0xE328F0C0 (msr cpsr_f, 0xc0) before 6.2.0 and by 0xF0C0A7F0 afterwards.
|
||||
*/
|
||||
const uint32_t *data = (const uint32_t *)package1->data;
|
||||
for (size_t i = 0; i < 3; i++) {
|
||||
|
@ -140,6 +140,7 @@ void *package1_get_warmboot_fw(const package1_header_t *package1) {
|
|||
data += package1->secmon_size / 4;
|
||||
break;
|
||||
case 0xE328F0C0:
|
||||
case 0xF0C0A7F0:
|
||||
data += package1->nx_bootloader_size / 4;
|
||||
break;
|
||||
default:
|
||||
|
|
259
fusee/fusee-secondary/src/smmu.c
Normal file
259
fusee/fusee-secondary/src/smmu.c
Normal file
|
@ -0,0 +1,259 @@
|
|||
/*
|
||||
* Copyright (c) 2018 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"
|
||||
|
||||
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);
|
||||
|
||||
/* 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();
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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;
|
||||
|
||||
/* 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();
|
||||
|
||||
/* Run the TSEC firmware. */
|
||||
tsec_run_fw();
|
||||
|
||||
/* Extract the keys from SE. */
|
||||
uint32_t key_buf[0x20/4] = {0};
|
||||
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->FALCON_CPUCTL & 0x10)) {
|
||||
if (*key_data != old_key_data) {
|
||||
old_key_data = *key_data;
|
||||
key_buf[buf_counter] = *key_data;
|
||||
buf_counter++;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
/* Enable back the aperture. */
|
||||
mc_enable_ahb_redirect();
|
||||
|
||||
/* 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);
|
||||
}
|
63
fusee/fusee-secondary/src/smmu.h
Normal file
63
fusee/fusee-secondary/src/smmu.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright (c) 2018 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
|
|
@ -49,17 +49,34 @@ static int tsec_dma_phys_to_flcn(bool is_imem, uint32_t flcn_offset, uint32_t ph
|
|||
return tsec_dma_wait_idle();
|
||||
}
|
||||
|
||||
int tsec_get_key(uint8_t *key, uint32_t rev, const void *tsec_fw, size_t tsec_fw_size)
|
||||
void tsec_enable_clkrst()
|
||||
{
|
||||
volatile tegra_tsec_t *tsec = tsec_get_regs();
|
||||
|
||||
/* Enable clocks. */
|
||||
/* Enable all devices used by TSEC. */
|
||||
clkrst_reboot(CARDEVICE_HOST1X);
|
||||
clkrst_reboot(CARDEVICE_TSEC);
|
||||
clkrst_reboot(CARDEVICE_SOR_SAFE);
|
||||
clkrst_reboot(CARDEVICE_SOR0);
|
||||
clkrst_reboot(CARDEVICE_SOR1);
|
||||
clkrst_reboot(CARDEVICE_KFUSE);
|
||||
}
|
||||
|
||||
void tsec_disable_clkrst()
|
||||
{
|
||||
/* Disable all devices used by TSEC. */
|
||||
clkrst_disable(CARDEVICE_KFUSE);
|
||||
clkrst_disable(CARDEVICE_SOR1);
|
||||
clkrst_disable(CARDEVICE_SOR0);
|
||||
clkrst_disable(CARDEVICE_SOR_SAFE);
|
||||
clkrst_disable(CARDEVICE_TSEC);
|
||||
clkrst_disable(CARDEVICE_HOST1X);
|
||||
}
|
||||
|
||||
int tsec_get_key(uint8_t *key, uint32_t rev, const void *tsec_fw, size_t tsec_fw_size)
|
||||
{
|
||||
volatile tegra_tsec_t *tsec = tsec_get_regs();
|
||||
|
||||
/* Enable clocks. */
|
||||
tsec_enable_clkrst();
|
||||
|
||||
/* Configure Falcon. */
|
||||
tsec->FALCON_DMACTL = 0;
|
||||
|
@ -70,12 +87,7 @@ int tsec_get_key(uint8_t *key, uint32_t rev, const void *tsec_fw, size_t tsec_fw
|
|||
if (!tsec_dma_wait_idle())
|
||||
{
|
||||
/* Disable clocks. */
|
||||
clkrst_disable(CARDEVICE_KFUSE);
|
||||
clkrst_disable(CARDEVICE_SOR1);
|
||||
clkrst_disable(CARDEVICE_SOR0);
|
||||
clkrst_disable(CARDEVICE_SOR_SAFE);
|
||||
clkrst_disable(CARDEVICE_TSEC);
|
||||
clkrst_disable(CARDEVICE_HOST1X);
|
||||
tsec_disable_clkrst();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
@ -87,12 +99,7 @@ int tsec_get_key(uint8_t *key, uint32_t rev, const void *tsec_fw, size_t tsec_fw
|
|||
if (!tsec_dma_phys_to_flcn(true, addr, addr))
|
||||
{
|
||||
/* Disable clocks. */
|
||||
clkrst_disable(CARDEVICE_KFUSE);
|
||||
clkrst_disable(CARDEVICE_SOR1);
|
||||
clkrst_disable(CARDEVICE_SOR0);
|
||||
clkrst_disable(CARDEVICE_SOR_SAFE);
|
||||
clkrst_disable(CARDEVICE_TSEC);
|
||||
clkrst_disable(CARDEVICE_HOST1X);
|
||||
tsec_disable_clkrst();
|
||||
|
||||
return -2;
|
||||
}
|
||||
|
@ -110,12 +117,7 @@ int tsec_get_key(uint8_t *key, uint32_t rev, const void *tsec_fw, size_t tsec_fw
|
|||
if (!tsec_dma_wait_idle())
|
||||
{
|
||||
/* Disable clocks. */
|
||||
clkrst_disable(CARDEVICE_KFUSE);
|
||||
clkrst_disable(CARDEVICE_SOR1);
|
||||
clkrst_disable(CARDEVICE_SOR0);
|
||||
clkrst_disable(CARDEVICE_SOR_SAFE);
|
||||
clkrst_disable(CARDEVICE_TSEC);
|
||||
clkrst_disable(CARDEVICE_HOST1X);
|
||||
tsec_disable_clkrst();
|
||||
|
||||
return -3;
|
||||
}
|
||||
|
@ -126,12 +128,7 @@ int tsec_get_key(uint8_t *key, uint32_t rev, const void *tsec_fw, size_t tsec_fw
|
|||
if (get_time_ms() > timeout)
|
||||
{
|
||||
/* Disable clocks. */
|
||||
clkrst_disable(CARDEVICE_KFUSE);
|
||||
clkrst_disable(CARDEVICE_SOR1);
|
||||
clkrst_disable(CARDEVICE_SOR0);
|
||||
clkrst_disable(CARDEVICE_SOR_SAFE);
|
||||
clkrst_disable(CARDEVICE_TSEC);
|
||||
clkrst_disable(CARDEVICE_HOST1X);
|
||||
tsec_disable_clkrst();
|
||||
|
||||
return -4;
|
||||
}
|
||||
|
@ -140,12 +137,7 @@ int tsec_get_key(uint8_t *key, uint32_t rev, const void *tsec_fw, size_t tsec_fw
|
|||
if (tsec->FALCON_SCRATCH1 != 0xB0B0B0B0)
|
||||
{
|
||||
/* Disable clocks. */
|
||||
clkrst_disable(CARDEVICE_KFUSE);
|
||||
clkrst_disable(CARDEVICE_SOR1);
|
||||
clkrst_disable(CARDEVICE_SOR0);
|
||||
clkrst_disable(CARDEVICE_SOR_SAFE);
|
||||
clkrst_disable(CARDEVICE_TSEC);
|
||||
clkrst_disable(CARDEVICE_HOST1X);
|
||||
tsec_disable_clkrst();
|
||||
|
||||
return -5;
|
||||
}
|
||||
|
@ -170,4 +162,55 @@ int tsec_get_key(uint8_t *key, uint32_t rev, const void *tsec_fw, size_t tsec_fw
|
|||
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();
|
||||
|
||||
/* Enable clocks. */
|
||||
tsec_enable_clkrst();
|
||||
|
||||
/* Configure Falcon. */
|
||||
tsec->FALCON_DMACTL = 0;
|
||||
tsec->FALCON_IRQMSET = 0xFFF2;
|
||||
tsec->FALCON_IRQDEST = 0xFFF0;
|
||||
tsec->FALCON_ITFEN = 3;
|
||||
|
||||
if (!tsec_dma_wait_idle())
|
||||
{
|
||||
/* Disable clocks. */
|
||||
tsec_disable_clkrst();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Load firmware. */
|
||||
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 -2;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tsec_run_fw()
|
||||
{
|
||||
volatile tegra_tsec_t *tsec = tsec_get_regs();
|
||||
|
||||
/* Unknown host1x write. */
|
||||
MAKE_HOST1X_REG(0x3300) = 0x34C2E1DA;
|
||||
|
||||
/* Execute firmware. */
|
||||
tsec->FALCON_SCRATCH1 = 0;
|
||||
tsec->FALCON_SCRATCH0 = 1;
|
||||
tsec->FALCON_BOOTVEC = 0;
|
||||
tsec->FALCON_CPUCTL = 2;
|
||||
}
|
|
@ -109,6 +109,10 @@ static inline volatile tegra_tsec_t *tsec_get_regs(void)
|
|||
return (volatile tegra_tsec_t *)TSEC_BASE;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue