Fuses

From Nintendo Switch Brew
Revision as of 07:33, 12 October 2017 by $andeor (talk | contribs)
Jump to navigation Jump to search

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_TSENSOR_1 0x7000F984
FUSE_TSENSOR_2 0x7000F988
FUSE_CP_REV 0x7000F990
FUSE_TSENSOR_0 0x7000F998
FUSE_FIRST_BOOTROM_PATCH_SIZE_REG 0x7000F99C
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_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 (0 = factory 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)
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).

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

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.

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

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.