Changes

2,262 bytes added ,  20:11, 19 August 2020
no edit summary
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;
6

edits