Line 1,228: |
Line 1,228: |
| | | |
| === Driver === | | === Driver === |
− | Same registers as in the Erista's fuse [[#Driver|driver]].
| + | {| class="wikitable" border="1" |
| + | ! Name |
| + | ! Address |
| + | |- |
| + | | [[#FUSE_FUSECTRL|FUSE_FUSECTRL]] |
| + | | 0x7000F800 |
| + | |- |
| + | | [[#FUSE_FUSEADDR|FUSE_FUSEADDR]] |
| + | | 0x7000F804 |
| + | |- |
| + | | [[#FUSE_FUSERDATA|FUSE_FUSERDATA]] |
| + | | 0x7000F808 |
| + | |- |
| + | | [[#FUSE_FUSEWDATA|FUSE_FUSEWDATA]] |
| + | | 0x7000F80C |
| + | |- |
| + | | [[#FUSE_FUSETIME_RD1|FUSE_FUSETIME_RD1]] |
| + | | 0x7000F810 |
| + | |- |
| + | | [[#FUSE_FUSETIME_RD2|FUSE_FUSETIME_RD2]] |
| + | | 0x7000F814 |
| + | |- |
| + | | [[#FUSE_FUSETIME_PGM1|FUSE_FUSETIME_PGM1]] |
| + | | 0x7000F818 |
| + | |- |
| + | | [[#FUSE_FUSETIME_PGM2|FUSE_FUSETIME_PGM2]] |
| + | | 0x7000F81C |
| + | |- |
| + | | [[#FUSE_PRIV2INTFC_START|FUSE_PRIV2INTFC_START]] |
| + | | 0x7000F820 |
| + | |- |
| + | | [[#FUSE_FUSEBYPASS|FUSE_FUSEBYPASS]] |
| + | | 0x7000F824 |
| + | |- |
| + | | [[#FUSE_PRIVATEKEYDISABLE|FUSE_PRIVATEKEYDISABLE]] |
| + | | 0x7000F828 |
| + | |- |
| + | | [[#FUSE_DISABLEREGPROGRAM|FUSE_DISABLEREGPROGRAM]] |
| + | | 0x7000F82C |
| + | |- |
| + | | [[#FUSE_WRITE_ACCESS_SW|FUSE_WRITE_ACCESS_SW]] |
| + | | 0x7000F830 |
| + | |- |
| + | | [[#FUSE_PRIV2RESHIFT|FUSE_PRIV2RESHIFT]] |
| + | | 0x7000F83C |
| + | |- |
| + | | [[#FUSE_FUSETIME_RD3|FUSE_FUSETIME_RD3]] |
| + | | 0x7000F84C |
| + | |- |
| + | | [[#FUSE_SPARE_ADDR_START|FUSE_SPARE_ADDR_START]] |
| + | | 0x7000F860 |
| + | |- |
| + | | [[#FUSE_PRIVATE_KEY0_NONZERO|FUSE_PRIVATE_KEY0_NONZERO]] |
| + | | 0x7000F880 |
| + | |- |
| + | | [[#FUSE_PRIVATE_KEY1_NONZERO|FUSE_PRIVATE_KEY1_NONZERO]] |
| + | | 0x7000F884 |
| + | |- |
| + | | [[#FUSE_PRIVATE_KEY2_NONZERO|FUSE_PRIVATE_KEY2_NONZERO]] |
| + | | 0x7000F888 |
| + | |- |
| + | | [[#FUSE_PRIVATE_KEY3_NONZERO|FUSE_PRIVATE_KEY3_NONZERO]] |
| + | | 0x7000F88C |
| + | |- |
| + | | [[#FUSE_PRIVATE_KEY4_NONZERO|FUSE_PRIVATE_KEY4_NONZERO]] |
| + | | 0x7000F890 |
| + | |} |
| + | |
| + | ==== FUSE_SPARE_ADDR_START ==== |
| + | {| class="wikitable" border="1" |
| + | ! Bits |
| + | ! Description |
| + | |- |
| + | | 0-31 |
| + | | FUSE_SPARE_ADDR_START_DATA |
| + | |} |
| + | |
| + | Returns the offset of the spare bit fuse registers (always 0x380). |
| | | |
| === Cache === | | === Cache === |
Line 1,301: |
Line 1,378: |
| | 0x7000F8EC | | | 0x7000F8EC |
| |- | | |- |
− | | | + | | FUSE_OPT_RAM_RTSEL_TSMCSP_PO4SVT |
| | 0x7000F8F0 | | | 0x7000F8F0 |
| |- | | |- |
− | | | + | | FUSE_OPT_RAM_WTSEL_TSMCSP_PO4SVT |
| | 0x7000F8F4 | | | 0x7000F8F4 |
| |- | | |- |
− | | | + | | FUSE_OPT_RAM_RTSEL_TSMCPDP_PO4SVT |
| | 0x7000F8F8 | | | 0x7000F8F8 |
| |- | | |- |
− | | | + | | FUSE_OPT_RAM_MTSEL_TSMCPDP_PO4SVT |
| | 0x7000F8FC | | | 0x7000F8FC |
| |- | | |- |
Line 1,586: |
Line 1,663: |
| | 0x7000FA68 | | | 0x7000FA68 |
| |- | | |- |
− | | | + | | FUSE_OPT_RAM_RTSEL_TSMCSP_PO4HVT |
| | 0x7000FA6C | | | 0x7000FA6C |
| |- | | |- |
− | | | + | | FUSE_OPT_RAM_WTSEL_TSMCSP_PO4HVT |
| | 0x7000FA70 | | | 0x7000FA70 |
| |- | | |- |
− | | | + | | FUSE_OPT_RAM_RTSEL_TSMCPDP_PO4HVT |
| | 0x7000FA74 | | | 0x7000FA74 |
| |- | | |- |
− | | | + | | FUSE_OPT_RAM_MTSEL_TSMCPDP_PO4HVT |
| | 0x7000FA78 | | | 0x7000FA78 |
| |- | | |- |
Line 1,628: |
Line 1,705: |
| | 0x7000FAA8 | | | 0x7000FAA8 |
| |- | | |- |
− | | | + | | FUSE_OPT_RAM_WTSEL_TSMCPDP_PO4SVT |
| | 0x7000FAB0 | | | 0x7000FAB0 |
| |- | | |- |
− | | | + | | FUSE_OPT_RAM_RCT_TSMCDP_PO4SVT |
| | 0x7000FAB4 | | | 0x7000FAB4 |
| |- | | |- |
− | | | + | | FUSE_OPT_RAM_WCT_TSMCDP_PO4SVT |
| | 0x7000FAB8 | | | 0x7000FAB8 |
| |- | | |- |
− | | | + | | FUSE_OPT_RAM_KP_TSMCDP_PO4SVT |
| | 0x7000FABC | | | 0x7000FABC |
| |- | | |- |
Line 1,706: |
Line 1,783: |
| | 0x7000FB20 | | | 0x7000FB20 |
| |- | | |- |
− | | | + | | FUSE_OPT_RAM_WTSEL_TSMCPDP_PO4HVT |
| | 0x7000FB24 | | | 0x7000FB24 |
| |- | | |- |
− | | | + | | FUSE_OPT_RAM_RCT_TSMCDP_PO4HVT |
| | 0x7000FB28 | | | 0x7000FB28 |
| |- | | |- |
− | | | + | | FUSE_OPT_RAM_WCT_TSMCDP_PO4HVT |
| | 0x7000FB2C | | | 0x7000FB2C |
| |- | | |- |
− | | | + | | FUSE_OPT_RAM_KP_TSMCDP_PO4HVT |
| | 0x7000FB30 | | | 0x7000FB30 |
| |- | | |- |
Line 2,781: |
Line 2,858: |
| | | |
| ==== IROM patch 1 ==== | | ==== IROM patch 1 ==== |
− | This patch is a bugfix. | + | This patch sets APBDEV_PMC_SCRATCH190_0 to 0x01, which LP0 resume code expects. |
− | | |
− | LP0 resume code expects APBDEV_PMC_SCRATCH190_0 to be set to 0x01, but the bootrom didn't set it. | |
| | | |
| <syntaxhighlight lang="c"> | | <syntaxhighlight lang="c"> |
Line 2,811: |
Line 2,886: |
| This patch ensures that waiting on PRC_PENDING from the XUSB_DEV register T_XUSB_DEV_XHCI_PORTSC never fails. | | This patch ensures that waiting on PRC_PENDING from the XUSB_DEV register T_XUSB_DEV_XHCI_PORTSC never fails. |
| | | |
− | In the second batch of patched units ([[#FUSE_OPT_FT_REV|FUSE_OPT_FT_REV]] set to revision 7.0) this patch has been replaced with a fix for [[Switch_System_Flaws#Hardware|CVE-2018-6242]] (arbitrary copy when handling USB control requests in RCM). By setting R1 to 0 at address 0x0010769A in the bootrom, the upper 16 bits of the USB control request's wLength field are cleared out, effectively limiting the request's size to a maximum of 255 bytes. | + | In the second batch of patched units ([[#FUSE_OPT_FT_REV|FUSE_OPT_FT_REV]] set to revision 7.0) this patch has been replaced with a fix for [[Switch_System_Flaws#Hardware|CVE-2018-6242]] (arbitrary copy when handling USB control requests in RCM). By setting R1 to 0 at address 0x0010769A in the bootrom, the upper 8 bits of the USB control request's wLength field are cleared out, effectively limiting the request's size to a maximum of 255 bytes. |
| | | |
| ==== IROM patch 4 ==== | | ==== IROM patch 4 ==== |
Line 2,861: |
Line 2,936: |
| | | |
| ==== IROM patch 6 ==== | | ==== IROM patch 6 ==== |
− | This patch is a factory backdoor. | + | This patch allows controlling the debug authentication configuration using a fuse. |
− | | |
− | It allows controlling the debug authentication configuration using a fuse.
| |
| | | |
| <syntaxhighlight lang="c"> | | <syntaxhighlight lang="c"> |
Line 2,884: |
Line 2,957: |
| | | |
| ==== IROM patch 7 ==== | | ==== IROM patch 7 ==== |
− | This patch is a bugfix. | + | This patch prevents overflowing IRAM (0x40010000) when copying the warmboot binary from DRAM. |
− | | |
− | It prevents overflowing IRAM (0x40010000) when copying the warmboot binary from DRAM.
| |
| | | |
| <syntaxhighlight lang="c"> | | <syntaxhighlight lang="c"> |
Line 2,916: |
Line 2,987: |
| | | |
| ==== IROM patch 8 ==== | | ==== IROM patch 8 ==== |
− | This patch is a bugfix. | + | This patch sets the correct warmboot binary entrypoint address for RSA signature verification, which would be done in DRAM instead of IRAM without this patch. |
− | | |
− | It sets the correct warmboot binary entrypoint address for RSA signature verification, which would be done in DRAM instead of IRAM without this patch.
| |
| | | |
| <syntaxhighlight lang="c"> | | <syntaxhighlight lang="c"> |
Line 3,346: |
Line 3,415: |
| RAM:00000000 ; 6: 0x4103df2b 0x00108206 0x0000df2b : svc #0x2b (offset 0x9e) | | RAM:00000000 ; 6: 0x4103df2b 0x00108206 0x0000df2b : svc #0x2b (offset 0x9e) |
| RAM:00000000 ; 7: 0x495c0060 0x001092b8 0x00000060 : lsls r0, r4, #1 | | RAM:00000000 ; 7: 0x495c0060 0x001092b8 0x00000060 : lsls r0, r4, #1 |
− | RAM:00000000 ; 8: 0x62e3ef5b 0x0010c5c6 0x0000ef5b | + | RAM:00000000 ; 8: 0x62e3ef5b 0x0010c5c6 0x0000ef5b : svc #0x5b (offset 0xfe) |
| RAM:00000000 ; 9: 0x10d1df6a 0x001021a2 0x0000df6a : svc #0x6a (offset 0x11c) | | RAM:00000000 ; 9: 0x10d1df6a 0x001021a2 0x0000df6a : svc #0x6a (offset 0x11c) |
| RAM:00000004 MOV R2, LR | | RAM:00000004 MOV R2, LR |
Line 3,545: |
Line 3,614: |
| RAM:000000FE | | RAM:000000FE |
| RAM:000000FE | | RAM:000000FE |
− | RAM:000000FE sub_FE | + | RAM:000000FE sub_FE ; 8: 0x62e3ef5b 0x0010c5c6 0x0000ef5b : svc #0x5b (offset 0xfe) |
| RAM:000000FE POP {R2} | | RAM:000000FE POP {R2} |
| RAM:00000100 MOV R4, SP | | RAM:00000100 MOV R4, SP |
Line 3,636: |
Line 3,705: |
| </syntaxhighlight> | | </syntaxhighlight> |
| | | |
− | = Anti-downgrade = | + | ==== IROM patch 1 ==== |
− | The first bootloader verifies [[#FUSE_RESERVED_ODM7|FUSE_RESERVED_ODM7]] to prevent downgrading. | + | This patch stubs the function responsible for disabling read access for the SE AES keyslots. |
− | How many fuses are expected to be burnt depends the device's unit type as below.
| + | |
| + | Due to a programming mistake, when loading the OEM AES keys the aforementioned function would be called with the wrong arguments. The patch prevents this by simply stubbing the function altogether, which is only acceptable because the Mariko's SE hardware already boots with keyslot reading permanently disabled. |
| + | |
| + | ==== IROM patch 2 ==== |
| + | This patch forces the function responsible for checking if SE context atomic save is enabled (by checking a fuse) to always return true. |
| + | |
| + | Some Mariko units have been found to not have the relevant fuse bit (bit 7 in [[#FUSE_BOOT_SECURITY_INFO|FUSE_BOOT_SECURITY_INFO]]) burned, so the patch serves as a workaround for this. |
| | | |
− | {| class="wikitable" border="1" | + | ==== IROM patch 3 ==== |
− | |- | + | This patch forces a jump to the same routine used by [[#IROM_patch_0_2|IROM patch 0]] if loading a bootloader failed. |
− | ! System version | + | |
− | ! Expected number of burnt fuses (production) | + | By setting all IRAM memory from 0x4000FC20 to 0x40040000 to 0xEAFFFFFE, a bootloader that somehow failed validation is effectively erased from memory. |
− | ! Expected number of burnt fuses (development) | + | |
| + | ==== IROM patch 4 ==== |
| + | This patch stores a stack cookie (value 0x5A55F0E1) after a RCM message is received and before it's validated. |
| + | |
| + | ==== IROM patch 5 ==== |
| + | This patch checks the stack cookie stored by [[#IROM_patch_4_2|IROM patch 4]] right after a RCM message is validated. |
| + | |
| + | If the stack cookie's value is still 0x5A55F0E1, the bootrom jumps to a panic. If it changed to anything other than 0, the same routine used by [[#IROM_patch_0_2|IROM patch 0]] is called. Presumably, this is an attempt at mitigating fault injection attacks against skipping the validation of RCM messages. |
| + | |
| + | ==== IROM patch 6 ==== |
| + | This patch sanitizes the crypto context right before receiving a RCM message. |
| + | |
| + | <syntaxhighlight lang="c"> |
| + | u32 FUSE_PRIVATEKEYDISABLE = 0x7000F828; |
| + | u32 SE1_CRYPTO_KEYTABLE_ADDR = 0x7001231C; |
| + | u32 SE2_CRYPTO_KEYTABLE_ADDR = 0x7041231C; |
| + | u32 SE1_CRYPTO_KEYTABLE_DATA = 0x70012320; |
| + | u32 SE2_CRYPTO_KEYTABLE_DATA = 0x70412320; |
| + | |
| + | // Hide the private key fuses |
| + | *(u32 *)FUSE_PRIVATEKEYDISABLE = 0x1; |
| + | |
| + | u32 crypto_keytable_val = 0xE0; |
| + | |
| + | // Clear SE1/SE2 keyslot 0xE (contains the SBK) |
| + | for (int i = 0; i < 0x7; i++) { |
| + | *(u32 *)SE1_CRYPTO_KEYTABLE_ADDR = crypto_keytable_val; |
| + | *(u32 *)SE1_CRYPTO_KEYTABLE_DATA = 0; |
| + | *(u32 *)SE2_CRYPTO_KEYTABLE_ADDR = crypto_keytable_val; |
| + | *(u32 *)SE2_CRYPTO_KEYTABLE_DATA = 0; |
| + | crypto_keytable_val++; |
| + | } |
| + | |
| + | crypto_keytable_val = 0xF0; |
| + | |
| + | // Clear SE1/SE2 keyslot 0xF (contains the SSK) |
| + | for (int i = 0; i < 0x07; i++) { |
| + | *(u32 *)SE1_CRYPTO_KEYTABLE_ADDR = crypto_keytable_val; |
| + | *(u32 *)SE1_CRYPTO_KEYTABLE_DATA = 0; |
| + | *(u32 *)SE2_CRYPTO_KEYTABLE_ADDR = crypto_keytable_val; |
| + | *(u32 *)SE2_CRYPTO_KEYTABLE_DATA = 0; |
| + | crypto_keytable_val++; |
| + | } |
| + | |
| + | crypto_keytable_val = 0xC0; |
| + | |
| + | // Clear SE1/SE2 keyslot 0xC (contains the KEK) |
| + | for (int i = 0; i < 0x7; i++) { |
| + | *(u32 *)SE1_CRYPTO_KEYTABLE_ADDR = crypto_keytable_val; |
| + | *(u32 *)SE1_CRYPTO_KEYTABLE_DATA = 0; |
| + | *(u32 *)SE2_CRYPTO_KEYTABLE_ADDR = crypto_keytable_val; |
| + | *(u32 *)SE2_CRYPTO_KEYTABLE_DATA = 0; |
| + | crypto_keytable_val++; |
| + | } |
| + | |
| + | u8 se_instance = 0; // SE1 |
| + | u8 se_src_key_slot = 0xD; |
| + | u8 se_src_key_size = 0; // 128 bits |
| + | u8 se_dst_key_slot = 0xD; |
| + | u8 se_dst_key_size = 0; // 128 bits |
| + | u8 *se_src_key_data = 0x40004164; |
| + | |
| + | // Overwrite SE1 keyslot 0xD (contains the BEK) |
| + | se_decrypt_key_into_key_slot(se_instance, se_src_key_slot, se_src_key_size, se_dst_key_slot, se_dst_key_size, se_src_key_data); |
| + | |
| + | se_instance = 1; // SE2 |
| + | |
| + | // Overwrite SE2 keyslot 0xD (contains the BEK) |
| + | se_decrypt_key_into_key_slot(se_instance, se_src_key_slot, se_src_key_size, se_dst_key_slot, se_dst_key_size, se_src_key_data); |
| + | |
| + | /* |
| + | Untranslated instructions: |
| + | |
| + | LDR R0, =0x4000FC20 |
| + | MOV R8, R0 |
| + | */ |
| + | |
| + | return; |
| + | </syntaxhighlight> |
| + | |
| + | ==== IROM patch 7 ==== |
| + | This patch doubles the maximum value passed to the function responsible for generating random numbers with the SE. These values are then used for randomizing the duration of wait loops scattered around the bootrom. |
| + | |
| + | ==== IROM patch 8 ==== |
| + | This patch forces memcpy to always fall outside of current stack limits. |
| + | |
| + | ==== IROM patch 9 ==== |
| + | This patch forces TZRAM to be cleared on any boot type (instead of clearing it only on coldboot). |
| + | |
| + | = Anti-downgrade = |
| + | The first bootloader verifies [[#FUSE_RESERVED_ODM7|FUSE_RESERVED_ODM7]] to prevent downgrading. |
| + | How many fuses are expected to be burnt depends the device's unit type as below. |
| + | |
| + | {| class="wikitable" border="1" |
| + | |- |
| + | ! System version |
| + | ! Expected number of burnt fuses (production) |
| + | ! Expected number of burnt fuses (development) |
| |- | | |- |
| | 1.0.0 | | | 1.0.0 |
Line 3,720: |
Line 3,892: |
| | 17.0.0-18.1.0 | | | 17.0.0-18.1.0 |
| | 19 | | | 19 |
| + | | 1 |
| + | |- |
| + | | 19.0.0-19.0.1 |
| + | | 20 |
| | 1 | | | 1 |
| |} | | |} |