fusee/sept: update fuse driver code

This commit is contained in:
hexkyz 2020-11-13 17:46:25 +00:00 committed by SciresM
parent 5e342d8c52
commit b625fd324e
12 changed files with 466 additions and 219 deletions

View file

@ -24,11 +24,6 @@
#include "pmc.h" #include "pmc.h"
#include "timers.h" #include "timers.h"
/* Prototypes for internal commands. */
void fuse_enable_power(void);
void fuse_disable_power(void);
void fuse_wait_idle(void);
/* Initialize the fuse driver */ /* Initialize the fuse driver */
void fuse_init(void) { void fuse_init(void) {
/* Make all fuse registers visible, disable the private key and disable programming. */ /* Make all fuse registers visible, disable the private key and disable programming. */
@ -43,7 +38,7 @@ void fuse_disable_private_key(void) {
fuse->FUSE_PRIVATEKEYDISABLE = 0x10; fuse->FUSE_PRIVATEKEYDISABLE = 0x10;
} }
/* Disables all fuse programming. */ /* Disable all fuse programming. */
void fuse_disable_programming(void) { void fuse_disable_programming(void) {
volatile tegra_fuse_t *fuse = fuse_get_regs(); volatile tegra_fuse_t *fuse = fuse_get_regs();
fuse->FUSE_DISABLEREGPROGRAM = 1; fuse->FUSE_DISABLEREGPROGRAM = 1;
@ -68,7 +63,7 @@ void fuse_disable_power(void) {
} }
/* Wait for the fuse driver to go idle. */ /* Wait for the fuse driver to go idle. */
void fuse_wait_idle(void) { static void fuse_wait_idle(void) {
volatile tegra_fuse_t *fuse = fuse_get_regs(); volatile tegra_fuse_t *fuse = fuse_get_regs();
uint32_t ctrl_val = 0; uint32_t ctrl_val = 0;
@ -120,7 +115,7 @@ void fuse_hw_write(uint32_t value, uint32_t addr) {
fuse_wait_idle(); fuse_wait_idle();
} }
/* Sense the fuse hardware array into the shadow cache. */ /* Sense the fuse hardware array into the fuse cache. */
void fuse_hw_sense(void) { void fuse_hw_sense(void) {
volatile tegra_fuse_t *fuse = fuse_get_regs(); volatile tegra_fuse_t *fuse = fuse_get_regs();
@ -137,19 +132,19 @@ void fuse_hw_sense(void) {
fuse_wait_idle(); fuse_wait_idle();
} }
/* Read the SKU info register from the shadow cache. */ /* Read the SKU info register. */
uint32_t fuse_get_sku_info(void) { uint32_t fuse_get_sku_info(void) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
return fuse_chip->FUSE_SKU_INFO; return fuse_chip->FUSE_SKU_INFO;
} }
/* Read the bootrom patch version from a register in the shadow cache. */ /* Read the bootrom patch version. */
uint32_t fuse_get_bootrom_patch_version(void) { uint32_t fuse_get_bootrom_patch_version(void) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
return fuse_chip->FUSE_SOC_SPEEDO_1_CALIB; return fuse_chip->FUSE_SOC_SPEEDO_1_CALIB;
} }
/* Read a spare bit register from the shadow cache */ /* Read a spare bit register. */
uint32_t fuse_get_spare_bit(uint32_t idx) { uint32_t fuse_get_spare_bit(uint32_t idx) {
if (idx < 32) { if (idx < 32) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
@ -159,7 +154,7 @@ uint32_t fuse_get_spare_bit(uint32_t idx) {
} }
} }
/* Read a reserved ODM register from the shadow cache. */ /* Read a reserved ODM register. */
uint32_t fuse_get_reserved_odm(uint32_t idx) { uint32_t fuse_get_reserved_odm(uint32_t idx) {
if (idx < 8) { if (idx < 8) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
@ -169,12 +164,12 @@ uint32_t fuse_get_reserved_odm(uint32_t idx) {
} }
} }
/* Get the DRAM ID using values in the shadow cache. */ /* Get the DramId. */
uint32_t fuse_get_dram_id(void) { uint32_t fuse_get_dram_id(void) {
return ((fuse_get_reserved_odm(4) >> 3) & 0x7); return ((fuse_get_reserved_odm(4) >> 3) & 0x7);
} }
/* Derive the Device ID using values in the shadow cache. */ /* Derive the DeviceId. */
uint64_t fuse_get_device_id(void) { uint64_t fuse_get_device_id(void) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
@ -200,46 +195,72 @@ uint64_t fuse_get_device_id(void) {
return device_id; return device_id;
} }
/* Derive the Hardware Type using values in the shadow cache. */ /* Derive the HardwareType with firmware specific checks. */
uint32_t fuse_get_hardware_type(uint32_t target_firmware) { uint32_t fuse_get_hardware_type_with_firmware_check(uint32_t target_firmware) {
uint32_t fuse_reserved_odm4 = fuse_get_reserved_odm(4); uint32_t fuse_reserved_odm4 = fuse_get_reserved_odm(4);
uint32_t hardware_type = (((fuse_reserved_odm4 >> 7) & 2) | ((fuse_reserved_odm4 >> 2) & 1)); uint32_t hardware_type = (((fuse_reserved_odm4 >> 7) & 2) | ((fuse_reserved_odm4 >> 2) & 1));
/* Firmware from versions 1.0.0 to 3.0.2. */
if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
if (hardware_type >= 1) { uint32_t fuse_spare_bit9 = (fuse_chip->FUSE_SPARE_BIT[9] & 1);
return (hardware_type > 2) ? 3 : hardware_type - 1;
} else if ((fuse_chip->FUSE_SPARE_BIT[9] & 1) == 0) { switch (hardware_type) {
return 0; case 0x00: return (fuse_spare_bit9 == 0) ? 0 : 3;
case 0x01: return 0; /* HardwareType_Icosa */
case 0x02: return 1; /* HardwareType_Copper */
default: return 3; /* HardwareType_Undefined */
}
} else { } else {
return 3;
}
} else if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_7_0_0) { /* Firmware versions from 4.0.0 to 6.2.0. */
static const uint32_t types[] = {0,1,4,3};
hardware_type |= ((fuse_reserved_odm4 >> 14) & 0x3C); hardware_type |= ((fuse_reserved_odm4 >> 14) & 0x3C);
hardware_type--;
return (hardware_type > 3) ? 4 : types[hardware_type]; if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_7_0_0) {
} else { /* Firmware versions from 7.0.0 onwards. */ switch (hardware_type) {
/* Always return 0 in retail. */ case 0x01: return 0; /* HardwareType_Icosa */
return 0; case 0x02: return 1; /* HardwareType_Copper */
case 0x04: return 3; /* HardwareType_Iowa */
default: return 4; /* HardwareType_Undefined */
}
} else {
if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_10_0_0) {
switch (hardware_type) {
case 0x01: return 0; /* HardwareType_Icosa */
case 0x02: return 4; /* HardwareType_Calcio */
case 0x04: return 3; /* HardwareType_Iowa */
case 0x08: return 2; /* HardwareType_Hoag */
default: return 0xF; /* HardwareType_Undefined */
}
} else {
switch (hardware_type) {
case 0x01: return 0; /* HardwareType_Icosa */
case 0x02: return 4; /* HardwareType_Calcio */
case 0x04: return 3; /* HardwareType_Iowa */
case 0x08: return 2; /* HardwareType_Hoag */
case 0x10: return 5; /* HardwareType_Five */
default: return 0xF; /* HardwareType_Undefined */
}
}
}
} }
} }
/* Derive the Retail Type using values in the shadow cache. */ /* Derive the HardwareType. */
uint32_t fuse_get_retail_type(void) { uint32_t fuse_get_hardware_type(void) {
/* Retail Type = IS_RETAIL | UNIT_TYPE. */ return fuse_get_hardware_type_with_firmware_check(ATMOSPHERE_TARGET_FIRMWARE_CURRENT);
}
/* Derive the HardwareState. */
uint32_t fuse_get_hardware_state(void) {
uint32_t fuse_reserved_odm4 = fuse_get_reserved_odm(4); uint32_t fuse_reserved_odm4 = fuse_get_reserved_odm(4);
uint32_t retail_type = (((fuse_reserved_odm4 >> 7) & 4) | (fuse_reserved_odm4 & 3)); uint32_t hardware_state = (((fuse_reserved_odm4 >> 7) & 4) | (fuse_reserved_odm4 & 3));
if (retail_type == 4) { /* Standard retail unit, IS_RETAIL | 0. */
return 1; switch (hardware_state) {
} else if (retail_type == 3) { /* Standard dev unit, 0 | DEV_UNIT. */ case 0x03: return 0; /* HardwareState_Development */
return 0; case 0x04: return 1; /* HardwareState_Production */
default: return 2; /* HardwareState_Undefined */
} }
return 2; /* IS_RETAIL | DEV_UNIT */
} }
/* Derive the 16-byte Hardware Info using values in the shadow cache, and copy to output buffer. */ /* Derive the 16-byte HardwareInfo and copy to output buffer. */
void fuse_get_hardware_info(void *dst) { void fuse_get_hardware_info(void *dst) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
uint32_t hw_info[0x4]; uint32_t hw_info[0x4];
@ -261,3 +282,28 @@ void fuse_get_hardware_info(void *dst) {
memcpy(dst, hw_info, 0x10); memcpy(dst, hw_info, 0x10);
} }
/* Get the DeviceUniqueKeyGeneration. */
uint32_t fuse_get_device_unique_key_generation(void) {
if ((fuse_get_reserved_odm(4) & 0x800) && (fuse_get_reserved_odm(0) == 0x8E61ECAE) && (fuse_get_reserved_odm(1) == 0xF2BA3BB2)) {
return (fuse_get_reserved_odm(2) & 0x1F);
} else {
return 0;
}
}
/* Get the SocType from the HardwareType. */
uint32_t fuse_get_soc_type(void) {
switch (fuse_get_hardware_type()) {
case 0:
case 1:
return 0; /* SocType_Erista */
case 3:
case 2:
case 4:
case 5:
return 1; /* SocType_Mariko */
default:
return 0xF; /* SocType_Undefined */
}
}

