The Nintendo Switch makes use of Tegra's fuse driver for a number of operations. This driver is mapped to physical address 0x7000F800 with a total size of 0x400 bytes and exposes several registers for fuse programming.

Registers from 0x7000F800 to 0x7000F800 + 0xFF can be used to directly program the hardware fuse array, while registers from 0x7000F800 + 0x100 (FUSE_CHIP_REG_START_OFFSET) to 0x7000F800 + 0x3FC (FUSE_CHIP_REG_END_OFFSET) represent cached values read from certain fuses.

Registers

Below is a list of fuse driver registers used by the Switch's bootloaders.

Driver registers

Name Address
FUSE_FUSECTRL 0x7000F800
FUSE_FUSEADDR 0x7000F804
FUSE_FUSERDATA 0x7000F808
FUSE_FUSEWDATA 0x7000F80C
FUSE_FUSETIME_RD1 0x7000F810
FUSE_FUSETIME_RD2 0x7000F814
FUSE_FUSETIME_PGM1 0x7000F818
FUSE_FUSETIME_PGM2 0x7000F81C
FUSE_PRIV2INTFC_START 0x7000F820
FUSE_FUSEBYPASS 0x7000F824
FUSE_PRIVATEKEYDISABLE 0x7000F828
FUSE_DISABLEREGPROGRAM 0x7000F82C
FUSE_WRITE_ACCESS_SW 0x7000F830
FUSE_PWR_GOOD_SW 0x7000F834
FUSE_PRIV2RESHIFT 0x7000F83C
FUSE_FUSETIME_RD3 0x7000F84C
FUSE_PRIVATE_KEY0_NONZERO 0x7000F880
FUSE_PRIVATE_KEY1_NONZERO 0x7000F884
FUSE_PRIVATE_KEY2_NONZERO 0x7000F888
FUSE_PRIVATE_KEY3_NONZERO 0x7000F88C
FUSE_PRIVATE_KEY4_NONZERO 0x7000F890

FUSE_FUSECTRL

Bits Description
0-1 FUSE_FUSECTRL_CMD
0x00: IDLE
0x01: READ
0x02: WRITE
0x03: SENSE_CTRL
16-20 FUSE_FUSECTRL_STATE
0x00: STATE_RESET
0x01: STATE_POST_RESET
0x02: STATE_LOAD_ROW0
0x03: STATE_LOAD_ROW1
0x04: STATE_IDLE
0x05: STATE_READ_SETUP
0x06: STATE_READ_STROBE
0x07: STATE_SAMPLE_FUSES
0x08: STATE_READ_HOLD
0x09: STATE_FUSE_SRC_SETUP
0x0A: STATE_WRITE_SETUP
0x0B: STATE_WRITE_ADDR_SETUP
0x0C: STATE_WRITE_PROGRAM
0x0D: STATE_WRITE_ADDR_HOLD
0x0E: STATE_FUSE_SRC_HOLD
0x0F: STATE_READ_BEFORE_WRITE_SETUP
21 FUSE_FUSECTRL_MARGIN_READ
22 FUSE_FUSECTRL_RWL
23 FUSE_FUSECTRL_TRCS
24 FUSE_FUSECTRL_AT1
25 FUSE_FUSECTRL_AT0
26 FUSE_FUSECTRL_PD_CTRL
30 FUSE_FUSECTRL_FUSE_SENSE_DONE
31 FUSE_FUSECTRL_RECORD_SHIFT_DONE

Before fuse reading/writing the power down mode must be disabled. SENSE_CTRL mode flushes programmed values into the cache registers.

FUSE_FUSEADDR

This register takes the address of the fuse to be read/written/sensed.

FUSE_FUSERDATA

This register receives the value read from the fuse.

FUSE_FUSEWDATA

This register takes the value to be written to the fuse.

FUSE_FUSETIME_PGM2

This register takes the fuse programming pulse (0xC0 == 19200 kHz).

FUSE_PRIV2INTFC_START

Bits Description
0 FUSE_PRIV2INTFC_START_DATA
1 FUSE_PRIV2INTFC_START_SKIP_RECORDS

FUSE_DISABLEREGPROGRAM

If set to 0x01, this register disables fuse programming.

FUSE_WRITE_ACCESS_SW

If set to 0x01, this register disables software writes to the fuse driver registers.

Cache registers

