Changes

Jump to navigation Jump to search
13,172 bytes added ,  19:33, 5 September 2020
m
Fix FALCON_HWCFG MMIO names
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
     u32 data_seg_size = *(u32 *)UC_CAPS;
+
     u32 data_seg_size = *(u32 *)FALCON_HWCFG;
 
     data_seg_size >>= 0x09;
 
     data_seg_size >>= 0x09;
 
     data_seg_size &= 0x1FF;
 
     data_seg_size &= 0x1FF;
Line 240: Line 240:     
=== Main ===
 
=== Main ===
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 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
    // Store the size as the last word
+
        | (size & 0x0000FF00) >> 0x08) << 0x10
     *(u32 *)(buf + 0x0C) = size;
+
        | ((size & 0x00FF0000) >> 0x10) << 0x08
 +
        | (size & 0xFF000000) >> 0x18
 +
    );
 
   
 
   
 
     // This will write buf into crypto register c3  
 
     // This will write buf into crypto register c3  
Line 719: Line 713:  
     crypto_load(0x05, buf);
 
     crypto_load(0x05, buf);
 
   
 
   
 +
    return;
 +
</pre>
 +
 +
===== crypto_store =====
 +
This method takes '''reg''' (a crypto register) and '''buf''' (a 16 bytes buffer) as arguments and loads the supplied buffer into the crypto register.
 +
<pre>
 +
    // The next two xfer instructions will be overridden
 +
    // and target changes from DMA to crypto
 +
    cxset(0x02);
 +
 +
    // Encode the source buffer and the destination register for the xfer
 +
    u32 crypto_xfer_flag = (u32)buf | reg << 0x10;
 +
 +
    // Transfer the supplied buffer to the supplied crypto register
 +
    xdst(crypto_xfer_flag, crypto_xfer_flag);
 +
 +
    // Wait for all data loads/stores to finish
 +
    xdwait();
 +
 +
    return;
 +
</pre>
 +
 +
===== crypto_load =====
 +
This method takes '''reg''' (a crypto register) and '''buf''' (a 16 bytes buffer) as arguments and loads the contents of the supplied register into the supplied buffer.
 +
<pre>
 +
    // The next two xfer instructions will be overridden
 +
    // and target changes from DMA to crypto
 +
    cxset(0x02);
 +
 +
    // Encode the destination buffer and the source register for the xfer
 +
    u32 crypto_xfer_flag = (u32)buf | reg << 0x10;
 +
 +
    // Transfer the contents of the supplied crypto register into the supplied buffer
 +
    xdld(crypto_xfer_flag, crypto_xfer_flag);
 +
 +
    // Wait for all data loads/stores to finish
 +
    xdwait();
 +
 
     return;
 
     return;
 
</pre>
 
</pre>
Line 752: Line 784:  
     ckeyreg(c4);
 
     ckeyreg(c4);
 
   
 
   
     // AES-128-CBC decrypt