View file

@ -209,6 +209,8 @@ static inline volatile tegra_fuse_chip_t *fuse_chip_get_regs(void)
void fuse_init(void); void fuse_init(void);
void fuse_disable_programming(void); void fuse_disable_programming(void);
void fuse_disable_private_key(void); void fuse_disable_private_key(void);
void fuse_enable_power(void);
void fuse_disable_power(void);
uint32_t fuse_get_sku_info(void); uint32_t fuse_get_sku_info(void);
uint32_t fuse_get_spare_bit(uint32_t idx); uint32_t fuse_get_spare_bit(uint32_t idx);
@ -216,9 +218,12 @@ uint32_t fuse_get_reserved_odm(uint32_t idx);
uint32_t fuse_get_bootrom_patch_version(void); uint32_t fuse_get_bootrom_patch_version(void);
uint64_t fuse_get_device_id(void); uint64_t fuse_get_device_id(void);
uint32_t fuse_get_dram_id(void); uint32_t fuse_get_dram_id(void);
uint32_t fuse_get_hardware_type(uint32_t target_firmware); uint32_t fuse_get_hardware_type_with_firmware_check(uint32_t target_firmware);
uint32_t fuse_get_hardware_type(void);
uint32_t fuse_get_retail_type(void); uint32_t fuse_get_retail_type(void);
void fuse_get_hardware_info(void *dst); void fuse_get_hardware_info(void *dst);
uint32_t fuse_get_device_unique_key_generation(void);
uint32_t fuse_get_soc_type(void);
uint32_t fuse_hw_read(uint32_t addr); uint32_t fuse_hw_read(uint32_t addr);
void fuse_hw_write(uint32_t value, uint32_t addr); void fuse_hw_write(uint32_t value, uint32_t addr);

View file

