Implement getters for fuse values, fill out smcGetConfig

This commit is contained in:
Michael Scire 2018-02-25 01:21:52 -08:00
parent b0079e404d
commit 82b4c6763d
8 changed files with 132 additions and 16 deletions

View File

@ -20,5 +20,9 @@ void bootconfig_clear(void);
bool bootconfig_is_package2_plaintext(void); bool bootconfig_is_package2_plaintext(void);
bool bootconfig_is_package2_unsigned(void); bool bootconfig_is_package2_unsigned(void);
bool bootconfig_disable_program_verification(void); bool bootconfig_disable_program_verification(void);
bool bootconfig_is_debug_mode(void);
uint64_t bootconfig_get_memory_arrangement(void);
uint64_t bootconfig_get_kernel_memory_configuration(void);
#endif #endif

View File

@ -26,14 +26,23 @@ bool configitem_is_recovery_boot(void) {
return is_recovery_boot != 0; return is_recovery_boot != 0;
} }
bool configitem_is_retail(void) {
uint64_t is_retail;
if (configitem_get(CONFIGITEM_ISRETAIL, &is_retail) != 0) {
generic_panic();
}
return is_retail != 0;
}
uint32_t configitem_get(enum ConfigItem item, uint64_t *p_outvalue) { uint32_t configitem_get(enum ConfigItem item, uint64_t *p_outvalue) {
uint32_t result = 0; uint32_t result = 0;
switch (item) { switch (item) {
case CONFIGITEM_DISABLEPROGRAMVERIFICATION: case CONFIGITEM_DISABLEPROGRAMVERIFICATION:
*p_outvalue = (int)(bootconfig_disable_program_verification()); *p_outvalue = (int)(bootconfig_disable_program_verification());
break; break;
case CONFIGITEM_MEMORYCONFIGURATION: case CONFIGITEM_DRAMID:
/* TODO: Fuse driver */ *p_outvalue = fuse_get_dram_id();
break; break;
case CONFIGITEM_SECURITYENGINEIRQ: case CONFIGITEM_SECURITYENGINEIRQ:
/* SE is interrupt #0x2C. */ /* SE is interrupt #0x2C. */
@ -44,29 +53,29 @@ uint32_t configitem_get(enum ConfigItem item, uint64_t *p_outvalue) {
*p_outvalue = PACKAGE2_MAXVER_400_CURRENT - 1; *p_outvalue = PACKAGE2_MAXVER_400_CURRENT - 1;
break; break;
case CONFIGITEM_HARDWARETYPE: case CONFIGITEM_HARDWARETYPE:
/* TODO: Fuse driver */ *p_outvalue = fuse_get_hardware_type();
break; break;
case CONFIGITEM_ISRETAIL: case CONFIGITEM_ISRETAIL:
/* TODO: Fuse driver */ *p_outvalue = fuse_get_retail_type();
break; break;
case CONFIGITEM_ISRECOVERYBOOT: case CONFIGITEM_ISRECOVERYBOOT:
/* TODO: This requires reading values passed to crt0 via NX_Bootloader. TBD pending crt0 implementation. */ /* TODO: This requires reading values passed to crt0 via NX_Bootloader. TBD pending crt0 implementation. */
*p_outvalue = 0; *p_outvalue = 0;
break; break;
case CONFIGITEM_DEVICEID: case CONFIGITEM_DEVICEID:
/* TODO: Fuse driver */ *p_outvalue = fuse_get_device_id();
break; break;
case CONFIGITEM_BOOTREASON: case CONFIGITEM_BOOTREASON:
/* TODO: This requires reading values passed to crt0 via NX_Bootloader. TBD pending crt0 implementation. */ /* TODO: This requires reading values passed to crt0 via NX_Bootloader. TBD pending crt0 implementation. */
break; break;
case CONFIGITEM_MEMORYARRANGE: case CONFIGITEM_MEMORYARRANGE:
/* TODO: More BootConfig stuff. */ *p_outvalue = bootconfig_get_memory_arrangement();
break; break;
case CONFIGITEM_ISDEBUGMODE: case CONFIGITEM_ISDEBUGMODE:
/* TODO: More BootConfig stuff. */ *p_outvalue = (int)(bootconfig_is_debug_mode());
break; break;
case CONFIGITEM_KERNELMEMORYCONFIGURATION: case CONFIGITEM_KERNELMEMORYCONFIGURATION:
/* TODO: More BootConfig stuff. */ *p_outvalue = bootconfig_get_kernel_memory_configuration();
break; break;
case CONFIGITEM_BATTERYPROFILE: case CONFIGITEM_BATTERYPROFILE:
*p_outvalue = g_battery_profile; *p_outvalue = g_battery_profile;

View File

@ -6,7 +6,7 @@
enum ConfigItem { enum ConfigItem {
CONFIGITEM_DISABLEPROGRAMVERIFICATION = 1, CONFIGITEM_DISABLEPROGRAMVERIFICATION = 1,
CONFIGITEM_MEMORYCONFIGURATION = 2, CONFIGITEM_DRAMID = 2,
CONFIGITEM_SECURITYENGINEIRQ = 3, CONFIGITEM_SECURITYENGINEIRQ = 3,
CONFIGITEM_VERSION = 4, CONFIGITEM_VERSION = 4,
CONFIGITEM_HARDWARETYPE = 5, CONFIGITEM_HARDWARETYPE = 5,

View File

@ -108,6 +108,17 @@ void fuse_hw_sense(void)
fuse_wait_idle(); fuse_wait_idle();
} }
/* Disables all fuse programming. */
void fuse_disable_programming(void) {
FUSE_REGS->FUSE_DIS_PGM = 1;
}
/* Unknown exactly what this does, but it alters the contents read from the fuse cache. */
void fuse_secondary_private_key_disable(void) {
FUSE_REGS->FUSE_PRIVATEKEYDISABLE = 0x10;
}
/* Read the SKU info register from the shadow cache */ /* Read the SKU info register from the shadow cache */
uint32_t fuse_get_sku_info(void) uint32_t fuse_get_sku_info(void)
{ {
@ -141,3 +152,80 @@ uint32_t fuse_get_reserved_odm(uint32_t idx)
return reserved_odm_val; return reserved_odm_val;
} }
/* Derive the Device ID using values in the shadow cache */
uint64_t fuse_get_device_id(void) {
uint64_t device_id = 0;
uint64_t y_coord = FUSE_CHIP_REGS->FUSE_Y_COORDINATE & 0x1FF;
uint64_t x_coord = FUSE_CHIP_REGS->FUSE_X_COORDINATE & 0x1FF;
uint64_t wafer_id = FUSE_CHIP_REGS->FUSE_WAFER_ID & 0x3F;
uint32_t lot_code = FUSE_CHIP_REGS->FUSE_LOT_CODE_0;
uint64_t fab_code = FUSE_CHIP_REGS->FUSE_FAB_CODE & 0x3F;
uint64_t derived_lot_code = 0;
for (unsigned int i = 0; i < 5; i++) {
derived_lot_code = (derived_lot_code * 0x24) + ((lot_code >> (24 - 6*i)) & 0x3F);
}
derived_lot_code &= 0x03FFFFFF;
device_id |= y_coord << 0;
device_id |= x_coord << 9;
device_id |= wafer_id << 18;
device_id |= derived_lot_code << 24;
device_id |= fab_code << 50;
return device_id;
}
/* Get the DRAM ID using values in the shadow cache */
uint32_t fuse_get_dram_id(void) {
return (FUSE_CHIP_REGS->FUSE_RESERVED_ODM[4] >> 3) & 0x7;
}
/* Derive the Hardware Type using values in the shadow cache */
uint32_t fuse_get_hardware_type(void) {
uint32_t hardware_type = ((FUSE_CHIP_REGS->FUSE_RESERVED_ODM[4] >> 7) & 2) | ((FUSE_CHIP_REGS->FUSE_RESERVED_ODM[4] >> 2) & 1);
if (hardware_type) {
if (hardware_type == 1) {
return 0;
}
if (hardware_type == 2) {
return 1;
}
} else if ((FUSE_CHIP_REGS->FUSE_SPARE_BIT[9] & 1) == 0) {
return 0;
}
return 3;
}
/* Derive the Retail Type using values in the shadow cache */
uint32_t fuse_get_retail_type(void) {
/* Retail type = IS_RETAIL | UNIT_TYPE */
uint32_t retail_type = ((FUSE_CHIP_REGS->FUSE_RESERVED_ODM[4] >> 7) & 4) | (FUSE_CHIP_REGS->FUSE_RESERVED_ODM[4] & 3);
if (retail_type == 4) { /* Standard retail unit, IS_RETAIL | 0. */
return 1;
} else if (retail_type == 3) { /* Standard dev unit, 0 | DEV_UNIT. */
return 0;
}
return 2; /* IS_RETAIL | DEV_UNIT */
}
/* Derive the 16-byte Hardware Info using values in the shadow cache, and copy to output buffer. */
void fuse_get_hardware_info(void *dst) {
uint32_t hw_info[0x4];
uint32_t unk_hw_fuse = FUSE_CHIP_REGS->_0x120 & 0x3F;
uint32_t y_coord = FUSE_CHIP_REGS->FUSE_Y_COORDINATE & 0x1FF;
uint32_t x_coord = FUSE_CHIP_REGS->FUSE_X_COORDINATE & 0x1FF;
uint32_t wafer_id = FUSE_CHIP_REGS->FUSE_WAFER_ID & 0x3F;
uint32_t lot_code_0 = FUSE_CHIP_REGS->FUSE_LOT_CODE_0;
uint32_t lot_code_1 = FUSE_CHIP_REGS->FUSE_LOT_CODE_1 & 0x0FFFFFFF;
uint32_t fab_code = FUSE_CHIP_REGS->FUSE_FAB_CODE & 0x3F;
uint32_t vendor_code = FUSE_CHIP_REGS->FUSE_VENDOR_CODE & 0xF;
/* Hardware Info = unk_hw_fuse || Y_COORD || X_COORD || WAFER_ID || LOT_CODE || FAB_CODE || VENDOR_ID */
hw_info[0] = (uint32_t)((lot_code_1 << 30) | (wafer_id << 24) | (x_coord << 15) | (y_coord << 6) | (unk_hw_fuse));
hw_info[1] = (uint32_t)((lot_code_0 << 26) | (lot_code_1 >> 2));
hw_info[2] = (uint32_t)((fab_code << 26) | (lot_code_0 >> 6));
hw_info[3] = (uint32_t)(vendor_code);
memcpy(dst, hw_info, 0x10);
}

View File

@ -176,10 +176,18 @@ void fuse_init(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);
void fuse_hw_sense(void); void fuse_hw_sense(void);
void fuse_disable_programming(void);
void fuse_secondary_private_key_disable(void);
uint32_t fuse_get_sku_info(void); uint32_t fuse_get_sku_info(void);
uint32_t fuse_get_bootrom_patch_version(void);
uint32_t fuse_get_spare_bit(uint32_t idx); uint32_t fuse_get_spare_bit(uint32_t idx);
uint32_t fuse_get_reserved_odm(uint32_t idx); uint32_t fuse_get_reserved_odm(uint32_t idx);
uint32_t fuse_get_bootrom_patch_version(void);
uint64_t fuse_get_device_id(void);
uint32_t fuse_get_dram_id(void);
uint32_t fuse_get_hardware_type(void);
uint32_t fuse_get_retail_type(void);
void fuse_get_hardware_info(void *dst);
#endif #endif

View File

@ -2,6 +2,7 @@
#include <string.h> #include <string.h>
#include "utils.h" #include "utils.h"
#include "fuse.h"
#include "gcm.h" #include "gcm.h"
#include "sealedkeys.h" #include "sealedkeys.h"
@ -158,7 +159,9 @@ size_t gcm_decrypt_key(void *dst, size_t dst_size, const void *src, size_t src_s
return 0; return 0;
} }
/* TODO: Validate Device ID matches in blob data from fuses. */ if (read64le(src_bytes, src_size - 0x28) != fuse_get_device_id()) {
return 0;
}
return src_size - 0x30; return src_size - 0x30;
} }

View File

@ -203,7 +203,7 @@ uint32_t user_load_aes_key(smc_args_t *args) {
wrapped_key[0] = args->X[4]; wrapped_key[0] = args->X[4];
wrapped_key[1] = args->X[5]; wrapped_key[1] = args->X[5];
/* TODO: Unseal the kek. */ /* Unseal the kek. */
unseal_key(KEYSLOT_SWITCH_TEMPKEY, sealed_kek, 0x10, CRYPTOUSECASE_AES); unseal_key(KEYSLOT_SWITCH_TEMPKEY, sealed_kek, 0x10, CRYPTOUSECASE_AES);
/* Unwrap the key. */ /* Unwrap the key. */
@ -301,7 +301,7 @@ uint32_t user_generate_specific_aes_key(smc_args_t *args) {
keyslot = KEYSLOT_SWITCH_DEVICEKEY; keyslot = KEYSLOT_SWITCH_DEVICEKEY;
} }
if (0 /* TODO: GET_BOOTROM_PATCH_VERSION < 0x7F */) { if (fuse_get_bootrom_patch_version() < 0x7F) {
/* On dev units, use a fixed "all-zeroes" seed. */ /* On dev units, use a fixed "all-zeroes" seed. */
/* Yes, this data really is all-zero in actual TrustZone .rodata. */ /* Yes, this data really is all-zero in actual TrustZone .rodata. */
uint8_t dev_specific_aes_key_source[0x10] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; uint8_t dev_specific_aes_key_source[0x10] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
@ -384,7 +384,7 @@ uint32_t user_load_rsa_oaep_key(smc_args_t *args) {
if (is_personalized && size != 0x240) { if (is_personalized && size != 0x240) {
return 2; return 2;
} }
if (!is_personalized && (size != 0x220 /* TODO: || GET_BOOTROM_PATCH_VERSION >= 0x7F */)) { if (!is_personalized && (size != 0x220 || fuse_get_bootrom_patch_version() >= 0x7F)) {
return 2; return 2;
} }
@ -431,7 +431,7 @@ uint32_t user_decrypt_rsa_private_key(smc_args_t *args) {
if (is_personalized && size < 0x31) { if (is_personalized && size < 0x31) {
return 2; return 2;
} }
if (!is_personalized && (size < 0x11 /* TODO: || GET_BOOTROM_PATCH_VERSION >= 0x7F */)) { if (!is_personalized && (size < 0x11 || fuse_get_bootrom_patch_version() >= 0x7F)) {
return 2; return 2;
} }
@ -479,7 +479,7 @@ uint32_t user_load_secure_exp_mod_key(smc_args_t *args) {
if (is_personalized && size != 0x130) { if (is_personalized && size != 0x130) {
return 2; return 2;
} }
if (!is_personalized && (size != 0x110 /* TODO: || GET_BOOTROM_PATCH_VERSION >= 0x7F */)) { if (!is_personalized && (size != 0x110 || fuse_get_bootrom_patch_version() >= 0x7F)) {
return 2; return 2;
} }

View File

@ -21,6 +21,10 @@ static inline uint32_t read32be(const unsigned char *dword, size_t offset) {
return __builtin_bswap32(read32le(dword, offset)); return __builtin_bswap32(read32le(dword, offset));
} }
static inline uint64_t read64le(const void *qword, size_t offset) {
return *(uint32_t *)((uintptr_t)dword + offset);
}
static __attribute__((noinline)) bool check_32bit_additive_overflow(uint32_t a, uint32_t b) { static __attribute__((noinline)) bool check_32bit_additive_overflow(uint32_t a, uint32_t b) {
uint64_t x = (uint64_t)a + (uint64_t)b; uint64_t x = (uint64_t)a + (uint64_t)b;
return x > (uint64_t)(UINT32_MAX); return x > (uint64_t)(UINT32_MAX);