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 (this is set if the engine is 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 (sub_400114FC). | + | 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 something in unknown host1x channel 0 sync register (HOST1X_SYNC_UNK_300) | + | // Set magic value in host1x scratch space |
− | // This appears to grant TSEC exclusive access to host1x
| |
| *(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 something in unknown host1x channel 0 sync register (HOST1X_SYNC_UNK_300) | + | // Clear magic value in host1x scratch space |
− | // This appears to revoke TSEC's exclusive access to host1x
| |
| *(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: Stage0, Stage1, Stage2 and key data. | + | 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. |
| | | |
− | == Stage 0 == | + | == Boot == |
− | During this stage key data is loaded and Stage 1 is authenticated, loaded and executed. | + | 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; |
| | | |
− | === Stage 1 loading === | + | === 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; |
− | u32 key_data_buf[0x7C]; | + | 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));
| + | xdst(0, (blob1_hash_addr | crypt_reg_flag)); |
| | | |
− | // Wait for all data loads/stores to finish
| + | // Wait for all data loads/stores to finish |
− | xdwait();
| + | xdwait(); |
| | | |
− | // Jump to Stage1 | + | // Jump to KeygenLdr |
− | u32 stage1_res = exec_stage1(key_buf, key_version, is_blob_dec); | + | 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 (stage1_res == 0) | + | 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; |
| | | |
− | == Stage 1 == | + | == KeygenLdr == |
− | This stage is responsible for reconfiguring the Falcon's crypto co-processor and loading, decrypting, authenticating and executing Stage 2. | + | This stage is responsible for reconfiguring the Falcon's crypto co-processor and loading, decrypting, authenticating and executing [[#Keygen|Keygen]]. |
| | | |
− | === Crypto setup === | + | === 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 Stage2 | + | // Decrypt and load Keygen stage |
− | load_stage2(key_buf, key_version, is_blob_dec); | + | 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; |
| | | |
− | === Stage 2 loading === | + | ==== 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 |
− | keygen(0, 0); | + | gen_usr_key(0, 0); |
| | | |
| // Encrypt buffer with c4 | | // Encrypt buffer with c4 |
− | u32 sig_key[0x10]; | + | 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 Stage2 blob if needed | + | // 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 Stage2's encrypted blob | + | // 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 |
− | keygen(0x01, 0x01); | + | 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 Stage2 | + | // 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 Stage2 | + | // Jump to Keygen |
| if (hovi_key_addr) | | if (hovi_key_addr) |
− | res = exec_stage2(hovi_key_addr, key_version); | + | 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; |
| | | |
− | ==== keygen ==== | + | ===== 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. |
− | u32 seed_buf[0x10]; | + | 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 Stage 1. It takes '''src_addr''', '''src_size''', '''iv_addr''', '''dst_addr''', '''mode''' and '''crypt_ver''' as arguments. | + | 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; |
| | | |
− | == Stage 2 == | + | == Keygen == |
− | This stage is decrypted by Stage 1 using a key generated by encrypting a seed with an hardware secret (see [[TSEC#keygen|keygen]]). | + | 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 Stage 0's code and used across all stages. | + | 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. |
| | | |