@ -24,11 +24,6 @@
#include "pmc.h" #include "pmc.h"
#include "timers.h" #include "timers.h"
/* Prototypes for internal commands. */
void fuse_enable_power(void);
void fuse_disable_power(void);
void fuse_wait_idle(void);
/* Initialize the fuse driver */ /* Initialize the fuse driver */
void fuse_init(void) { void fuse_init(void) {
/* Make all fuse registers visible, disable the private key and disable programming. */ /* Make all fuse registers visible, disable the private key and disable programming. */
@ -43,7 +38,7 @@ void fuse_disable_private_key(void) {
fuse->FUSE_PRIVATEKEYDISABLE = 0x10; fuse->FUSE_PRIVATEKEYDISABLE = 0x10;
} }
/* Disables all fuse programming. */ /* Disable all fuse programming. */
void fuse_disable_programming(void) { void fuse_disable_programming(void) {
volatile tegra_fuse_t *fuse = fuse_get_regs(); volatile tegra_fuse_t *fuse = fuse_get_regs();
fuse->FUSE_DISABLEREGPROGRAM = 1; fuse->FUSE_DISABLEREGPROGRAM = 1;
@ -68,7 +63,7 @@ void fuse_disable_power(void) {
} }
/* Wait for the fuse driver to go idle. */ /* Wait for the fuse driver to go idle. */
void fuse_wait_idle(void) { static void fuse_wait_idle(void) {
volatile tegra_fuse_t *fuse = fuse_get_regs(); volatile tegra_fuse_t *fuse = fuse_get_regs();
uint32_t ctrl_val = 0; uint32_t ctrl_val = 0;
@ -120,7 +115,7 @@ void fuse_hw_write(uint32_t value, uint32_t addr) {
fuse_wait_idle(); fuse_wait_idle();
} }
/* Sense the fuse hardware array into the shadow cache. */ /* Sense the fuse hardware array into the fuse cache. */
void fuse_hw_sense(void) { void fuse_hw_sense(void) {
volatile tegra_fuse_t *fuse = fuse_get_regs(); volatile tegra_fuse_t *fuse = fuse_get_regs();
@ -137,19 +132,19 @@ void fuse_hw_sense(void) {
fuse_wait_idle(); fuse_wait_idle();
} }
/* Read the SKU info register from the shadow cache. */ /* Read the SKU info register. */
uint32_t fuse_get_sku_info(void) { uint32_t fuse_get_sku_info(void) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
return fuse_chip->FUSE_SKU_INFO; return fuse_chip->FUSE_SKU_INFO;
} }
/* Read the bootrom patch version from a register in the shadow cache. */ /* Read the bootrom patch version. */
uint32_t fuse_get_bootrom_patch_version(void) { uint32_t fuse_get_bootrom_patch_version(void) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
return fuse_chip->FUSE_SOC_SPEEDO_1_CALIB; return fuse_chip->FUSE_SOC_SPEEDO_1_CALIB;
} }
/* Read a spare bit register from the shadow cache */ /* Read a spare bit register. */
uint32_t fuse_get_spare_bit(uint32_t idx) { uint32_t fuse_get_spare_bit(uint32_t idx) {
if (idx < 32) { if (idx < 32) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
@ -159,7 +154,7 @@ uint32_t fuse_get_spare_bit(uint32_t idx) {
} }
} }
/* Read a reserved ODM register from the shadow cache. */ /* Read a reserved ODM register. */
uint32_t fuse_get_reserved_odm(uint32_t idx) { uint32_t fuse_get_reserved_odm(uint32_t idx) {
if (idx < 8) { if (idx < 8) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
@ -169,12 +164,12 @@ uint32_t fuse_get_reserved_odm(uint32_t idx) {
} }
} }
/* Get the DRAM ID using values in the shadow cache. */ /* Get the DramId. */
uint32_t fuse_get_dram_id(void) { uint32_t fuse_get_dram_id(void) {
return ((fuse_get_reserved_odm(4) >> 3) & 0x7); return ((fuse_get_reserved_odm(4) >> 3) & 0x7);
} }
/* Derive the Device ID using values in the shadow cache. */ /* Derive the DeviceId. */
uint64_t fuse_get_device_id(void) { uint64_t fuse_get_device_id(void) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
@ -200,46 +195,72 @@ uint64_t fuse_get_device_id(void) {
return device_id; return device_id;
} }
/* Derive the Hardware Type using values in the shadow cache. */ /* Derive the HardwareType with firmware specific checks. */
uint32_t fuse_get_hardware_type(uint32_t target_firmware) { uint32_t fuse_get_hardware_type_with_firmware_check(uint32_t target_firmware) {
uint32_t fuse_reserved_odm4 = fuse_get_reserved_odm(4); uint32_t fuse_reserved_odm4 = fuse_get_reserved_odm(4);
uint32_t hardware_type = (((fuse_reserved_odm4 >> 7) & 2) | ((fuse_reserved_odm4 >> 2) & 1)); uint32_t hardware_type = (((fuse_reserved_odm4 >> 7) & 2) | ((fuse_reserved_odm4 >> 2) & 1));
/* Firmware from versions 1.0.0 to 3.0.2. */
if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
if (hardware_type >= 1) { uint32_t fuse_spare_bit9 = (fuse_chip->FUSE_SPARE_BIT[9] & 1);
return (hardware_type > 2) ? 3 : hardware_type - 1;
} else if ((fuse_chip->FUSE_SPARE_BIT[9] & 1) == 0) { switch (hardware_type) {
return 0; case 0x00: return (fuse_spare_bit9 == 0) ? 0 : 3;
case 0x01: return 0; /* HardwareType_Icosa */
case 0x02: return 1; /* HardwareType_Copper */
default: return 3; /* HardwareType_Undefined */
}
} else { } else {
return 3;
}
} else if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_7_0_0) { /* Firmware versions from 4.0.0 to 6.2.0. */
static const uint32_t types[] = {0,1,4,3};
hardware_type |= ((fuse_reserved_odm4 >> 14) & 0x3C); hardware_type |= ((fuse_reserved_odm4 >> 14) & 0x3C);
hardware_type--;
return (hardware_type > 3) ? 4 : types[hardware_type]; if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_7_0_0) {
} else { /* Firmware versions from 7.0.0 onwards. */ switch (hardware_type) {
/* Always return 0 in retail. */ case 0x01: return 0; /* HardwareType_Icosa */
return 0; case 0x02: return 1; /* HardwareType_Copper */
case 0x04: return 3; /* HardwareType_Iowa */
default: return 4; /* HardwareType_Undefined */
}
} else {
if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_10_0_0) {
switch (hardware_type) {
case 0x01: return 0; /* HardwareType_Icosa */
case 0x02: return 4; /* HardwareType_Calcio */
case 0x04: return 3; /* HardwareType_Iowa */
case 0x08: return 2; /* HardwareType_Hoag */
default: return 0xF; /* HardwareType_Undefined */
}
} else {
switch (hardware_type) {
case 0x01: return 0; /* HardwareType_Icosa */
case 0x02: return 4; /* HardwareType_Calcio */
case 0x04: return 3; /* HardwareType_Iowa */
case 0x08: return 2; /* HardwareType_Hoag */
case 0x10: return 5; /* HardwareType_Five */
default: return 0xF; /* HardwareType_Undefined */
}
}
}
} }
} }
/* Derive the Retail Type using values in the shadow cache. */ /* Derive the HardwareType. */
uint32_t fuse_get_retail_type(void) { uint32_t fuse_get_hardware_type(void) {
/* Retail Type = IS_RETAIL | UNIT_TYPE. */ return fuse_get_hardware_type_with_firmware_check(ATMOSPHERE_TARGET_FIRMWARE_CURRENT);
}
/* Derive the HardwareState. */
uint32_t fuse_get_hardware_state(void) {
uint32_t fuse_reserved_odm4 = fuse_get_reserved_odm(4); uint32_t fuse_reserved_odm4 = fuse_get_reserved_odm(4);
uint32_t retail_type = (((fuse_reserved_odm4 >> 7) & 4) | (fuse_reserved_odm4 & 3)); uint32_t hardware_state = (((fuse_reserved_odm4 >> 7) & 4) | (fuse_reserved_odm4 & 3));
if (retail_type == 4) { /* Standard retail unit, IS_RETAIL | 0. */
return 1; switch (hardware_state) {
} else if (retail_type == 3) { /* Standard dev unit, 0 | DEV_UNIT. */ case 0x03: return 0; /* HardwareState_Development */
return 0; case 0x04: return 1; /* HardwareState_Production */
default: return 2; /* HardwareState_Undefined */
} }
return 2; /* IS_RETAIL | DEV_UNIT */
} }
/* Derive the 16-byte Hardware Info using values in the shadow cache, and copy to output buffer. */ /* Derive the 16-byte HardwareInfo and copy to output buffer. */
void fuse_get_hardware_info(void *dst) { void fuse_get_hardware_info(void *dst) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
uint32_t hw_info[0x4]; uint32_t hw_info[0x4];
@ -261,3 +282,28 @@ void fuse_get_hardware_info(void *dst) {
memcpy(dst, hw_info, 0x10); memcpy(dst, hw_info, 0x10);
} }
/* Get the DeviceUniqueKeyGeneration. */
uint32_t fuse_get_device_unique_key_generation(void) {
if ((fuse_get_reserved_odm(4) & 0x800) && (fuse_get_reserved_odm(0) == 0x8E61ECAE) && (fuse_get_reserved_odm(1) == 0xF2BA3BB2)) {
return (fuse_get_reserved_odm(2) & 0x1F);
} else {
return 0;
}
}
/* Get the SocType from the HardwareType. */
uint32_t fuse_get_soc_type(void) {
switch (fuse_get_hardware_type()) {
case 0:
case 1:
return 0; /* SocType_Erista */
case 3:
case 2:
case 4:
case 5:
return 1; /* SocType_Mariko */
default:
return 0xF; /* SocType_Undefined */
}
}

View file

@ -209,6 +209,8 @@ static inline volatile tegra_fuse_chip_t *fuse_chip_get_regs(void)
void fuse_init(void); void fuse_init(void);
void fuse_disable_programming(void); void fuse_disable_programming(void);
void fuse_disable_private_key(void); void fuse_disable_private_key(void);
void fuse_enable_power(void);
void fuse_disable_power(void);
uint32_t fuse_get_sku_info(void); uint32_t fuse_get_sku_info(void);
uint32_t fuse_get_spare_bit(uint32_t idx); uint32_t fuse_get_spare_bit(uint32_t idx);
@ -216,9 +218,12 @@ uint32_t fuse_get_reserved_odm(uint32_t idx);
uint32_t fuse_get_bootrom_patch_version(void); uint32_t fuse_get_bootrom_patch_version(void);
uint64_t fuse_get_device_id(void); uint64_t fuse_get_device_id(void);
uint32_t fuse_get_dram_id(void); uint32_t fuse_get_dram_id(void);
uint32_t fuse_get_hardware_type(uint32_t target_firmware); uint32_t fuse_get_hardware_type_with_firmware_check(uint32_t target_firmware);
uint32_t fuse_get_hardware_type(void);
uint32_t fuse_get_retail_type(void); uint32_t fuse_get_retail_type(void);
void fuse_get_hardware_info(void *dst); void fuse_get_hardware_info(void *dst);
uint32_t fuse_get_device_unique_key_generation(void);
uint32_t fuse_get_soc_type(void);
uint32_t fuse_hw_read(uint32_t addr); uint32_t fuse_hw_read(uint32_t addr);
void fuse_hw_write(uint32_t value, uint32_t addr); void fuse_hw_write(uint32_t value, uint32_t addr);

View file

