Line 571: |
Line 571: |
| | | |
| == Device key generation == | | == Device key generation == |
− | The Falcon device key is generated by reading SOR registers modified by Falcon. | + | 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 something in unknown host1x channel 0 sync register (HOST1X_SYNC_UNK_300) |
| // This appears to revoke TSEC's exclusive access to host1x | | // This appears to revoke TSEC's exclusive access to host1x |
| *(u32 *)0x50003300 = 0; | | *(u32 *)0x50003300 = 0; |
| | | |
− | // Generate Falcon device key | + | // Read TSEC device key |
− | u32 falcon_device_key[4]; | + | u32 tsec_device_key[4]; |
− | falcon_device_key[0] = *(u32 *)NV_SOR_DP_HDCP_BKSV_LSB; | + | tsec_device_key[0] = *(u32 *)NV_SOR_DP_HDCP_BKSV_LSB; |
− | falcon_device_key[1] = *(u32 *)NV_SOR_TMDS_HDCP_BKSV_LSB; | + | tsec_device_key[1] = *(u32 *)NV_SOR_TMDS_HDCP_BKSV_LSB; |
− | falcon_device_key[2] = *(u32 *)NV_SOR_TMDS_HDCP_CN_MSB; | + | tsec_device_key[2] = *(u32 *)NV_SOR_TMDS_HDCP_CN_MSB; |
− | falcon_device_key[3] = *(u32 *)NV_SOR_TMDS_HDCP_CN_LSB; | + | tsec_device_key[3] = *(u32 *)NV_SOR_TMDS_HDCP_CN_LSB; |
| | | |
− | // Clear SOR registers | + | // Clear SOR1 registers |
| *(u32 *)NV_SOR_DP_HDCP_BKSV_LSB = 0; | | *(u32 *)NV_SOR_DP_HDCP_BKSV_LSB = 0; |
| *(u32 *)NV_SOR_TMDS_HDCP_BKSV_LSB = 0; | | *(u32 *)NV_SOR_TMDS_HDCP_BKSV_LSB = 0; |
Line 592: |
Line 592: |
| out_size = 0x10; | | out_size = 0x10; |
| | | |
− | // Copy back the Falcon key | + | // Copy back the TSEC device key |
− | memcpy(out_buf, falcon_device_key, out_size); | + | memcpy(out_buf, tsec_device_key, out_size); |
| | | |
| == Cleanup == | | == Cleanup == |
Line 744: |
Line 744: |
| } | | } |
| | | |
− | // Write Falcon device key to registers | + | // Write TSEC device key to registers |
| set_device_key(key_data_buf); | | set_device_key(key_data_buf); |
| | | |
| return boot_res; | | return boot_res; |
| + | |
| + | ==== set_device_key ==== |
| + | This method takes '''key_data_buf''' as argument and writes the TSEC key to SOR1 registers. |
| + | // This is TSEC_MMIO + 0x1000 + (0x1C300 / 0x40) |
| + | *(u32 *)TSEC_DMA_UNK = 0xFFF; |
| + | |
| + | // Read the key's words |
| + | u32 key0 = *(u32 *)(key_data_buf + 0x00); |
| + | u32 key1 = *(u32 *)(key_data_buf + 0x04); |
| + | u32 key2 = *(u32 *)(key_data_buf + 0x08); |
| + | u32 key3 = *(u32 *)(key_data_buf + 0x0C); |
| + | |
| + | u32 result = 0; |
| + | |
| + | // Write to SOR1 register |
| + | result = tsec_dma_write(NV_SOR_DP_HDCP_BKSV_LSB, key0); |
| + | |
| + | // Failed to write |
| + | if (result) |
| + | return result; |
| + | |
| + | // Write to SOR1 register |
| + | result = tsec_dma_write(NV_SOR_TMDS_HDCP_BKSV_LSB, key1); |
| + | |
| + | // Failed to write |
| + | if (result) |
| + | return result; |
| + | |
| + | // Write to SOR1 register |
| + | result = tsec_dma_write(NV_SOR_TMDS_HDCP_CN_MSB, key2); |
| + | |
| + | // Failed to write |
| + | if (result) |
| + | return result; |
| + | |
| + | // Write to SOR1 register |
| + | result = tsec_dma_write(NV_SOR_TMDS_HDCP_CN_LSB, key3); |
| + | |
| + | // Failed to write |
| + | if (result) |
| + | return result; |
| + | |
| + | return result; |
| + | |
| + | ===== tsec_dma_write ===== |
| + | This method takes '''addr''' and '''value''' as arguments and performs a DMA write using TSEC MMIO. |
| + | u32 result = 0; |
| + | |
| + | // Wait for TSEC DMA engine |
| + | // This waits for bit 0x0C in TSEC_DMA_CMD to be 0 |
| + | result = wait_tsec_dma(); |
| + | |
| + | // Wait failed |
| + | if (result) |
| + | return 1; |
| + | |
| + | // Set the destination address |
| + | // This is TSEC_MMIO + 0x1000 + (0x1C100 / 0x40) |
| + | *(u32 *)TSEC_DMA_ADDR = addr; |
| + | |
| + | // Set the value |
| + | // This is TSEC_MMIO + 0x1000 + (0x1C200 / 0x40) |
| + | *(u32 *)TSEC_DMA_VAL = value; |
| + | |
| + | // Start transfer? |
| + | // This is TSEC_MMIO + 0x1000 + (0x1C000 / 0x40) |
| + | *(u32 *)TSEC_DMA_CMD = 0x800000F2; |
| + | |
| + | // Wait for TSEC DMA engine |
| + | // This waits for bit 0x0C in TSEC_DMA_CMD to be 0 |
| + | result = wait_tsec_dma(); |
| + | |
| + | // Wait failed |
| + | if (result) |
| + | return 1; |
| + | |
| + | return 0; |
| | | |
| == Stage 1 == | | == Stage 1 == |
Line 763: |
Line 840: |
| | | |
| // fuc5 crypt cauth instruction | | // fuc5 crypt cauth instruction |
− | // Clear auth_addr | + | // Clear bit 0x13 in cauth |
− | cauth(cauth_old & 0x7FFFF); | + | cauth(cauth_old & ~(1 << 0x13)); |
| | | |
| // Set the target port for memory transfers | | // Set the target port for memory transfers |
− | // Target will now be 0 (DMA)
| |
| xtargets(0); | | xtargets(0); |
| | | |
Line 872: |
Line 948: |
| | | |
| // Compare the hashes | | // Compare the hashes |
− | if (memcmp(sig_key, key_buf + 0x10, 0x10)) | + | if (memcmp(dst_addr, key_buf + 0x10, 0x10)) |
| { | | { |
| res = 0xDEADBEEF; | | res = 0xDEADBEEF; |
Line 975: |
Line 1,051: |
| get_seed(seed_buf, type); | | get_seed(seed_buf, type); |
| | | |
− | // This will write the seed into crypt register c0 | + | // This will write the seed into crypto register c0 |
| crypt_store(0, seed_buf); | | crypt_store(0, seed_buf); |
| | | |
| // fuc5 csecret instruction | | // fuc5 csecret instruction |
− | // Load selected secret into crypt register c1 | + | // Load selected secret into crypto register c1 |
| csecret(c1, 0x26); | | csecret(c1, 0x26); |
| | | |
| // fuc5 ckeyreg instruction | | // fuc5 ckeyreg instruction |
− | // Binds c1 register as the key for enc/dec operations | + | // Bind c1 register as the key for enc/dec operations |
| ckeyreg(c1); | | ckeyreg(c1); |
| | | |
| // fuc5 cenc instruction | | // fuc5 cenc instruction |
− | // Encrypts seed_buf (in c0) using keyreg value as key into c1 | + | // Encrypt seed_buf (in c0) using keyreg value as key into c1 |
| cenc(c1, c0); | | cenc(c1, c0); |
| | | |
| // fuc5 csigenc instruction | | // fuc5 csigenc instruction |
− | // Encrypt code sig with c1 register as key | + | // Encrypt c1 register with the auth signature stored in c6 |
| csigenc(c1, c1); | | csigenc(c1, c1); |
| | | |
Line 1,016: |
Line 1,092: |
| *(u32 *)(buf + 0x0C) = size; | | *(u32 *)(buf + 0x0C) = size; |
| | | |
− | // This will write buf into crypt register c3 | + | // This will write buf into crypto register c3 |
| crypt_store(0x03, buf); | | crypt_store(0x03, buf); |
| | | |
| // fuc5 ckeyreg instruction | | // fuc5 ckeyreg instruction |
− | // Binds c4 register (from keygen) as the key for enc/dec operations | + | // Bind c4 register (from keygen) as the key for enc/dec operations |
| ckeyreg(c4); | | ckeyreg(c4); |
| | | |
| // fuc5 cenc instruction | | // fuc5 cenc instruction |
− | // Encrypts buf (in c3) using keyreg value as key into c5 | + | // Encrypt buf (in c3) using keyreg value as key into c5 |
| cenc(c5, c3); | | cenc(c5, c3); |
| | | |
− | // This will read into buf from crypt register c5 | + | // This will read into buf from crypto register c5 |
| crypt_load(0x05, buf); | | crypt_load(0x05, buf); |
| | | |
Line 1,049: |
Line 1,125: |
| if (iv_addr) | | if (iv_addr) |
| { | | { |
− | // This will write the iv_addr into crypt register c5 | + | // This will write the iv_addr into crypto register c5 |
| crypt_store(0x05, iv_addr); | | crypt_store(0x05, iv_addr); |
| } | | } |
Line 1,061: |
Line 1,137: |
| ckeyreg(c4); | | ckeyreg(c4); |
| | | |
− | // AES-128-ECB? decrypt | + | // AES-128-ECB decrypt |
| if (mode == 0x00) | | if (mode == 0x00) |
| { | | { |
Line 1,073: |
Line 1,149: |
| cmov(c5, c3); // Move c3 into c5 | | cmov(c5, c3); // Move c3 into c5 |
| } | | } |
− | else if (mode == 0x01) // AES-128-ECB? encrypt | + | else if (mode == 0x01) // AES-128-ECB encrypt |
| { | | { |
| // Create crypto script with 4 instructions | | // Create crypto script with 4 instructions |
Line 1,092: |
Line 1,168: |
| cenc(c5, c5); // Encrypt from c5 into c5 | | cenc(c5, c5); // Encrypt from c5 into c5 |
| } | | } |
− | else if (mode == 0x03) // AES-128-ECB? decrypt (no IV) | + | else if (mode == 0x03) // AES-128-ECB decrypt (no IV) |
| { | | { |
| // Create crypto script with 3 instructions | | // Create crypto script with 3 instructions |
Line 1,101: |
Line 1,177: |
| cxsout(c5); // Write 0x10 bytes into crypto stream from c5 | | cxsout(c5); // Write 0x10 bytes into crypto stream from c5 |
| } | | } |
− | else if (mode == 0x04) // AES-128-ECB? encrypt (no IV) | + | else if (mode == 0x04) // AES-128-ECB encrypt (no IV) |
| { | | { |
| // Create crypto script with 3 instructions | | // Create crypto script with 3 instructions |
Line 1,176: |
Line 1,252: |
| // fuc5 crypt cxset instruction | | // fuc5 crypt cxset instruction |
| // The next xfer instruction will be overridden | | // The next xfer instruction will be overridden |
− | // and target changes from DMA to crypto | + | // and target changes from DMA to crypto input/output stream |
| if (crypt_ver == 0x01) | | if (crypt_ver == 0x01) |
− | cxset(0xA1); // Flag 0xA0 is unknown | + | cxset(0xA1); // Flag 0xA0 is (0x80 | 0x20) |
| else | | else |
− | cxset(0x21); // Flag 0x20 is unknown | + | cxset(0x21); // Flag 0x20 is external mem <-> crypto input/output stream |
| | | |
− | // Transfer data into the selected crypto register | + | // Transfer data into the crypto input/output stream |
| xdst(crypt_xfer_src, crypt_xfer_src); | | xdst(crypt_xfer_src, crypt_xfer_src); |
| | | |
Line 1,190: |
Line 1,266: |
| // fuc5 crypt cxset instruction | | // fuc5 crypt cxset instruction |
| // The next xfer instruction will be overridden | | // The next xfer instruction will be overridden |
− | // and target changes from DMA to crypto | + | // and target changes from DMA to crypto input/output stream |
| if (crypt_ver == 0x01) | | if (crypt_ver == 0x01) |
− | cxset(0xA1); // Flag 0xA0 is unknown | + | cxset(0xA1); // Flag 0xA0 is (0x80 | 0x20) |
| else | | else |
− | cxset(0x21); // Flag 0x20 is unknown | + | cxset(0x21); // Flag 0x20 is external mem <-> crypto input/output stream |
| | | |
| // Wait for all data loads/stores to finish | | // Wait for all data loads/stores to finish |
Line 1,203: |
Line 1,279: |
| // fuc5 crypt cxset instruction | | // fuc5 crypt cxset instruction |
| // The next 2 xfer instructions will be overridden | | // The next 2 xfer instructions will be overridden |
− | // and target changes from DMA to crypto | + | // and target changes from DMA to crypto input/output stream |
| if (crypt_ver == 0x01) | | if (crypt_ver == 0x01) |
− | cxset(0xA2); // Flag 0xA0 is unknown | + | cxset(0xA2); // Flag 0xA0 is (0x80 | 0x20) |
| else | | else |
− | cxset(0x22); // Flag 0x20 is unknown | + | cxset(0x22); // Flag 0x20 is external mem <-> crypto input/output stream |
| | | |
− | // Transfer data from the selected crypto register | + | // Transfer data from the crypto input/output stream |
− | xdst(crypt_xfer_dst, crypt_xfer_dst); | + | xdld(crypt_xfer_dst, crypt_xfer_dst); |
| | | |
| // Wait for all data loads/stores to finish | | // Wait for all data loads/stores to finish |
Line 1,229: |
Line 1,305: |
| if (mode == 0x02) | | if (mode == 0x02) |
| { | | { |
− | // This will read into dst_addr from crypt register c5 | + | // This will read into dst_addr from crypto register c5 |
| crypt_load(0x05, dst_addr); | | crypt_load(0x05, dst_addr); |
| } | | } |