Name Address
FUSE_PRODUCTION_MODE 0x7000F900
FUSE_JTAG_SECUREID_VALID 0x7000F904
FUSE_ODM_LOCK 0x7000F908
FUSE_OPT_OPENGL_EN 0x7000F90C
FUSE_SKU_INFO 0x7000F910
FUSE_CPU_SPEEDO_0_CALIB 0x7000F914
FUSE_CPU_IDDQ_CALIB 0x7000F918
FUSE_DAC_CRT_CALIB 0x7000F91C
FUSE_DAC_HDTV_CALIB 0x7000F920
FUSE_DAC_SDTV_CALIB 0x7000F924
FUSE_OPT_FT_REV 0x7000F928
FUSE_CPU_SPEEDO_1_CALIB 0x7000F92C
FUSE_CPU_SPEEDO_2_CALIB 0x7000F930
FUSE_SOC_SPEEDO_0_CALIB 0x7000F934
FUSE_SOC_SPEEDO_1_CALIB 0x7000F938
FUSE_SOC_SPEEDO_2_CALIB 0x7000F93C
FUSE_SOC_IDDQ_CALIB 0x7000F940
FUSE_RESERVED_PRODUCTION_WP 0x7000F944
FUSE_FA 0x7000F948
FUSE_RESERVED_PRODUCTION 0x7000F94C
FUSE_HDMI_LANE0_CALIB 0x7000F950
FUSE_HDMI_LANE1_CALIB 0x7000F954
FUSE_HDMI_LANE2_CALIB 0x7000F958
FUSE_HDMI_LANE3_CALIB 0x7000F95C
FUSE_ENCRYPTION_RATE 0x7000F960
FUSE_PUBLIC_KEY0 0x7000F964
FUSE_PUBLIC_KEY1 0x7000F968
FUSE_PUBLIC_KEY2 0x7000F96C
FUSE_PUBLIC_KEY3 0x7000F970
FUSE_PUBLIC_KEY4 0x7000F974
FUSE_PUBLIC_KEY5 0x7000F978
FUSE_PUBLIC_KEY6 0x7000F97C
FUSE_PUBLIC_KEY7 0x7000F980
FUSE_TSENSOR1_CALIB 0x7000F984
FUSE_TSENSOR2_CALIB 0x7000F988
FUSE_VSENSOR_CALIB 0x7000F98C
FUSE_OPT_CP_REV 0x7000F990
FUSE_OPT_PFG 0x7000F994
FUSE_TSENSOR0_CALIB 0x7000F998
FUSE_FIRST_BOOTROM_PATCH_SIZE 0x7000F99C
FUSE_SECURITY_MODE 0x7000F9A0
FUSE_PRIVATE_KEY0 0x7000F9A4
FUSE_PRIVATE_KEY1 0x7000F9A8
FUSE_PRIVATE_KEY2 0x7000F9AC
FUSE_PRIVATE_KEY3 0x7000F9B0
FUSE_PRIVATE_KEY4 0x7000F9B4
FUSE_ARM_JTAG_DIS 0x7000F9B8
FUSE_BOOT_DEVICE_INFO 0x7000F9BC
FUSE_RESERVED_SW 0x7000F9C0
FUSE_OPT_VP9_DISABLE 0x7000F9C4
FUSE_RESERVED_ODM0 0x7000F9C8
FUSE_RESERVED_ODM1 0x7000F9CC
FUSE_RESERVED_ODM2 0x7000F9D0
FUSE_RESERVED_ODM3 0x7000F9D4
FUSE_RESERVED_ODM4 0x7000F9D8
FUSE_RESERVED_ODM5 0x7000F9DC
FUSE_RESERVED_ODM6 0x7000F9E0
FUSE_RESERVED_ODM7 0x7000F9E4
FUSE_OBS_DIS 0x7000F9E8
FUSE_NOR_INFO 0x7000F9EC
FUSE_USB_CALIB 0x7000F9F0
FUSE_SKU_DIRECT_CONFIG 0x7000F9F4
FUSE_KFUSE_PRIVKEY_CTRL 0x7000F9F8
FUSE_PACKAGE_INFO 0x7000F9FC
FUSE_OPT_VENDOR_CODE 0x7000FA00
FUSE_OPT_FAB_CODE 0x7000FA04
FUSE_OPT_LOT_CODE_0 0x7000FA08
FUSE_OPT_LOT_CODE_1 0x7000FA0C
FUSE_OPT_WAFER_ID 0x7000FA10
FUSE_OPT_X_COORDINATE 0x7000FA14
FUSE_OPT_Y_COORDINATE 0x7000FA18
FUSE_OPT_SEC_DEBUG_EN 0x7000FA1C
FUSE_OPT_OPS_RESERVED 0x7000FA20
FUSE_SATA_CALIB 0x7000FA24
FUSE_GPU_IDDQ_CALIB 0x7000FA28
FUSE_TSENSOR3_CALIB 0x7000FA2C
FUSE_SKU_BOND_OUT_L 0x7000FA30
FUSE_SKU_BOND_OUT_H 0x7000FA34
FUSE_SKU_BOND_OUT_U 0x7000FA38
FUSE_SKU_BOND_OUT_V 0x7000FA3C
FUSE_SKU_BOND_OUT_W 0x7000FA40
FUSE_OPT_SAMPLE_TYPE 0x7000FA44
FUSE_OPT_SUBREVISION 0x7000FA48
FUSE_OPT_SW_RESERVED_0 0x7000FA4C
FUSE_OPT_SW_RESERVED_1 0x7000FA50
FUSE_TSENSOR4_CALIB 0x7000FA54
FUSE_TSENSOR5_CALIB 0x7000FA58
FUSE_TSENSOR6_CALIB 0x7000FA5C
FUSE_TSENSOR7_CALIB 0x7000FA60
FUSE_OPT_PRIV_SEC_EN 0x7000FA64
FUSE_PKC_DISABLE 0x7000FA68
FUSE_FUSE2TSEC_DEBUG_DISABLE 0x7000FA7C
FUSE_TSENSOR_COMMON 0x7000FA80
FUSE_OPT_CP_BIN 0x7000FA84
FUSE_OPT_GPU_DISABLE 0x7000FA88
FUSE_OPT_FT_BIN 0x7000FA8C
FUSE_OPT_DONE_MAP 0x7000FA90
FUSE_APB2JTAG_DISABLE 0x7000FA98
FUSE_ODM_INFO 0x7000FA9C
FUSE_ARM_CRYPT_DE_FEATURE 0x7000FAA8
FUSE_WOA_SKU_FLAG 0x7000FAC0
FUSE_ECO_RESERVE_1 0x7000FAC4
FUSE_GCPLEX_CONFIG_FUSE 0x7000FAC8
FUSE_PRODUCTION_MONTH 0x7000FACC
FUSE_RAM_REPAIR_INDICATOR 0x7000FAD0
FUSE_TSENSOR9_CALIB 0x7000FAD4
FUSE_VMIN_CALIBRATION 0x7000FADC
FUSE_AGING_SENSOR_CALIBRATION 0x7000FAE0
FUSE_DEBUG_AUTHENTICATION 0x7000FAE4
FUSE_SECURE_PROVISION_INDEX 0x7000FAE8
FUSE_SECURE_PROVISION_INFO 0x7000FAEC
FUSE_OPT_GPU_DISABLE_CP1 0x7000FAF0
FUSE_SPARE_ENDIS 0x7000FAF4
FUSE_ECO_RESERVE_0 0x7000FAF8
FUSE_SPARE_REALIGNMENT_REG_OLD 0x7000FAFC
FUSE_RESERVED_CALIB0 0x7000FB04
FUSE_RESERVED_CALIB1 0x7000FB08
FUSE_OPT_GPU_TPC0_DISABLE 0x7000FB0C
FUSE_OPT_GPU_TPC0_DISABLE_CP1 0x7000FB10
FUSE_OPT_CPU_DISABLE 0x7000FB14
FUSE_OPT_CPU_DISABLE_CP1 0x7000FB18
FUSE_TSENSOR10_CALIB 0x7000FB1C
FUSE_TSENSOR10_CALIB_AUX 0x7000FB20
FUSE_OPT_RAM_SVOP_DP 0x7000FB24
FUSE_OPT_RAM_SVOP_PDP 0x7000FB28
FUSE_OPT_RAM_SVOP_REG 0x7000FB2C
FUSE_OPT_RAM_SVOP_SP 0x7000FB30
FUSE_OPT_RAM_SVOP_SMPDP 0x7000FB34
FUSE_OPT_GPU_TPC0_DISABLE_CP2 0x7000FB38
FUSE_OPT_GPU_TPC1_DISABLE 0x7000FB3C
FUSE_OPT_GPU_TPC1_DISABLE_CP1 0x7000FB40
FUSE_OPT_GPU_TPC1_DISABLE_CP2 0x7000FB44
FUSE_OPT_CPU_DISABLE_CP2 0x7000FB48
FUSE_OPT_GPU_DISABLE_CP2 0x7000FB4C
FUSE_USB_CALIB_EXT 0x7000FB50
FUSE_RESERVED_FIELD 0x7000FB54
FUSE_OPT_ECC_EN 0x7000FB58
FUSE_SPARE_REALIGNMENT_REG 0x7000FB7C
FUSE_SPARE_BIT_0 0x7000FB80
FUSE_SPARE_BIT_1 0x7000FB84
FUSE_SPARE_BIT_2 0x7000FB88
FUSE_SPARE_BIT_3 0x7000FB8C
FUSE_SPARE_BIT_4 0x7000FB90
FUSE_SPARE_BIT_5 0x7000FB94
FUSE_SPARE_BIT_6 0x7000FB98
FUSE_SPARE_BIT_7 0x7000FB9C
FUSE_SPARE_BIT_8 0x7000FBA0
FUSE_SPARE_BIT_9 0x7000FBA4
FUSE_SPARE_BIT_10 0x7000FBA8
FUSE_SPARE_BIT_11 0x7000FBAC
FUSE_SPARE_BIT_12 0x7000FBB0
FUSE_SPARE_BIT_13 0x7000FBB4
FUSE_SPARE_BIT_14 0x7000FBB8
FUSE_SPARE_BIT_15 0x7000FBBC
FUSE_SPARE_BIT_16 0x7000FBC0
FUSE_SPARE_BIT_17 0x7000FBC4
FUSE_SPARE_BIT_18 0x7000FBC8
FUSE_SPARE_BIT_19 0x7000FBCC
FUSE_SPARE_BIT_20 0x7000FBD0
FUSE_SPARE_BIT_21 0x7000FBD4
FUSE_SPARE_BIT_22 0x7000FBD8
FUSE_SPARE_BIT_23 0x7000FBDC
FUSE_SPARE_BIT_24 0x7000FBE0
FUSE_SPARE_BIT_25 0x7000FBE4
FUSE_SPARE_BIT_26 0x7000FBE8
FUSE_SPARE_BIT_27 0x7000FBEC
FUSE_SPARE_BIT_28 0x7000FBF0
FUSE_SPARE_BIT_29 0x7000FBF4
FUSE_SPARE_BIT_30 0x7000FBF8
FUSE_SPARE_BIT_31 0x7000FBFC