@ -24,11 +24,6 @@
#include "pmc.h" #include "pmc.h"
#include "timers.h" #include "timers.h"
/* Prototypes for internal commands. */
void fuse_enable_power(void);
void fuse_disable_power(void);
void fuse_wait_idle(void);
/* Initialize the fuse driver */ /* Initialize the fuse driver */
void fuse_init(void) { void fuse_init(void) {
/* Make all fuse registers visible, disable the private key and disable programming. */ /* Make all fuse registers visible, disable the private key and disable programming. */
@ -43,7 +38,7 @@ void fuse_disable_private_key(void) {
fuse->FUSE_PRIVATEKEYDISABLE = 0x10; fuse->FUSE_PRIVATEKEYDISABLE = 0x10;
} }
/* Disables all fuse programming. */ /* Disable all fuse programming. */
void fuse_disable_programming(void) { void fuse_disable_programming(void) {
volatile tegra_fuse_t *fuse = fuse_get_regs(); volatile tegra_fuse_t *fuse = fuse_get_regs();
fuse->FUSE_DISABLEREGPROGRAM = 1; fuse->FUSE_DISABLEREGPROGRAM = 1;
@ -68,7 +63,7 @@ void fuse_disable_power(void) {
} }
/* Wait for the fuse driver to go idle. */ /* Wait for the fuse driver to go idle. */
void fuse_wait_idle(void) { static void fuse_wait_idle(void) {
volatile tegra_fuse_t *fuse = fuse_get_regs(); volatile tegra_fuse_t *fuse = fuse_get_regs();
uint32_t ctrl_val = 0; uint32_t ctrl_val = 0;
@ -120,7 +115,7 @@ void fuse_hw_write(uint32_t value, uint32_t addr) {
fuse_wait_idle(); fuse_wait_idle();
} }
/* Sense the fuse hardware array into the shadow cache. */ /* Sense the fuse hardware array into the fuse cache. */
void fuse_hw_sense(void) { void fuse_hw_sense(void) {
volatile tegra_fuse_t *fuse = fuse_get_regs(); volatile tegra_fuse_t *fuse = fuse_get_regs();
@ -137,19 +132,19 @@ void fuse_hw_sense(void) {
fuse_wait_idle(); fuse_wait_idle();
} }
/* Read the SKU info register from the shadow cache. */ /* Read the SKU info register. */
uint32_t fuse_get_sku_info(void) { uint32_t fuse_get_sku_info(void) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
return fuse_chip->FUSE_SKU_INFO; return fuse_chip->FUSE_SKU_INFO;
} }
/* Read the bootrom patch version from a register in the shadow cache. */ /* Read the bootrom patch version. */
uint32_t fuse_get_bootrom_patch_version(void) { uint32_t fuse_get_bootrom_patch_version(void) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
return fuse_chip->FUSE_SOC_SPEEDO_1_CALIB; return fuse_chip->FUSE_SOC_SPEEDO_1_CALIB;
} }
/* Read a spare bit register from the shadow cache */ /* Read a spare bit register. */
uint32_t fuse_get_spare_bit(uint32_t idx) { uint32_t fuse_get_spare_bit(uint32_t idx) {
if (idx < 32) { if (idx < 32) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
@ -159,7 +154,7 @@ uint32_t fuse_get_spare_bit(uint32_t idx) {
} }
} }
/* Read a reserved ODM register from the shadow cache. */ /* Read a reserved ODM register. */
uint32_t fuse_get_reserved_odm(uint32_t idx) { uint32_t fuse_get_reserved_odm(uint32_t idx) {
if (idx < 8) { if (idx < 8) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
@ -169,12 +164,12 @@ uint32_t fuse_get_reserved_odm(uint32_t idx) {
} }
} }
/* Get the DRAM ID using values in the shadow cache. */ /* Get the DramId. */
uint32_t fuse_get_dram_id(void) { uint32_t fuse_get_dram_id(void) {
return ((fuse_get_reserved_odm(4) >> 3) & 0x7); return ((fuse_get_reserved_odm(4) >> 3) & 0x7);
} }
/* Derive the Device ID using values in the shadow cache. */ /* Derive the DeviceId. */
uint64_t fuse_get_device_id(void) { uint64_t fuse_get_device_id(void) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
@ -200,46 +195,72 @@ uint64_t fuse_get_device_id(void) {
return device_id; return device_id;
} }
/* Derive the Hardware Type using values in the shadow cache. */ /* Derive the HardwareType with firmware specific checks. */
uint32_t fuse_get_hardware_type(uint32_t target_firmware) { uint32_t fuse_get_hardware_type_with_firmware_check(uint32_t target_firmware) {
uint32_t fuse_reserved_odm4 = fuse_get_reserved_odm(4); uint32_t fuse_reserved_odm4 = fuse_get_reserved_odm(4);
uint32_t hardware_type = (((fuse_reserved_odm4 >> 7) & 2) | ((fuse_reserved_odm4 >> 2) & 1)); uint32_t hardware_type = (((fuse_reserved_odm4 >> 7) & 2) | ((fuse_reserved_odm4 >> 2) & 1));
/* Firmware from versions 1.0.0 to 3.0.2. */
if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
if (hardware_type >= 1) { uint32_t fuse_spare_bit9 = (fuse_chip->FUSE_SPARE_BIT[9] & 1);
return (hardware_type > 2) ? 3 : hardware_type - 1;
} else if ((fuse_chip->FUSE_SPARE_BIT[9] & 1) == 0) { switch (hardware_type) {
return 0; case 0x00: return (fuse_spare_bit9 == 0) ? 0 : 3;
case 0x01: return 0; /* HardwareType_Icosa */
case 0x02: return 1; /* HardwareType_Copper */
default: return 3; /* HardwareType_Undefined */
}
} else { } else {
return 3;
}
} else if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_7_0_0) { /* Firmware versions from 4.0.0 to 6.2.0. */
static const uint32_t types[] = {0,1,4,3};
hardware_type |= ((fuse_reserved_odm4 >> 14) & 0x3C); hardware_type |= ((fuse_reserved_odm4 >> 14) & 0x3C);
hardware_type--;
return (hardware_type > 3) ? 4 : types[hardware_type]; if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_7_0_0) {
} else { /* Firmware versions from 7.0.0 onwards. */ switch (hardware_type) {
/* Always return 0 in retail. */ case 0x01: return 0; /* HardwareType_Icosa */
return 0; case 0x02: return 1; /* HardwareType_Copper */
case 0x04: return 3; /* HardwareType_Iowa */
default: return 4; /* HardwareType_Undefined */
}
} else {
if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_10_0_0) {
switch (hardware_type) {
case 0x01: return 0; /* HardwareType_Icosa */
case 0x02: return 4; /* HardwareType_Calcio */
case 0x04: return 3; /* HardwareType_Iowa */
case 0x08: return 2; /* HardwareType_Hoag */
default: return 0xF; /* HardwareType_Undefined */
}
} else {
switch (hardware_type) {
case 0x01: return 0; /* HardwareType_Icosa */
case 0x02: return 4; /* HardwareType_Calcio */
case 0x04: return 3; /* HardwareType_Iowa */
case 0x08: return 2; /* HardwareType_Hoag */
case 0x10: return 5; /* HardwareType_Five */
default: return 0xF; /* HardwareType_Undefined */
}
}
}
} }
} }
/* Derive the Retail Type using values in the shadow cache. */ /* Derive the HardwareType. */
uint32_t fuse_get_retail_type(void) { uint32_t fuse_get_hardware_type(void) {
/* Retail Type = IS_RETAIL | UNIT_TYPE. */ return fuse_get_hardware_type_with_firmware_check(ATMOSPHERE_TARGET_FIRMWARE_CURRENT);
}
/* Derive the HardwareState. */
uint32_t fuse_get_hardware_state(void) {
uint32_t fuse_reserved_odm4 = fuse_get_reserved_odm(4); uint32_t fuse_reserved_odm4 = fuse_get_reserved_odm(4);
uint32_t retail_type = (((fuse_reserved_odm4 >> 7) & 4) | (fuse_reserved_odm4 & 3)); uint32_t hardware_state = (((fuse_reserved_odm4 >> 7) & 4) | (fuse_reserved_odm4 & 3));
if (retail_type == 4) { /* Standard retail unit, IS_RETAIL | 0. */
return 1; switch (hardware_state) {
} else if (retail_type == 3) { /* Standard dev unit, 0 | DEV_UNIT. */ case 0x03: return 0; /* HardwareState_Development */
return 0; case 0x04: return 1; /* HardwareState_Production */
default: return 2; /* HardwareState_Undefined */
} }
return 2; /* IS_RETAIL | DEV_UNIT */
} }
/* Derive the 16-byte Hardware Info using values in the shadow cache, and copy to output buffer. */ /* Derive the 16-byte HardwareInfo and copy to output buffer. */
void fuse_get_hardware_info(void *dst) { void fuse_get_hardware_info(void *dst) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
uint32_t hw_info[0x4]; uint32_t hw_info[0x4];
@ -262,11 +283,27 @@ void fuse_get_hardware_info(void *dst) {
memcpy(dst, hw_info, 0x10); memcpy(dst, hw_info, 0x10);
} }
/* Get the Key Generation value. */ /* Get the DeviceUniqueKeyGeneration. */
uint32_t fuse_get_5x_key_generation(void) { uint32_t fuse_get_device_unique_key_generation(void) {
if ((fuse_get_reserved_odm(4) & 0x800) && (fuse_get_reserved_odm(0) == 0x8E61ECAE) && (fuse_get_reserved_odm(1) == 0xF2BA3BB2)) { if ((fuse_get_reserved_odm(4) & 0x800) && (fuse_get_reserved_odm(0) == 0x8E61ECAE) && (fuse_get_reserved_odm(1) == 0xF2BA3BB2)) {
return (fuse_get_reserved_odm(2) & 0x1F); return (fuse_get_reserved_odm(2) & 0x1F);
} else { } else {
return 0; return 0;
} }
} }
/* Get the SocType from the HardwareType. */
uint32_t fuse_get_soc_type(void) {
switch (fuse_get_hardware_type()) {
case 0:
case 1:
return 0; /* SocType_Erista */
case 3:
case 2:
case 4:
case 5:
return 1; /* SocType_Mariko */
default:
return 0xF; /* SocType_Undefined */
}
}

