Changes

3,049 bytes added ,  20:24, 19 August 2017
no edit summary
Line 1: Line 1: −
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.
+
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 ==
 
== Registers ==
 
Below is a list of fuse driver registers used by the Switch's bootloaders.
 
Below is a list of fuse driver registers used by the Switch's bootloaders.
    +
=== Driver registers ===
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
 
!  Name
 
!  Name
 
!  Address
 
!  Address
!  Width
   
|-
 
|-
 
| [[#FUSE_CTRL|FUSE_CTRL]]
 
| [[#FUSE_CTRL|FUSE_CTRL]]
 
| 0x7000F800
 
| 0x7000F800
| 0x04
   
|-
 
|-
 
| [[#FUSE_REG_ADDR|FUSE_REG_ADDR]]
 
| [[#FUSE_REG_ADDR|FUSE_REG_ADDR]]
 
| 0x7000F804
 
| 0x7000F804
| 0x04
   
|-
 
|-
 
| [[#FUSE_REG_READ|FUSE_REG_READ]]
 
| [[#FUSE_REG_READ|FUSE_REG_READ]]
 
| 0x7000F808
 
| 0x7000F808
| 0x04
   
|-
 
|-
 
| [[#FUSE_REG_WRITE|FUSE_REG_WRITE]]
 
| [[#FUSE_REG_WRITE|FUSE_REG_WRITE]]
 
| 0x7000F80C
 
| 0x7000F80C
| 0x04
+
|-
 +
| FUSE_TIME_RD1
 +
| 0x7000F810
 +
|-
 +
| FUSE_TIME_RD2
 +
| 0x7000F814
 +
|-
 +
| FUSE_TIME_PGM1
 +
| 0x7000F818
 
|-
 
|-
 
| [[#FUSE_TIME_PGM2|FUSE_TIME_PGM2]]
 
| [[#FUSE_TIME_PGM2|FUSE_TIME_PGM2]]
 
| 0x7000F81C
 
| 0x7000F81C
| 0x04
+
|-
 +
| FUSE_PRIV2INTFC
 +
| 0x7000F820
 +
|-
 +
| FUSE_FUSEBYPASS
 +
| 0x7000F824
 +
|-
 +
| FUSE_PRIVATEKEYDISABLE
 +
| 0x7000F828
 
|-
 
|-
 
| [[#FUSE_DIS_PGM|FUSE_DIS_PGM]]
 
| [[#FUSE_DIS_PGM|FUSE_DIS_PGM]]
 
| 0x7000F82C
 
| 0x7000F82C
| 0x04
   
|-
 
|-
 
| [[#FUSE_WRITE_ACCESS|FUSE_WRITE_ACCESS]]
 
| [[#FUSE_WRITE_ACCESS|FUSE_WRITE_ACCESS]]
 
| 0x7000F830
 
| 0x7000F830
| 0x04
+
|-
 +
| FUSE_PWR_GOOD_SW
 +
| 0x7000F834
 +
|-
 +
|}
 +
 
 +
==== 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_SENSE mode flushes programmed values into the [[Fuses#Cache_registers|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 ===
 +
{| class="wikitable" border="1"
 +
!  Name
 +
!  Address
 
|-
 
|-
 
| [[#FUSE_SKU_INFO|FUSE_SKU_INFO]]
 
| [[#FUSE_SKU_INFO|FUSE_SKU_INFO]]
 
| 0x7000F910
 
| 0x7000F910
| 0x04
   
|-
 
|-
| [[#FUSE_SPEEDO_1_CALIB_0|FUSE_SPEEDO_1_CALIB_0]]
+
| 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|FUSE_SOC_SPEEDO_1]]
 
| 0x7000F938
 
| 0x7000F938
| 0x04
   
|-
 
|-
| FUSE_GCPLEX_CONFIG_FUSE_0
+
| FUSE_SOC_SPEEDO_2
 +
| 0x7000F93C
 +
|-
 +
| FUSE_SOC_IDDQ
 +
| 0x7000F940
 +
|-
 +
| 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
 
| 0x7000F9C8
| 0x04
   
|-
 
|-
| FUSE_GCPLEX_CONFIG_FUSE_1
+
| FUSE_RESERVED_ODM1
 
| 0x7000F9CC
 
| 0x7000F9CC
| 0x04
   
|-
 
|-
| FUSE_GCPLEX_CONFIG_FUSE_2
+
| FUSE_RESERVED_ODM2
 
| 0x7000F9D0
 
| 0x7000F9D0
| 0x04
   
|-
 
|-
| FUSE_GCPLEX_CONFIG_FUSE_3
+
| FUSE_RESERVED_ODM3
 
| 0x7000F9D4
 
| 0x7000F9D4
| 0x04
   
|-
 
|-
| [[#FUSE_GCPLEX_CONFIG_FUSE_4|FUSE_GCPLEX_CONFIG_FUSE_4]]
+
| [[#FUSE_RESERVED_ODM4|FUSE_RESERVED_ODM4]]
 
| 0x7000F9D8
 
| 0x7000F9D8
| 0x04
   
|-
 
|-
| FUSE_GCPLEX_CONFIG_FUSE_5
+
| FUSE_RESERVED_ODM5
 
| 0x7000F9DC
 
| 0x7000F9DC
| 0x04
   
|-
 
|-
| [[#FUSE_BURNT_FUSE_COUNT_0|FUSE_BURNT_FUSE_COUNT_0]]
+
| [[#FUSE_RESERVED_ODM6|FUSE_RESERVED_ODM6]]
 
| 0x7000F9E0
 
| 0x7000F9E0
| 0x04
   
|-
 
|-
| [[#FUSE_BURNT_FUSE_COUNT_1|FUSE_BURNT_FUSE_COUNT_1]]
+
| [[#FUSE_RESERVED_ODM7|FUSE_RESERVED_ODM7]]
 
| 0x7000F9E4
 
| 0x7000F9E4
| 0x04
   
|-
 
|-
| [[#FUSE_MASTER_KEY_VER|FUSE_MASTER_KEY_VER]]
+
| 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|FUSE_SPARE_BIT_5]]
 
| 0x7000FB94
 
| 0x7000FB94
| 0x04
   
|-
 
|-
|}
+
| FUSE_SPARE_BIT_6
 
+
| 0x7000FB98
=== FUSE_CTRL ===
+
|-
{| class="wikitable" border="1"
+
| FUSE_SPARE_BIT_7
!  Bits
+
| 0x7000FB9C
!  Description
+
|-
 +
| 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
 
|-
 
|-
| 0-1
+
| FUSE_SPARE_BIT_30
| Fuse command (1 = FUSE_READ; 2 = FUSE_WRITE; 3 = FUSE_SENSE)
+
| 0x7000FBF8
 
|-
 
|-
| 26
+
| FUSE_SPARE_BIT_31
| Fuse power down mode flag (FUSE_CTRL_PD)
+
| 0x7000FBFC
 
|-
 
|-
 
|}
 
|}
   −
Before fuse reading/writing the power down mode must be disabled.
+
==== FUSE_SKU_INFO ====
 
  −
=== 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).
 
Stores the SKU ID (must be 0x83).
   −
=== FUSE_SPEEDO_1_CALIB_0 ===
+
==== FUSE_SOC_SPEEDO_1 ====
Seems to store the bootrom patch version.
+
Stores the bootrom patch version.
   −
=== FUSE_GCPLEX_CONFIG_FUSE_4 ===
+
==== FUSE_RESERVED_ODM4 ====
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
 
!  Bits
 
!  Bits
Line 143: Line 358:  
This stores some device configuration parameters.
 
This stores some device configuration parameters.
   −
=== FUSE_MASTER_KEY_VER ===
+
==== 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.
 
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).
 
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 ==
 
== eFuses ==
 
The actual hardware fuses can be programmed through the fuse driver after enabling fuse programming.
 
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).
+
Below is a list of common fuse indexes 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).
+
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).
    
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
 
!  Name
 
!  Name
Offset
+
Index
 
!  Bits
 
!  Bits
 
|-
 
|-
Line 216: Line 431:  
=== odm_reserved ===
 
=== odm_reserved ===
 
The first bootloader only burns fuses in this region.
 
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.
+
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|FUSE_RESERVED_ODM6]] and [[#FUSE_RESERVED_ODM7|FUSE_RESERVED_ODM7]].
    
== Anti-downgrade ==
 
== Anti-downgrade ==
The first bootloader verifies [[#FUSE_BURNT_FUSE_COUNT_1|FUSE_BURNT_FUSE_COUNT_1]] to prevent downgrading.
+
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.
 
How many fuses are expected to be burnt depends the device's unit type as below.
   Line 247: Line 462:  
If too many fuses are burnt the bootloader will panic immediately.
 
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.
+
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, 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.
+
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.
 
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.