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_CTRL | 0x7000F800 |
FUSE_REG_ADDR | 0x7000F804 |
FUSE_REG_READ | 0x7000F808 |
FUSE_REG_WRITE | 0x7000F80C |
FUSE_TIME_RD1 | 0x7000F810 |
FUSE_TIME_RD2 | 0x7000F814 |
FUSE_TIME_PGM1 | 0x7000F818 |
FUSE_TIME_PGM2 | 0x7000F81C |
FUSE_PRIV2INTFC | 0x7000F820 |
FUSE_FUSEBYPASS | 0x7000F824 |
FUSE_PRIVATEKEYDISABLE | 0x7000F828 |
FUSE_DIS_PGM | 0x7000F82C |
FUSE_WRITE_ACCESS | 0x7000F830 |
FUSE_PWR_GOOD_SW | 0x7000F834 |
FUSE_CTRL
Bits | Description |
---|---|
0-1 | Fuse command (1 = FUSE_READ; 2 = FUSE_WRITE; 3 = FUSE_SENSE) |
26 | Fuse power down mode flag (FUSE_CTRL_PD) |
Before fuse reading/writing the power down mode must be disabled. FUSE_SENSE mode flushes programmed values into the cache registers.
FUSE_REG_ADDR
This register takes the address of the fuse to be read/written/sensed.
FUSE_REG_READ
This register receives the value read from the fuse.
FUSE_REG_WRITE
This register takes the value to be written to the fuse.
FUSE_TIME_PGM2
This register takes the fuse programming pulse (0xC0 == 19200 kHz).
FUSE_DIS_PGM
If set to 0x01, this register disables fuse programming.
FUSE_WRITE_ACCESS
If set to 0x01, this register disables software writes to the fuse driver registers.
Cache registers
Name | Address |
---|---|
FUSE_SKU_INFO | 0x7000F910 |
FUSE_CPU_SPEEDO_0 | 0x7000F914 |
FUSE_CPU_IDDQ | 0x7000F918 |
FUSE_FT_REV | 0x7000F928 |
FUSE_CPU_SPEEDO_1 | 0x7000F92C |
FUSE_CPU_SPEEDO_2 | 0x7000F930 |
FUSE_SOC_SPEEDO_0 | 0x7000F934 |
FUSE_SOC_SPEEDO_1 | 0x7000F938 |
FUSE_SOC_SPEEDO_2 | 0x7000F93C |
FUSE_SOC_IDDQ | 0x7000F940 |
FUSE_FA | 0x7000F948 |
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_TSENSOR_1 | 0x7000F984 |
FUSE_TSENSOR_2 | 0x7000F988 |
FUSE_CP_REV | 0x7000F990 |
FUSE_TSENSOR_0 | 0x7000F998 |
FUSE_FIRST_BOOTROM_PATCH_SIZE_REG | 0x7000F99C |
FUSE_PRIVATE_KEY0 | 0x7000F9A4 |
FUSE_PRIVATE_KEY1 | 0x7000F9A8 |
FUSE_PRIVATE_KEY2 | 0x7000F9AC |
FUSE_PRIVATE_KEY3 | 0x7000F9B0 |
FUSE_PRIVATE_KEY4 | 0x7000F9B4 |
FUSE_VP8_ENABLE | 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_SKU_USB_CALIB | 0x7000F9F0 |
FUSE_SKU_DIRECT_CONFIG | 0x7000F9F4 |
FUSE_VENDOR_CODE | 0x7000FA00 |
FUSE_FAB_CODE | 0x7000FA04 |
FUSE_LOT_CODE_0 | 0x7000FA08 |
FUSE_LOT_CODE_1 | 0x7000FA0C |
FUSE_WAFER_ID | 0x7000FA10 |
FUSE_X_COORDINATE | 0x7000FA14 |
FUSE_Y_COORDINATE | 0x7000FA18 |
FUSE_SATA_CALIB | 0x7000FA24 |
FUSE_GPU_IDDQ | 0x7000FA28 |
FUSE_TSENSOR_3 | 0x7000FA2C |
FUSE_OPT_SUBREVISION | 0x7000FA48 |
FUSE_TSENSOR_4 | 0x7000FA54 |
FUSE_TSENSOR_5 | 0x7000FA58 |
FUSE_TSENSOR_6 | 0x7000FA5C |
FUSE_TSENSOR_7 | 0x7000FA60 |
FUSE_OPT_PRIV_SEC_DIS | 0x7000FA64 |
FUSE_TSENSOR_COMMON | 0x7000FA80 |
FUSE_TSENSOR_8 | 0x7000FAD4 |
FUSE_RESERVED_CALIB | 0x7000FB04 |
FUSE_TSENSOR_9 | 0x7000FB1C |
FUSE_USB_CALIB_EXT | 0x7000FB50 |
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_FA
Stores failure analysis mode.
FUSE_SOC_SPEEDO_1
Stores the bootrom patch version.
FUSE_RESERVED_ODM4
Bits | Description |
---|---|
0-1 | Unit type (3 = debug; 0 = retail) |
2 | Unknown config (must be 1 on retail) |
3 | Memory Configuration |
8 | Unknown config mask (must be 0 on retail) |
9 | Unit type mask (0 = debug; 1 = retail) |
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_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 retail master key seeds (only the last one matches the retail master key seed).
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_PUBLIC_KEY
This stores the SHA256 hash of the 2048-bit RSA key expected at BCT+0x210.
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 |
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 revision stored in FUSE_CP_REV indicates the unique set of values stored in ipatch fuses.
The following represents the patch data dumped from a 2.0.0 Switch console:
Patch address Patch data 0x001016AE 0xDF00 // svc #0x00 (offset 0x48) 0x00103040 0xDF22 // svc #0x22 (offset 0x8c) 0x00106F2E 0xDF26 // svc #0x26 (offset 0x94) 0x0010FB3C 0x2000 // movs r0, #0x00 0x00100856 0xDF2C // svc #0x2c (offset 0xa0) 0x00106F54 0xDF42 // svc #0x42 (offset 0xcc) 0x001012E4 0xDF4B // svc #0x4b (offset 0xde) 0x00104526 0xDF54 // svc #0x54 (offset 0xf0) 0x001043F4 0xDF5D // svc #0x5d (offset 0x102) 0x00117744 0xAC57 // data 0x00117758 0x3D19 // data 0x00103D2A 0x2001 // movs r0, #0x01
The last 4 patches are exclusive to the Switch, while the remaining ones are often included in most Tegra210 based devices.
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 | 5 | 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 a magic value (0x21 == TEGRA210) 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 magic value (0x21 == TEGRA210). 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.