Changes

5,601 bytes added ,  19:57, 1 August 2017
Created page with "The Nintendo Switch makes use of Tegra's efuse driver for a number of operations. This driver is mapped to physical address 0x7000F800 with a total size of 0x400 bytes and exp..."
The Nintendo Switch makes use of Tegra's efuse 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 and other miscellaneous functions.

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

{| class="wikitable" border="1"
! Name
! Address
! Width
|-
| [[#FUSE_CTRL|FUSE_CTRL]]
| 0x7000F800
| 0x04
|-
| [[#FUSE_REG_ADDR|FUSE_REG_ADDR]]
| 0x7000F804
| 0x04
|-
| [[#FUSE_REG_READ|FUSE_REG_READ]]
| 0x7000F808
| 0x04
|-
| [[#FUSE_REG_WRITE|FUSE_REG_WRITE]]
| 0x7000F80C
| 0x04
|-
| [[#FUSE_TIME_PGM2|FUSE_TIME_PGM2]]
| 0x7000F81C
| 0x04
|-
| [[#FUSE_DIS_PGM|FUSE_DIS_PGM]]
| 0x7000F82C
| 0x04
|-
| [[#FUSE_WRITE_ACCESS|FUSE_WRITE_ACCESS]]
| 0x7000F830
| 0x04
|-
| [[#FUSE_SKU_INFO|FUSE_SKU_INFO]]
| 0x7000F910
| 0x04
|-
| [[#FUSE_SPEEDO_1_CALIB_0|FUSE_SPEEDO_1_CALIB_0]]
| 0x7000F938
| 0x04
|-
| FUSE_GCPLEX_CONFIG_FUSE_0
| 0x7000F9C8
| 0x04
|-
| FUSE_GCPLEX_CONFIG_FUSE_1
| 0x7000F9CC
| 0x04
|-
| FUSE_GCPLEX_CONFIG_FUSE_2
| 0x7000F9D0
| 0x04
|-
| FUSE_GCPLEX_CONFIG_FUSE_3
| 0x7000F9D4
| 0x04
|-
| [[#FUSE_GCPLEX_CONFIG_FUSE_4|FUSE_GCPLEX_CONFIG_FUSE_4]]
| 0x7000F9D8
| 0x04
|-
| FUSE_GCPLEX_CONFIG_FUSE_5
| 0x7000F9DC
| 0x04
|-
| [[#FUSE_BURNT_FUSE_COUNT_0|FUSE_BURNT_FUSE_COUNT_0]]
| 0x7000F9E0
| 0x04
|-
| [[#FUSE_BURNT_FUSE_COUNT_1|FUSE_BURNT_FUSE_COUNT_1]]
| 0x7000F9E4
| 0x04
|-
| [[#FUSE_MASTER_KEY_VER|FUSE_MASTER_KEY_VER]]
| 0x7000FB94
| 0x04
|-
|}

=== FUSE_CTRL ===
{| class="wikitable" border="1"
! 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_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 registers.

=== FUSE_SKU_INFO ===
Stores the SKU ID (must be 0x83).

=== FUSE_SPEEDO_1_CALIB_0 ===
Seems to store the bootrom patch version.

=== FUSE_GCPLEX_CONFIG_FUSE_4 ===
{| class="wikitable" border="1"
! 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_MASTER_KEY_VER ===
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_BURNT_FUSE_COUNT_0 ===
This register returns the value programmed into offset 0x3A of the fuse array.

=== FUSE_BURNT_FUSE_COUNT_1 ===
This register returns the value programmed into offset 0x3C of the fuse array.

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

Below is a list of common fuse offsets used by Tegra devices (and applicable to the Switch).
Note that offsets are relative to the start of the fuse array and a single fuse write operation always writes the same word at both (fuse_offset + 0) and (fuse_offset + 1).

{| class="wikitable" border="1"
! Name
! Offset
! 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|odm_reserved]]
| 0x2E
| 256
|-
| pkc_disable
| 0x52
| 1
|-
|}

=== odm_reserved ===
The first bootloader only burns fuses in this region.
Both fuse offsets 0x3A (odm_reserved + 0x0C) and 0x3C (odm_reserved + 0x0E) are used for anti-downgrade control.

== Anti-downgrade ==
The fisrt bootloader verifies [[#FUSE_BURNT_FUSE_COUNT_1|FUSE_BURNT_FUSE_COUNT_1]] 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 (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
| 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 offsets 0x3A and 0x3C. Afterwards, fuse programming is disabled and a magic value (0x21 == TEGRA210) is written to an unknown PMC 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, PMC_RST_STATUS (0x7000E5B4) is checked and if set to 0x01 (watchdog reset) the unknown PMC register (0x7000EC40) will be checked for the magic 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.