FUSE_SKU_INFO

Stores the SKU ID (must be 0x83).

FUSE_OPT_FT_REV

Stores the FT (Final Test) revision.

Original launch units have this value set to 0xA0 (revision 5.0). The first batch of patched units have this value set to 0xC0 (revision 6.0). The second batch of patched units have this value set to 0xE0 (revision 7.0)

FUSE_FA

Stores failure analysis mode.

FUSE_SOC_SPEEDO_1_CALIB

Stores the bootrom patch version.

FUSE_RESERVED_ODM0

This stores an hardware ID.

FUSE_RESERVED_ODM1

This stores an hardware ID.

FUSE_RESERVED_ODM2

Bits Description
0-4 [5.0.0+] Used as key generation (patched units only)

This stores an hardware ID in original launch units, but in patched units it stores a single value (key generation).

FUSE_RESERVED_ODM3

This stores an hardware ID in original launch units, but in patched units it's empty.

FUSE_RESERVED_ODM4

Bits Description
0-1 Unit type (3 = debug; 0 = retail)
2 Unknown config (must be 1 on retail)
[1.0.0-3.0.2] 3-5

[4.0.0+] 3-7

DRAM ID
8 Unknown config mask (must be 0 on retail)
9 Unit type mask (0 = debug; 1 = retail)
10 [3.0.0+] Kiosk mode (0 = retail; 1 = kiosk)
11 [5.0.0+] Unit patch flag (0 = unpatched; 1 = patched)
16-19 [4.0.0+] New unit type (0 = Erista; 1 = Mariko; 2 = Mariko Lite)

This stores some device configuration parameters.

FUSE_RESERVED_ODM6

