Line 227: |
Line 227: |
| | | |
| === Initialization === | | === Initialization === |
− | Falcon sets up it's own stack pointer.
| + | 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 |
Line 240: |
Line 240: |
| | | |
| === Main === | | === Main === |
− | Falcon reads the [[#Key data|key data]] and then authenticates, loads and executes [[#KeygenLdr|KeygenLdr]] which sets the TSEC key.
| + | 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 boot_base_addr = 0; | + | u32 dmem_start = 0; |
| u8 key_data_buf[0x7C]; | | u8 key_data_buf[0x7C]; |
| | | |
− | // Read the key data from memory | + | // 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 boot base | + | // 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(boot_base_addr, blob1_addr, blob1_size); | + | memcpy_i2d(dmem_start, blob1_addr, blob1_size); |
| | | |
− | // Upload the next code segment into Falcon's CODE region | + | // 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, boot_base_addr, blob1_size, blob1_virt_addr, use_secret); | + | memcpy_d2i(blob1_virt_addr, dmem_start, blob1_size, blob1_virt_addr, use_secret); |
| | | |
| u32 boot_res = 0; | | u32 boot_res = 0; |
− | bool is_done = false;
| |
| u32 time = 0; | | u32 time = 0; |
| bool is_blob_dec = false; | | bool is_blob_dec = false; |
| | | |
− | while (!is_done) | + | while (true) |
| { | | { |
− | if (time > 4000000) | + | 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) | (0x300 >> 0x08)); | + | $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) |
− | is_done = true; | + | break; |
| } | | } |
| | | |
Line 342: |
Line 341: |
| </pre> | | </pre> |
| | | |
− | [6.2.0+] Falcon reads the [[#Key data|key data]] and jumps to [[#SecureBootLdr|SecureBootLdr]]. | + | [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 from memory | + | // 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 = ((((blob0_size + blob1_size) + 0x100) + blob2_size) + blob4_size); | + | 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 register | + | // 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); |
− |
| |
− | // Failed to write
| |
| if (result) | | if (result) |
| return result; | | return result; |
| | | |
− | // Write to SOR1 register | + | // 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); |
− |
| |
− | // Failed to write
| |
| if (result) | | if (result) |
| return result; | | return result; |
| | | |
− | // Write to SOR1 register | + | // 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); |
− |
| |
− | // Failed to write
| |
| if (result) | | if (result) |
| return result; | | return result; |
| | | |
− | // Write to SOR1 register | + | // 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); |
− |
| |
− | // Failed to write
| |
| 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 &= 0xEFFFF; | + | *(u32 *)TSEC_TEGRA_CTL &= 0xFFFEFFFF; |
| | | |
| // Set TSEC_SCP_CTL_PKEY_REQUEST_RELOAD | | // Set TSEC_SCP_CTL_PKEY_REQUEST_RELOAD |
Line 505: |
Line 496: |
| | | |
| // Read data segment size from IO space | | // Read data segment size from IO space |
− | u32 data_seg_size = *(u32 *)FALCON_HWCFG; | + | u32 data_seg_size = *(u32 *)UC_CAPS; |
| data_seg_size >>= 0x09; | | data_seg_size >>= 0x09; |
| data_seg_size &= 0x1FF; | | data_seg_size &= 0x1FF; |
Line 514: |
Line 505: |
| exit(); | | exit(); |
| | | |
− | // Decrypt and load Keygen stage | + | // 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 boot_base_addr = 0; | + | 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 again | + | // Load blob0 code to the start of the data segment |
− | memcpy_i2d(boot_base_addr, blob0_addr, blob0_size); | + | 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 = boot_base_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 hashes | + | // 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 boot_base_addr = 0;
| |
| u32 blob2_virt_addr = blob0_size + blob1_size; | | u32 blob2_virt_addr = blob0_size + blob1_size; |
− | u32 blob2_addr = blob2_virt_addr + 0x100; | + | u32 blob2_phys_addr = blob2_virt_addr + 0x100; |
| | | |
− | // Read Keygen encrypted blob | + | // Read the encrypted Keygen blob |
− | memcpy_i2d(boot_base_addr, blob2_addr, blob2_size); | + | 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 = boot_base_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 = boot_base_addr; | + | u32 dst_addr = dmem_start; |
− | u32 mode = 0; // AES-128-ECB | + | 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 the next code segment into Falcon's CODE region | + | // Upload decrypted Keygen into Falcon's code segment |
| bool use_secret = true; | | bool use_secret = true; |
− | memcpy_d2i(blob2_virt_addr, boot_base_addr, blob2_size, blob2_virt_addr, use_secret); | + | 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(boot_base_addr, 0, blob2_size); | + | 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 data to crypto register c6 | + | // 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 == 0: "CODE_SIG_01" + null padding |
− | Type 1: "CODE_ENC_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_buffer ===== | + | ===== 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 |
− | hswap(size);
| + | *(u32 *)(buf + 0x0C) = ( |
− |
| + | ((size & 0x000000FF) << 0x08 | (size & 0x0000FF00) >> 0x08) << 0x10 |
− | // Store the size as the last word
| + | | ((size & 0x00FF0000) >> 0x10) << 0x08 |
− | *(u32 *)(buf + 0x0C) = size; | + | | (size & 0xFF000000) >> 0x18 |
| + | ); |
| | | |
| // This will write buf into crypto register c3 | | // This will write buf into crypto register c3 |
Line 719: |
Line 712: |
| 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 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 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 783: |
| ckeyreg(c4); | | ckeyreg(c4); |
| | | |
− | // AES-128-CBC decrypt | + | if (mode == 0x00) // AES-128-CBC decrypt |
− | if (mode == 0x00)
| |
| { | | { |
| // Create crypto script with 5 instructions | | // Create crypto script with 5 instructions |
Line 780: |
Line 810: |
| | | |
| 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 c3 | + | 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 955: |
| | | |
| == 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 key. | + | 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 flcn_version = *(u32 *)FALCON_HWCFG2 & 0x0F; | + | u32 falcon_rev = *(u32 *)UC_CAPS2 & 0x0F; |
| | | |
− | // Falcon hardware version must be 5 | + | // Falcon hardware revision must be 5 |
− | if (flcn_version != 0x05) | + | if (falcon_rev != 0x05) |
| exit(); | | exit(); |
| | | |
Line 964: |
Line 994: |
| | | |
| ==== gen_tsec_key ==== | | ==== gen_tsec_key ==== |
− | This is the method responsible for generating the final TSEC key. It takes '''key_addr''' and '''key_type''' as arguments. | + | 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,009: |
| cxset(0x02); | | cxset(0x02); |
| | | |
− | // Transfer data to crypto register c0 | + | // 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,039: |
| // 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,050: |
| cxset(0x02); | | cxset(0x02); |
| | | |
− | // Transfer data from crypto register c2 | + | // 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,072: |
| // 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); |
− |
| |
− | // Encrypt c1 and store in c2
| |
− | cenc($c2, $c1);
| |
| | | |
| // The next 0x02 xfer instructions will be overridden | | // The next 0x02 xfer instructions will be overridden |
Line 1,047: |
Line 1,077: |
| cxset(0x02); | | cxset(0x02); |
| | | |
− | // Transfer data from crypto register c2 | + | // 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,089: |
| 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,226: |
Line 1,261: |
| <pre> | | <pre> |
| // Read data segment size from IO space | | // Read data segment size from IO space |
− | u32 data_seg_size = *(u32 *)FALCON_HWCFG; | + | u32 data_seg_size = *(u32 *)UC_CAPS; |
| data_seg_size >>= 0x01; | | data_seg_size >>= 0x01; |
| data_seg_size &= 0xFF00; | | data_seg_size &= 0xFF00; |
Line 1,243: |
Line 1,278: |
| | | |
| ==== 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,317: |
| $flags.ie0 = 0; | | $flags.ie0 = 0; |
| $flags.ie1 = 0; | | $flags.ie1 = 0; |
| + | $flags.ie2 = 0; |
| | | |
| // Jump to the SecureBoot blob's Falcon OS image | | // Jump to the SecureBoot blob's Falcon OS image |
Line 1,317: |
Line 1,354: |
| $flags.ie0 = 0; | | $flags.ie0 = 0; |
| $flags.ie1 = 0; | | $flags.ie1 = 0; |
| + | $flags.ie2 = 0; |
| | | |
| // Fill remaining IMEM with secret pages | | // Fill remaining IMEM with secret pages |
Line 1,349: |
Line 1,387: |
| | | |
| // Read data segment size from IO space | | // Read data segment size from IO space |
− | u32 data_seg_size = *(u32 *)FALCON_HWCFG; | + | u32 data_seg_size = *(u32 *)UC_CAPS; |
| data_seg_size >>= 0x01; | | data_seg_size >>= 0x01; |
| data_seg_size &= 0xFF00; | | data_seg_size &= 0xFF00; |
Line 1,481: |
Line 1,519: |
| | | |
| // Read data segment size from IO space | | // Read data segment size from IO space |
− | u32 data_seg_size = *(u32 *)FALCON_HWCFG; | + | u32 data_seg_size = *(u32 *)UC_CAPS; |
| data_seg_size >>= 0x01; | | data_seg_size >>= 0x01; |
| data_seg_size &= 0xFF00; | | data_seg_size &= 0xFF00; |