diff --git a/exosphere/bootconfig.h b/exosphere/bootconfig.h new file mode 100644 index 000000000..cfb5ba1df --- /dev/null +++ b/exosphere/bootconfig.h @@ -0,0 +1,18 @@ +#ifndef EXOSPHERE_BOOTCONFIG_H +#define EXOSPHERE_BOOTCONFIG_H + +#include + +/* This provides management for Switch BootConfig. */ + +typedef struct { + uint8_t unsigned_config[0x200]; + uint8_t signature[0x100]; + uint8_t header[0x100]; + uint8_t signed_config[0x24]; +} bootconfig_t; + +void bootconfig_load_and_verify(const bootconfig_t *bootconfig) +void bootconfig_clear(void); + +#endif \ No newline at end of file diff --git a/exosphere/configitem.h b/exosphere/configitem.h index 58e2d4706..c5a35f9cc 100644 --- a/exosphere/configitem.h +++ b/exosphere/configitem.h @@ -24,5 +24,6 @@ uint32_t configitem_set(enum ConfigItem item, uint64_t value); uint32_t configitem_get(enum ConfigItem item, uint64_t *p_outvalue); uint64_t configitem_is_recovery_boot(void); +uint64_t configitem_is_retail(void); #endif \ No newline at end of file diff --git a/exosphere/masterkey.h b/exosphere/masterkey.h index 570b796a8..b6bd59e42 100644 --- a/exosphere/masterkey.h +++ b/exosphere/masterkey.h @@ -6,6 +6,11 @@ /* TODO: Update to 0x5 on release of new master key. */ #define MASTERKEY_REVISION_MAX 0x4 +#define MASTERKEY_REVISION_100_230 0x00 +#define MASTERKEY_REVISION_300 0x01 +#define MASTERKEY_REVISION_301_302 0x02 +#define MASTERKEY_REVISION_400_CURRENT 0x03 + /* This should be called early on in initialization. */ void mkey_detect_revision(void); diff --git a/exosphere/package2.c b/exosphere/package2.c new file mode 100644 index 000000000..8942c3b67 --- /dev/null +++ b/exosphere/package2.c @@ -0,0 +1,139 @@ +#include + +#include "utils.h" + +#include "package2.h" +#include "configitem.h" +#include "se.h" +#include "masterkey.h" +#include "cache.h" +#include "randomcache.h" +#include "timers.h" + +void setup_mmio_virtual_addresses(void) { + /* TODO: Set Timers address to 0x1F008B000. */ + /* TODO: Set Security Engine address to 0x1F008F000. */ + /* TODO: Set CAR address to 0x1F0087000. */ + /* TODO: Set PMC address to 0x1F0089400. */ + /* TODO: Set Fuse address to 0x1F0096800. */ + /* TODO: Set Interrupt addresses to 0x1F0080000, 0x1F0082000. */ + /* TODO: Set Flow Controller address to 0x1F009D000. */ + /* TODO: Set UART-A address to 0x1F0085000. */ + /* TODO: Set I2C-0 address to 0x1F00A5000. */ + /* TODO: Set I2C-4 address to 0x1F00A1000. */ + /* TODO: Set MISC address to 0x70000000. */ + /* TODO: Set GPIO-1 address to 0x6000D000. */ +} + +/* Hardware init, sets up the RNG and SESSION keyslots, derives new DEVICE key. */ +void setup_se(void) { + uint8_t work_buffer[0x10]; + + /* Sanity check the Security Engine. */ + se_verify_flags_cleared(); + se_clear_interrupts(); + + /* Perform some sanity initialization. */ + security_engine_t *p_security_engine = get_security_engine_address(); + p_security_engine->_0x4 = 0; + p_security_engine->AES_KEY_READ_DISABLE_REG = 0; + p_security_engine->RSA_KEY_READ_SAIBEL_REG = 0; + p_security_engine->_0x0 &= 0xFFFFFFFB; + + /* Currently unknown what each flag does. */ + for (unsigned int i = 0; i < KEYSLOT_AES_MAX; i++) { + set_aes_keyslot_flags(i, 0x15); + } + + for (unsigned int i = 4; i < KEYSLOT_AES_MAX; i++) { + set_aes_keyslot_flags(i, 0x40); + } + + for (unsigned int i = 0; i < KEYSLOT_RSA_MAX; i++) { + set_rsa_keyslot_flags(i, 0x41); + } + + /* Detect Master Key revision. */ + mkey_detect_revision(); + + /* Setup new device key, if necessary. */ + if (mkey_get_revision() >= MASTERKEY_REVISION_400_CURRENT) { + const uint8_t 4x_new_devicekey_source[0x10] = {0x8B, 0x4E, 0x1C, 0x22, 0x42, 0x07, 0xC8, 0x73, 0x56, 0x94, 0x08, 0x8B, 0xCC, 0x47, 0x0F, 0x5D}; + se_aes_ecb_decrypt_block(KEYSLOT_SWITCH_4XNEWDEVICEKEYGENKEY, work_buffer, 0x10, 4x_new_devicekey_source, 0x10); + decrypt_data_into_keyslot(KEYSLOT_SWITCH_DEVICEKEY, KEYSLOT_SWITCH_4XNEWCONSOLEKEYGENKEY, work_buffer, 0x10); + clear_aes_keyslot(KEYSLOT_SWITCH_4XNEWCONSOLEKEYGENKEY); + set_aes_keyslot_flags(KEYSLOT_SWITCH_DEVICEKEY, 0xFF); + } + + se_initialize_rng(KEYSLOT_SWITCH_DEVICEKEY); + + /* Generate random data, transform with device key to get RNG key. */ + se_generate_random(KEYSLOT_SWITCH_DEVICEKEY, work_buffer, 0x10); + decrypt_data_into_keyslot(KEYSLOT_SWITCH_RNGKEY, KEYSLOT_SWITCH_DEVICEKEY, work_buffer, 0x10); + set_aes_keyslot_flags(KEYSLOT_SWITCH_RNGKEY, 0xFF); + + /* Repeat for Session key. */ + se_generate_random(KEYSLOT_SWITCH_DEVICEKEY, work_buffer, 0x10); + decrypt_data_into_keyslot(KEYSLOT_SWITCH_SESSIONKEY, KEYSLOT_SWITCH_DEVICEKEY, work_buffer, 0x10); + set_aes_keyslot_flags(KEYSLOT_SWITCH_SESSIONKEY, 0xFF); + + /* TODO: Create Test Vector, to validate keyslot data is unchanged post warmboot. */ +} + +void setup_boot_config(void) { + /* Load boot config only if dev unit. */ + if (configitem_is_retail()) { + bootconfig_clear(); + } else { + flush_dcache_range((uint8_t *)NX_BOOTLOADER_BOOTCONFIG_POINTER, (uint8_t *)NX_BOOTLOADER_BOOTCONFIG_POINTER + sizeof(bootconfig_t)); + bootconfig_load_and_verify((bootconfig_t *)NX_BOOTLOADER_BOOTCONFIG_POINTER); + } +} + +/* This function is called during coldboot crt0, and validates a package2. */ +/* This package2 is read into memory by a concurrent BPMP bootloader. */ +void load_package2(void) { + /* Setup MMIO virtual pointers. */ + setup_mmio_virtual_addresses(); + + /* Setup the Security Engine. */ + setup_se(); + + /* TODO: bootup_misc_mmio(). */ + /* This func will also be called on warmboot. */ + /* And will verify stored SE Test Vector, clear keyslots, */ + /* Generate an SRK, set the warmboot firmware location, */ + /* Configure the GPU uCode carveout, configure the Kernel default carveouts, */ + /* Initialize the PMC secure scratch registers, initialize MISC registers, */ + /* And assign "se_operation_completed" to Interrupt 0x5A. */ + + /* TODO: Read and save BOOTREASON stored by NX_BOOTLOADER at 0x1F009FE00 */ + + /* Initialize cache'd random bytes for kernel. */ + randomcache_init(); + + /* TODO: memclear the initial copy of Exosphere running in IRAM (relocated to TZRAM by earlier code). */ + + /* Let NX Bootloader know that we're running. */ + MAILBOX_NX_BOOTLOADER_IS_SECMON_AWAKE = 1; + + /* Synchronize with NX BOOTLOADER. */ + if (MAILBOX_NX_BOOTLOADER_SETUP_STATE == NX_BOOTLOADER_STATE_INIT) { + while (MAILBOX_NX_BOOTLOADER_SETUP_STATE < NX_BOOTLOADER_STATE_MOVED_BOOTCONFIG) { + wait(1); + } + } + + /* Load Boot Config into global. */ + setup_boot_config(); + + /* Synchronize with NX BOOTLOADER. */ + if (MAILBOX_NX_BOOTLOADER_SETUP_STATE == NX_BOOTLOADER_STATE_MOVED_BOOTCONFIG) { + while (MAILBOX_NX_BOOTLOADER_SETUP_STATE < NX_BOOTLOADER_STATE_LOADED_PACKAGE2) { + wait(1); + } + } + + /* TODO: Parse/load Package2. */ + +} \ No newline at end of file diff --git a/exosphere/package2.h b/exosphere/package2.h new file mode 100644 index 000000000..54c585c76 --- /dev/null +++ b/exosphere/package2.h @@ -0,0 +1,24 @@ +#ifndef EXOSPHERE_PACKAGE2_H +#define EXOSPHERE_PACKAGE2_H + +/* This is code responsible for validating a package2. Actual file reading is done by bootloader. */ + +#include +#include "bootconfig.h" + +/* Physaddr 0x40002EF8 */ +#define MAILBOX_NX_BOOTLOADER_SETUP_STATE (*((volatile uint32_t *)(0x1F009FEF8ULL))) + +#define NX_BOOTLOADER_STATE_INIT 0 +#define NX_BOOTLOADER_STATE_MOVED_BOOTCONFIG 1 +#define NX_BOOTLOADER_STATE_LOADED_PACKAGE2 2 +#define NX_BOOTLOADER_STATE_FINISHED 3 + +/* Physaddr 0x40002EFC */ +#define MAILBOX_NX_BOOTLOADER_IS_SECMON_AWAKE (*((volatile uint32_t *)(0x1F009FEFCULL))) + +#define NX_BOOTLOADER_BOOTCONFIG_POINTER ((void *)(0x4003D000ULL)); + +void load_package2(void); + +#endif \ No newline at end of file diff --git a/exosphere/se.c b/exosphere/se.c index 42513d0cb..5632f678b 100644 --- a/exosphere/se.c +++ b/exosphere/se.c @@ -62,6 +62,17 @@ void se_operation_completed(void) { } } + +void se_verify_flags_cleared(void) { + if (g_security_engine == NULL || g_security_engine->FLAGS_REG & 3) { + panic(); + } +} + +void se_clear_interrupts(void) { + /* TODO */ +} + /* Set the flags for an AES keyslot. */ void set_aes_keyslot_flags(unsigned int keyslot, unsigned int flags) { if (g_security_engine == NULL || keyslot >= KEYSLOT_AES_MAX) { diff --git a/exosphere/se.h b/exosphere/se.h index ae6964bb5..a93e65863 100644 --- a/exosphere/se.h +++ b/exosphere/se.h @@ -13,6 +13,8 @@ #define KEYSLOT_SWITCH_DEVICEKEY 0xD /* This keyslot was added in 4.0.0. */ +#define KEYSLOT_SWITCH_4XNEWDEVICEKEYGENKEY 0xD +#define KEYSLOT_SWITCH_4XNEWCONSOLEKEYGENKEY 0xE #define KEYSLOT_SWITCH_4XOLDDEVICEKEY 0xF #define KEYSLOT_AES_MAX 0x10 @@ -75,7 +77,11 @@ typedef struct security_engine { unsigned int _0x328; unsigned int _0x32C; unsigned int CRYPTO_KEYTABLE_DST_REG; - unsigned char _0x334[0xCC]; + unsigned char _0x334[0xC]; + unsigned int RNG_CONFIG_REG; + unsigned int RNG_SRC_CONFIG_REG; + unsigned int RNG_RESEED_INTERVAL_REG; + unsigned char _0x34C[0xB4]; unsigned int RSA_CONFIG; unsigned int RSA_KEY_SIZE_REG; unsigned int RSA_EXP_SIZE_REG; @@ -123,6 +129,9 @@ security_engine_t *get_security_engine_address(void); void se_check_for_error(void); void se_trigger_interrupt(void); +void se_verify_flags_cleared(void); +void se_clear_interrupts(void); + void set_aes_keyslot_flags(unsigned int keyslot, unsigned int flags); void set_rsa_keyslot_flags(unsigned int keyslot, unsigned int flags); void clear_aes_keyslot(unsigned int keyslot); @@ -154,6 +163,8 @@ void se_calculate_sha256(void *dst, const void *src, size_t src_size); void se_exp_mod(unsigned int keyslot, void *buf, size_t size, unsigned int (*callback)(void)); void se_get_exp_mod_output(void *buf, size_t size); +/* RNG API */ +void se_initialize_rng(unsigned int keyslot); void se_generate_random(unsigned int keyslot, void *dst, size_t size); /* TODO: SE context save API, consider extending AES API for secure world vs non-secure world operations. */ diff --git a/exosphere/smc_user.c b/exosphere/smc_user.c index 8d13452e4..02033d9d3 100644 --- a/exosphere/smc_user.c +++ b/exosphere/smc_user.c @@ -158,7 +158,7 @@ uint32_t user_generate_aes_kek(smc_args_t *args) { unsigned int keyslot; if (is_personalized) { /* Behavior changed in 4.0.0. */ - if (mkey_get_revision() >= 4) { + if (mkey_get_revision() >= MASTERKEY_REVISION_400_CURRENT) { if (master_key_rev >= 1) { keyslot = KEYSLOT_SWITCH_DEVICEKEY; /* New device key, 4.x. */ } else { @@ -288,7 +288,7 @@ uint32_t user_generate_specific_aes_key(smc_args_t *args) { unsigned int keyslot; /* Behavior changed in 4.0.0. */ - if (mkey_get_revision() >= 4) { + if (mkey_get_revision() >= MASTERKEY_REVISION_400_CURRENT) { if (master_key_rev >= 2) { keyslot = KEYSLOT_SWITCH_DEVICEKEY; /* New device key, 4.x. */ } else { diff --git a/exosphere/start.cold.s b/exosphere/start.cold.s index f959358bc..92b5cb003 100644 --- a/exosphere/start.cold.s +++ b/exosphere/start.cold.s @@ -82,7 +82,7 @@ __init_cold: msr spsel, #0 ldr x20, =__pk2_load_stack_top__ mov sp, x20 - bl loadPk2 + bl load_package2 ldr x20, =__cold_init_stack_top__ mov sp, x20 b coldboot_main