TSEC: Difference between revisions
mNo edit summary |
6.2.0 changes |
||
Line 392: | Line 392: | ||
| TSEC_TFBIF_UNK2 | | TSEC_TFBIF_UNK2 | ||
| 0x54501640 | | 0x54501640 | ||
| 0x04 | |||
|- | |||
| TSEC_TFBIF_UNK3 | |||
| 0x54501644 | |||
| 0x04 | |||
|- | |||
| TSEC_TFBIF_UNK4 | |||
| 0x54501648 | |||
| 0x04 | | 0x04 | ||
|- | |- | ||
Line 429: | Line 437: | ||
| 0x54501838 | | 0x54501838 | ||
| 0x04 | | 0x04 | ||
|} | |} | ||
Line 454: | Line 461: | ||
| 1 | | 1 | ||
| FALCON_ITFEN_MTHDEN | | FALCON_ITFEN_MTHDEN | ||
|} | |} | ||
Line 466: | Line 472: | ||
| 0 | | 0 | ||
| FALCON_IDLESTATE_FALCON_BUSY | | FALCON_IDLESTATE_FALCON_BUSY | ||
|} | |} | ||
Line 493: | Line 498: | ||
| 5 | | 5 | ||
| FALCON_CPUCTL_STOPPED | | FALCON_CPUCTL_STOPPED | ||
|} | |} | ||
Line 520: | Line 524: | ||
| 7 | | 7 | ||
| FALCON_DMACTL_SECURE_STAT | | FALCON_DMACTL_SECURE_STAT | ||
|} | |} | ||
Line 540: | Line 543: | ||
|- | |- | ||
| 1 | | 1 | ||
| FALCON_DMATRFCMD_IDLE | | FALCON_DMATRFCMD_IDLE | ||
|- | |- | ||
| 2-3 | | 2-3 | ||
Line 556: | Line 559: | ||
| 12-14 | | 12-14 | ||
| FALCON_DMATRFCMD_CTXDMA | | FALCON_DMATRFCMD_CTXDMA | ||
|} | |} | ||
Line 571: | Line 573: | ||
| 20 | | 20 | ||
| TSEC_SCP_CTL_STAT_DEBUG_MODE | | TSEC_SCP_CTL_STAT_DEBUG_MODE | ||
|} | |} | ||
Line 584: | Line 585: | ||
| 1 | | 1 | ||
| TSEC_SCP_CTL_PKEY_LOADED | | TSEC_SCP_CTL_PKEY_LOADED | ||
|} | |} | ||
Line 609: | Line 609: | ||
| 31 | | 31 | ||
| TSEC_DMA_CMD_INIT | | TSEC_DMA_CMD_INIT | ||
|} | |} | ||
Line 649: | Line 648: | ||
| 27 | | 27 | ||
| TSEC_TEGRA_CTL_TMPI_DISABLE_OUTPUT_I2C | | TSEC_TEGRA_CTL_TMPI_DISABLE_OUTPUT_I2C | ||
|} | |} | ||
= Boot Process = | = Boot Process = | ||
TSEC is configured and initialized by the first bootloader during key generation | TSEC is configured and initialized by the first bootloader during key generation. | ||
[6.2.0+] TSEC is now configured at the end of the first bootloader's main function. | |||
== Initialization == | == Initialization == | ||
Line 709: | Line 709: | ||
// Load code into Falcon (0x100 bytes at a time) | // Load code into Falcon (0x100 bytes at a time) | ||
while (src_offset < 0xF00) | while (src_offset < 0xF00) | ||
{ | |||
flcn_load_firm(trf_mode, src_offset, dst_offset); | |||
src_offset += 0x100; | |||
dst_offset += 0x100; | |||
} | |||
[6.2.0+] The transfer base address and size of the Falcon firmware code changed. | |||
// Set DMA transfer base address to 0x40010E00 >> 0x08 | |||
*(u32 *)FALCON_DMATRFBASE = 0x40010E; | |||
u32 trf_mode = 0; // A value of 0 sets FALCON_DMATRFCMD_IMEM | |||
u32 dst_offset = 0; | |||
u32 src_offset = 0; | |||
// Load code into Falcon (0x100 bytes at a time) | |||
while (src_offset < 0x2900) | |||
{ | { | ||
flcn_load_firm(trf_mode, src_offset, dst_offset); | flcn_load_firm(trf_mode, src_offset, dst_offset); | ||
Line 717: | Line 733: | ||
== Firmware booting == | == Firmware booting == | ||
Falcon is booted up and the first bootloader waits for it to finish. | Falcon is booted up and the first bootloader waits for it to finish. | ||
// Set | // Set magic value in host1x scratch space | ||
*(u32 *)0x50003300 = 0x34C2E1DA; | *(u32 *)0x50003300 = 0x34C2E1DA; | ||
Line 757: | Line 772: | ||
if (boot_res != 0xB0B0B0B0) | if (boot_res != 0xB0B0B0B0) | ||
panic(); | panic(); | ||
[6.2.0+] Falcon is booted up, but the first bootloader is left in an infinite loop. | |||
// Set magic value in host1x scratch space | |||
*(u32 *)0x50003300 = 0x34C2E1DA; | |||
// Clear Falcon scratch1 MMIO | |||
*(u32 *)FALCON_SCRATCH1 = 0; | |||
// Set Falcon boot key version in scratch0 MMIO | |||
*(u32 *)FALCON_SCRATCH0 = 0x01; | |||
// Set Falcon's boot vector address | |||
*(u32 *)FALCON_BOOTVEC = 0; | |||
// Signal Falcon's CPU | |||
*(u32 *)FALCON_CPUCTL = 0x02; | |||
// Infinite loop | |||
deadlock(); | |||
== Device key generation == | == Device key generation == | ||
The TSEC device key is generated by reading SOR1 registers modified by the Falcon CPU. | The TSEC device key is generated by reading SOR1 registers modified by the Falcon CPU. | ||
// Clear | // Clear magic value in host1x scratch space | ||
*(u32 *)0x50003300 = 0; | *(u32 *)0x50003300 = 0; | ||
Line 782: | Line 815: | ||
// Copy back the TSEC device key | // Copy back the TSEC device key | ||
memcpy(out_buf, tsec_device_key, out_size); | memcpy(out_buf, tsec_device_key, out_size); | ||
[6.2.0+] This is now done inside an encrypted TSEC payload. | |||
== Cleanup == | == Cleanup == | ||
Line 813: | Line 848: | ||
= TSEC Firmware = | = TSEC Firmware = | ||
The actual code loaded into TSEC is assembled in NVIDIA's proprietary fuc5 ISA using crypto extensions. | The actual code loaded into TSEC is assembled in NVIDIA's proprietary fuc5 ISA using crypto extensions. | ||
Stored inside the first bootloader, this firmware binary is split into 4 blobs: | Stored inside the first bootloader, this firmware binary is split into 4 blobs (names are unofficial): [[#Boot|Boot]] (unencrypted and unauthenticated code), [[#KeygenLdr|KeygenLdr]] (unencrypted and authenticated code), [[#Keygen|Keygen]] (encrypted and authenticated code) and [[#Key data|key data]]. | ||
[6.2.0+] There are now 6 blobs (names are unofficial): [[#Boot|Boot]] (unencrypted and unauthenticated code), [[#Loader|Loader]] (unencrypted and unauthenticated code), [[#KeygenLdr|KeygenLdr]] (unencrypted and authenticated code), [[#Keygen|Keygen]] (encrypted and authenticated code), [[#Payload|Payload]] (part unencrypted and unauthenticated code, part encrypted and authenticated code) and [[#Key data|key data]]. | |||
Firmware can be disassembled with [http://envytools.readthedocs.io/en/latest/ envytools'] [https://github.com/envytools/envytools/tree/master/envydis envydis]: | Firmware can be disassembled with [http://envytools.readthedocs.io/en/latest/ envytools'] [https://github.com/envytools/envytools/tree/master/envydis envydis]: | ||
Line 821: | Line 858: | ||
Note that the instruction set has variable length instructions, and the disassembler is not very good at detecting locations it should start disassembling from. One needs to disassemble multiple sub-regions and join them together. | Note that the instruction set has variable length instructions, and the disassembler is not very good at detecting locations it should start disassembling from. One needs to disassemble multiple sub-regions and join them together. | ||
== | == Boot == | ||
During this stage key data is loaded and | During this stage, [[#Key data|key data]] is loaded and [[#KeygenLdr|KeygenLdr]] is authenticated, loaded and executed. | ||
Before returning, this stage writes back to the host (using MMIO registers) and sets the device key used by the first bootloader. | Before returning, this stage writes back to the host (using MMIO registers) and sets the device key used by the first bootloader. | ||
[6.2.0+] During this stage, [[#Key data|key data]] is loaded and execution jumps to [[#Loader|Loader]]. | |||
=== Initialization === | === Initialization === | ||
Line 836: | Line 875: | ||
*(u32 *)sp = data_seg_size; | *(u32 *)sp = data_seg_size; | ||
=== | === Main === | ||
Falcon reads the [[#Key data|key data]], authenticates, loads and executes [[#KeygenLdr|KeygenLdr]] and finally sets the device key. | |||
u32 boot_base_addr = 0; | u32 boot_base_addr = 0; | ||
u8 key_data_buf[0x7C]; | |||
// Read the key data from memory | // Read the key data from memory | ||
Line 908: | Line 948: | ||
// Transfer data to crypto register c6 | // Transfer data to crypto register c6 | ||
xdst(0, (blob1_hash_addr | crypt_reg_flag)); | |||
// Wait for all data loads/stores to finish | |||
xdwait(); | |||
// Jump to | // Jump to KeygenLdr | ||
u32 | u32 keygenldr_res = exec_keygenldr(key_buf, key_version, is_blob_dec); | ||
is_blob_dec = true; // Set this to prevent decrypting again | is_blob_dec = true; // Set this to prevent decrypting again | ||
// Set boot finish magic on success | // Set boot finish magic on success | ||
if ( | if (keygenldr_res == 0) | ||
boot_res = 0xB0B0B0B0 | boot_res = 0xB0B0B0B0 | ||
} | } | ||
Line 936: | Line 976: | ||
return boot_res; | return boot_res; | ||
[6.2.0+] Falcon reads the [[#Key data|key data]] and jumps to [[#Loader|Loader]]. | |||
u8 key_data_buf[0x84]; | |||
// Read the key data from memory | |||
u32 key_data_addr = 0x300; | |||
u32 key_data_size = 0x84; | |||
read_code(key_data_buf, key_data_addr, key_data_size); | |||
// Calculate the next blob's address | |||
u32 blob4_size = *(u32 *)(key_data_buf + 0x80); | |||
u32 blob0_size = *(u32 *)(key_data_buf + 0x70); | |||
u32 blob1_size = *(u32 *)(key_data_buf + 0x74); | |||
u32 blob2_size = *(u32 *)(key_data_buf + 0x78); | |||
u32 blob3_addr = ((((blob0_size + blob1_size) + 0x100) + blob2_size) + blob4_size); | |||
// Jump to next blob | |||
(void *)blob3_addr(); | |||
return 0; | |||
==== set_device_key ==== | ==== set_device_key ==== | ||
Line 1,014: | Line 1,074: | ||
return 0; | return 0; | ||
== | == KeygenLdr == | ||
This stage is responsible for reconfiguring the Falcon's crypto co-processor and loading, decrypting, authenticating and executing | This stage is responsible for reconfiguring the Falcon's crypto co-processor and loading, decrypting, authenticating and executing [[#Keygen|Keygen]]. | ||
=== | === Main === | ||
// Clear interrupt flags | // Clear interrupt flags | ||
*(u8 *)flags_ie0 = 0; | *(u8 *)flags_ie0 = 0; | ||
Line 1,085: | Line 1,145: | ||
exit(); | exit(); | ||
// Decrypt and load | // Decrypt and load Keygen stage | ||
load_keygen(key_buf, key_version, is_blob_dec); | |||
// Partially unknown fuc5 instruction | // Partially unknown fuc5 instruction | ||
Line 1,108: | Line 1,168: | ||
return; | return; | ||
=== | ==== load_keygen ==== | ||
u32 res = 0; | u32 res = 0; | ||
Line 1,119: | Line 1,179: | ||
// Generate "CODE_SIG_01" key into c4 crypto register | // Generate "CODE_SIG_01" key into c4 crypto register | ||
gen_usr_key(0, 0); | |||
// Encrypt buffer with c4 | // Encrypt buffer with c4 | ||
u8 sig_key[0x10]; | |||
enc_buf(sig_key, blob0_size); | enc_buf(sig_key, blob0_size); | ||
Line 1,144: | Line 1,204: | ||
u32 blob1_size = *(u32 *)(key_buf + 0x74); | u32 blob1_size = *(u32 *)(key_buf + 0x74); | ||
// Decrypt | // Decrypt Keygen blob if needed | ||
if (!is_blob_dec) | if (!is_blob_dec) | ||
{ | { | ||
Line 1,157: | Line 1,217: | ||
u32 blob2_addr = blob2_virt_addr + 0x100; | u32 blob2_addr = blob2_virt_addr + 0x100; | ||
// Read | // Read Keygen encrypted blob | ||
read_code(boot_base_addr, blob2_addr, blob2_size); | read_code(boot_base_addr, blob2_addr, blob2_size); | ||
// Generate "CODE_ENC_01" key into c4 crypt register | // Generate "CODE_ENC_01" key into c4 crypt register | ||
gen_usr_key(0x01, 0x01); | |||
u32 src_addr = boot_base_addr; | u32 src_addr = boot_base_addr; | ||
Line 1,170: | Line 1,230: | ||
u32 version = 0; | u32 version = 0; | ||
// Decrypt | // Decrypt Keygen blob | ||
do_crypto(src_addr, src_size, iv_addr, dst_addr, mode, version); | do_crypto(src_addr, src_size, iv_addr, dst_addr, mode, version); | ||
Line 1,215: | Line 1,275: | ||
res = 0xD0D0D0D0 | res = 0xD0D0D0D0 | ||
// Jump to | // Jump to Keygen | ||
if (hovi_key_addr) | if (hovi_key_addr) | ||
res = | res = exec_keygen(hovi_key_addr, key_version); | ||
// Clear out key data | // Clear out key data | ||
Line 1,228: | Line 1,288: | ||
return res; | return res; | ||
==== | ===== gen_usr_key ===== | ||
This method takes '''type''' and '''mode''' as arguments and generates a key. | This method takes '''type''' and '''mode''' as arguments and generates a key. | ||
u8 seed_buf[0x10]; | |||
// Read a 16 bytes seed based on supplied type | // Read a 16 bytes seed based on supplied type | ||
Line 1,267: | Line 1,327: | ||
return; | return; | ||
==== enc_buffer ==== | ===== enc_buffer ===== | ||
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. | ||
// Set first 3 words to null | // Set first 3 words to null | ||
Line 1,296: | Line 1,356: | ||
return; | return; | ||
==== do_crypto ==== | ===== do_crypto ===== | ||
This is the method responsible for all crypto operations performed during | This is the method responsible for all crypto operations performed during [[#KeygenLdr|KeygenLdr]]. It takes '''src_addr''', '''src_size''', '''iv_addr''', '''dst_addr''', '''mode''' and '''crypt_ver''' as arguments. | ||
// Check for invalid source data size | // Check for invalid source data size | ||
if (!src_size || (src_size & 0x0F)) | if (!src_size || (src_size & 0x0F)) | ||
Line 1,499: | Line 1,559: | ||
return; | return; | ||
== | == Keygen == | ||
This stage is decrypted by | This stage is decrypted by [[#KeygenLdr|KeygenLdr]] using a key generated by encrypting a seed with an hardware secret. It will generate the final TSEC device key. | ||
== Loader == | |||
This stage starts by authenticating and executing [[#KeygenLdr|KeygenLdr]] which in turn authenticates, decrypts and executes [[#Keygen|Keygen]] (both blobs remain unchanged from previous firmware versions). | |||
After the TSEC device key has been generated, execution returns to this stage which then parses and executes [[#Payload|Payload]]. | |||
=== Main === | |||
u8 key_data_buf[0x84]; | |||
u8 tmp_key_data_buf[0x84]; | |||
// Read the key data from memory | |||
u32 key_data_addr = 0x300; | |||
u32 key_data_size = 0x84; | |||
read_code(key_data_buf, key_data_addr, key_data_size); | |||
// Read the KeygenLdr blob from memory | |||
u32 boot_base_addr = 0; | |||
u32 blob1_addr = 0x400; | |||
u32 blob1_size = *(u32 *)(key_data_buf + 0x74); | |||
read_code(boot_base_addr, blob1_addr, blob1_size); | |||
// Upload the next code segment into Falcon's CODE region | |||
u32 blob1_virt_addr = 0x300; | |||
bool use_secret = true; | |||
upload_code(blob1_virt_addr, boot_base_addr, blob1_size, blob1_virt_addr, use_secret); | |||
// Backup the key data | |||
memcpy(tmp_key_data_buf, key_data_buf, 0x84); | |||
// Save previous cauth value | |||
u32 c_old = cauth_old; | |||
// fuc5 crypt cauth instruction | |||
// Set auth_addr to 0x300 and auth_size to blob1_size | |||
cauth((blob1_size << 0x10) | (0x300 >> 0x08)); | |||
// fuc5 crypt cxset instruction | |||
// The next 2 xfer instructions will be overridden | |||
// and target changes from DMA to crypto | |||
cxset(0x02); | |||
u32 crypt_reg_flag = 0x00060000; | |||
u32 blob1_hash_addr = tmp_key_data_buf + 0x20; | |||
// Transfer data to crypto register c6 | |||
xdst(0, (blob1_hash_addr | crypt_reg_flag)); | |||
// Wait for all data loads/stores to finish | |||
xdwait(); | |||
u32 key_version = 0x01; | |||
bool is_blob_dec = false; | |||
// Jump to KeygenLdr | |||
u32 keygenldr_res = exec_keygenldr(tmp_key_data_buf, key_version, is_blob_dec); | |||
// Set boot finish magic on success | |||
if (keygenldr_res == 0) | |||
keygenldr_res = 0xB0B0B0B0 | |||
// Write result to FALCON_SCRATCH1 | |||
*(u32 *)FALCON_SCRATCH1 = keygenldr_res; | |||
if (keygenldr_res != 0xB0B0B0B0) | |||
return keygenldr_res; | |||
// fuc5 crypt cauth instruction | |||
// Restore previous cauth value | |||
cauth(c_old); | |||
u8 flcn_hdr_buf[0x18]; | |||
u8 flcn_os_hdr_buf[0x10]; | |||
blob1_size = *(u32 *)(key_data_buf + 0x74); | |||
u32 blob2_size = *(u32 *)(key_data_buf + 0x78); | |||
u32 blob0_size = *(u32 *)(key_data_buf + 0x70); | |||
// Read the Payload blob's Falcon header from memory | |||
u32 blob4_flcn_hdr_addr = (((blob0_size + blob1_size) + 0x100) + blob2_size); | |||
read_code(flcn_hdr_buf, blob4_flcn_hdr_addr, 0x18); | |||
blob1_size = *(u32 *)(key_data_buf + 0x74); | |||
blob2_size = *(u32 *)(key_data_buf + 0x78); | |||
blob0_size = *(u32 *)(key_data_buf + 0x70); | |||
u32 flcn_hdr_size = *(u32 *)(flcn_hdr_buf + 0x0C); | |||
// Read the Payload blob's Falcon OS header from memory | |||
u32 blob4_flcn_os_hdr_addr = ((((blob0_size + blob1_size) + 0x100) + blob2_size) + flcn_hdr_size); | |||
read_code(flcn_os_hdr_buf, blob4_flcn_os_hdr_addr, 0x10); | |||
blob1_size = *(u32 *)(key_data_buf + 0x74); | |||
blob2_size = *(u32 *)(key_data_buf + 0x78); | |||
blob0_size = *(u32 *)(key_data_buf + 0x70); | |||
u32 flcn_code_hdr_size = *(u32 *)(flcn_hdr_buf + 0x10); | |||
u32 flcn_os_size = *(u32 *)(flcn_os_hdr_buf + 0x04); | |||
// Read the Payload blob's Falcon OS image from memory | |||
u32 blob4_flcn_os_addr = ((((blob0_size + blob1_size) + 0x100) + blob2_size) + flcn_code_hdr_size); | |||
read_code(boot_base_addr, blob4_flcn_os_hdr_addr, flcn_os_size); | |||
// Upload the Payload's Falcon OS image boot stub code segment into Falcon's CODE region | |||
u32 blob4_flcn_os_boot_virt_addr = 0; | |||
u32 blob4_flcn_os_boot_size = 0x100; | |||
use_secret = false; | |||
upload_code(blob4_flcn_os_boot_virt_addr, boot_base_addr, blob4_flcn_os_boot_size, blob4_flcn_os_boot_virt_addr, use_secret); | |||
flcn_os_size = *(u32 *)(flcn_os_hdr_buf + 0x04); | |||
// Upload the Payload blob's Falcon OS encrypted image code segment into Falcon's CODE region | |||
u32 blob4_flcn_os_img_virt_addr = 0x100; | |||
u32 blob4_flcn_os_img_size = (flcn_os_size - 0x100); | |||
use_secret = true; | |||
upload_code(blob4_flcn_os_img_virt_addr, boot_base_addr + 0x100, blob4_flcn_os_img_size, blob4_flcn_os_img_virt_addr, use_secret); | |||
// Wait for all code loads to finish | |||
xcwait(); | |||
blob1_size = *(u32 *)(key_data_buf + 0x74); | |||
blob2_size = *(u32 *)(key_data_buf + 0x78); | |||
blob0_size = *(u32 *)(key_data_buf + 0x70); | |||
flcn_code_hdr_size = *(u32 *)(flcn_hdr_buf + 0x10); | |||
u32 flcn_os_code_size = *(u32 *)(flcn_os_hdr_buf + 0x08); | |||
// Read the Payload blob's falcon OS image's hash from memory | |||
u32 blob4_flcn_os_img_hash_addr = (((((blob0_size + blob1_size) + 0x100) + blob2_size) + flcn_code_hdr_size) + flcn_os_code_size); | |||
read_code(0, blob4_flcn_os_img_hash_addr, 0x10); | |||
// Read data segment size from IO space | |||
u32 data_seg_size = *(u32 *)UC_CAPS; | |||
data_seg_size >>= 0x03; | |||
data_seg_size &= 0x3FC0; | |||
u32 data_addr = 0x10; | |||
// Clear all data except the first 0x10 bytes (Payload blob's Falcon OS image's hash) | |||
for (int data_word_count = 0x04; data_word_count < data_seg_size; data_word_count++) | |||
{ | |||
*(u32 *)(data_addr) = 0; | |||
data_addr += 0x04; | |||
} | |||
// 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); | |||
// Partially unknown fuc5 instruction | |||
// Likely forces a change of permissions | |||
cchmod(c0, c0); | |||
// Jump to Payload | |||
exec_payload(); | |||
return 0xB0B0B0B0; | |||
== Payload == | |||
This stage prepares the stack then authenticates, decrypts and executes the Payload blob's Falcon OS image. | |||
=== Main === | |||
// Read data segment size from IO space | |||
u32 data_seg_size = *(u32 *)UC_CAPS; | |||
data_seg_size >>= 0x01; | |||
data_seg_size &= 0xFF00; | |||
// Set the stack pointer | |||
*(u32 *)sp = data_seg_size; | |||
// Jump to the Payload blob's Falcon OS image boot stub | |||
exec_flcn_os_boot(); | |||
// Halt execution | |||
exit(); | |||
return; | |||
==== exec_flcn_os_boot ==== | |||
// Read the transfer base address from IO space | |||
u32 xfer_ext_base_addr = *(u32 *)XFER_EXT_BASE; | |||
// Copy transfer base address to data memory | |||
u32 scratch_data_addr = 0x300; | |||
*(u32 *)scratch_data_addr = xfer_ext_base_addr; | |||
// Set the transfer base address | |||
xcbase(xfer_ext_base_addr); | |||
// fuc5 crypt cxset instruction | |||
// The next xfer instruction will be overridden | |||
// and target changes from DMA to crypto | |||
cxset(0x01); | |||
u32 crypt_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 | crypt_reg_flag)); | |||
// fuc5 crypt cxset instruction | |||
// 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); | |||
// fuc5 crypt cauth instruction | |||
// Set auth_addr to 0x100, auth_size to 0x1300 and some unknown flags | |||
cauth((0x02 << 0x10) | (0x01 << 0x10) | (0x1300 << 0x10) | (0x100 >> 0x08)); | |||
// Clear interrupt flags | |||
*(u8 *)flags_ie0 = 0; | |||
*(u8 *)flags_ie1 = 0; | |||
// Jump to the Payload blob's Falcon OS image | |||
exec_flcn_os_img(); | |||
return 0x0F0F0F0F; | |||
== Key data == | == Key data == | ||
Small buffer stored after | Small buffer stored after the [[#Boot|Boot]] blob and used across all stages. | ||
{| class="wikitable" border="1" | {| class="wikitable" border="1" | ||
Line 1,516: | Line 1,800: | ||
| 0x10 | | 0x10 | ||
| 0x10 | | 0x10 | ||
| blob0 auth hash | | blob0 ([[#Boot|Boot]]) auth hash | ||
|- | |- | ||
| 0x20 | | 0x20 | ||
| 0x10 | | 0x10 | ||
| blob1 auth hash | | blob1 ([[#KeygenLdr|KeygenLdr]]) auth hash | ||
|- | |- | ||
| 0x30 | | 0x30 | ||
| 0x10 | | 0x10 | ||
| blob2 auth hash | | blob2 ([[#Keygen|Keygen]]) auth hash | ||
|- | |- | ||
| 0x40 | | 0x40 | ||
| 0x10 | | 0x10 | ||
| blob2 AES IV | | blob2 ([[#Keygen|Keygen]]) AES IV | ||
|- | |- | ||
| 0x50 | | 0x50 | ||
Line 1,540: | Line 1,824: | ||
| 0x70 | | 0x70 | ||
| 0x04 | | 0x04 | ||
| blob0 size | | blob0 ([[#Boot|Boot]]) size | ||
|- | |- | ||
| 0x74 | | 0x74 | ||
| 0x04 | | 0x04 | ||
| blob1 size | | blob1 ([[#KeygenLdr|KeygenLdr]]) size | ||
|- | |- | ||
| 0x78 | | 0x78 | ||
| 0x04 | | 0x04 | ||
| blob2 size | | blob2 ([[#Keygen|Keygen]]) size | ||
|- | |||
| 0x7C | |||
| 0x04 | |||
| [6.2.0+] blob3 ([[#Loader|Loader]]) size | |||
|- | |- | ||
| 0x80 | |||
| 0x04 | |||
| [6.2.0+] blob4 ([[#Payload|Payload]]) size | |||
|} | |} | ||
== Notes == | == Notes == | ||
[https://wiki.0x04.net/wiki/Marcin_Ko%C5%9Bcielnicki mwk] shared additional info learned from RE of falcon processors over the years, which hasn't made it into envytools documentation yet: | [https://wiki.0x04.net/wiki/Marcin_Ko%C5%9Bcielnicki mwk] shared additional info learned from RE of falcon processors over the years, which hasn't made it into envytools documentation yet: | ||
=== cxset === | === cxset === | ||
cxset instruction provides a way to change behavior of a variable amount of successively executed DMA-related instructions. | cxset instruction provides a way to change behavior of a variable amount of successively executed DMA-related instructions. | ||
Line 1,567: | Line 1,856: | ||
==== Override Types ==== | ==== Override Types ==== | ||
Unlisted values are unknown, but probably do something. | Unlisted values are unknown, but probably do something. | ||
Line 1,583: | Line 1,871: | ||
==== DMA-Related Instructions ==== | ==== DMA-Related Instructions ==== | ||
At least the following instructions may have changed behavior, and count against the cxset "count" argument: <code>xdwait</code>, <code>xdst</code>, <code>xdld</code>. | At least the following instructions may have changed behavior, and count against the cxset "count" argument: <code>xdwait</code>, <code>xdst</code>, <code>xdld</code>. | ||
Line 1,589: | Line 1,876: | ||
=== Register ACLs === | === Register ACLs === | ||
Falcon tracks permission metadata about each crypto reg. Permissions include read/write ability per execution mode, as well as ability to use the reg for encrypt/decrypt, among other permissions. Permissions are propagated when registers are referenced by instructions (e.g. moving a value from read-protected $cX to $cY will result in $cY also being read-protected). | Falcon tracks permission metadata about each crypto reg. Permissions include read/write ability per execution mode, as well as ability to use the reg for encrypt/decrypt, among other permissions. Permissions are propagated when registers are referenced by instructions (e.g. moving a value from read-protected $cX to $cY will result in $cY also being read-protected). | ||
=== Authenticated Mode Entry/Exit === | === Authenticated Mode Entry/Exit === | ||
Entry to Authenticated Mode always sets $pc to the address supplied in $cauth (ie the base of the signature-checked region). This takes effect when trying to branch to any address within the range covered by $cauth. Entry to Authenticated Mode (also called "Secure Mode") computes a MAC over the $cauth region and compares it to $c6 in order to perform the signature check. | Entry to Authenticated Mode always sets $pc to the address supplied in $cauth (ie the base of the signature-checked region). This takes effect when trying to branch to any address within the range covered by $cauth. Entry to Authenticated Mode (also called "Secure Mode") computes a MAC over the $cauth region and compares it to $c6 in order to perform the signature check. | ||
Line 1,599: | Line 1,884: | ||
=== Unknown Instructions === | === Unknown Instructions === | ||
<code>00000000: f5 3c XY e0 cchmod $cY $cX</code> - likely forces a change of permissions. | <code>00000000: f5 3c XY e0 cchmod $cY $cX</code> - likely forces a change of permissions. | ||