View file

@ -209,6 +209,8 @@ static inline volatile tegra_fuse_chip_t *fuse_chip_get_regs(void)
void fuse_init(void); void fuse_init(void);
void fuse_disable_programming(void); void fuse_disable_programming(void);
void fuse_disable_private_key(void); void fuse_disable_private_key(void);
void fuse_enable_power(void);
void fuse_disable_power(void);
uint32_t fuse_get_sku_info(void); uint32_t fuse_get_sku_info(void);
uint32_t fuse_get_spare_bit(uint32_t idx); uint32_t fuse_get_spare_bit(uint32_t idx);
@ -216,10 +218,12 @@ uint32_t fuse_get_reserved_odm(uint32_t idx);
uint32_t fuse_get_bootrom_patch_version(void); uint32_t fuse_get_bootrom_patch_version(void);
uint64_t fuse_get_device_id(void); uint64_t fuse_get_device_id(void);
uint32_t fuse_get_dram_id(void); uint32_t fuse_get_dram_id(void);
uint32_t fuse_get_hardware_type(uint32_t target_firmware); uint32_t fuse_get_hardware_type_with_firmware_check(uint32_t target_firmware);
uint32_t fuse_get_hardware_type(void);
uint32_t fuse_get_retail_type(void); uint32_t fuse_get_retail_type(void);
void fuse_get_hardware_info(void *dst); void fuse_get_hardware_info(void *dst);
uint32_t fuse_get_5x_key_generation(void); uint32_t fuse_get_device_unique_key_generation(void);
uint32_t fuse_get_soc_type(void);
uint32_t fuse_hw_read(uint32_t addr); uint32_t fuse_hw_read(uint32_t addr);
void fuse_hw_write(uint32_t value, uint32_t addr); void fuse_hw_write(uint32_t value, uint32_t addr);

View file