This register returns the value programmed into index 0x3A of the fuse array.

FUSE_RESERVED_ODM7

This register returns the value programmed into index 0x3C of the fuse array.

FUSE_PUBLIC_KEY

This stores the SHA256 hash of the 2048-bit RSA key expected at BCT+0x210.

FUSE_OPT_CP_REV

Stores the CP (Chip Probing) revision.

Original launch units have this value set to 0xA0 (revision 5.0). Patched units have this value set to 0x103 (revision 8.3).

FUSE_PRIVATE_KEY

This stores the 160-bit private key (128 bit SBK + 32-bit device key). Reads to these registers after the SBK is locked out produce all-FF output.

FUSE_RESERVED_SW

Bits Description
0-2 Boot device
3 Skip device selection straps (0 = don't skip; 1 = skip)
4 ENABLE_CHARGER_DETECT
5 ENABLE_WATCHDOG
6 Forced RCM two button mode (0 = Only VOLUME_UP; 1 = VOLUME_UP + HOME)
7 RCM USB controller mode (0 = USB 2.0; 1 = XUSB)

This caches the value of the sw_reserved fuse from the hardware array.

Original launch units have the RCM USB controller mode set to USB 2.0, while the first batch of patched units have the RCM USB controller mode set to XUSB.

FUSE_PKC_DISABLE

This caches the value of the pkc_disable fuse from the hardware array.

FUSE_SPARE_BIT_2

Stores part of the speedo fusing revision.

FUSE_SPARE_BIT_3

Stores part of the speedo fusing revision.

FUSE_SPARE_BIT_4

Stores part of the speedo fusing revision.

FUSE_SPARE_BIT_5

Must be non-zero on retail units, otherwise the first bootloader panics. On debug units it can be zero, which tells the bootloader to choose from two debug master key seeds. If set to non-zero on a debug unit, it tells the bootloader to choose from two master key seeds (with the second one being the retail master key seed).

[4.0.0+] This value is no longer used during boot.

eFuses

The actual hardware fuses can be programmed through the fuse driver after enabling fuse programming.

Below is a list of common fuse indexes used by Tegra devices (and applicable to the Switch). Note that the indexes are relative to the start of the fuse array and each element is a 4 byte word. A single fuse write operation always writes the same word at both fuse_array + 0 (PRIMARY_ALIAS) and fuse_array + 1 (REDUNDANT_ALIAS).

Name Index Bits
jtag_disable 0x00 1
odm_production_mode 0x00 1
odm_lock 0x00 4
public_key 0x0C 256
secure_boot_key 0x22 128
device_key 0x2A 32
sec_boot_dev_cfg 0x2C 16
sec_boot_dev_sel 0x2C 3
sw_reserved 0x2E 12
ignore_dev_sel_straps 0x2E 1
odm_reserved 0x2E 256
pkc_disable 0x52 1
debug_authentication 0x5A 5
aid 0x67 32
bootrom_ipatch 0x72 624

odm_reserved

The first bootloader only burns fuses in this region. Both fuse indexes 0x3A (odm_reserved + 0x0C) and 0x3C (odm_reserved + 0x0E) are used for anti-downgrade control. These fuses will have their values cached into FUSE_RESERVED_ODM6 and FUSE_RESERVED_ODM7.

bootrom_ipatch

Tegra210 based hardware such as the Switch provides support for bootrom patches. The patch data is burned to the hardware fuse array using a specific format (see shuffle2's ipatch decoder). The bootrom reads these fuses in order to initialize the IPATCH hardware, which allows overriding data returned for code and data fetches done by BPMP.

The following represents the patch data dumped from a Switch console:

RAM:00000000 ; =============== S U B R O U T I N E =======================================
RAM:00000000
RAM:00000000
RAM:00000000 irom_svc_dispatch
RAM:00000000   STMFD   SP!, {R0-R2}                  ; ipatches (new):
RAM:00000000                                         ;  0  b57df00     16ae     df00 : svc #0x00 (offset 0x48)
RAM:00000000                                         ;  1 1820df22     3040     df22 : svc #0x22 (offset 0x8c)
RAM:00000000                                         ;  2 3797df26     6f2e     df26 : svc #0x26 (offset 0x94)
RAM:00000000                                         ;  3 3b4d2100     769a     2100 : movs r1, #0x00
RAM:00000000                                         ;  4  42bdf2c      856     df2c : svc #0x2c (offset 0xa0)
RAM:00000000                                         ;  5 37aadf42     6f54     df42 : svc #0x42 (offset 0xcc)
RAM:00000000                                         ;  6  972df4b     12e4     df4b : svc #0x4b (offset 0xde)
RAM:00000000                                         ;  7 2293df54     4526     df54 : svc #0x54 (offset 0xf0)
RAM:00000000                                         ;  8 21fadf5d     43f4     df5d : svc #0x5d (offset 0x102)
RAM:00000000                                         ;  9 bba2ac57    17744     ac57 : data
RAM:00000000                                         ; 10 bbac3d19    17758     3d19 : data
RAM:00000000                                         ; 11 1e952001     3d2a     2001 : movs r0, #0x01
RAM:00000000                                         ; 
RAM:00000000                                         ; ipatches (old):
RAM:00000000                                         ;  0  b57df00     16ae     df00 : svc #0x00 (offset 0x48)
RAM:00000000                                         ;  1 1820df22     3040     df22 : svc #0x22 (offset 0x8c)
RAM:00000000                                         ;  2 3797df26     6f2e     df26 : svc #0x26 (offset 0x94)
RAM:00000000                                         ;  3 7d9e2000     fb3c     2000 : movs r0, #0x00
RAM:00000000                                         ;  4  42bdf2c      856     df2c : svc #0x2c (offset 0xa0)
RAM:00000000                                         ;  5 37aadf42     6f54     df42 : svc #0x42 (offset 0xcc)
RAM:00000000                                         ;  6  972df4b     12e4     df4b : svc #0x4b (offset 0xde)
RAM:00000000                                         ;  7 2293df54     4526     df54 : svc #0x54 (offset 0xf0)
RAM:00000000                                         ;  8 21fadf5d     43f4     df5d : svc #0x5d (offset 0x102)
RAM:00000000                                         ;  9 bba2ac57    17744     ac57 : data
RAM:00000000                                         ; 10 bbac3d19    17758     3d19 : data
RAM:00000000                                         ; 11 1e952001     3d2a     2001 : movs r0, #0x01
RAM:00000004   MOV     R2, LR
RAM:00000008   SUB     R2, R2, #2
RAM:0000000C   LDR     R2, [R2]
RAM:00000010   AND     R2, R2, #0xFF
RAM:00000014   MOV     R2, R2,LSL#1
RAM:00000018   LDR     R0, =0x1007B0
RAM:0000001C   LDR     R1, =0x1007F8
RAM:00000020   SUB     R1, R1, R0
RAM:00000024   LDR     R0, =0x40004C30
RAM:00000028   ADD     R0, R0, R1
RAM:0000002C   ADD     R2, R2, R0
RAM:00000030   ORR     R2, R2, #1
RAM:00000034   LDMFD   SP!, {R0,R1}
RAM:00000038   BX      R2
RAM:00000038 ; End of function irom_svc_dispatch
RAM:00000038
RAM:00000038 ; ---------------------------------------------------------------------------
RAM:0000003C dword_3C DCD 0x1007B0                   ; DATA XREF: irom_svc_dispatch+18↑r
RAM:00000040 dword_40 DCD 0x1007F8                   ; DATA XREF: irom_svc_dispatch+1C↑r
RAM:00000044 dword_44 DCD 0x40004C30                 ; DATA XREF: irom_svc_dispatch+24↑r
RAM:00000048   CODE16
RAM:00000048
RAM:00000048 ; =============== S U B R O U T I N E =======================================
RAM:00000048
RAM:00000048
RAM:00000048 sub_48
RAM:00000048   MOVS    R2, #0                        ; 0  b57df00     16ae     df00 : svc #0x00 (offset 0x48)
RAM:0000004A   MVNS    R2, R2
RAM:0000004C   LDR     R1, =0x60006410
RAM:0000004E   STR     R2, [R1,#0x30]
RAM:00000050   STR     R2, [R1,#0x38]
RAM:00000052   LDR     R1, =0x600060F8
RAM:00000054   STR     R2, [R1]
RAM:00000056   STR     R2, [R1,#4]
RAM:00000058   LDR     R1, =0x60006284
RAM:0000005A   STR     R2, [R1]
RAM:0000005C   STR     R2, [R1,#0x18]
RAM:0000005E   ADDS    R1, #0x80
RAM:00000060   ADDS    R1, #0x1C
RAM:00000062   STR     R2, [R1]
RAM:00000064   STR     R2, [R1,#8]
RAM:00000066   STR     R2, [R1,#0x10]
RAM:00000068   ADDS    R1, #0x80
RAM:0000006A   STR     R2, [R1]
RAM:0000006C   STR     R2, [R1,#4]
RAM:0000006E   LDR     R1, =0x60006554
RAM:00000070   STR     R2, [R1]
RAM:00000072   MOVS    R2, #0xA0000000
RAM:00000076   LDR     R1, =0x60006148
RAM:00000078   STR     R2, [R1]
RAM:0000007A   ADDS    R1, #0x38 ; '8'
RAM:0000007C   STR     R2, [R1]
RAM:0000007E   MOVS    R2, #0xE0000000
RAM:00000082   LDR     R1, =0x600066A0
RAM:00000084   STR     R2, [R1]
RAM:00000086   MOVS    R1, #0
RAM:00000088   MOVS    R0, #0xE
RAM:0000008A   B       pop_r2_mov_pc_lr
RAM:0000008A ; End of function sub_48
RAM:0000008A
RAM:0000008C
RAM:0000008C ; =============== S U B R O U T I N E =======================================
RAM:0000008C
RAM:0000008C
RAM:0000008C sub_8C
RAM:0000008C   LDR     R0, [R1,#0x18]                ; 1 1820df22     3040     df22 : svc #0x22 (offset 0x8c)
RAM:0000008E   MOVS    R2, #1
RAM:00000090   ORRS    R0, R2
RAM:00000092   B       pop_r2_mov_pc_lr
RAM:00000092 ; End of function sub_8C
RAM:00000092
RAM:00000094
RAM:00000094 ; =============== S U B R O U T I N E =======================================
RAM:00000094
RAM:00000094
RAM:00000094 sub_94
RAM:00000094   LDR     R2, [R4,#0x50]                ; 2 3797df26     6f2e     df26 : svc #0x26 (offset 0x94)
RAM:00000096   ADDS    R2, R2, #2
RAM:00000098   STR     R2, [R4,#0x50]
RAM:0000009A   SUBS    R1, #0x80
RAM:0000009C   STR     R1, [R4,#0x34]
RAM:0000009E   B       pop_r2_mov_pc_lr
RAM:0000009E ; End of function sub_94
RAM:0000009E
RAM:000000A0
RAM:000000A0 ; =============== S U B R O U T I N E =======================================
RAM:000000A0
RAM:000000A0
RAM:000000A0 sub_A0
RAM:000000A0
RAM:000000A0 ; FUNCTION CHUNK AT RAM:00000148 SIZE 00000004 BYTES
RAM:000000A0
RAM:000000A0   MOVS    R0, #0x70000000               ; 4  42bdf2c      856     df2c : svc #0x2c (offset 0xa0)
RAM:000000A4   LDR     R6, =dword_7000EF14
RAM:000000A6   LDR     R2, =dword_7000E5B4
RAM:000000A8   LDR     R2, [R2]
RAM:000000AA   CMP     R2, #0
RAM:000000AC   BEQ     loc_B4
RAM:000000AE   LDR     R2, [R6]
RAM:000000B0   STR     R2, [R0,#8]
RAM:000000B2   B       loc_BC
RAM:000000B4 ; ---------------------------------------------------------------------------
RAM:000000B4
RAM:000000B4 loc_B4                                  ; CODE XREF: sub_A0+C↑j
RAM:000000B4   LDR     R2, [R0,#8]
RAM:000000B6   LSRS    R0, R0, #0x12
RAM:000000B8   ORRS    R2, R0
RAM:000000BA   STR     R2, [R6]
RAM:000000BC
RAM:000000BC loc_BC                                  ; CODE XREF: sub_A0+12↑j
RAM:000000BC   LDR     R6, =dword_7000E9C0
RAM:000000BE   LDR     R0, [R6]
RAM:000000C0   MOVS    R2, #0x4000
RAM:000000C4   ORRS    R2, R0
RAM:000000C6   STR     R2, [R6]
RAM:000000C8   LDR     R0, [R5,#0x10]
RAM:000000CA   B       pop_r2_mov_pc_lr
RAM:000000CA ; End of function sub_A0
RAM:000000CA
RAM:000000CC
RAM:000000CC ; =============== S U B R O U T I N E =======================================
RAM:000000CC
RAM:000000CC
RAM:000000CC sub_CC
RAM:000000CC   MOVS    R2, #0xF000000                ; 5 37aadf42     6f54     df42 : svc #0x42 (offset 0xcc)
RAM:000000D0   BICS    R1, R2
RAM:000000D2   STR     R1, [R4,#0x10]
RAM:000000D4   LDR     R1, [R4,#0x50]
RAM:000000D6   MOVS    R2, #7
RAM:000000D8   BICS    R1, R2
RAM:000000DA   STR     R1, [R4,#0x50]
RAM:000000DC   B       pop_r2_mov_pc_lr
RAM:000000DC ; End of function sub_CC
RAM:000000DC
RAM:000000DE
RAM:000000DE ; =============== S U B R O U T I N E =======================================
RAM:000000DE
RAM:000000DE
RAM:000000DE sub_DE
RAM:000000DE   LDR     R2, =dword_7000FA9C           ; 6  972df4b     12e4     df4b : svc #0x4b (offset 0xde)
RAM:000000E0   LDR     R2, [R2]
RAM:000000E2   LSRS    R2, R2, #8
RAM:000000E4   LSLS    R2, R2, #1
RAM:000000E6   LDR     R1, [R4]
RAM:000000E8   BICS    R1, R2
RAM:000000EA   STR     R1, [R4]
RAM:000000EC   CMP     R0, #0
RAM:000000EE   B       pop_r2_mov_pc_lr
RAM:000000EE ; End of function sub_DE
RAM:000000EE
RAM:000000F0
RAM:000000F0 ; =============== S U B R O U T I N E =======================================
RAM:000000F0
RAM:000000F0
RAM:000000F0 sub_F0
RAM:000000F0
RAM:000000F0 arg_0=  0
RAM:000000F0
RAM:000000F0   LDR     R0, =0x400049F0               ; 7 2293df54     4526     df54 : svc #0x54 (offset 0xf0)
RAM:000000F2   LDR     R2, [R0]
RAM:000000F4   STR     R2, [SP,#arg_0]
RAM:000000F6   LDR     R0, =0x40010000
RAM:000000F8   LSRS    R2, R2, #17
RAM:000000FA   BEQ     pop_r2_mov_pc_lr              ; if ([0x400049F0] >> 17) == 0) {
RAM:000000FA                                         ;   r2 = [0x400049F0];
RAM:000000FA                                         ; } else {
RAM:000000FC   LDR     R0, =APBDEV_PMC_CNTRL_0
RAM:000000FE   STR     R4, [R0]                      ;  write APBDEV_PMC_CNTRL
RAM:00000100
RAM:00000100 loc_100                                 ; CODE XREF: sub_F0:loc_100↓j
RAM:00000100   B       loc_100                       ;  hang
RAM:00000100 ; End of function sub_F0                ; }
RAM:00000100
RAM:00000102
RAM:00000102 ; =============== S U B R O U T I N E =======================================
RAM:00000102
RAM:00000102
RAM:00000102 sub_102
RAM:00000102
RAM:00000102 arg_0=  0
RAM:00000102
RAM:00000102   LDR     R2, =0x40010220               ; 8 21fadf5d     43f4     df5d : svc #0x5d (offset 0x102)
RAM:00000104   STR     R2, [SP,#arg_0]               ; set r2 retval = [0x40010220]
RAM:00000106   LDR     R2, [R2,#0x18]
RAM:00000108   ADDS    R0, #0xFC
RAM:0000010A   STR     R2, [R0,#0x1C]                ; [r0+0x118] = [0x40010220 + 0x18]
RAM:0000010C   B       pop_r2_mov_pc_lr
RAM:0000010C ; End of function sub_102
RAM:0000010C
RAM:0000010C ; ---------------------------------------------------------------------------
RAM:0000010E   DCB    0
RAM:0000010F   DCB    0
RAM:00000110 off_110 DCD 0x60006410                  ; DATA XREF: sub_48+4↑r
RAM:00000114 off_114 DCD 0x600060F8                  ; DATA XREF: sub_48+A↑r
RAM:00000118 off_118 DCD 0x60006284                  ; DATA XREF: sub_48+10↑r
RAM:0000011C off_11C DCD 0x60006554                  ; DATA XREF: sub_48+26↑r
RAM:00000120 off_120 DCD 0x60006148                  ; DATA XREF: sub_48+2E↑r
RAM:00000124 off_124 DCD 0x600066A0                  ; DATA XREF: sub_48+3A↑r
RAM:00000128 off_128 DCD dword_7000EF14              ; DATA XREF: sub_A0+4↑r
RAM:0000012C off_12C DCD dword_7000E5B4              ; DATA XREF: sub_A0+6↑r
RAM:00000130 off_130 DCD dword_7000E9C0              ; DATA XREF: sub_A0:loc_BC↑r
RAM:00000134 off_134 DCD dword_7000FA9C              ; DATA XREF: sub_DE↑r
RAM:00000138 off_138 DCD 0x400049F0                  ; DATA XREF: sub_F0↑r
RAM:0000013C dword_13C DCD 0x40010000                ; DATA XREF: sub_F0+6↑r
RAM:00000140 off_140 DCD APBDEV_PMC_CNTRL_0          ; DATA XREF: sub_F0+C↑r
RAM:00000144 off_144 DCD 0x40010220                  ; DATA XREF: sub_102↑r
RAM:00000148 ; ---------------------------------------------------------------------------
RAM:00000148 ; START OF FUNCTION CHUNK FOR sub_A0
RAM:00000148
RAM:00000148 pop_r2_mov_pc_lr                        ; CODE XREF: sub_48+42↑j
RAM:00000148                                         ; sub_8C+6↑j ...
RAM:00000148   POP     {R2}
RAM:0000014A   MOV     PC, LR
RAM:0000014A ; END OF FUNCTION CHUNK FOR sub_A0

The last 4 patches are exclusive to the Switch, while the remaining ones are often included in most Tegra210 based devices.

ipatch 0

This patch configures clock enables and clock gate overrides for new hardware.

 u32 CLK_ENB_H_SET = 0x60006328;
 u32 CLK_ENB_L_SET = 0x60006320;
 u32 CLK_ENB_U_SET = 0x60006330; 
 u32 CLK_ENB_V_SET = 0x60006440;
 u32 CLK_ENB_W_SET = 0x60006448;
 u32 CLK_ENB_X_SET = 0x60006284;
 u32 CLK_ENB_Y_SET = 0x6000629C;
 u32 LVL2_CLK_GATE_OVRA = 0x600060F8;
 u32 LVL2_CLK_GATE_OVRB = 0x600060FC;
 u32 LVL2_CLK_GATE_OVRC = 0x600063A0;
 u32 LVL2_CLK_GATE_OVRD = 0x600063A4;
 u32 LVL2_CLK_GATE_OVRE = 0x60006554;
 u32 CLK_SOURCE_VI = 0x60006148;
 u32 CLK_SOURCE_HOST1X = 0x60006180;
 u32 CLK_SOURCE_NVENC = 0x600066A0; 

 // Set all clock enables and overrides
 *(u32 *)CLK_ENB_V_SET = 0xFFFFFFFF;
 *(u32 *)CLK_ENB_W_SET = 0xFFFFFFFF;
 *(u32 *)LVL2_CLK_GATE_OVRA = 0xFFFFFFFF;
 *(u32 *)LVL2_CLK_GATE_OVRB = 0xFFFFFFFF;
 *(u32 *)CLK_ENB_X_SET = 0xFFFFFFFF;
 *(u32 *)CLK_ENB_Y_SET = 0xFFFFFFFF;
 *(u32 *)CLK_ENB_L_SET = 0xFFFFFFFF;
 *(u32 *)CLK_ENB_H_SET = 0xFFFFFFFF;
 *(u32 *)CLK_ENB_U_SET = 0xFFFFFFFF;
 *(u32 *)LVL2_CLK_GATE_OVRC = 0xFFFFFFFF;
 *(u32 *)LVL2_CLK_GATE_OVRD = 0xFFFFFFFF;
 *(u32 *)LVL2_CLK_GATE_OVRE = 0xFFFFFFFF;

 // Set VI, HOST1X and NVENC clock sources to CLK_M
 *(u32 *)CLK_SOURCE_VI = 0xA0000000;
 *(u32 *)CLK_SOURCE_HOST1X = 0xA0000000;
 *(u32 *)CLK_SOURCE_NVENC = 0xE0000000;

 /*
     Untranslated instructions:

     MOVS    R1, #0
     MOVS    R0, #0xE
 */
 
 return;

ipatch 1

This patch is a bugfix.

LP0 resume code expects APBDEV_PMC_SCRATCH190_0 to be set to 0x01, but the bootrom didn't set it.

 u32 APBDEV_PMC_SCRATCH190_0 = 0x7000EC18;
 u32 pmc_scratch190_val = *(u32 *)APBDEV_PMC_SCRATCH190_0;
 
 return (pmc_scratch190_val | 0x01);

ipatch 2

This patch adjusts USB configurations.

 u32 USB1_UTMIP_SPARE_CFG0_0 = 0x7D000834;
 u32 USB1_UTMIP_BIAS_CFG2_0 = 0x7D000850;
 
 // Increase UTMIP_HSSQUELCH_LEVEL_NEW by 0x02
 *(u32 *)USB1_UTMIP_BIAS_CFG2_0 += 0x02;

 // Clear FUSE_HS_IREF_CAP_CFG
 *(u32 *)USB1_UTMIP_SPARE_CFG0_0 = ((*(u32 *)USB1_UTMIP_SPARE_CFG0_0 & ~(0x118)) - 0x80);
 
 return;

ipatch 3

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 set to revision 7.0) this patch has been replaced with a fix for 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.

ipatch 4

This patch allows backing up and restoring strapping options for warmboot.

 u32 APBDEV_PMC_SCRATCH0_0 = 0x7000E450;
 u32 APBDEV_PMC_RST_STATUS_0 = 0x7000E5B4;
 u32 APBDEV_PMC_SEC_DISABLE8_0 = 0x7000E9C0;
 u32 APBDEV_PMC_SECURE_SCRATCH111_0 = 0x7000EF14;
 u32 APB_MISC_PP_STRAPPING_OPT_A_0 = 0x70000008; 

 u32 reset_status = *(u32 *)APBDEV_PMC_RST_STATUS_0;

 // Check for regular power on
 if (reset_status == 0)
 {    
     // Set all buttons in RCM_STRAPS and backup to PMC scratch
     *(u32 *)APBDEV_PMC_SECURE_SCRATCH111_0 = (*(u32 *)APB_MISC_PP_STRAPPING_OPT_A_0 | 0x1C00);
 }
 else
 {
     // Restore strapping options from PMC scratch
     *(u32 *)APB_MISC_PP_STRAPPING_OPT_A_0 = *(u32 *)APBDEV_PMC_SECURE_SCRATCH111_0;
 }

 // Disable write access to APBDEV_PMC_SECURE_SCRATCH111_0
 *(u32 *)APBDEV_PMC_SEC_DISABLE8_0 |= 0x4000;
 
 return *(u32 *)APBDEV_PMC_SCRATCH0_0;

ipatch 5

This patch adjusts USB configurations.

 u32 USB1_UTMIP_HSRX_CFG0_0 = 0x7D000810;
 u32 USB1_UTMIP_BIAS_CFG2_0 = 0x7D000850;

 // Clear UTMIP_IDLE_WAIT, UTMIP_ELASTIC_LIMIT and UTMIP_PCOUNT_UPDN_DIV,
 // and set UTMIP_IDLE_WAIT to 0x11 and UTMIP_ELASTIC_LIMIT to 0x10 
 *(u32 *)USB1_UTMIP_HSRX_CFG0_0 = ((*(u32 *)USB1_UTMIP_HSRX_CFG0_0 & ~(0xF8000) + 0x88000 & ~(0x7C00) + 0x4000) & ~(0xF000000));
 
 // Clear UTMIP_HSSQUELCH_LEVEL_NEW
 *(u32 *)USB1_UTMIP_BIAS_CFG2_0 &= ~(0x07);
 
 return;

ipatch 6

This patch is a factory backdoor.

It allows controlling the debug authentication configuration using a fuse.

 u32 FUSE_ODM_INFO = 0x7000FA9C;

 u32 odm_info = *(u32 *)FUSE_ODM_INFO;
 debug_auth_override_val = ((odm_info >> 0x08) << 0x01);

 // Override debug authentication value stored in IRAM
 *(u32 *)0x400028E4 &= ~(debug_auth_override_val);

 /*
     Untranslated instructions:
 
     CMP     R0, #0
 */
 
 return;

ipatch 7

This patch is a bugfix.

It prevents overflowing IRAM (0x40010000) when copying the warmboot binary from DRAM.

 u32 APBDEV_PMC_CNTRL_0 = 0x7000E400;
 
 u32 warmboot_header_addr = 0x400049F0;
 u32 warmboot_bin_size = *(u32 *)warmboot_bin_header_addr;
 
 // Invalid warmboot binary size
 if (warmboot_size >> 0x11)
 {
     // Assert MAIN_RST
     // 0x40004BF0 comes from R4 and the bootrom doesn't bother to change it
     *(u32 *)APBDEV_PMC_CNTRL_0 = 0x40004BF0;

     // Deadlock
     while(1);
 }
 
 /*
     Untranslated instructions:
 
     LDR     R0, =0x40010000
     LDR     R2, [warmboot_bin_header_addr]
 */

 return;

ipatch 8

This patch is a bugfix.

It sets the correct warmboot binary entrypoint address for RSA signature verification, which would be done in DRAM instead of IRAM without this patch.

 u32 warmboot_addr_ptr = 0x40010238;
 u32 warmboot_entry_addr_ptr = 0x40004C28;

 *(u32 *)warmboot_entry_addr_ptr = *(u32 *)warmboot_addr_ptr;

 /*
     Untranslated instructions:
 
     LDR     R2, [warmboot_addr_ptr]
 */
 
 return;

ipatches 9 and 10

These patches modify the 256-bit Secure Provisioning AES key with index 0x3A.

ipatch 11

This patch forces the value of SE_TZRAM_SECURITY to be 0x01 instead of restoring it from the saved SE context.

Anti-downgrade

The first bootloader verifies FUSE_RESERVED_ODM7 to prevent downgrading. How many fuses are expected to be burnt depends the device's unit type as below.

System version Expected number of burnt fuses (retail) Expected number of burnt fuses (non-retail)
1.0.0 1 0
2.0.0-2.3.0 2 0
3.0.0 3 1
3.0.1-3.0.2 4 1
4.0.0-4.1.0 5 1
5.0.0-5.1.0 6 1
6.0.0-6.1.0 7 1
6.2.0 8 1
7.0.0-8.0.1 9 1
8.1.0 10 1

If too many fuses are burnt the bootloader will panic immediately.

If too few are burnt, the bootloader will enable fuse programming and write the expected value to fuse indexes 0x3A and 0x3C. Afterwards, fuse programming is disabled and the panic value 0x21 is written to PMC_SCRATCH200 register (0x7000EC40). Finally, the watchdog timer is initialized and programmed to force a reset.

On a subsequent boot, after the anti-downgrade fuses are checked again, the PMC_RST_STATUS register (0x7000E5B4) is checked and if set to 0x01 (watchdog reset) the PMC_SCRATCH200 register (0x7000EC40) will be checked for the panic value 0x21. PMC_RST_STATUS will only be set back to 0 (power on reset) if the fuse count matches the new expected value, otherwise the system will panic.