+
     if (mode == 0x00)               // AES-128-CBC decrypt
    if (mode == 0x00)
   
     {
 
     {
 
         // Create crypto script with 5 instructions
 
         // Create crypto script with 5 instructions
Line 780: Line 811:  
 
 
 
 
         cxsin($c3);                  // Read 0x10 bytes from crypto stream into c3
 
         cxsin($c3);                  // Read 0x10 bytes from crypto stream into c3
         cxor($c5, $c3);              // XOR c5 with c3 and store in 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 956:     
== 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 *)FALCON_HWCFG2 & 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 995:     
==== 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,010:  
     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,040:  
         // Encrypt the auth signature with c2 and store in c2
 
         // Encrypt the auth signature with c2 and store in c2
 
         csigenc($c2, $c2);
 
         csigenc($c2, $c2);
 +
 +
        // Bind c2 register as the key for enc/dec operations
 +
        ckeyreg($c2);
 
          
 
          
 
         // Encrypt c1 and store in c2
 
         // Encrypt c1 and store in c2
Line 1,017: Line 1,051:  
         cxset(0x02);
 
         cxset(0x02);
 
          
 
          
         // Transfer 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,073:  
         // Encrypt the auth signature with c2 and store in c2
 
         // Encrypt the auth signature with c2 and store in c2
 
         csigenc($c2, $c2);
 
         csigenc($c2, $c2);
       
  −
        // 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,078:  
         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,090:  
     return;
 
     return;
 
</pre>
 
</pre>
 +
 +
==== sor1_set_key ====
 +
This method takes '''key_addr''' (start address of a 16 bytes buffer) as argument and transfers its contents to SOR1 registers.
 +
 +
The implementation is equivalent to [[#tsec_set_key|tsec_set_key]].
    
== SecureBootLdr ==
 
== SecureBootLdr ==
Line 1,243: Line 1,279:     
==== init_secboot ====
 
==== init_secboot ====
 +
This method takes no arguments and is responsible for loading, authenticating and executing [[#SecureBoot|SecureBoot]].
 
<pre>
 
<pre>
 
     // Read the transfer base address from IO space
 
     // Read the transfer base address from IO space
Line 1,281: Line 1,318:  
     $flags.ie0 = 0;
 
     $flags.ie0 = 0;
 
     $flags.ie1 = 0;
 
     $flags.ie1 = 0;
 +
    $flags.ie2 = 0;
 +
 +
    // Jump to the SecureBoot blob's Falcon OS image
 +
    exec_secboot();
 +
 +
    return 0x0F0F0F0F;
 +
</pre>
 +
 +
[8.1.0+] Removed transfer base address setting and added IMEM protection.
 +
<pre>
 +
    // The next xfer instruction will be overridden
 +
    // and target changes from DMA to crypto
 +
    cxset(0x01);
 +
 +
    u32 crypto_reg_flag = 0x00060000;
 +
    u32 blob4_flcn_os_img_hash_addr = 0;
 +
 +
    // Transfer data to crypto register c6
 +
    xdst(0, (blob4_flcn_os_img_hash_addr | crypto_reg_flag));
 +
 +
    // The next xfer instruction will be overridden
 +
    // and target changes from DMA to crypto
 +
    cxset(0x01);
 +
 +
    // Wait for all data loads/stores to finish
 +
    xdwait();
 +
 +
    cmov($c7, $c6);
 +
    cxor($c7, $c7);
 +
 +
    // Set auth_addr to 0x100, auth_size to 0x1D00,
 +
    // bit 16 (use_secret) and bit 17 (is_encrypted)
 +
    $cauth = ((0x02 << 0x10) | (0x01 << 0x10) | (0x1D00 << 0x10) | (0x100 >> 0x08));
 +
 +
    // Clear interrupt flags
 +
    $flags.ie0 = 0;
 +
    $flags.ie1 = 0;
 +
    $flags.ie2 = 0;
 +
 +
    // Fill remaining IMEM with secret pages
 +
    bool use_secret = true;
 +
    memcpy_d2i(0x1E00, 0, 0x2200, 0x1E00, use_secret);
 +
    memcpy_d2i(0x4000, 0, 0x4000, 0x4000, use_secret);
 +
 +
    // Wait for all code loads to finish
 +
    xcwait();
 
   
 
   
 
     // Jump to the SecureBoot blob's Falcon OS image
 
     // Jump to the SecureBoot blob's Falcon OS image
Line 1,387: Line 1,470:  
     set_excp_vec(entry_addr);
 
     set_excp_vec(entry_addr);
   −
     // Fill the top 0x500 bytes in DMEM with a pointer to trap function (just exits)
+
     // Fill the top 0x500 bytes in DMEM with a pointer to trap function (just exits 3 times)
 
     for (int i = 0; i < 0x500; i += 0x04) {
 
     for (int i = 0; i < 0x500; i += 0x04) {
 
         *(u32 *)i = (u32)trap_func();
 
         *(u32 *)i = (u32)trap_func();
Line 1,421: Line 1,504:  
</pre>
 
</pre>
   −
[7.0.0+]
+
[7.0.0+] Many changes were introduced to mitigate and prevent attacks.
 +
<pre>
 +
    // Recover the transfer base address from the stack
 +
    u32 xfer_ext_base_addr = *(u32 *)scratch_data_addr;
 +
 
 +
    // Return the TLB entry that covers the virtual address
 +
    u32 tlb_entry = vtlb(xfer_ext_base_addr);
 +
   
 +
    // Clear Falcon CPU control
 +
    *(u32 *)FALCON_CPUCTL = 0;
 +
   
 +
    // Halt if the external page is marked as secret
 +
    if ((tlb_entry & 0x4000000) != 0)
 +
        exit();
 +
   
 +
    // Read data segment size from IO space
 +
    u32 data_seg_size = *(u32 *)FALCON_HWCFG;
 +
    data_seg_size >>= 0x01;
 +
    data_seg_size &= 0xFF00;
 +
 +
    // Set the stack pointer
 +
    $sp = data_seg_size;
 +
   
 +
    // Fill all DMEM with a pointer to a trap function (just exits 3 times)
 +
    for (int i = 0; i < data_seg_size; i += 0x04) {
 +
        *(u32 *)i = (u32)trap_func();
 +
    }
 +
 
 +
    // Initialize the TRNG and generate random data in DMEM
 +
    init_rnd();
 +
   
 +
    // Issue a randomized delay and return a random value
 +
    u32 rnd_val = rnd_delay(0xFF);
 +
 
 +
    // Enable and test SMMU bypassing in the TFBIF
 +
    tfbif_smmu_cfg(0x01);
 +
 
 +
    // Issue a randomized delay and return a random value
 +
    rnd_val = rnd_delay(0xFF);
 +
 
 +
    // Test SMMU bypassing in the TFBIF
 +
    tfbif_smmu_cfg(0x00);
 +
 
 +
    // Issue a randomized delay and return a random value
 +
    rnd_val = rnd_delay(0xFF);
 +
 
 +
    // Test SMMU bypassing in the TFBIF
 +
    tfbif_smmu_cfg(0x00);
 +
 
 +
    // Fill SE keyslots 12 and 13 with random data
 +
    se_set_keyslot_rnd();
 +
 
 +
    // Test randomized offsets for read/write integrity in MC, FUSE, IRAM and TZRAM
 +
    u32 test_res = test_mc_fuse_iram_tzram();
 +
 
 +
    if (test_res != 0xAAAAAAAA)
 +
    {
 +
        // Fill SE keyslots 12 and 13 with random data
 +
        se_set_keyslot_rnd();
 +
 
 +
        // Clear the entire DMEM region and every crypto register
 +
        clear_dmem_and_crypto();
 +
 
 +
        // Halt 5 times for no good reason
 +
        exit();
 +
        exit();
 +
        exit();
 +
        exit();
 +
        exit();
 +
    }
 +
 
 +
    // Try to detect virtualization by enabling and disabling random CAR devices
 +
    test_res = test_car();
 +
   
 +
    if (test_res != 0xAAAAAAAA)
 +
    {
 +
        // Fill SE keyslots 12 and 13 with random data
 +
        se_set_keyslot_rnd();
 +
 
 +
        // Clear the entire DMEM region and every crypto register
 +
        clear_dmem_and_crypto();
 +
 
 +
        // Halt 5 times for no good reason
 +
        exit();
 +
        exit();
 +
        exit();
 +
        exit();
 +
        exit();
 +
    }
 +
 
 +
    // Test memory transfer integrity
 +
    test_res = test_mem_xfer();
 +
 
 +
    if (test_res != 0xAAAAAAAA)
 +
    {
 +
        // Fill SE keyslots 12 and 13 with random data
 +
        se_set_keyslot_rnd();
 +
 
 +
        // Clear the entire DMEM region and every crypto register
 +
        clear_dmem_and_crypto();
 +
 
 +
        // Halt 5 times for no good reason
 +
        exit();
 +
        exit();
 +
        exit();
 +
        exit();
 +
        exit();
 +
    }
 +
 
 +
    // Set FLOW_MODE_WAITEVENT in FLOW_CTLR_HALT_COP_EVENTS_0
 +
    halt_bpmp();
 +
 
 +
    // Initialize the CCPLEX
 +
    ccplex_init();
 +
 
 +
    // Check if SE is ready
 +
    u32 se_status = check_se_status();
 +
 
 +
    if (se_status != 0)
 +
    {
 +
        // Fill SE keyslots 12 and 13 with random data
 +
        se_set_keyslot_rnd();
 +
 
 +
        // Clear the entire DMEM region and every crypto register
 +
        clear_dmem_and_crypto();
 +
 
 +
        // Halt 5 times for no good reason
 +
        exit();
 +
        exit();
 +
        exit();
 +
        exit();
 +
        exit();
 +
    }
 +
 
 +
    // Load the TSEC key from SOR1 registers into DMEM
 +
    sor1_get_key();
 +
 
 +
    // Initialize CAR registers
 +
    car_init();
 +
 
 +
    // Check certain CAR, PMC and FUSE registers
 +
    test_car_pmc_fuse();
 +
 
 +
    // Try to detect virtualization by enabling and disabling random CAR devices
 +
    test_res = test_car();
 +
   
 +
    if (test_res != 0xAAAAAAAA)
 +
    {
 +
        // Fill SE keyslots 12 and 13 with random data
 +
        se_set_keyslot_rnd();
 +
 
 +
        // Clear the entire DMEM region and every crypto register
 +
        clear_dmem_and_crypto();
 +
 
 +
        // Halt 5 times for no good reason
 +
        exit();
 +
        exit();
 +
        exit();
 +
        exit();
 +
        exit();
 +
    }
 +
 
 +
    // Ensure FUSE_SKU_INFO is 0x83
 +
    test_fuse_sku_info();
 +
 
 +
    // Try to detect virtualization using MC_SMMU_AVPC_ASID and FUSE_ECO_RESERVE_0
 +
    test_smmu_fuse();
 +
 
 +
    // Test MC_IRAM_BOM and MC_IRAM_TOM
 +
    test_res = test_mc_iram_aperture();
 +
 
 +
    if (test_res != 0xAAAAAAAA)
 +
    {
 +
        // Fill SE keyslots 12 and 13 with random data
 +
        se_set_keyslot_rnd();
 +
 
 +
        // Clear the entire DMEM region and every crypto register
 +
        clear_dmem_and_crypto();
 +
 
 +
        // Halt 5 times for no good reason
 +
        exit();
 +
        exit();
 +
        exit();
 +
        exit();
 +
        exit();
 +
    }
 +
 
 +
    // Check certain CAR, PMC and FUSE registers
 +
    test_car_pmc_fuse();
 +
 
 +
    // Test memory transfer integrity
 +
    test_res = test_mem_xfer();
 +
 
 +
    if (test_res != 0xAAAAAAAA)
 +
    {
 +
        // Fill SE keyslots 12 and 13 with random data
 +
        se_set_keyslot_rnd();
 +
 
 +
        // Clear the entire DMEM region and every crypto register
 +
        clear_dmem_and_crypto();
 +
 
 +
        // Halt 5 times for no good reason
 +
        exit();
 +
        exit();
 +
        exit();
 +
        exit();
 +
        exit();
 +
    }
 +
 
 +
    // Try to detect virtualization using MC_SMMU_AVPC_ASID and FUSE_ECO_RESERVE_0
 +
    test_smmu_fuse();
 +
 
 +
    // Test MC_IRAM_BOM and MC_IRAM_TOM
 +
    test_res = test_mc_iram_aperture();
 +
 
 +
    if (test_res != 0xAAAAAAAA)
 +
    {
 +
        // Fill SE keyslots 12 and 13 with random data
 +
        se_set_keyslot_rnd();
 +
 
 +
        // Clear the entire DMEM region and every crypto register
 +
        clear_dmem_and_crypto();
 +
 
 +
        // Halt 5 times for no good reason
 +
        exit();
 +
        exit();
 +
        exit();
 +
        exit();
 +
        exit();
 +
    }
 +
 
 +
    // Test SMMU bypassing in the TFBIF
 +
    tfbif_smmu_cfg(0x00);
 +
 
 +
    // Decrypt Package1
 +
    decrypt_pk11();
 +
 
 +
    // Write TSEC root key to SE keyslot 0x0D
 +
    se_set_keyslot_13();
 +
 
 +
    // Write TSEC key to SE keyslot 0x0C
 +
    se_set_keyslot_12();
 +
 
 +
    // Clear the cauth signature
 +
    csigclr();
 +
 
 +
    // Check certain CAR, PMC and FUSE registers
 +
    test_car_pmc_fuse();
 +
 
 +
    // Test memory transfer integrity
 +
    test_res = test_mem_xfer();
 +
 
 +
    if (test_res != 0xAAAAAAAA)
 +
    {
 +
        // Fill SE keyslots 12 and 13 with random data
 +
        se_set_keyslot_rnd();
 +
 
 +
        // Clear the entire DMEM region and every crypto register
 +
        clear_dmem_and_crypto();
 +
 
 +
        // Halt 5 times for no good reason
 +
        exit();
 +
        exit();
 +
        exit();
 +
        exit();
 +
        exit();
 +
    }
 +
   
 +
    // Try to detect virtualization using MC_SMMU_AVPC_ASID and FUSE_ECO_RESERVE_0
 +
    test_smmu_fuse();
 +
 
 +
    // Test randomized offsets for read/write integrity in MC, FUSE, IRAM and TZRAM
 +
    test_res = test_mc_fuse_iram_tzram();
 +
 
 +
    if (test_res != 0xAAAAAAAA)
 +
    {
 +
        // Fill SE keyslots 12 and 13 with random data
 +
        se_set_keyslot_rnd();
 +
 
 +
        // Clear the entire DMEM region and every crypto register
 +
        clear_dmem_and_crypto();
 +
 
 +
        // Halt 5 times for no good reason
 +
        exit();
 +
        exit();
 +
        exit();
 +
        exit();
 +
        exit();
 +
    }
 +
 
 +
    // Test MC_IRAM_BOM and MC_IRAM_TOM
 +
    test_res = test_mc_iram_aperture();
 +
 
 +
    if (test_res != 0xAAAAAAAA)
 +
    {
 +
        // Fill SE keyslots 12 and 13 with random data
 +
        se_set_keyslot_rnd();
 +
 
 +
        // Clear the entire DMEM region and every crypto register
 +
        clear_dmem_and_crypto();
 +
 
 +
        // Halt 5 times for no good reason
 +
        exit();
 +
        exit();
 +
        exit();
 +
        exit();
 +
        exit();
 +
    }
 +
 
 +
    // Test SMMU bypassing in the TFBIF
 +
    tfbif_smmu_cfg(0x00);
 +
 
 +
    // Parse Package1 header and return entry address
 +
    u32 entry_addr = parse_pk11();
 +
 
 +
    // Set the exception vectors
 +
    set_excp_vec(entry_addr);
 +
 
 +
    // Fill the top 0x500 bytes in DMEM with a pointer to trap function (just exits)
 +
    for (int i = 0; i < 0x500; i += 0x04) {
 +
        *(u32 *)i = (u32)trap_func();
 +
    }
 +
 
 +
    // Clear all crypto registers
 +
    cxor($c0, $c0);
 +
    cxor($c1, $c1);
 +
    cxor($c2, $c2);
 +
    cxor($c3, $c3);
 +
    cxor($c4, $c4);
 +
    cxor($c5, $c5);
 +
    cxor($c6, $c6);
 +
    cxor($c7, $c7);
 +
   
 +
    // Take SCP out of lockdown
 +
    unlock_scp();
 +
 
 +
    // Test memory transfer integrity
 +
    test_res = test_mem_xfer();
 +
 
 +
    if (test_res != 0xAAAAAAAA)
 +
    {
 +
        // Fill SE keyslots 12 and 13 with random data
 +
        se_set_keyslot_rnd();
 +
 
 +
        // Clear the entire DMEM region and every crypto register
 +
        clear_dmem_and_crypto();
 +
 
 +
        // Halt 5 times for no good reason
 +
        exit();
 +
        exit();
 +
        exit();
 +
        exit();
 +
        exit();
 +
    }
 +
 
 +
    // Try to detect virtualization using MC_SMMU_AVPC_ASID and FUSE_ECO_RESERVE_0
 +
    test_smmu_fuse();
 +
 
 +
    // Test MC_IRAM_BOM and MC_IRAM_TOM
 +
    test_res = test_mc_iram_aperture();
 +
 
 +
    if (test_res != 0xAAAAAAAA)
 +
    {
 +
        // Fill SE keyslots 12 and 13 with random data
 +
        se_set_keyslot_rnd();
 +
 
 +
        // Clear the entire DMEM region and every crypto register
 +
        clear_dmem_and_crypto();
 +
 
 +
        // Halt 5 times for no good reason
 +
        exit();
 +
        exit();
 +
        exit();
 +
        exit();
 +
        exit();
 +
    }
 +
 
 +
    // Test SMMU bypassing in the TFBIF
 +
    tfbif_smmu_cfg(0x00);
 +
 
 +
    // Clear FLOW_CTLR_HALT_COP_EVENTS_0
 +
    resume_bpmp();
 +
 
 +
    // Clear the entire DMEM region and every crypto register
 +
    clear_dmem_and_crypto();
 +
       
 +
    // Halt 5 times for no good reason
 +
    exit();
 +
    exit();
 +
    exit();
 +
    exit();
 +
    exit();
 +
 
 +
    return;
 +
</pre>
 +
 
 +
[8.1.0+] Key derivation algorithm was changed. Very minor changes were introduced to mitigate and prevent attacks.
    
== Key data ==
 
== Key data ==
6

edits

Navigation menu