@ -169,7 +169,7 @@ int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, ui
if (memcmp(g_dec_keyblobs[desired_keyblob].master_kek, zeroes, 0x10) == 0) { if (memcmp(g_dec_keyblobs[desired_keyblob].master_kek, zeroes, 0x10) == 0) {
/* Try reading the keys from a file. */ /* Try reading the keys from a file. */
const char *keyfile = fuse_get_retail_type() != 0 ? "atmosphere/prod.keys" : "atmosphere/dev.keys"; const char *keyfile = fuse_get_hardware_state() != 0 ? "atmosphere/prod.keys" : "atmosphere/dev.keys";
FILE *extkey_file = fopen(keyfile, "r"); FILE *extkey_file = fopen(keyfile, "r");
AL16 fusee_extkeys_t extkeys = {0}; AL16 fusee_extkeys_t extkeys = {0};
if (extkey_file == NULL) { if (extkey_file == NULL) {
@ -212,7 +212,7 @@ int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, ui
decrypt_data_into_keyslot(0xD, 0xD, masterkey_seed, 0x10); decrypt_data_into_keyslot(0xD, 0xD, masterkey_seed, 0x10);
/* Setup master key revision, derive older master keys for use. */ /* Setup master key revision, derive older master keys for use. */
return mkey_detect_revision(fuse_get_retail_type() != 0); return mkey_detect_revision(fuse_get_hardware_state() != 0);
} }
static void generate_specific_aes_key(void *dst, const void *wrapped_key, bool should_mask, uint32_t target_firmware, uint32_t generation) { static void generate_specific_aes_key(void *dst, const void *wrapped_key, bool should_mask, uint32_t target_firmware, uint32_t generation) {
@ -273,7 +273,7 @@ void derive_bis_key(void *dst, BisPartition partition_id, uint32_t target_firmwa
} }
}; };
uint32_t bis_key_generation = fuse_get_5x_key_generation(); uint32_t bis_key_generation = fuse_get_device_unique_key_generation();
if (bis_key_generation > 0) { if (bis_key_generation > 0) {
bis_key_generation -= 1; bis_key_generation -= 1;
} }

View file

@ -794,7 +794,7 @@ uint32_t nxboot_main(void) {
} }
if (tsec_fw_size == 0x3000) { if (tsec_fw_size == 0x3000) {
if (fuse_get_retail_type() != 0) { if (fuse_get_hardware_state() != 0) {
sept_secondary_enc = sept_secondary_00_enc; sept_secondary_enc = sept_secondary_00_enc;
sept_secondary_enc_size = sept_secondary_00_enc_size; sept_secondary_enc_size = sept_secondary_00_enc_size;
} else { } else {
@ -802,7 +802,7 @@ uint32_t nxboot_main(void) {
sept_secondary_enc_size = sept_secondary_dev_00_enc_size; sept_secondary_enc_size = sept_secondary_dev_00_enc_size;
} }
} else if (tsec_fw_size == 0x3300) { } else if (tsec_fw_size == 0x3300) {
if (fuse_get_retail_type() != 0) { if (fuse_get_hardware_state() != 0) {
sept_secondary_enc = sept_secondary_01_enc; sept_secondary_enc = sept_secondary_01_enc;
sept_secondary_enc_size = sept_secondary_01_enc_size; sept_secondary_enc_size = sept_secondary_01_enc_size;
} else { } else {
@ -817,7 +817,7 @@ uint32_t nxboot_main(void) {
fatal_error("[NXBOOT] Failed to read the TSEC firmware from Package1loader!\n"); fatal_error("[NXBOOT] Failed to read the TSEC firmware from Package1loader!\n");
} }
if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_8_1_0) { if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_8_1_0) {
if (fuse_get_retail_type() != 0) { if (fuse_get_hardware_state() != 0) {
sept_secondary_enc = sept_secondary_01_enc; sept_secondary_enc = sept_secondary_01_enc;
sept_secondary_enc_size = sept_secondary_01_enc_size; sept_secondary_enc_size = sept_secondary_01_enc_size;
} else { } else {
@ -826,7 +826,7 @@ uint32_t nxboot_main(void) {
} }
tsec_fw_size = 0x3300; tsec_fw_size = 0x3300;
} else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_7_0_0) { } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_7_0_0) {
if (fuse_get_retail_type() != 0) { if (fuse_get_hardware_state() != 0) {
sept_secondary_enc = sept_secondary_00_enc; sept_secondary_enc = sept_secondary_00_enc;
sept_secondary_enc_size = sept_secondary_00_enc_size; sept_secondary_enc_size = sept_secondary_00_enc_size;
} else { } else {
@ -851,7 +851,7 @@ uint32_t nxboot_main(void) {
if (!get_and_clear_has_run_sept()) { if (!get_and_clear_has_run_sept()) {
reboot_to_sept(tsec_fw, tsec_fw_size, sept_secondary_enc, sept_secondary_enc_size); reboot_to_sept(tsec_fw, tsec_fw_size, sept_secondary_enc, sept_secondary_enc_size);
} else { } else {
if (mkey_detect_revision(fuse_get_retail_type() != 0) != 0) { if (mkey_detect_revision(fuse_get_hardware_state() != 0) != 0) {
fatal_error("[NXBOOT] Sept derived incorrect keys!\n"); fatal_error("[NXBOOT] Sept derived incorrect keys!\n");
} }
} }
@ -885,7 +885,7 @@ uint32_t nxboot_main(void) {
/* Derive new device keys. */ /* Derive new device keys. */
{ {
derive_new_device_keys(fuse_get_retail_type() != 0, KEYSLOT_SWITCH_5XNEWDEVICEKEYGENKEY, target_firmware); derive_new_device_keys(fuse_get_hardware_state() != 0, KEYSLOT_SWITCH_5XNEWDEVICEKEYGENKEY, target_firmware);
} }
/* Set the system partition's keys. */ /* Set the system partition's keys. */
@ -957,7 +957,7 @@ uint32_t nxboot_main(void) {
ams_header->ams_metadata.target_firmware = target_firmware; ams_header->ams_metadata.target_firmware = target_firmware;
/* Set RSA modulus */ /* Set RSA modulus */
const uint8_t *pkc_modulus = fuse_get_retail_type() != 0 ? retail_pkc_modulus : dev_pkc_modulus; const uint8_t *pkc_modulus = fuse_get_hardware_state() != 0 ? retail_pkc_modulus : dev_pkc_modulus;
memcpy(ams_header->rsa_modulus, pkc_modulus, sizeof(ams_header->rsa_modulus)); memcpy(ams_header->rsa_modulus, pkc_modulus, sizeof(ams_header->rsa_modulus));
} }
} }

View file

@ -24,15 +24,12 @@
#include "pmc.h" #include "pmc.h"
#include "timers.h" #include "timers.h"
/* Prototypes for internal commands. */
void fuse_enable_power(void);
void fuse_disable_power(void);
void fuse_wait_idle(void);
/* Initialize the fuse driver */ /* Initialize the fuse driver */
void fuse_init(void) { void fuse_init(void) {
/* Make all fuse registers visible. */ /* Make all fuse registers visible, disable the private key and disable programming. */
clkrst_enable_fuse_regs(true); clkrst_enable_fuse_regs(true);
/* fuse_disable_private_key(); */
/* fuse_disable_programming(); */
} }
/* Disable access to the private key and set the TZ sticky bit. */ /* Disable access to the private key and set the TZ sticky bit. */
@ -41,7 +38,7 @@ void fuse_disable_private_key(void) {
fuse->FUSE_PRIVATEKEYDISABLE = 0x10; fuse->FUSE_PRIVATEKEYDISABLE = 0x10;
} }
/* Disables all fuse programming. */ /* Disable all fuse programming. */
void fuse_disable_programming(void) { void fuse_disable_programming(void) {
volatile tegra_fuse_t *fuse = fuse_get_regs(); volatile tegra_fuse_t *fuse = fuse_get_regs();
fuse->FUSE_DISABLEREGPROGRAM = 1; fuse->FUSE_DISABLEREGPROGRAM = 1;
@ -66,7 +63,7 @@ void fuse_disable_power(void) {
} }
/* Wait for the fuse driver to go idle. */ /* Wait for the fuse driver to go idle. */
void fuse_wait_idle(void) { static void fuse_wait_idle(void) {
volatile tegra_fuse_t *fuse = fuse_get_regs(); volatile tegra_fuse_t *fuse = fuse_get_regs();
uint32_t ctrl_val = 0; uint32_t ctrl_val = 0;
@ -118,7 +115,7 @@ void fuse_hw_write(uint32_t value, uint32_t addr) {
fuse_wait_idle(); fuse_wait_idle();
} }
/* Sense the fuse hardware array into the shadow cache. */ /* Sense the fuse hardware array into the fuse cache. */
void fuse_hw_sense(void) { void fuse_hw_sense(void) {
volatile tegra_fuse_t *fuse = fuse_get_regs(); volatile tegra_fuse_t *fuse = fuse_get_regs();
@ -135,19 +132,19 @@ void fuse_hw_sense(void) {
fuse_wait_idle(); fuse_wait_idle();
} }
/* Read the SKU info register from the shadow cache. */ /* Read the SKU info register. */
uint32_t fuse_get_sku_info(void) { uint32_t fuse_get_sku_info(void) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
return fuse_chip->FUSE_SKU_INFO; return fuse_chip->FUSE_SKU_INFO;
} }
/* Read the bootrom patch version from a register in the shadow cache. */ /* Read the bootrom patch version. */
uint32_t fuse_get_bootrom_patch_version(void) { uint32_t fuse_get_bootrom_patch_version(void) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
return fuse_chip->FUSE_SOC_SPEEDO_1_CALIB; return fuse_chip->FUSE_SOC_SPEEDO_1_CALIB;
} }
/* Read a spare bit register from the shadow cache */ /* Read a spare bit register. */
uint32_t fuse_get_spare_bit(uint32_t idx) { uint32_t fuse_get_spare_bit(uint32_t idx) {
if (idx < 32) { if (idx < 32) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
@ -157,7 +154,7 @@ uint32_t fuse_get_spare_bit(uint32_t idx) {
} }
} }
/* Read a reserved ODM register from the shadow cache. */ /* Read a reserved ODM register. */
uint32_t fuse_get_reserved_odm(uint32_t idx) { uint32_t fuse_get_reserved_odm(uint32_t idx) {
if (idx < 8) { if (idx < 8) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
@ -167,12 +164,12 @@ uint32_t fuse_get_reserved_odm(uint32_t idx) {
} }
} }
/* Get the DRAM ID using values in the shadow cache. */ /* Get the DramId. */
uint32_t fuse_get_dram_id(void) { uint32_t fuse_get_dram_id(void) {
return ((fuse_get_reserved_odm(4) >> 3) & 0x7); return ((fuse_get_reserved_odm(4) >> 3) & 0x7);
} }
/* Derive the Device ID using values in the shadow cache. */ /* Derive the DeviceId. */
uint64_t fuse_get_device_id(void) { uint64_t fuse_get_device_id(void) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
@ -198,46 +195,72 @@ uint64_t fuse_get_device_id(void) {
return device_id; return device_id;
} }
/* Derive the Hardware Type using values in the shadow cache. */ /* Derive the HardwareType with firmware specific checks. */
uint32_t fuse_get_hardware_type(uint32_t target_firmware) { uint32_t fuse_get_hardware_type_with_firmware_check(uint32_t target_firmware) {
uint32_t fuse_reserved_odm4 = fuse_get_reserved_odm(4); uint32_t fuse_reserved_odm4 = fuse_get_reserved_odm(4);
uint32_t hardware_type = (((fuse_reserved_odm4 >> 7) & 2) | ((fuse_reserved_odm4 >> 2) & 1)); uint32_t hardware_type = (((fuse_reserved_odm4 >> 7) & 2) | ((fuse_reserved_odm4 >> 2) & 1));
/* Firmware from versions 1.0.0 to 3.0.2. */
if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
if (hardware_type >= 1) { uint32_t fuse_spare_bit9 = (fuse_chip->FUSE_SPARE_BIT[9] & 1);
return (hardware_type > 2) ? 3 : hardware_type - 1;
} else if ((fuse_chip->FUSE_SPARE_BIT[9] & 1) == 0) { switch (hardware_type) {
return 0; case 0x00: return (fuse_spare_bit9 == 0) ? 0 : 3;
case 0x01: return 0; /* HardwareType_Icosa */
case 0x02: return 1; /* HardwareType_Copper */
default: return 3; /* HardwareType_Undefined */
}
} else { } else {
return 3;
}
} else if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_7_0_0) { /* Firmware versions from 4.0.0 to 6.2.0. */
static const uint32_t types[] = {0,1,4,3};
hardware_type |= ((fuse_reserved_odm4 >> 14) & 0x3C); hardware_type |= ((fuse_reserved_odm4 >> 14) & 0x3C);
hardware_type--;
return (hardware_type > 3) ? 4 : types[hardware_type]; if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_7_0_0) {
} else { /* Firmware versions from 7.0.0 onwards. */ switch (hardware_type) {
/* Always return 0 in retail. */ case 0x01: return 0; /* HardwareType_Icosa */
return 0; case 0x02: return 1; /* HardwareType_Copper */
case 0x04: return 3; /* HardwareType_Iowa */
default: return 4; /* HardwareType_Undefined */
}
} else {
if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_10_0_0) {
switch (hardware_type) {
case 0x01: return 0; /* HardwareType_Icosa */
case 0x02: return 4; /* HardwareType_Calcio */
case 0x04: return 3; /* HardwareType_Iowa */
case 0x08: return 2; /* HardwareType_Hoag */
default: return 0xF; /* HardwareType_Undefined */
}
} else {
switch (hardware_type) {
case 0x01: return 0; /* HardwareType_Icosa */
case 0x02: return 4; /* HardwareType_Calcio */
case 0x04: return 3; /* HardwareType_Iowa */
case 0x08: return 2; /* HardwareType_Hoag */
case 0x10: return 5; /* HardwareType_Five */
default: return 0xF; /* HardwareType_Undefined */
}
}
}
} }
} }
/* Derive the Retail Type using values in the shadow cache. */ /* Derive the HardwareType. */
uint32_t fuse_get_retail_type(void) { uint32_t fuse_get_hardware_type(void) {
/* Retail Type = IS_RETAIL | UNIT_TYPE. */ return fuse_get_hardware_type_with_firmware_check(ATMOSPHERE_TARGET_FIRMWARE_CURRENT);
}
/* Derive the HardwareState. */
uint32_t fuse_get_hardware_state(void) {
uint32_t fuse_reserved_odm4 = fuse_get_reserved_odm(4); uint32_t fuse_reserved_odm4 = fuse_get_reserved_odm(4);
uint32_t retail_type = (((fuse_reserved_odm4 >> 7) & 4) | (fuse_reserved_odm4 & 3)); uint32_t hardware_state = (((fuse_reserved_odm4 >> 7) & 4) | (fuse_reserved_odm4 & 3));
if (retail_type == 4) { /* Standard retail unit, IS_RETAIL | 0. */
return 1; switch (hardware_state) {
} else if (retail_type == 3) { /* Standard dev unit, 0 | DEV_UNIT. */ case 0x03: return 0; /* HardwareState_Development */
return 0; case 0x04: return 1; /* HardwareState_Production */
default: return 2; /* HardwareState_Undefined */
} }
return 2; /* IS_RETAIL | DEV_UNIT */
} }
/* Derive the 16-byte Hardware Info using values in the shadow cache, and copy to output buffer. */ /* Derive the 16-byte HardwareInfo and copy to output buffer. */
void fuse_get_hardware_info(void *dst) { void fuse_get_hardware_info(void *dst) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
uint32_t hw_info[0x4]; uint32_t hw_info[0x4];
@ -259,3 +282,28 @@ void fuse_get_hardware_info(void *dst) {
memcpy(dst, hw_info, 0x10); memcpy(dst, hw_info, 0x10);
} }
/* Get the DeviceUniqueKeyGeneration. */
uint32_t fuse_get_device_unique_key_generation(void) {
if ((fuse_get_reserved_odm(4) & 0x800) && (fuse_get_reserved_odm(0) == 0x8E61ECAE) && (fuse_get_reserved_odm(1) == 0xF2BA3BB2)) {
return (fuse_get_reserved_odm(2) & 0x1F);
} else {
return 0;
}
}
/* Get the SocType from the HardwareType. */
uint32_t fuse_get_soc_type(void) {
switch (fuse_get_hardware_type()) {
case 0:
case 1:
return 0; /* SocType_Erista */
case 3:
case 2:
case 4:
case 5:
return 1; /* SocType_Mariko */
default:
return 0xF; /* SocType_Undefined */
}
}

