TSEC Firmware: Difference between revisions
No edit summary |
→Main: correct secureboot falcon os address memcpy source |
||
(6 intermediate revisions by 2 users not shown) | |||
Line 227: | Line 227: | ||
=== Initialization === | === Initialization === | ||
The firmware initially sets up the stack pointer to the end of the available data segment. | |||
<pre> | <pre> | ||
// Read data segment size from IO space | // Read data segment size from IO space | ||
u32 data_seg_size = *(u32 *) | u32 data_seg_size = *(u32 *)FALCON_HWCFG; | ||
data_seg_size >>= 0x09; | data_seg_size >>= 0x09; | ||
data_seg_size &= 0x1FF; | data_seg_size &= 0x1FF; | ||
Line 240: | Line 240: | ||
=== Main === | === Main === | ||
The firmware reads the [[#Key data|key data]] blob and then loads, authenticates and executes [[#KeygenLdr|KeygenLdr]] which sets the TSEC key. | |||
<pre> | <pre> | ||
u32 | u32 dmem_start = 0; | ||
u8 key_data_buf[0x7C]; | u8 key_data_buf[0x7C]; | ||
// Read the key data from | // Read the key data blob from code segment | ||
u32 key_data_addr = 0x300; | u32 key_data_addr = 0x300; | ||
u32 key_data_size = 0x7C; | u32 key_data_size = 0x7C; | ||
memcpy_i2d(key_data_buf, key_data_addr, key_data_size); | memcpy_i2d(key_data_buf, key_data_addr, key_data_size); | ||
// Read the next code segment into | // Read the next stage from code segment into data space | ||
u32 blob1_addr = 0x400; | u32 blob1_addr = 0x400; | ||
u32 blob1_size = *(u32 *)(key_data_buf + 0x74); | u32 blob1_size = *(u32 *)(key_data_buf + 0x74); | ||
memcpy_i2d( | memcpy_i2d(dmem_start, blob1_addr, blob1_size); | ||
// Upload the next | // Upload the next stage into Falcon's code segment | ||
u32 blob1_virt_addr = 0x300; | u32 blob1_virt_addr = 0x300; | ||
bool use_secret = true; | bool use_secret = true; | ||
memcpy_d2i(blob1_virt_addr, | memcpy_d2i(blob1_virt_addr, dmem_start, blob1_size, blob1_virt_addr, use_secret); | ||
u32 boot_res = 0; | u32 boot_res = 0; | ||
u32 time = 0; | u32 time = 0; | ||
bool is_blob_dec = false; | bool is_blob_dec = false; | ||
while ( | while (true) | ||
{ | { | ||
if (time | if (time == 4000001) | ||
{ | { | ||
// Write boot failed (timeout) magic to FALCON_SCRATCH1 | // Write boot failed (timeout) magic to FALCON_SCRATCH1 | ||
Line 304: | Line 303: | ||
// Set auth_addr to 0x300 and auth_size to blob1_size | // Set auth_addr to 0x300 and auth_size to blob1_size | ||
$cauth = ((blob1_size << 0x10) | | $cauth = ((blob1_size << 0x10) | 0x03); | ||
// The next 2 xfer instructions will be overridden | // The next 2 xfer instructions will be overridden | ||
Line 329: | Line 328: | ||
if (boot_res == 0xB0B0B0B0) | if (boot_res == 0xB0B0B0B0) | ||
break; | |||
} | } | ||
Line 342: | Line 341: | ||
</pre> | </pre> | ||
[6.2.0+] | [6.2.0+] The firmware calculates the start address of [[#SecureBootLdr|SecureBootLdr]] through [[#Key data|key data]] and jumps to it. | ||
<pre> | <pre> | ||
u8 key_data_buf[0x84]; | u8 key_data_buf[0x84]; | ||
// Read the key data | // Read the key data blob | ||
u32 key_data_addr = 0x300; | u32 key_data_addr = 0x300; | ||
u32 key_data_size = 0x84; | u32 key_data_size = 0x84; | ||
memcpy_i2d(key_data_buf, key_data_addr, key_data_size); | memcpy_i2d(key_data_buf, key_data_addr, key_data_size); | ||
// Calculate the next blob's address | // Calculate the next blob's address in Falcon code segment | ||
u32 blob4_size = *(u32 *)(key_data_buf + 0x80); | u32 blob4_size = *(u32 *)(key_data_buf + 0x80); | ||
u32 blob0_size = *(u32 *)(key_data_buf + 0x70); | u32 blob0_size = *(u32 *)(key_data_buf + 0x70); | ||
u32 blob1_size = *(u32 *)(key_data_buf + 0x74); | u32 blob1_size = *(u32 *)(key_data_buf + 0x74); | ||
u32 blob2_size = *(u32 *)(key_data_buf + 0x78); | u32 blob2_size = *(u32 *)(key_data_buf + 0x78); | ||
u32 blob3_addr = | u32 blob3_addr = blob0_size + blob1_size + 0x100 + blob2_size + blob4_size; | ||
// Jump to next blob | // Jump to next blob | ||
Line 378: | Line 377: | ||
u32 result = 0; | u32 result = 0; | ||
// Write to SOR1 | // Write key0 to SOR1 and check for errors | ||
result = tsec_dma_write(NV_SOR_DP_HDCP_BKSV_LSB, key0); | result = tsec_dma_write(NV_SOR_DP_HDCP_BKSV_LSB, key0); | ||
if (result) | if (result) | ||
return result; | return result; | ||
// Write to SOR1 | // Write key1 to SOR1 and check for errors | ||
result = tsec_dma_write(NV_SOR_TMDS_HDCP_BKSV_LSB, key1); | result = tsec_dma_write(NV_SOR_TMDS_HDCP_BKSV_LSB, key1); | ||
if (result) | if (result) | ||
return result; | return result; | ||
// Write to SOR1 | // Write key2 to SOR1 and check for errors | ||
result = tsec_dma_write(NV_SOR_TMDS_HDCP_CN_MSB, key2); | result = tsec_dma_write(NV_SOR_TMDS_HDCP_CN_MSB, key2); | ||
if (result) | if (result) | ||
return result; | return result; | ||
// Write to SOR1 | // Write key3 to SOR1 and check for errors | ||
result = tsec_dma_write(NV_SOR_TMDS_HDCP_CN_LSB, key3); | result = tsec_dma_write(NV_SOR_TMDS_HDCP_CN_LSB, key3); | ||
if (result) | if (result) | ||
return result; | return result; | ||
Line 492: | Line 483: | ||
// Clear TSEC_TEGRA_CTL_TKFI_KFUSE | // Clear TSEC_TEGRA_CTL_TKFI_KFUSE | ||
// This is TSEC_MMIO + 0x1000 + (0x20E00 / 0x40) | // This is TSEC_MMIO + 0x1000 + (0x20E00 / 0x40) | ||
*(u32 *)TSEC_TEGRA_CTL &= | *(u32 *)TSEC_TEGRA_CTL &= 0xFFFEFFFF; | ||
// Set TSEC_SCP_CTL_PKEY_REQUEST_RELOAD | // Set TSEC_SCP_CTL_PKEY_REQUEST_RELOAD | ||
Line 514: | Line 505: | ||
exit(); | exit(); | ||
// | // Load and execute the Keygen stage | ||
load_keygen(key_buf, key_version, is_blob_dec); | load_keygen(key_buf, key_version, is_blob_dec); | ||
Line 538: | Line 529: | ||
==== load_keygen ==== | ==== load_keygen ==== | ||
This method takes '''key_buf''', '''key_version''' and '''is_blob_dec''' as arguments and is responsible for loading, decrypting, authenticating and executing [[#Keygen|Keygen]]. | |||
Notably, it also does AES-CMAC over the unauthorized [[#Boot|Boot]] blob to make sure it hasn't been tampered with. | |||
<pre> | <pre> | ||
u32 res = 0; | u32 res = 0; | ||
u32 | u32 dmem_start = 0; | ||
u32 blob0_addr = 0; | u32 blob0_addr = 0; | ||
u32 blob0_size = *(u32 *)(key_buf + 0x70); | u32 blob0_size = *(u32 *)(key_buf + 0x70); | ||
// Load blob0 code | // Load blob0 code to the start of the data segment | ||
memcpy_i2d( | memcpy_i2d(dmem_start, blob0_addr, blob0_size); | ||
// Generate "CODE_SIG_01" key into c4 crypto register | // Generate "CODE_SIG_01" key into c4 crypto register | ||
Line 555: | Line 548: | ||
enc_buf(sig_key, blob0_size); | enc_buf(sig_key, blob0_size); | ||
u32 src_addr = | u32 src_addr = dmem_start; | ||
u32 src_size = blob0_size; | u32 src_size = blob0_size; | ||
u32 iv_addr = sig_key; | u32 iv_addr = sig_key; | ||
Line 565: | Line 558: | ||
do_crypto(src_addr, src_size, iv_addr, dst_addr, mode, use_imem); | do_crypto(src_addr, src_size, iv_addr, dst_addr, mode, use_imem); | ||
// Compare the | // Compare the resulting hash with the one from the key buffer | ||
if (memcmp(dst_addr, key_buf + 0x10, 0x10)) | if (memcmp(dst_addr, key_buf + 0x10, 0x10)) | ||
{ | { | ||
Line 583: | Line 576: | ||
if ($sp > blob2_size) | if ($sp > blob2_size) | ||
{ | { | ||
u32 blob2_virt_addr = blob0_size + blob1_size; | u32 blob2_virt_addr = blob0_size + blob1_size; | ||
u32 | u32 blob2_phys_addr = blob2_virt_addr + 0x100; | ||
// Read Keygen | // Read the encrypted Keygen blob | ||
memcpy_i2d( | memcpy_i2d(dmem_start, blob2_phys_addr, blob2_size); | ||
// Generate "CODE_ENC_01" key into c4 crypto register | // Generate "CODE_ENC_01" key into c4 crypto register | ||
gen_usr_key(0x01, 0x01); | gen_usr_key(0x01, 0x01); | ||
u32 src_addr = | u32 src_addr = dmem_start; | ||
u32 src_size = blob2_size; | u32 src_size = blob2_size; | ||
u32 iv_addr = key_buf + 0x40; | u32 iv_addr = key_buf + 0x40; | ||
u32 dst_addr = | u32 dst_addr = dmem_start; | ||
u32 mode = 0; // AES-128- | u32 mode = 0; // AES-128-CBC | ||
u32 use_imem = 0; | u32 use_imem = 0; | ||
// Decrypt Keygen blob | // Decrypt Keygen blob with AES-128-CBC | ||
do_crypto(src_addr, src_size, iv_addr, dst_addr, mode, use_imem); | do_crypto(src_addr, src_size, iv_addr, dst_addr, mode, use_imem); | ||
// Upload | // Upload decrypted Keygen into Falcon's code segment | ||
bool use_secret = true; | bool use_secret = true; | ||
memcpy_d2i(blob2_virt_addr, | memcpy_d2i(blob2_virt_addr, dmem_start, blob2_size, blob2_virt_addr, use_secret); | ||
// Clear out the decrypted blob | // Clear out the decrypted blob | ||
memset( | memset(dmem_start, 0, blob2_size); | ||
} | } | ||
} | } | ||
Line 619: | Line 611: | ||
u32 blob2_hash_addr = key_buf + 0x30; | u32 blob2_hash_addr = key_buf + 0x30; | ||
// Transfer | // Transfer the Keygen auth hash to crypto register c6 | ||
xdst(0, (blob2_hash_addr | crypto_reg_flag)); | xdst(0, (blob2_hash_addr | crypto_reg_flag)); | ||
Line 663: | Line 655: | ||
// Read a 16 bytes seed based on supplied type | // Read a 16 bytes seed based on supplied type | ||
/* | /* | ||
type == 0: "CODE_SIG_01" + null padding | |||
type == 1: "CODE_ENC_01" + null padding | |||
*/ | */ | ||
get_seed(seed_buf, type); | get_seed(seed_buf, type); | ||
Line 693: | Line 685: | ||
</pre> | </pre> | ||
===== | ===== enc_buf ===== | ||
This method takes '''buf''' (a 16 bytes buffer) and '''size''' as arguments and encrypts the supplied buffer. | This method takes '''buf''' (a 16 bytes buffer) and '''size''' as arguments and encrypts the supplied buffer. | ||
<pre> | <pre> | ||
Line 701: | Line 693: | ||
*(u32 *)(buf + 0x08) = 0; | *(u32 *)(buf + 0x08) = 0; | ||
// Swap halves (b16, b32 and b16 again) | // Swap halves (b16, b32 and b16 again) and store it as the last word | ||
*(u32 *)(buf + 0x0C) = ( | |||
((size & 0x000000FF) << 0x08 | |||
| (size & 0x0000FF00) >> 0x08) << 0x10 | |||
*(u32 *)(buf + 0x0C) = size; | | ((size & 0x00FF0000) >> 0x10) << 0x08 | ||
| (size & 0xFF000000) >> 0x18 | |||
); | |||
// This will write buf into crypto register c3 | // This will write buf into crypto register c3 | ||
Line 719: | Line 713: | ||
crypto_load(0x05, buf); | crypto_load(0x05, buf); | ||
return; | |||
</pre> | |||
===== crypto_store ===== | |||
This method takes '''reg''' (a crypto register) and '''buf''' (a 16 bytes buffer) as arguments and loads the supplied buffer into the crypto register. | |||
<pre> | |||
// The next two xfer instructions will be overridden | |||
// and target changes from DMA to crypto | |||
cxset(0x02); | |||
// Encode the source buffer and the destination register for the xfer | |||
u32 crypto_xfer_flag = (u32)buf | reg << 0x10; | |||
// Transfer the supplied buffer to the supplied crypto register | |||
xdst(crypto_xfer_flag, crypto_xfer_flag); | |||
// Wait for all data loads/stores to finish | |||
xdwait(); | |||
return; | |||
</pre> | |||
===== crypto_load ===== | |||
This method takes '''reg''' (a crypto register) and '''buf''' (a 16 bytes buffer) as arguments and loads the contents of the supplied register into the supplied buffer. | |||
<pre> | |||
// The next two xfer instructions will be overridden | |||
// and target changes from DMA to crypto | |||
cxset(0x02); | |||
// Encode the destination buffer and the source register for the xfer | |||
u32 crypto_xfer_flag = (u32)buf | reg << 0x10; | |||
// Transfer the contents of the supplied crypto register into the supplied buffer | |||
xdld(crypto_xfer_flag, crypto_xfer_flag); | |||
// Wait for all data loads/stores to finish | |||
xdwait(); | |||
return; | return; | ||
</pre> | </pre> | ||
Line 752: | Line 784: | ||
ckeyreg(c4); | ckeyreg(c4); | ||
// AES-128-CBC decrypt | if (mode == 0x00) // AES-128-CBC decrypt | ||
{ | { | ||
// Create crypto script with 5 instructions | // Create crypto script with 5 instructions | ||
Line 780: | Line 811: | ||
cxsin($c3); // Read 0x10 bytes from crypto stream into c3 | cxsin($c3); // Read 0x10 bytes from crypto stream into c3 | ||
cxor($c5, $c3); // XOR c5 with c3 and store in | cxor($c5, $c3); // XOR c5 with c3 and store in c5 | ||
cenc($c5, $c5); // Encrypt from c5 into c5 | cenc($c5, $c5); // Encrypt from c5 into c5 | ||
} | } | ||
Line 925: | Line 956: | ||
== Keygen == | == Keygen == | ||
This stage is decrypted by [[#KeygenLdr|KeygenLdr]] using a key generated by encrypting a seed with | This stage is decrypted by [[#KeygenLdr|KeygenLdr]] using a key generated by encrypting the KeygenLdr auth signature with a seed encrypted with a csecret. It will generate the final TSEC key. | ||
=== Main === | === Main === | ||
The main function takes '''key_addr''' and '''key_type''' as arguments from [[#KeygenLdr|KeygenLdr]]. | The main function takes '''key_addr''' and '''key_type''' as arguments from [[#KeygenLdr|KeygenLdr]]. | ||
<pre> | <pre> | ||
u32 | u32 falcon_rev = *(u32 *)FALCON_HWCFG2 & 0x0F; | ||
// Falcon hardware | // Falcon hardware revision must be 5 | ||
if ( | if (falcon_rev != 0x05) | ||
exit(); | exit(); | ||
Line 964: | Line 995: | ||
==== gen_tsec_key ==== | ==== gen_tsec_key ==== | ||
This is | This method is responsible for generating the final TSEC key. It takes '''key_addr''' and '''key_type''' as arguments. | ||
<pre> | <pre> | ||
// This will use TSEC DMA to look for 0x34C2E1DA in host1x scratch space | // This will use TSEC DMA to look for 0x34C2E1DA in host1x scratch space | ||
Line 979: | Line 1,010: | ||
cxset(0x02); | cxset(0x02); | ||
// Transfer | // Transfer the seed in key_addr to crypto register c0 | ||
xdst(0, (key_addr | crypto_reg_flag)); | xdst(0, (key_addr | crypto_reg_flag)); | ||
Line 1,009: | Line 1,040: | ||
// Encrypt the auth signature with c2 and store in c2 | // Encrypt the auth signature with c2 and store in c2 | ||
csigenc($c2, $c2); | csigenc($c2, $c2); | ||
// Bind c2 register as the key for enc/dec operations | |||
ckeyreg($c2); | |||
// Encrypt c1 and store in c2 | // Encrypt c1 and store in c2 | ||
Line 1,017: | Line 1,051: | ||
cxset(0x02); | cxset(0x02); | ||
// Transfer | // Transfer the resulting key from crypto register c2 to key_addr | ||
xdld(0, (key_addr | crypto_reg_flag)); | xdld(0, (key_addr | crypto_reg_flag)); | ||
Line 1,039: | Line 1,073: | ||
// Encrypt the auth signature with c2 and store in c2 | // Encrypt the auth signature with c2 and store in c2 | ||
csigenc($c2, $c2); | csigenc($c2, $c2); | ||
// The next 0x02 xfer instructions will be overridden | // The next 0x02 xfer instructions will be overridden | ||
Line 1,047: | Line 1,078: | ||
cxset(0x02); | cxset(0x02); | ||
// Transfer | // Transfer the resulting key from crypto register c2 to key_addr | ||
xdld(0, (key_addr | crypto_reg_flag)); | xdld(0, (key_addr | crypto_reg_flag)); | ||
Line 1,059: | Line 1,090: | ||
return; | return; | ||
</pre> | </pre> | ||
==== sor1_set_key ==== | |||
This method takes '''key_addr''' (start address of a 16 bytes buffer) as argument and transfers its contents to SOR1 registers. | |||
The implementation is equivalent to [[#tsec_set_key|tsec_set_key]]. | |||
== SecureBootLdr == | == SecureBootLdr == | ||
Line 1,156: | Line 1,192: | ||
// Read the SecureBoot blob's Falcon OS image from memory | // Read the SecureBoot blob's Falcon OS image from memory | ||
u32 blob4_flcn_os_addr = ((((blob0_size + blob1_size) + 0x100) + blob2_size) + flcn_code_hdr_size); | u32 blob4_flcn_os_addr = ((((blob0_size + blob1_size) + 0x100) + blob2_size) + flcn_code_hdr_size); | ||
memcpy_i2d(boot_base_addr, | memcpy_i2d(boot_base_addr, blob4_flcn_os_addr, flcn_os_size); | ||
// Upload the SecureBoot's Falcon OS image boot stub code segment into Falcon's CODE region | // Upload the SecureBoot's Falcon OS image boot stub code segment into Falcon's CODE region | ||
Line 1,243: | Line 1,279: | ||
==== init_secboot ==== | ==== init_secboot ==== | ||
This method takes no arguments and is responsible for loading, authenticating and executing [[#SecureBoot|SecureBoot]]. | |||
<pre> | <pre> | ||
// Read the transfer base address from IO space | // Read the transfer base address from IO space | ||
Line 1,281: | Line 1,318: | ||
$flags.ie0 = 0; | $flags.ie0 = 0; | ||
$flags.ie1 = 0; | $flags.ie1 = 0; | ||
$flags.ie2 = 0; | |||
// Jump to the SecureBoot blob's Falcon OS image | |||
exec_secboot(); | |||
return 0x0F0F0F0F; | |||
</pre> | |||
[8.1.0+] Removed transfer base address setting and added IMEM protection. | |||
<pre> | |||
// The next xfer instruction will be overridden | |||
// and target changes from DMA to crypto | |||
cxset(0x01); | |||
u32 crypto_reg_flag = 0x00060000; | |||
u32 blob4_flcn_os_img_hash_addr = 0; | |||
// Transfer data to crypto register c6 | |||
xdst(0, (blob4_flcn_os_img_hash_addr | crypto_reg_flag)); | |||
// The next xfer instruction will be overridden | |||
// and target changes from DMA to crypto | |||
cxset(0x01); | |||
// Wait for all data loads/stores to finish | |||
xdwait(); | |||
cmov($c7, $c6); | |||
cxor($c7, $c7); | |||
// Set auth_addr to 0x100, auth_size to 0x1D00, | |||
// bit 16 (use_secret) and bit 17 (is_encrypted) | |||
$cauth = ((0x02 << 0x10) | (0x01 << 0x10) | (0x1D00 << 0x10) | (0x100 >> 0x08)); | |||
// Clear interrupt flags | |||
$flags.ie0 = 0; | |||
$flags.ie1 = 0; | |||
$flags.ie2 = 0; | |||
// Fill remaining IMEM with secret pages | |||
bool use_secret = true; | |||
memcpy_d2i(0x1E00, 0, 0x2200, 0x1E00, use_secret); | |||
memcpy_d2i(0x4000, 0, 0x4000, 0x4000, use_secret); | |||
// Wait for all code loads to finish | |||
xcwait(); | |||
// Jump to the SecureBoot blob's Falcon OS image | // Jump to the SecureBoot blob's Falcon OS image | ||
Line 1,387: | Line 1,470: | ||
set_excp_vec(entry_addr); | set_excp_vec(entry_addr); | ||
// Fill the top 0x500 bytes in DMEM with a pointer to trap function (just exits) | // Fill the top 0x500 bytes in DMEM with a pointer to trap function (just exits 3 times) | ||
for (int i = 0; i < 0x500; i += 0x04) { | for (int i = 0; i < 0x500; i += 0x04) { | ||
*(u32 *)i = (u32)trap_func(); | *(u32 *)i = (u32)trap_func(); | ||
Line 1,421: | Line 1,504: | ||
</pre> | </pre> | ||
[7.0.0+] | [7.0.0+] Many changes were introduced to mitigate and prevent attacks. | ||
<pre> | |||
// Recover the transfer base address from the stack | |||
u32 xfer_ext_base_addr = *(u32 *)scratch_data_addr; | |||
// Return the TLB entry that covers the virtual address | |||
u32 tlb_entry = vtlb(xfer_ext_base_addr); | |||
// Clear Falcon CPU control | |||
*(u32 *)FALCON_CPUCTL = 0; | |||
// Halt if the external page is marked as secret | |||
if ((tlb_entry & 0x4000000) != 0) | |||
exit(); | |||
// Read data segment size from IO space | |||
u32 data_seg_size = *(u32 *)FALCON_HWCFG; | |||
data_seg_size >>= 0x01; | |||
data_seg_size &= 0xFF00; | |||
// Set the stack pointer | |||
$sp = data_seg_size; | |||
// Fill all DMEM with a pointer to a trap function (just exits 3 times) | |||
for (int i = 0; i < data_seg_size; i += 0x04) { | |||
*(u32 *)i = (u32)trap_func(); | |||
} | |||
// Initialize the TRNG and generate random data in DMEM | |||
init_rnd(); | |||
// Issue a randomized delay and return a random value | |||
u32 rnd_val = rnd_delay(0xFF); | |||
// Enable and test SMMU bypassing in the TFBIF | |||
tfbif_smmu_cfg(0x01); | |||
// Issue a randomized delay and return a random value | |||
rnd_val = rnd_delay(0xFF); | |||
// Test SMMU bypassing in the TFBIF | |||
tfbif_smmu_cfg(0x00); | |||
// Issue a randomized delay and return a random value | |||
rnd_val = rnd_delay(0xFF); | |||
// Test SMMU bypassing in the TFBIF | |||
tfbif_smmu_cfg(0x00); | |||
// Fill SE keyslots 12 and 13 with random data | |||
se_set_keyslot_rnd(); | |||
// Test randomized offsets for read/write integrity in MC, FUSE, IRAM and TZRAM | |||
u32 test_res = test_mc_fuse_iram_tzram(); | |||
if (test_res != 0xAAAAAAAA) | |||
{ | |||
// Fill SE keyslots 12 and 13 with random data | |||
se_set_keyslot_rnd(); | |||
// Clear the entire DMEM region and every crypto register | |||
clear_dmem_and_crypto(); | |||
// Halt 5 times for no good reason | |||
exit(); | |||
exit(); | |||
exit(); | |||
exit(); | |||
exit(); | |||
} | |||
// Try to detect virtualization by enabling and disabling random CAR devices | |||
test_res = test_car(); | |||
if (test_res != 0xAAAAAAAA) | |||
{ | |||
// Fill SE keyslots 12 and 13 with random data | |||
se_set_keyslot_rnd(); | |||
// Clear the entire DMEM region and every crypto register | |||
clear_dmem_and_crypto(); | |||
// Halt 5 times for no good reason | |||
exit(); | |||
exit(); | |||
exit(); | |||
exit(); | |||
exit(); | |||
} | |||
// Test memory transfer integrity | |||
test_res = test_mem_xfer(); | |||
if (test_res != 0xAAAAAAAA) | |||
{ | |||
// Fill SE keyslots 12 and 13 with random data | |||
se_set_keyslot_rnd(); | |||
// Clear the entire DMEM region and every crypto register | |||
clear_dmem_and_crypto(); | |||
// Halt 5 times for no good reason | |||
exit(); | |||
exit(); | |||
exit(); | |||
exit(); | |||
exit(); | |||
} | |||
// Set FLOW_MODE_WAITEVENT in FLOW_CTLR_HALT_COP_EVENTS_0 | |||
halt_bpmp(); | |||
// Initialize the CCPLEX | |||
ccplex_init(); | |||
// Check if SE is ready | |||
u32 se_status = check_se_status(); | |||
if (se_status != 0) | |||
{ | |||
// Fill SE keyslots 12 and 13 with random data | |||
se_set_keyslot_rnd(); | |||
// Clear the entire DMEM region and every crypto register | |||
clear_dmem_and_crypto(); | |||
// Halt 5 times for no good reason | |||
exit(); | |||
exit(); | |||
exit(); | |||
exit(); | |||
exit(); | |||
} | |||
// Load the TSEC key from SOR1 registers into DMEM | |||
sor1_get_key(); | |||
// Initialize CAR registers | |||
car_init(); | |||
// Check certain CAR, PMC and FUSE registers | |||
test_car_pmc_fuse(); | |||
// Try to detect virtualization by enabling and disabling random CAR devices | |||
test_res = test_car(); | |||
if (test_res != 0xAAAAAAAA) | |||
{ | |||
// Fill SE keyslots 12 and 13 with random data | |||
se_set_keyslot_rnd(); | |||
// Clear the entire DMEM region and every crypto register | |||
clear_dmem_and_crypto(); | |||
// Halt 5 times for no good reason | |||
exit(); | |||
exit(); | |||
exit(); | |||
exit(); | |||
exit(); | |||
} | |||
// Ensure FUSE_SKU_INFO is 0x83 | |||
test_fuse_sku_info(); | |||
// Try to detect virtualization using MC_SMMU_AVPC_ASID and FUSE_ECO_RESERVE_0 | |||
test_smmu_fuse(); | |||
// Test MC_IRAM_BOM and MC_IRAM_TOM | |||
test_res = test_mc_iram_aperture(); | |||
if (test_res != 0xAAAAAAAA) | |||
{ | |||
// Fill SE keyslots 12 and 13 with random data | |||
se_set_keyslot_rnd(); | |||
// Clear the entire DMEM region and every crypto register | |||
clear_dmem_and_crypto(); | |||
// Halt 5 times for no good reason | |||
exit(); | |||
exit(); | |||
exit(); | |||
exit(); | |||
exit(); | |||
} | |||
// Check certain CAR, PMC and FUSE registers | |||
test_car_pmc_fuse(); | |||
// Test memory transfer integrity | |||
test_res = test_mem_xfer(); | |||
if (test_res != 0xAAAAAAAA) | |||
{ | |||
// Fill SE keyslots 12 and 13 with random data | |||
se_set_keyslot_rnd(); | |||
// Clear the entire DMEM region and every crypto register | |||
clear_dmem_and_crypto(); | |||
// Halt 5 times for no good reason | |||
exit(); | |||
exit(); | |||
exit(); | |||
exit(); | |||
exit(); | |||
} | |||
// Try to detect virtualization using MC_SMMU_AVPC_ASID and FUSE_ECO_RESERVE_0 | |||
test_smmu_fuse(); | |||
// Test MC_IRAM_BOM and MC_IRAM_TOM | |||
test_res = test_mc_iram_aperture(); | |||
if (test_res != 0xAAAAAAAA) | |||
{ | |||
// Fill SE keyslots 12 and 13 with random data | |||
se_set_keyslot_rnd(); | |||
// Clear the entire DMEM region and every crypto register | |||
clear_dmem_and_crypto(); | |||
// Halt 5 times for no good reason | |||
exit(); | |||
exit(); | |||
exit(); | |||
exit(); | |||
exit(); | |||
} | |||
// Test SMMU bypassing in the TFBIF | |||
tfbif_smmu_cfg(0x00); | |||
// Decrypt Package1 | |||
decrypt_pk11(); | |||
// Write TSEC root key to SE keyslot 0x0D | |||
se_set_keyslot_13(); | |||
// Write TSEC key to SE keyslot 0x0C | |||
se_set_keyslot_12(); | |||
// Clear the cauth signature | |||
csigclr(); | |||
// Check certain CAR, PMC and FUSE registers | |||
test_car_pmc_fuse(); | |||
// Test memory transfer integrity | |||
test_res = test_mem_xfer(); | |||
if (test_res != 0xAAAAAAAA) | |||
{ | |||
// Fill SE keyslots 12 and 13 with random data | |||
se_set_keyslot_rnd(); | |||
// Clear the entire DMEM region and every crypto register | |||
clear_dmem_and_crypto(); | |||
// Halt 5 times for no good reason | |||
exit(); | |||
exit(); | |||
exit(); | |||
exit(); | |||
exit(); | |||
} | |||
// Try to detect virtualization using MC_SMMU_AVPC_ASID and FUSE_ECO_RESERVE_0 | |||
test_smmu_fuse(); | |||
// Test randomized offsets for read/write integrity in MC, FUSE, IRAM and TZRAM | |||
test_res = test_mc_fuse_iram_tzram(); | |||
if (test_res != 0xAAAAAAAA) | |||
{ | |||
// Fill SE keyslots 12 and 13 with random data | |||
se_set_keyslot_rnd(); | |||
// Clear the entire DMEM region and every crypto register | |||
clear_dmem_and_crypto(); | |||
// Halt 5 times for no good reason | |||
exit(); | |||
exit(); | |||
exit(); | |||
exit(); | |||
exit(); | |||
} | |||
// Test MC_IRAM_BOM and MC_IRAM_TOM | |||
test_res = test_mc_iram_aperture(); | |||
if (test_res != 0xAAAAAAAA) | |||
{ | |||
// Fill SE keyslots 12 and 13 with random data | |||
se_set_keyslot_rnd(); | |||
// Clear the entire DMEM region and every crypto register | |||
clear_dmem_and_crypto(); | |||
// Halt 5 times for no good reason | |||
exit(); | |||
exit(); | |||
exit(); | |||
exit(); | |||
exit(); | |||
} | |||
// Test SMMU bypassing in the TFBIF | |||
tfbif_smmu_cfg(0x00); | |||
// Parse Package1 header and return entry address | |||
u32 entry_addr = parse_pk11(); | |||
// Set the exception vectors | |||
set_excp_vec(entry_addr); | |||
// Fill the top 0x500 bytes in DMEM with a pointer to trap function (just exits) | |||
for (int i = 0; i < 0x500; i += 0x04) { | |||
*(u32 *)i = (u32)trap_func(); | |||
} | |||
// Clear all crypto registers | |||
cxor($c0, $c0); | |||
cxor($c1, $c1); | |||
cxor($c2, $c2); | |||
cxor($c3, $c3); | |||
cxor($c4, $c4); | |||
cxor($c5, $c5); | |||
cxor($c6, $c6); | |||
cxor($c7, $c7); | |||
// Take SCP out of lockdown | |||
unlock_scp(); | |||
// Test memory transfer integrity | |||
test_res = test_mem_xfer(); | |||
if (test_res != 0xAAAAAAAA) | |||
{ | |||
// Fill SE keyslots 12 and 13 with random data | |||
se_set_keyslot_rnd(); | |||
// Clear the entire DMEM region and every crypto register | |||
clear_dmem_and_crypto(); | |||
// Halt 5 times for no good reason | |||
exit(); | |||
exit(); | |||
exit(); | |||
exit(); | |||
exit(); | |||
} | |||
// Try to detect virtualization using MC_SMMU_AVPC_ASID and FUSE_ECO_RESERVE_0 | |||
test_smmu_fuse(); | |||
// Test MC_IRAM_BOM and MC_IRAM_TOM | |||
test_res = test_mc_iram_aperture(); | |||
if (test_res != 0xAAAAAAAA) | |||
{ | |||
// Fill SE keyslots 12 and 13 with random data | |||
se_set_keyslot_rnd(); | |||
// Clear the entire DMEM region and every crypto register | |||
clear_dmem_and_crypto(); | |||
// Halt 5 times for no good reason | |||
exit(); | |||
exit(); | |||
exit(); | |||
exit(); | |||
exit(); | |||
} | |||
// Test SMMU bypassing in the TFBIF | |||
tfbif_smmu_cfg(0x00); | |||
// Clear FLOW_CTLR_HALT_COP_EVENTS_0 | |||
resume_bpmp(); | |||
// Clear the entire DMEM region and every crypto register | |||
clear_dmem_and_crypto(); | |||
// Halt 5 times for no good reason | |||
exit(); | |||
exit(); | |||
exit(); | |||
exit(); | |||
exit(); | |||
return; | |||
</pre> | |||
[8.1.0+] Key derivation algorithm was changed. Very minor changes were introduced to mitigate and prevent attacks. | |||
== Key data == | == Key data == |