diff --git a/exosphere/bootconfig.h b/exosphere/bootconfig.h index 6ce8cf970..5fda11563 100644 --- a/exosphere/bootconfig.h +++ b/exosphere/bootconfig.h @@ -20,5 +20,9 @@ void bootconfig_clear(void); bool bootconfig_is_package2_plaintext(void); bool bootconfig_is_package2_unsigned(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 \ No newline at end of file diff --git a/exosphere/configitem.c b/exosphere/configitem.c index 8a26f18b3..2e21a4dbf 100644 --- a/exosphere/configitem.c +++ b/exosphere/configitem.c @@ -26,14 +26,23 @@ bool configitem_is_recovery_boot(void) { 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 result = 0; switch (item) { case CONFIGITEM_DISABLEPROGRAMVERIFICATION: *p_outvalue = (int)(bootconfig_disable_program_verification()); break; - case CONFIGITEM_MEMORYCONFIGURATION: - /* TODO: Fuse driver */ + case CONFIGITEM_DRAMID: + *p_outvalue = fuse_get_dram_id(); break; case CONFIGITEM_SECURITYENGINEIRQ: /* 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; break; case CONFIGITEM_HARDWARETYPE: - /* TODO: Fuse driver */ + *p_outvalue = fuse_get_hardware_type(); break; case CONFIGITEM_ISRETAIL: - /* TODO: Fuse driver */ + *p_outvalue = fuse_get_retail_type(); break; case CONFIGITEM_ISRECOVERYBOOT: /* TODO: This requires reading values passed to crt0 via NX_Bootloader. TBD pending crt0 implementation. */ *p_outvalue = 0; break; case CONFIGITEM_DEVICEID: - /* TODO: Fuse driver */ + *p_outvalue = fuse_get_device_id(); break; case CONFIGITEM_BOOTREASON: /* TODO: This requires reading values passed to crt0 via NX_Bootloader. TBD pending crt0 implementation. */ break; case CONFIGITEM_MEMORYARRANGE: - /* TODO: More BootConfig stuff. */ + *p_outvalue = bootconfig_get_memory_arrangement(); break; case CONFIGITEM_ISDEBUGMODE: - /* TODO: More BootConfig stuff. */ + *p_outvalue = (int)(bootconfig_is_debug_mode()); break; case CONFIGITEM_KERNELMEMORYCONFIGURATION: - /* TODO: More BootConfig stuff. */ + *p_outvalue = bootconfig_get_kernel_memory_configuration(); break; case CONFIGITEM_BATTERYPROFILE: *p_outvalue = g_battery_profile; diff --git a/exosphere/configitem.h b/exosphere/configitem.h index 474566760..f176d9bde 100644 --- a/exosphere/configitem.h +++ b/exosphere/configitem.h @@ -6,7 +6,7 @@ enum ConfigItem { CONFIGITEM_DISABLEPROGRAMVERIFICATION = 1, - CONFIGITEM_MEMORYCONFIGURATION = 2, + CONFIGITEM_DRAMID = 2, CONFIGITEM_SECURITYENGINEIRQ = 3, CONFIGITEM_VERSION = 4, CONFIGITEM_HARDWARETYPE = 5, diff --git a/exosphere/fuse.c b/exosphere/fuse.c index 90142bb92..c034c1460 100644 --- a/exosphere/fuse.c +++ b/exosphere/fuse.c @@ -108,6 +108,17 @@ void fuse_hw_sense(void) 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 */ uint32_t fuse_get_sku_info(void) { @@ -140,4 +151,81 @@ uint32_t fuse_get_reserved_odm(uint32_t idx) reserved_odm_val = FUSE_CHIP_REGS->FUSE_RESERVED_ODM[idx]; 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); } \ No newline at end of file diff --git a/exosphere/fuse.h b/exosphere/fuse.h index 20bf38695..9a68725b5 100644 --- a/exosphere/fuse.h +++ b/exosphere/fuse.h @@ -176,10 +176,18 @@ void fuse_init(void); uint32_t fuse_hw_read(uint32_t addr); void fuse_hw_write(uint32_t, value, uint32_t addr); 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_bootrom_patch_version(void); uint32_t fuse_get_spare_bit(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 diff --git a/exosphere/gcm.c b/exosphere/gcm.c index 211cd8252..f09ae2d08 100644 --- a/exosphere/gcm.c +++ b/exosphere/gcm.c @@ -2,6 +2,7 @@ #include #include "utils.h" +#include "fuse.h" #include "gcm.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; } - /* 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; } diff --git a/exosphere/smc_user.c b/exosphere/smc_user.c index b4fb500f5..51cb1ddf8 100644 --- a/exosphere/smc_user.c +++ b/exosphere/smc_user.c @@ -203,7 +203,7 @@ uint32_t user_load_aes_key(smc_args_t *args) { wrapped_key[0] = args->X[4]; wrapped_key[1] = args->X[5]; - /* TODO: Unseal the kek. */ + /* Unseal the kek. */ unseal_key(KEYSLOT_SWITCH_TEMPKEY, sealed_kek, 0x10, CRYPTOUSECASE_AES); /* Unwrap the key. */ @@ -301,7 +301,7 @@ uint32_t user_generate_specific_aes_key(smc_args_t *args) { 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. */ /* 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}; @@ -384,7 +384,7 @@ uint32_t user_load_rsa_oaep_key(smc_args_t *args) { if (is_personalized && size != 0x240) { 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; } @@ -431,7 +431,7 @@ uint32_t user_decrypt_rsa_private_key(smc_args_t *args) { if (is_personalized && size < 0x31) { 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; } @@ -479,7 +479,7 @@ uint32_t user_load_secure_exp_mod_key(smc_args_t *args) { if (is_personalized && size != 0x130) { 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; } diff --git a/exosphere/utils.h b/exosphere/utils.h index bb0d6bf77..dae7ce8f3 100644 --- a/exosphere/utils.h +++ b/exosphere/utils.h @@ -21,6 +21,10 @@ static inline uint32_t read32be(const unsigned char *dword, size_t 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) { uint64_t x = (uint64_t)a + (uint64_t)b; return x > (uint64_t)(UINT32_MAX);