View file

@ -209,6 +209,8 @@ static inline volatile tegra_fuse_chip_t *fuse_chip_get_regs(void)
void fuse_init(void); void fuse_init(void);
void fuse_disable_programming(void); void fuse_disable_programming(void);
void fuse_disable_private_key(void); void fuse_disable_private_key(void);
void fuse_enable_power(void);
void fuse_disable_power(void);
uint32_t fuse_get_sku_info(void); uint32_t fuse_get_sku_info(void);
uint32_t fuse_get_spare_bit(uint32_t idx); uint32_t fuse_get_spare_bit(uint32_t idx);
@ -216,9 +218,12 @@ uint32_t fuse_get_reserved_odm(uint32_t idx);
uint32_t fuse_get_bootrom_patch_version(void); uint32_t fuse_get_bootrom_patch_version(void);
uint64_t fuse_get_device_id(void); uint64_t fuse_get_device_id(void);
uint32_t fuse_get_dram_id(void); uint32_t fuse_get_dram_id(void);
uint32_t fuse_get_hardware_type(uint32_t target_firmware); uint32_t fuse_get_hardware_type_with_firmware_check(uint32_t target_firmware);
uint32_t fuse_get_hardware_type(void);
uint32_t fuse_get_retail_type(void); uint32_t fuse_get_retail_type(void);
void fuse_get_hardware_info(void *dst); void fuse_get_hardware_info(void *dst);
uint32_t fuse_get_device_unique_key_generation(void);
uint32_t fuse_get_soc_type(void);
uint32_t fuse_hw_read(uint32_t addr); uint32_t fuse_hw_read(uint32_t addr);
void fuse_hw_write(uint32_t value, uint32_t addr); void fuse_hw_write(uint32_t value, uint32_t addr);

View file

@ -24,11 +24,6 @@
#include "pmc.h" #include "pmc.h"
#include "timers.h" #include "timers.h"
/* Prototypes for internal commands. */
void fuse_enable_power(void);
void fuse_disable_power(void);
void fuse_wait_idle(void);
/* Initialize the fuse driver */ /* Initialize the fuse driver */
void fuse_init(void) { void fuse_init(void) {
/* Make all fuse registers visible, disable the private key and disable programming. */ /* Make all fuse registers visible, disable the private key and disable programming. */
@ -43,7 +38,7 @@ void fuse_disable_private_key(void) {
fuse->FUSE_PRIVATEKEYDISABLE = 0x10; fuse->FUSE_PRIVATEKEYDISABLE = 0x10;
} }
/* Disables all fuse programming. */ /* Disable all fuse programming. */
void fuse_disable_programming(void) { void fuse_disable_programming(void) {
volatile tegra_fuse_t *fuse = fuse_get_regs(); volatile tegra_fuse_t *fuse = fuse_get_regs();
fuse->FUSE_DISABLEREGPROGRAM = 1; fuse->FUSE_DISABLEREGPROGRAM = 1;
@ -68,7 +63,7 @@ void fuse_disable_power(void) {
} }
/* Wait for the fuse driver to go idle. */ /* Wait for the fuse driver to go idle. */
void fuse_wait_idle(void) { static void fuse_wait_idle(void) {
volatile tegra_fuse_t *fuse = fuse_get_regs(); volatile tegra_fuse_t *fuse = fuse_get_regs();
uint32_t ctrl_val = 0; uint32_t ctrl_val = 0;
@ -120,7 +115,7 @@ void fuse_hw_write(uint32_t value, uint32_t addr) {
fuse_wait_idle(); fuse_wait_idle();
} }
/* Sense the fuse hardware array into the shadow cache. */ /* Sense the fuse hardware array into the fuse cache. */
void fuse_hw_sense(void) { void fuse_hw_sense(void) {
volatile tegra_fuse_t *fuse = fuse_get_regs(); volatile tegra_fuse_t *fuse = fuse_get_regs();
@ -137,19 +132,19 @@ void fuse_hw_sense(void) {
fuse_wait_idle(); fuse_wait_idle();
} }
/* Read the SKU info register from the shadow cache. */ /* Read the SKU info register. */
uint32_t fuse_get_sku_info(void) { uint32_t fuse_get_sku_info(void) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
return fuse_chip->FUSE_SKU_INFO; return fuse_chip->FUSE_SKU_INFO;
} }
/* Read the bootrom patch version from a register in the shadow cache. */ /* Read the bootrom patch version. */
uint32_t fuse_get_bootrom_patch_version(void) { uint32_t fuse_get_bootrom_patch_version(void) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
return fuse_chip->FUSE_SOC_SPEEDO_1_CALIB; return fuse_chip->FUSE_SOC_SPEEDO_1_CALIB;
} }
/* Read a spare bit register from the shadow cache */ /* Read a spare bit register. */
uint32_t fuse_get_spare_bit(uint32_t idx) { uint32_t fuse_get_spare_bit(uint32_t idx) {
if (idx < 32) { if (idx < 32) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
@ -159,7 +154,7 @@ uint32_t fuse_get_spare_bit(uint32_t idx) {
} }
} }
/* Read a reserved ODM register from the shadow cache. */ /* Read a reserved ODM register. */
uint32_t fuse_get_reserved_odm(uint32_t idx) { uint32_t fuse_get_reserved_odm(uint32_t idx) {
if (idx < 8) { if (idx < 8) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
@ -169,12 +164,12 @@ uint32_t fuse_get_reserved_odm(uint32_t idx) {
} }
} }
/* Get the DRAM ID using values in the shadow cache. */ /* Get the DramId. */
uint32_t fuse_get_dram_id(void) { uint32_t fuse_get_dram_id(void) {
return ((fuse_get_reserved_odm(4) >> 3) & 0x7); return ((fuse_get_reserved_odm(4) >> 3) & 0x7);
} }
/* Derive the Device ID using values in the shadow cache. */ /* Derive the DeviceId. */
uint64_t fuse_get_device_id(void) { uint64_t fuse_get_device_id(void) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
@ -200,46 +195,72 @@ uint64_t fuse_get_device_id(void) {
return device_id; return device_id;
} }
/* Derive the Hardware Type using values in the shadow cache. */ /* Derive the HardwareType with firmware specific checks. */
uint32_t fuse_get_hardware_type(uint32_t target_firmware) { uint32_t fuse_get_hardware_type_with_firmware_check(uint32_t target_firmware) {
uint32_t fuse_reserved_odm4 = fuse_get_reserved_odm(4); uint32_t fuse_reserved_odm4 = fuse_get_reserved_odm(4);
uint32_t hardware_type = (((fuse_reserved_odm4 >> 7) & 2) | ((fuse_reserved_odm4 >> 2) & 1)); uint32_t hardware_type = (((fuse_reserved_odm4 >> 7) & 2) | ((fuse_reserved_odm4 >> 2) & 1));
/* Firmware from versions 1.0.0 to 3.0.2. */
if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) { if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_4_0_0) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
if (hardware_type >= 1) { uint32_t fuse_spare_bit9 = (fuse_chip->FUSE_SPARE_BIT[9] & 1);
return (hardware_type > 2) ? 3 : hardware_type - 1;
} else if ((fuse_chip->FUSE_SPARE_BIT[9] & 1) == 0) { switch (hardware_type) {
return 0; case 0x00: return (fuse_spare_bit9 == 0) ? 0 : 3;
case 0x01: return 0; /* HardwareType_Icosa */
case 0x02: return 1; /* HardwareType_Copper */
default: return 3; /* HardwareType_Undefined */
}
} else { } else {
return 3;
}
} else if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_7_0_0) { /* Firmware versions from 4.0.0 to 6.2.0. */
static const uint32_t types[] = {0,1,4,3};
hardware_type |= ((fuse_reserved_odm4 >> 14) & 0x3C); hardware_type |= ((fuse_reserved_odm4 >> 14) & 0x3C);
hardware_type--;
return (hardware_type > 3) ? 4 : types[hardware_type]; if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_7_0_0) {
} else { /* Firmware versions from 7.0.0 onwards. */ switch (hardware_type) {
/* Always return 0 in retail. */ case 0x01: return 0; /* HardwareType_Icosa */
return 0; case 0x02: return 1; /* HardwareType_Copper */
case 0x04: return 3; /* HardwareType_Iowa */
default: return 4; /* HardwareType_Undefined */
}
} else {
if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_10_0_0) {
switch (hardware_type) {
case 0x01: return 0; /* HardwareType_Icosa */
case 0x02: return 4; /* HardwareType_Calcio */
case 0x04: return 3; /* HardwareType_Iowa */
case 0x08: return 2; /* HardwareType_Hoag */
default: return 0xF; /* HardwareType_Undefined */
}
} else {
switch (hardware_type) {
case 0x01: return 0; /* HardwareType_Icosa */
case 0x02: return 4; /* HardwareType_Calcio */
case 0x04: return 3; /* HardwareType_Iowa */
case 0x08: return 2; /* HardwareType_Hoag */
case 0x10: return 5; /* HardwareType_Five */
default: return 0xF; /* HardwareType_Undefined */
}
}
}
} }
} }
/* Derive the Retail Type using values in the shadow cache. */ /* Derive the HardwareType. */
uint32_t fuse_get_retail_type(void) { uint32_t fuse_get_hardware_type(void) {
/* Retail Type = IS_RETAIL | UNIT_TYPE. */ return fuse_get_hardware_type_with_firmware_check(ATMOSPHERE_TARGET_FIRMWARE_CURRENT);
}
/* Derive the HardwareState. */
uint32_t fuse_get_hardware_state(void) {
uint32_t fuse_reserved_odm4 = fuse_get_reserved_odm(4); uint32_t fuse_reserved_odm4 = fuse_get_reserved_odm(4);
uint32_t retail_type = (((fuse_reserved_odm4 >> 7) & 4) | (fuse_reserved_odm4 & 3)); uint32_t hardware_state = (((fuse_reserved_odm4 >> 7) & 4) | (fuse_reserved_odm4 & 3));
if (retail_type == 4) { /* Standard retail unit, IS_RETAIL | 0. */
return 1; switch (hardware_state) {
} else if (retail_type == 3) { /* Standard dev unit, 0 | DEV_UNIT. */ case 0x03: return 0; /* HardwareState_Development */
return 0; case 0x04: return 1; /* HardwareState_Production */
default: return 2; /* HardwareState_Undefined */
} }
return 2; /* IS_RETAIL | DEV_UNIT */
} }
/* Derive the 16-byte Hardware Info using values in the shadow cache, and copy to output buffer. */ /* Derive the 16-byte HardwareInfo and copy to output buffer. */
void fuse_get_hardware_info(void *dst) { void fuse_get_hardware_info(void *dst) {
volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs(); volatile tegra_fuse_chip_t *fuse_chip = fuse_chip_get_regs();
uint32_t hw_info[0x4]; uint32_t hw_info[0x4];
@ -261,3 +282,28 @@ void fuse_get_hardware_info(void *dst) {
memcpy(dst, hw_info, 0x10); memcpy(dst, hw_info, 0x10);
} }
/* Get the DeviceUniqueKeyGeneration. */
uint32_t fuse_get_device_unique_key_generation(void) {
if ((fuse_get_reserved_odm(4) & 0x800) && (fuse_get_reserved_odm(0) == 0x8E61ECAE) && (fuse_get_reserved_odm(1) == 0xF2BA3BB2)) {
return (fuse_get_reserved_odm(2) & 0x1F);
} else {
return 0;
}
}
/* Get the SocType from the HardwareType. */
uint32_t fuse_get_soc_type(void) {
switch (fuse_get_hardware_type()) {
case 0:
case 1:
return 0; /* SocType_Erista */
case 3:
case 2:
case 4:
case 5:
return 1; /* SocType_Mariko */
default:
return 0xF; /* SocType_Undefined */
}
}

View file

@ -209,6 +209,8 @@ static inline volatile tegra_fuse_chip_t *fuse_chip_get_regs(void)
void fuse_init(void); void fuse_init(void);
void fuse_disable_programming(void); void fuse_disable_programming(void);
void fuse_disable_private_key(void); void fuse_disable_private_key(void);
void fuse_enable_power(void);
void fuse_disable_power(void);
uint32_t fuse_get_sku_info(void); uint32_t fuse_get_sku_info(void);
uint32_t fuse_get_spare_bit(uint32_t idx); uint32_t fuse_get_spare_bit(uint32_t idx);
@ -216,9 +218,12 @@ uint32_t fuse_get_reserved_odm(uint32_t idx);
uint32_t fuse_get_bootrom_patch_version(void); uint32_t fuse_get_bootrom_patch_version(void);
uint64_t fuse_get_device_id(void); uint64_t fuse_get_device_id(void);
uint32_t fuse_get_dram_id(void); uint32_t fuse_get_dram_id(void);
uint32_t fuse_get_hardware_type(uint32_t target_firmware); uint32_t fuse_get_hardware_type_with_firmware_check(uint32_t target_firmware);
uint32_t fuse_get_hardware_type(void);
uint32_t fuse_get_retail_type(void); uint32_t fuse_get_retail_type(void);
void fuse_get_hardware_info(void *dst); void fuse_get_hardware_info(void *dst);
uint32_t fuse_get_device_unique_key_generation(void);
uint32_t fuse_get_soc_type(void);
uint32_t fuse_hw_read(uint32_t addr); uint32_t fuse_hw_read(uint32_t addr);
void fuse_hw_write(uint32_t value, uint32_t addr); void fuse_hw_write(uint32_t value, uint32_t addr);