From c2eed3caf6b7d04e7e51f1b07ccf019eb4ef6fb6 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 25 Mar 2018 15:05:08 -0600 Subject: [PATCH] Integrate 5.x SMC API changes, add 4.x specific setup, implement target firmware selection --- exosphere/src/actmon.h | 2 + exosphere/src/bootconfig.c | 11 ++ exosphere/src/bootconfig.h | 3 + exosphere/src/bootup.c | 66 ++++++++- exosphere/src/bpmp.h | 2 + exosphere/src/car.c | 2 + exosphere/src/car.h | 1 + exosphere/src/coldboot_init.c | 7 +- exosphere/src/configitem.c | 29 +++- exosphere/src/configitem.h | 3 + exosphere/src/exocfg.c | 37 +++++ exosphere/src/exocfg.h | 41 ++++++ exosphere/src/fuse.c | 11 +- exosphere/src/fuse.h | 2 + exosphere/src/gcm.c | 43 +++++- exosphere/src/gcm.h | 10 +- exosphere/src/lp0.c | 5 +- exosphere/src/masterkey.c | 34 +++++ exosphere/src/masterkey.h | 12 +- exosphere/src/mc.h | 5 + exosphere/src/package2.c | 67 +++++++-- exosphere/src/pmc.h | 14 ++ exosphere/src/se.h | 3 + exosphere/src/sealedkeys.c | 20 ++- exosphere/src/sealedkeys.h | 4 + exosphere/src/smc_api.c | 42 +++++- exosphere/src/smc_api.h | 2 + exosphere/src/smc_user.c | 246 +++++++++++++++++++++++++++++++--- exosphere/src/smc_user.h | 3 + exosphere/src/start.s | 3 +- exosphere/src/sysreg.h | 2 + exosphere/src/utils.h | 3 +- exosphere/src/warmboot_init.c | 71 ++++++---- exosphere/src/warmboot_main.c | 8 +- 34 files changed, 734 insertions(+), 80 deletions(-) create mode 100644 exosphere/src/exocfg.c create mode 100644 exosphere/src/exocfg.h diff --git a/exosphere/src/actmon.h b/exosphere/src/actmon.h index fa567dbb3..fdabfff5b 100644 --- a/exosphere/src/actmon.h +++ b/exosphere/src/actmon.h @@ -11,7 +11,9 @@ #define MAKE_ACTMON_REG(n) MAKE_REG32(ACTMON_BASE + n) #define ACTMON_GLB_STATUS_0 MAKE_ACTMON_REG(0x000) +#define ACTMON_GLB_PERIOD_CTRL_0 MAKE_ACTMON_REG(0x004) #define ACTMON_COP_CTRL_0 MAKE_ACTMON_REG(0x0C0) +#define ACTMON_COP_UPPER_WMARK_0 MAKE_ACTMON_REG(0x0C4) #define ACTMON_COP_INTR_STATUS_0 MAKE_ACTMON_REG(0x0E4) void actmon_interrupt_handler(void); diff --git a/exosphere/src/bootconfig.c b/exosphere/src/bootconfig.c index 9bb4873b3..476d6643d 100644 --- a/exosphere/src/bootconfig.c +++ b/exosphere/src/bootconfig.c @@ -9,6 +9,7 @@ #include "bootconfig.h" static boot_reason_t g_boot_reason = {0}; +static uint64_t g_package2_hash_for_recovery[4] = {0}; bool bootconfig_matches_hardware_info(void) { uint32_t hardware_info[4]; @@ -114,6 +115,16 @@ void bootconfig_load_boot_reason(volatile boot_reason_t *boot_reason) { g_boot_reason = *boot_reason; } +void bootconfig_set_package2_hash_for_recovery(const void *package2, size_t package2_size) { + se_calculate_sha256(g_package2_hash_for_recovery, package2, package2_size); +} + +void bootconfig_get_package2_hash_for_recovery(uint64_t *out_hash) { + for (unsigned int i = 0; i < 4; i++) { + out_hash[i] = g_package2_hash_for_recovery[i]; + } +} + bool bootconfig_is_recovery_boot(void) { return (g_boot_reason.is_recovery_boot != 0); } diff --git a/exosphere/src/bootconfig.h b/exosphere/src/bootconfig.h index 3a789ede0..c027c140a 100644 --- a/exosphere/src/bootconfig.h +++ b/exosphere/src/bootconfig.h @@ -47,6 +47,9 @@ void bootconfig_clear(void); void bootconfig_load_boot_reason(volatile boot_reason_t *boot_reason); +void bootconfig_set_package2_hash_for_recovery(const void *package2, size_t package2_size); +void bootconfig_get_package2_hash_for_recovery(uint64_t *out_hash); + /* Actual configuration getters. */ bool bootconfig_is_package2_plaintext(void); bool bootconfig_is_package2_unsigned(void); diff --git a/exosphere/src/bootup.c b/exosphere/src/bootup.c index 59558a7b5..fb710c838 100644 --- a/exosphere/src/bootup.c +++ b/exosphere/src/bootup.c @@ -5,6 +5,7 @@ #include "bootup.h" #include "fuse.h" +#include "bpmp.h" #include "flow.h" #include "pmc.h" #include "mc.h" @@ -20,6 +21,7 @@ #include "cpu_context.h" #include "actmon.h" #include "sysctr0.h" +#include "exocfg.h" #include "mmu.h" #include "arm.h" @@ -49,7 +51,7 @@ void bootup_misc_mmio(void) { se_generate_srk(KEYSLOT_SWITCH_SRKGENKEY); /* TODO: Why does this DRAM write occur? */ - if (!g_has_booted_up && mkey_get_revision() >= MASTERKEY_REVISION_400_CURRENT) { + if (!g_has_booted_up && exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_400) { /* 4.x writes this magic number into DRAM. Why? */ (*(volatile uint32_t *)(0x8005FFFC)) = 0xC0EDBBCC; } @@ -94,7 +96,7 @@ void bootup_misc_mmio(void) { /* Also mark I2C5 secure only, */ sec_disable_1 |= 0x20000000; } - if (hardware_type != 0 && mkey_get_revision() >= MASTERKEY_REVISION_400_CURRENT) { + if (hardware_type != 0 && exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_400) { /* Starting on 4.x on non-dev units, mark UARTB, UARTC, SPI4, I2C3 secure only. */ sec_disable_1 |= 0x10806000; /* Starting on 4.x on non-dev units, mark SDMMC1 secure only. */ @@ -164,7 +166,7 @@ void bootup_misc_mmio(void) { set_core_is_active(core, false); } g_has_booted_up = true; - } else if (mkey_get_revision() < MASTERKEY_REVISION_400_CURRENT) { + } else if (exosphere_get_target_firmware() < EXOSPHERE_TARGET_FIRMWARE_400) { /* TODO: What are these MC reg writes? */ MAKE_MC_REG(0x65C) = 0xFFFFF000; MAKE_MC_REG(0x660) = 0; @@ -174,7 +176,61 @@ void bootup_misc_mmio(void) { } void setup_4x_mmio(void) { - /* TODO */ + /* TODO: What are these MC reg writes? */ + MAKE_MC_REG(0x65C) = 0xFFFFF000; + MAKE_MC_REG(0x660) = 0; + MAKE_MC_REG(0x964) |= 1; + CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRD_0 &= 0xFFF7FFFF; + /* TODO: What are these PMC scratch writes? */ + APBDEV_PMC_SECURE_SCRATCH51_0 = (APBDEV_PMC_SECURE_SCRATCH51_0 & 0xFFFF8000) | 0x4000; + APBDEV_PMC_SECURE_SCRATCH16_0 &= 0x3FFFFFFF; + APBDEV_PMC_SECURE_SCRATCH55_0 = (APBDEV_PMC_SECURE_SCRATCH55_0 & 0xFF000FFF) | 0x1000; + APBDEV_PMC_SECURE_SCRATCH74_0 = 0x40008000; + APBDEV_PMC_SECURE_SCRATCH75_0 = 0x40000; + APBDEV_PMC_SECURE_SCRATCH76_0 = 0x0; + APBDEV_PMC_SECURE_SCRATCH77_0 = 0x0; + APBDEV_PMC_SECURE_SCRATCH78_0 = 0x0; + APBDEV_PMC_SECURE_SCRATCH99_0 = 0x40008000; + APBDEV_PMC_SECURE_SCRATCH100_0 = 0x40000; + APBDEV_PMC_SECURE_SCRATCH101_0 = 0x0; + APBDEV_PMC_SECURE_SCRATCH102_0 = 0x0; + APBDEV_PMC_SECURE_SCRATCH103_0 = 0x0; + APBDEV_PMC_SECURE_SCRATCH39_0 = (APBDEV_PMC_SECURE_SCRATCH39_0 & 0xF8000000) | 0x88; + /* TODO: Do we want to bother locking the secure scratch registers? */ + /* 4.x Jamais Vu mitigations. */ + /* Overwrite exception vectors. */ + BPMP_VECTOR_RESET = BPMP_MITIGATION_RESET_VAL; + BPMP_VECTOR_UNDEF = BPMP_MITIGATION_RESET_VAL; + BPMP_VECTOR_SWI = BPMP_MITIGATION_RESET_VAL; + BPMP_VECTOR_PREFETCH_ABORT = BPMP_MITIGATION_RESET_VAL; + BPMP_VECTOR_DATA_ABORT = BPMP_MITIGATION_RESET_VAL; + BPMP_VECTOR_UNK = BPMP_MITIGATION_RESET_VAL; + BPMP_VECTOR_IRQ = BPMP_MITIGATION_RESET_VAL; + BPMP_VECTOR_FIQ = BPMP_MITIGATION_RESET_VAL; + /* Disable AHB arbitration for the BPMP. */ + AHB_ARBITRATION_DISABLE_0 |= 2; + /* Set SMMU for BPMP/APB-DMA to point to TZRAM. */ + MC_SMMU_PTB_ASID_0 = 1; + MC_SMMU_PTB_DATA_0 = 0x70012; + MC_SMMU_AVPC_ASID_0 = 0x80000001; + MC_SMMU_PPCS1_ASID_0 = 0x80000001; + /* Wait for the BPMP to halt. */ + while ((FLOW_CTLR_HALT_COP_EVENTS_0 >> 29) != 5) { + wait(1); + } + /* If not in a debugging context, setup the activity monitor. */ + if ((get_debug_authentication_status() & 3) != 3) { + FLOW_CTLR_HALT_COP_EVENTS_0 = 0x40000000; + clkrst_reboot(CARDEVICE_ACTMON); + /* Sample every microsecond. */ + ACTMON_GLB_PERIOD_CTRL_0 = 0x100; + /* Fire interrupt every wakeup. */ + ACTMON_COP_UPPER_WMARK_0 = 0; + /* Cause a panic() on BPMP wakeup. */ + actmon_set_callback(actmon_on_bpmp_wakeup); + /* Enable interrupt when above watermark, periodic sampling. */ + ACTMON_COP_CTRL_0 = 0xC0040000; + } } void setup_current_core_state(void) { @@ -226,7 +282,7 @@ void identity_unmap_iram_cd_tzram(void) { } void secure_additional_devices(void) { - if (mkey_get_revision() >= MASTERKEY_REVISION_400_CURRENT) { + if (exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_400) { APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0 |= 0x2000; /* make PMC secure-only (2.x+ but see note below) */ APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG1_0 |= 0X510; /* make MC0, MC1, MCB secure-only (4.x+) */ } else { diff --git a/exosphere/src/bpmp.h b/exosphere/src/bpmp.h index 50328a893..e7171bc91 100644 --- a/exosphere/src/bpmp.h +++ b/exosphere/src/bpmp.h @@ -23,4 +23,6 @@ static inline uintptr_t get_bpmp_vector_base(void) { #define BPMP_VECTOR_IRQ MAKE_REG32(BPMP_VECTOR_BASE + 0x218) #define BPMP_VECTOR_FIQ MAKE_REG32(BPMP_VECTOR_BASE + 0x21C) +#define BPMP_MITIGATION_RESET_VAL 0x7D000000 + #endif diff --git a/exosphere/src/car.c b/exosphere/src/car.c index 88a35c9b1..58aeb519d 100644 --- a/exosphere/src/car.c +++ b/exosphere/src/car.c @@ -10,6 +10,7 @@ static inline uint32_t get_special_clk_reg(CarDevice dev) { case CARDEVICE_UARTB: return 0x17C; case CARDEVICE_I2C1: return 0x124; case CARDEVICE_I2C5: return 0x128; + case CARDEVICE_ACTMON: return 0x3E8; case CARDEVICE_BPMP: return 0; default: generic_panic(); } @@ -21,6 +22,7 @@ static inline uint32_t get_special_clk_val(CarDevice dev) { case CARDEVICE_UARTB: return 0; case CARDEVICE_I2C1: return (6 << 29); case CARDEVICE_I2C5: return (6 << 29); + case CARDEVICE_ACTMON: return (6 << 29); case CARDEVICE_BPMP: return 0; default: generic_panic(); } diff --git a/exosphere/src/car.h b/exosphere/src/car.h index e97954fe0..3fb511bc2 100644 --- a/exosphere/src/car.h +++ b/exosphere/src/car.h @@ -22,6 +22,7 @@ typedef enum { CARDEVICE_UARTB = 7, CARDEVICE_I2C1 = 12, CARDEVICE_I2C5 = 47, + CARDEVICE_ACTMON = 119, CARDEVICE_BPMP = 1 } CarDevice; diff --git a/exosphere/src/coldboot_init.c b/exosphere/src/coldboot_init.c index 4ed41f915..132398a36 100644 --- a/exosphere/src/coldboot_init.c +++ b/exosphere/src/coldboot_init.c @@ -5,6 +5,7 @@ #include "arm.h" #include "package2.h" #include "timers.h" +#include "exocfg.h" #undef MAILBOX_NX_BOOTLOADER_BASE #undef TIMERS_BASE @@ -149,6 +150,7 @@ void coldboot_init(coldboot_crt0_reloc_list_t *reloc_list, boot_func_list_t *fun */ func_list->funcs.flush_dcache_all(); func_list->funcs.invalidate_icache_all(); + /* TODO: Set NX BOOTLOADER clock time field */ @@ -157,10 +159,13 @@ void coldboot_init(coldboot_crt0_reloc_list_t *reloc_list, boot_func_list_t *fun do_relocation(reloc_list, i); } /* At this point, we can (and will) access functions located in .warm_crt0 */ + + /* Set target firmware. */ + func_list->target_firmware = exosphere_get_target_firmware_physical(); /* Initialize DMA controllers, and write to AHB_GIZMO_TZRAM. */ /* TZRAM accesses should work normally after this point. */ - func_list->funcs.init_dma_controllers(); + func_list->funcs.init_dma_controllers(func_list->target_firmware); configure_ttbls(); func_list->funcs.set_memory_registers_enable_mmu(); diff --git a/exosphere/src/configitem.c b/exosphere/src/configitem.c index 63a4a19a1..17f1c1afc 100644 --- a/exosphere/src/configitem.c +++ b/exosphere/src/configitem.c @@ -8,6 +8,7 @@ #include "fuse.h" #include "utils.h" #include "masterkey.h" +#include "exocfg.h" static bool g_battery_profile = false; @@ -81,7 +82,7 @@ uint32_t configitem_get(ConfigItem item, uint64_t *p_outvalue) { break; case CONFIGITEM_BOOTREASON: /* For some reason, Nintendo removed it on 4.0 */ - if (mkey_get_revision() < MASTERKEY_REVISION_400_CURRENT) { + if (exosphere_get_target_firmware() < EXOSPHERE_TARGET_FIRMWARE_400) { *p_outvalue = bootconfig_get_boot_reason(); } else { result = 2; @@ -101,12 +102,36 @@ uint32_t configitem_get(ConfigItem item, uint64_t *p_outvalue) { break; case CONFIGITEM_ODM4BIT10_4X: /* Added on 4.x ... where is it being used? */ - if (mkey_get_revision() >= MASTERKEY_REVISION_400_CURRENT) { + if (exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_400) { *p_outvalue = (fuse_get_reserved_odm(4) >> 10) & 1; } else { result = 2; } break; + case CONFIGITEM_NEWHARDWARETYPE_5X: + /* Added in 5.x, currently hardcoded to 0. */ + if (exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_500) { + *p_outvalue = 0; + } else { + result = 2; + } + break; + case CONFIGITEM_NEWKEYGENERATION_5X: + /* Added in 5.x. */ + if (exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_500) { + *p_outvalue = fuse_get_5x_key_generation(); + } else { + result = 2; + } + break; + case CONFIGITEM_PACKAGE2HASH_5X: + /* Added in 5.x. */ + if (exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_500 && bootconfig_is_recovery_boot()) { + bootconfig_get_package2_hash_for_recovery(p_outvalue); + } else { + result = 2; + } + break; default: result = 2; break; diff --git a/exosphere/src/configitem.h b/exosphere/src/configitem.h index 29f0bf12c..c9cd1d699 100644 --- a/exosphere/src/configitem.h +++ b/exosphere/src/configitem.h @@ -19,6 +19,9 @@ typedef enum { CONFIGITEM_KERNELMEMORYCONFIGURATION = 12, CONFIGITEM_BATTERYPROFILE = 13, CONFIGITEM_ODM4BIT10_4X = 14, + CONFIGITEM_NEWHARDWARETYPE_5X = 15, + CONFIGITEM_NEWKEYGENERATION_5X = 16, + CONFIGITEM_PACKAGE2HASH_5X = 17 } ConfigItem; uint32_t configitem_set(ConfigItem item, uint64_t value); diff --git a/exosphere/src/exocfg.c b/exosphere/src/exocfg.c new file mode 100644 index 000000000..e9519b6d2 --- /dev/null +++ b/exosphere/src/exocfg.c @@ -0,0 +1,37 @@ +#include +#include + +#include "utils.h" +#include "exocfg.h" +#include "mmu.h" +#include "memory_map.h" + +#define MAILBOX_BASE (MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_NXBOOTLOADER_MAILBOX)) + +/* TODO: Should this be at a non-static location? */ +#define MAILBOX_EXOSPHERE_CONFIG (*((volatile exosphere_config_t *)(MAILBOX_BASE + 0xE40ULL))) + +static exosphere_config_t g_exosphere_cfg = {MAGIC_ATMOSPHERE, EXOSPHERE_TARGET_FIRMWARE_DEFAULT_FOR_DEBUG}; +static bool g_has_loaded_config = false; + +/* Read config out of IRAM, return target firmware version. */ +unsigned int exosphere_load_config(void) { + if (g_has_loaded_config) { + generic_panic(); + } + g_has_loaded_config = true; + + if (MAILBOX_EXOSPHERE_CONFIG.magic == MAGIC_ATMOSPHERE) { + g_exosphere_cfg = MAILBOX_EXOSPHERE_CONFIG; + } + + return g_exosphere_cfg.target_firmware; +} + +unsigned int exosphere_get_target_firmware(void) { + if (!g_has_loaded_config) { + generic_panic(); + } + + return g_exosphere_cfg.target_firmware; +} \ No newline at end of file diff --git a/exosphere/src/exocfg.h b/exosphere/src/exocfg.h new file mode 100644 index 000000000..a1e8b29f2 --- /dev/null +++ b/exosphere/src/exocfg.h @@ -0,0 +1,41 @@ +#ifndef EXOSPHERE_EXOSPHERE_CONFIG_H +#define EXOSPHERE_EXOSPHERE_CONFIG_H + +#include +#include "utils.h" + +#include "memory_map.h" + +/* This serves to set configuration for *exosphere itself*, separate from the SecMon Exosphere mimics. */ + +/* "AMS0" */ +#define MAGIC_ATMOSPHERE (0x30534D41) + +#define EXOSPHERE_TARGET_FIRMWARE_100 1 +#define EXOSPHERE_TARGET_FIRMWARE_200 2 +#define EXOSPHERE_TARGET_FIRMWARE_300 3 +#define EXOSPHERE_TARGET_FIRMWARE_400 4 +#define EXOSPHERE_TARGET_FIRMWARE_500 5 + +/* TODO: What should this be, for release? */ +#define EXOSPHERE_TARGET_FIRMWARE_DEFAULT_FOR_DEBUG EXOSPHERE_TARGET_FIRMWARE_200 + +#define MAILBOX_BASE_PHYS (MMIO_GET_DEVICE_PA(MMIO_DEVID_NXBOOTLOADER_MAILBOX)) + +/* TODO: Should this be at a non-static location? */ +#define MAILBOX_EXOSPHERE_CONFIG_PHYS (*((volatile exosphere_config_t *)(MAILBOX_BASE_PHYS + 0xE40ULL))) + + +typedef struct { + unsigned int magic; + unsigned int target_firmware; +} exosphere_config_t; + +unsigned int exosphere_load_config(void); +unsigned int exosphere_get_target_firmware(void); + +static inline unsigned int exosphere_get_target_firmware_physical(void) { + return MAILBOX_EXOSPHERE_CONFIG_PHYS.magic == MAGIC_ATMOSPHERE ? MAILBOX_EXOSPHERE_CONFIG_PHYS.target_firmware : EXOSPHERE_TARGET_FIRMWARE_DEFAULT_FOR_DEBUG; +} + +#endif \ No newline at end of file diff --git a/exosphere/src/fuse.c b/exosphere/src/fuse.c index e5006b333..94654c451 100644 --- a/exosphere/src/fuse.c +++ b/exosphere/src/fuse.c @@ -4,6 +4,7 @@ #include "fuse.h" #include "utils.h" #include "timers.h" +#include "exocfg.h" #include "masterkey.h" /* Prototypes for internal commands. */ @@ -150,6 +151,14 @@ uint32_t fuse_get_reserved_odm(uint32_t idx) return FUSE_CHIP_REGS->FUSE_RESERVED_ODM[idx]; } +uint32_t fuse_get_5x_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; + } +} + /* Derive the Device ID using values in the shadow cache */ uint64_t fuse_get_device_id(void) { uint64_t device_id = 0; @@ -182,7 +191,7 @@ uint32_t fuse_get_hardware_type(void) { /* This function is very different between 4.x and < 4.x */ uint32_t hardware_type = ((FUSE_CHIP_REGS->FUSE_RESERVED_ODM[4] >> 7) & 2) | ((FUSE_CHIP_REGS->FUSE_RESERVED_ODM[4] >> 2) & 1); - if (mkey_get_revision() >= MASTERKEY_REVISION_400_CURRENT) { + if (exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_400) { static const uint32_t types[] = {0,1,4,3}; hardware_type |= (FUSE_CHIP_REGS->FUSE_RESERVED_ODM[4] >> 14) & 0x3C; diff --git a/exosphere/src/fuse.h b/exosphere/src/fuse.h index 1a5a5cc58..c4d1240a7 100644 --- a/exosphere/src/fuse.h +++ b/exosphere/src/fuse.h @@ -190,6 +190,8 @@ uint32_t fuse_get_sku_info(void); uint32_t fuse_get_spare_bit(uint32_t idx); uint32_t fuse_get_reserved_odm(uint32_t idx); +uint32_t fuse_get_5x_key_generation(void); + uint32_t fuse_get_bootrom_patch_version(void); uint64_t fuse_get_device_id(void); uint32_t fuse_get_dram_id(void); diff --git a/exosphere/src/gcm.c b/exosphere/src/gcm.c index f65c3b106..7350cd5d6 100644 --- a/exosphere/src/gcm.c +++ b/exosphere/src/gcm.c @@ -1,6 +1,7 @@ #include #include +#include "arm.h" #include "utils.h" #include "fuse.h" #include "gcm.h" @@ -112,7 +113,7 @@ static void ghash(void *dst, const void *data, size_t data_size, const void *j_b /* This function is a doozy. It decrypts and validates a (non-standard) AES-GCM wrapped keypair. */ -size_t gcm_decrypt_key(void *dst, size_t dst_size, const void *src, size_t src_size, const void *sealed_kek, size_t kek_size, const void *wrapped_key, size_t key_size, unsigned int usecase, bool is_personalized) { +size_t gcm_decrypt_key(void *dst, size_t dst_size, const void *src, size_t src_size, const void *sealed_kek, size_t kek_size, const void *wrapped_key, size_t key_size, unsigned int usecase, bool is_personalized, uint8_t *out_deviceid_high) { if (is_personalized == 0) { /* Devkit keys use a different keyformat without a MAC/Device ID. */ if (src_size <= 0x10 || src_size - 0x10 > dst_size) { @@ -163,8 +164,48 @@ size_t gcm_decrypt_key(void *dst, size_t dst_size, const void *src, size_t src_s if ((read64be(intermediate_buf, src_size - 0x28) & 0x00FFFFFFFFFFFFFFULL) != fuse_get_device_id()) { return 0; } + + if (out_deviceid_high != NULL) { + *out_deviceid_high = intermediate_buf[src_size - 0x28]; + } memcpy(dst, intermediate_buf, src_size - 0x30); memset(intermediate_buf, 0, sizeof(intermediate_buf)); return src_size - 0x30; } + +void gcm_encrypt_key(void *dst, size_t dst_size, const void *src, size_t src_size, const void *sealed_kek, size_t kek_size, const void *wrapped_key, size_t key_size, unsigned int usecase, uint64_t deviceid_high) { + uint8_t intermediate_buf[0x400] = {0}; + if (src_size + 0x30 > dst_size) { + generic_panic(); + } + + /* Unwrap the key */ + unseal_key(KEYSLOT_SWITCH_TEMPKEY, sealed_kek, kek_size, usecase); + decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, KEYSLOT_SWITCH_TEMPKEY, wrapped_key, key_size); + + /* Generate a random CTR. */ + flush_dcache_range(intermediate_buf, intermediate_buf + 0x10); + se_generate_random(KEYSLOT_SWITCH_RNGKEY, intermediate_buf, 0x10); + flush_dcache_range(intermediate_buf, intermediate_buf + 0x10); + + /* Write Device ID. */ + write64be(intermediate_buf, src_size + 0x18, fuse_get_device_id() | (deviceid_high << 56)); + + + /* J = GHASH(CTR); */ + uint8_t j_block[0x10]; + ghash(j_block, intermediate_buf, 0x10, NULL, false); + + /* MAC = GHASH(PLAINTEXT) ^ ENCRYPT(J) */ + /* Note: That MAC is calculated over plaintext is non-standard. */ + /* It is supposed to be over the ciphertext. */ + ghash(intermediate_buf + src_size + 0x20, intermediate_buf + 0x10, src_size + 0x10, j_block, true); + + /* Encrypt the GCM keypair, AES-CTR with CTR = blob[:0x10]. */ + se_aes_ctr_crypt(KEYSLOT_SWITCH_TEMPKEY, intermediate_buf + 0x10, src_size + 0x10, intermediate_buf + 0x10, src_size + 0x10, intermediate_buf, 0x10); + + /* Copy the wrapped key out. */ + memcpy(dst, intermediate_buf, src_size + 0x30); + memset(intermediate_buf, 0, sizeof(intermediate_buf)); +} diff --git a/exosphere/src/gcm.h b/exosphere/src/gcm.h index 15caa11aa..bd53bc9bf 100644 --- a/exosphere/src/gcm.h +++ b/exosphere/src/gcm.h @@ -8,6 +8,14 @@ size_t gcm_decrypt_key(void *dst, size_t dst_size, const void *src, size_t src_size, const void *sealed_kek, size_t kek_size, const void *wrapped_key, size_t key_size, - unsigned int usecase, bool is_personalized); + unsigned int usecase, bool is_personalized, + uint8_t *out_deviceid_high); + + +void gcm_encrypt_key(void *dst, size_t dst_size, + const void *src, size_t src_size, + const void *sealed_kek, size_t kek_size, + const void *wrapped_key, size_t key_size, + unsigned int usecase, uint64_t deviceid_high); #endif diff --git a/exosphere/src/lp0.c b/exosphere/src/lp0.c index c0e28f658..1675ab9e4 100644 --- a/exosphere/src/lp0.c +++ b/exosphere/src/lp0.c @@ -19,6 +19,7 @@ #include "smc_api.h" #include "timers.h" #include "misc.h" +#include "exocfg.h" extern const uint8_t bpmpfw_bin[]; extern const uint32_t bpmpfw_bin_size; @@ -123,14 +124,14 @@ uint32_t cpu_suspend(uint64_t power_state, uint64_t entrypoint, uint64_t argumen } /* For debugging, make this check always pass. */ - if ((mkey_get_revision() < MASTERKEY_REVISION_400_CURRENT || (get_debug_authentication_status() & 3) == 3)) { + if ((exosphere_get_target_firmware() < EXOSPHERE_TARGET_FIRMWARE_400 || (get_debug_authentication_status() & 3) == 3)) { FLOW_CTLR_HALT_COP_EVENTS_0 = 0x50000000; } else { FLOW_CTLR_HALT_COP_EVENTS_0 = 0x40000000; } /* Jamais Vu mitigation #2: Ensure the BPMP is halted. */ - if (mkey_get_revision() < MASTERKEY_REVISION_400_CURRENT || (get_debug_authentication_status() & 3) == 3) { + if (exosphere_get_target_firmware() < EXOSPHERE_TARGET_FIRMWARE_400 || (get_debug_authentication_status() & 3) == 3) { /* BPMP should just be plainly halted, in debugging conditions. */ if (FLOW_CTLR_HALT_COP_EVENTS_0 != 0x50000000) { generic_panic(); diff --git a/exosphere/src/masterkey.c b/exosphere/src/masterkey.c index 1e28b5659..c126a4af3 100644 --- a/exosphere/src/masterkey.c +++ b/exosphere/src/masterkey.c @@ -1,5 +1,6 @@ #include #include +#include #include "utils.h" #include "masterkey.h" @@ -9,6 +10,7 @@ static unsigned int g_mkey_revision = 0; static bool g_determined_mkey_revision = false; static uint8_t g_old_masterkeys[MASTERKEY_REVISION_MAX][0x10]; +static uint8_t g_old_devicekeys[MASTERKEY_NUM_NEW_DEVICE_KEYS - 1][0x10]; /* TODO: Dev keys. */ @@ -19,6 +21,7 @@ static const uint8_t mkey_vectors[MASTERKEY_REVISION_MAX][0x10] = {0x29, 0x4C, 0x04, 0xC8, 0xEB, 0x10, 0xED, 0x9D, 0x51, 0x64, 0x97, 0xFB, 0xF3, 0x4D, 0x50, 0xDD}, /* Master key 00 encrypted with Master key 01. */ {0xDE, 0xCF, 0xEB, 0xEB, 0x10, 0xAE, 0x74, 0xD8, 0xAD, 0x7C, 0xF4, 0x9E, 0x62, 0xE0, 0xE8, 0x72}, /* Master key 01 encrypted with Master key 02. */ {0x0A, 0x0D, 0xDF, 0x34, 0x22, 0x06, 0x6C, 0xA4, 0xE6, 0xB1, 0xEC, 0x71, 0x85, 0xCA, 0x4E, 0x07}, /* Master key 02 encrypted with Master key 03. */ + {0x6E, 0x7D, 0x2D, 0xC3, 0x0F, 0x59, 0xC8, 0xFA, 0x87, 0xA8, 0x2E, 0xD5, 0x89, 0x5E, 0xF3, 0xE9}, /* Master key 03 encrypted with Master key 04. */ }; bool check_mkey_revision(unsigned int revision) { @@ -88,3 +91,34 @@ unsigned int mkey_get_keyslot(unsigned int revision) { return KEYSLOT_SWITCH_TEMPKEY; } } + + +void set_old_devkey(unsigned int revision, const uint8_t *key) { + if (revision < MASTERKEY_REVISION_400_410 || MASTERKEY_REVISION_500_CURRENT <= revision) { + generic_panic(); + } + + memcpy(g_old_devicekeys[revision - MASTERKEY_REVISION_400_410], key, 0x10); +} + +unsigned int devkey_get_keyslot(unsigned int revision) { + if (!g_determined_mkey_revision || revision >= MASTERKEY_REVISION_MAX) { + generic_panic(); + } + + if (revision > g_mkey_revision) { + generic_panic(); + } + + if (revision >= 1) { + if (revision == MASTERKEY_REVISION_500_CURRENT) { + return KEYSLOT_SWITCH_DEVICEKEY; + } else { + /* Load into a temp keyslot. */ + set_aes_keyslot(KEYSLOT_SWITCH_TEMPKEY, g_old_devicekeys[revision - MASTERKEY_REVISION_400_410], 0x10); + return KEYSLOT_SWITCH_TEMPKEY; + } + } else { + return KEYSLOT_SWITCH_4XOLDDEVICEKEY; + } +} \ No newline at end of file diff --git a/exosphere/src/masterkey.h b/exosphere/src/masterkey.h index b6bd59e42..5a6a523bb 100644 --- a/exosphere/src/masterkey.h +++ b/exosphere/src/masterkey.h @@ -3,13 +3,16 @@ /* This is glue code to enable master key support across versions. */ -/* TODO: Update to 0x5 on release of new master key. */ -#define MASTERKEY_REVISION_MAX 0x4 +/* TODO: Update to 0x6 on release of new master key. */ +#define MASTERKEY_REVISION_MAX 0x5 #define MASTERKEY_REVISION_100_230 0x00 #define MASTERKEY_REVISION_300 0x01 #define MASTERKEY_REVISION_301_302 0x02 -#define MASTERKEY_REVISION_400_CURRENT 0x03 +#define MASTERKEY_REVISION_400_410 0x03 +#define MASTERKEY_REVISION_500_CURRENT 0x04 + +#define MASTERKEY_NUM_NEW_DEVICE_KEYS (MASTERKEY_REVISION_MAX - MASTERKEY_REVISION_400_410) /* This should be called early on in initialization. */ void mkey_detect_revision(void); @@ -18,4 +21,7 @@ unsigned int mkey_get_revision(void); unsigned int mkey_get_keyslot(unsigned int revision); +void set_old_devkey(unsigned int revision, const uint8_t *key); +unsigned int devkey_get_keyslot(unsigned int revision); + #endif \ No newline at end of file diff --git a/exosphere/src/mc.h b/exosphere/src/mc.h index 5a52e6c6b..af98dd262 100644 --- a/exosphere/src/mc.h +++ b/exosphere/src/mc.h @@ -14,6 +14,11 @@ static inline uintptr_t get_mc_base(void) { #define MAKE_MC_REG(n) MAKE_REG32(MC_BASE + n) +#define MC_SMMU_PTB_ASID_0 MAKE_MC_REG(0x01C) +#define MC_SMMU_PTB_DATA_0 MAKE_MC_REG(0x020) +#define MC_SMMU_AVPC_ASID_0 MAKE_MC_REG(0x23C) +#define MC_SMMU_PPCS1_ASID_0 MAKE_MC_REG(0x298) + #define MC_SECURITY_CFG0_0 MAKE_MC_REG(0x070) #define MC_SECURITY_CFG1_0 MAKE_MC_REG(0x074) #define MC_SECURITY_CFG3_0 MAKE_MC_REG(0x9BC) diff --git a/exosphere/src/package2.c b/exosphere/src/package2.c index 0c37d8a9e..6e202a184 100644 --- a/exosphere/src/package2.c +++ b/exosphere/src/package2.c @@ -15,10 +15,37 @@ #include "randomcache.h" #include "timers.h" #include "bootconfig.h" +#include "exocfg.h" +#include "smc_api.h" extern void *__start_cold_addr; extern size_t __bin_size; +static void derive_new_device_keys(unsigned int keygen_keyslot) { + uint8_t work_buffer[0x10]; + static const uint8_t new_device_key_sources[MASTERKEY_NUM_NEW_DEVICE_KEYS][0x10] = { + {0x8B, 0x4E, 0x1C, 0x22, 0x42, 0x07, 0xC8, 0x73, 0x56, 0x94, 0x08, 0x8B, 0xCC, 0x47, 0x0F, 0x5D}, /* 4.x New Device Key Source. */ + {0x6C, 0xEF, 0xC6, 0x27, 0x8B, 0xEC, 0x8A, 0x91, 0x99, 0xAB, 0x24, 0xAC, 0x4F, 0x1C, 0x8F, 0x1C} /* 5.x New Device Key Source. */ + }; + + static const uint8_t new_device_keygen_sources[MASTERKEY_NUM_NEW_DEVICE_KEYS][0x10] = { + {0x88, 0x62, 0x34, 0x6E, 0xFA, 0xF7, 0xD8, 0x3F, 0xE1, 0x30, 0x39, 0x50, 0xF0, 0xB7, 0x5D, 0x5D}, /* 4.x New Device Keygen Source. */ + {0x06, 0x1E, 0x7B, 0xE9, 0x6D, 0x47, 0x8C, 0x77, 0xC5, 0xC8, 0xE7, 0x94, 0x9A, 0xA8, 0x5F, 0x2E} /* 5.x New Device Keygen Source. */ + }; + for (unsigned int revision = 0; revision < MASTERKEY_NUM_NEW_DEVICE_KEYS; revision++) { + se_aes_ecb_decrypt_block(keygen_keyslot, work_buffer, 0x10, new_device_key_sources[revision], 0x10); + decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, mkey_get_keyslot(0), new_device_keygen_sources[revision], 0x10); + if (revision < MASTERKEY_NUM_NEW_DEVICE_KEYS - 1) { + se_aes_ecb_decrypt_block(KEYSLOT_SWITCH_TEMPKEY, work_buffer, 0x10, work_buffer, 0x10); + set_old_devkey(revision + MASTERKEY_REVISION_400_410, work_buffer); + } else { + decrypt_data_into_keyslot(KEYSLOT_SWITCH_DEVICEKEY, KEYSLOT_SWITCH_TEMPKEY, work_buffer, 0x10); + } + } + set_aes_keyslot_flags(KEYSLOT_SWITCH_DEVICEKEY, 0xFF); + clear_aes_keyslot(keygen_keyslot); +} + /* Hardware init, sets up the RNG and SESSION keyslots, derives new DEVICE key. */ static void setup_se(void) { uint8_t work_buffer[0x10]; @@ -52,13 +79,18 @@ static void setup_se(void) { /* Detect Master Key revision. */ mkey_detect_revision(); - /* Setup new device key, if necessary. */ - if (mkey_get_revision() >= MASTERKEY_REVISION_400_CURRENT) { - const uint8_t new_devicekey_source_4x[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, new_devicekey_source_4x, 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); + /* Derive new device keys. */ + switch (exosphere_get_target_firmware()) { + case EXOSPHERE_TARGET_FIRMWARE_100: + case EXOSPHERE_TARGET_FIRMWARE_200: + case EXOSPHERE_TARGET_FIRMWARE_300: + break; + case EXOSPHERE_TARGET_FIRMWARE_400: + derive_new_device_keys(KEYSLOT_SWITCH_4XNEWDEVICEKEYGENKEY); + break; + case EXOSPHERE_TARGET_FIRMWARE_500: + derive_new_device_keys(KEYSLOT_SWITCH_5XNEWDEVICEKEYGENKEY); + break; } se_initialize_rng(KEYSLOT_SWITCH_DEVICEKEY); @@ -151,6 +183,10 @@ static void verify_header_signature(package2_header_t *header) { } } +static uint32_t get_package2_size(package2_meta_t *metadata) { + return metadata->ctr_dwords[0] ^ metadata->ctr_dwords[2] ^ metadata->ctr_dwords[3]; +} + static bool validate_package2_metadata(package2_meta_t *metadata) { if (metadata->magic != MAGIC_PK21) { return false; @@ -355,6 +391,9 @@ uintptr_t get_pk2ldr_stack_address(void) { /* This function is called during coldboot init, and validates a package2. */ /* This package2 is read into memory by a concurrent BPMP bootloader. */ void load_package2(coldboot_crt0_reloc_list_t *reloc_list) { + /* Load Exosphere-specific config. */ + exosphere_load_config(); + /* Setup the Security Engine. */ setup_se(); @@ -385,7 +424,7 @@ void load_package2(coldboot_crt0_reloc_list_t *reloc_list) { setup_boot_config(); /* Synchronize with NX BOOTLOADER. */ - if (mkey_get_revision() >= MASTERKEY_REVISION_400_CURRENT) { + if (exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_400) { sync_with_nx_bootloader(NX_BOOTLOADER_STATE_DRAM_INITIALIZED_4X); /* TODO: copy_warmboot_bin_to_dram(); */ sync_with_nx_bootloader(NX_BOOTLOADER_STATE_LOADED_PACKAGE2_4X); @@ -411,6 +450,11 @@ void load_package2(coldboot_crt0_reloc_list_t *reloc_list) { /* Decrypt header, get key revision required. */ uint32_t package2_mkey_rev = decrypt_and_validate_header(&header); + /* Copy hash, if necessary. */ + if (bootconfig_is_recovery_boot()) { + bootconfig_set_package2_hash_for_recovery(NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS, get_package2_size(&header.metadata)); + } + /* Load Package2 Sections. */ load_package2_sections(&header.metadata, package2_mkey_rev); @@ -427,12 +471,15 @@ void load_package2(coldboot_crt0_reloc_list_t *reloc_list) { /* Synchronize with NX BOOTLOADER. */ sync_with_nx_bootloader(NX_BOOTLOADER_STATE_FINISHED); - if (mkey_get_revision() >= MASTERKEY_REVISION_400_CURRENT) { + if (exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_400) { sync_with_nx_bootloader(NX_BOOTLOADER_STATE_FINISHED_4X); - setup_4x_mmio(); /* TODO */ + setup_4x_mmio(); } else { sync_with_nx_bootloader(NX_BOOTLOADER_STATE_FINISHED); } + + /* Prepare the SMC API with version-dependent SMCs. */ + set_version_specific_smcs(); /* Update SCR_EL3 depending on value in Bootconfig. */ set_extabt_serror_taken_to_el3(bootconfig_take_extabt_serror_to_el3()); diff --git a/exosphere/src/pmc.h b/exosphere/src/pmc.h index 300e6893e..5b274a9bc 100644 --- a/exosphere/src/pmc.h +++ b/exosphere/src/pmc.h @@ -39,6 +39,20 @@ static inline uintptr_t get_pmc_base(void) { #define APBDEV_PMC_SECURE_SCRATCH34_0 MAKE_REG32(PMC_BASE + 0x368) #define APBDEV_PMC_SECURE_SCRATCH35_0 MAKE_REG32(PMC_BASE + 0x36C) +#define APBDEV_PMC_SECURE_SCRATCH16_0 MAKE_REG32(PMC_BASE + 0x320) +#define APBDEV_PMC_SECURE_SCRATCH51_0 MAKE_REG32(PMC_BASE + 0x3AC) +#define APBDEV_PMC_SECURE_SCRATCH55_0 MAKE_REG32(PMC_BASE + 0x3BC) +#define APBDEV_PMC_SECURE_SCRATCH74_0 MAKE_REG32(PMC_BASE + 0x408) +#define APBDEV_PMC_SECURE_SCRATCH75_0 MAKE_REG32(PMC_BASE + 0x40C) +#define APBDEV_PMC_SECURE_SCRATCH76_0 MAKE_REG32(PMC_BASE + 0x410) +#define APBDEV_PMC_SECURE_SCRATCH77_0 MAKE_REG32(PMC_BASE + 0x414) +#define APBDEV_PMC_SECURE_SCRATCH78_0 MAKE_REG32(PMC_BASE + 0x418) +#define APBDEV_PMC_SECURE_SCRATCH99_0 MAKE_REG32(PMC_BASE + 0xAE4) +#define APBDEV_PMC_SECURE_SCRATCH100_0 MAKE_REG32(PMC_BASE + 0xAE8) +#define APBDEV_PMC_SECURE_SCRATCH101_0 MAKE_REG32(PMC_BASE + 0xAEC) +#define APBDEV_PMC_SECURE_SCRATCH102_0 MAKE_REG32(PMC_BASE + 0xAF0) +#define APBDEV_PMC_SECURE_SCRATCH103_0 MAKE_REG32(PMC_BASE + 0xAF4) +#define APBDEV_PMC_SECURE_SCRATCH39_0 MAKE_REG32(PMC_BASE + 0x37C) #endif diff --git a/exosphere/src/se.h b/exosphere/src/se.h index fa5feff36..b79e2c0aa 100644 --- a/exosphere/src/se.h +++ b/exosphere/src/se.h @@ -24,6 +24,9 @@ #define KEYSLOT_SWITCH_4XNEWCONSOLEKEYGENKEY 0xE #define KEYSLOT_SWITCH_4XOLDDEVICEKEY 0xF +/* This keyslot was added in 5.0.0. */ +#define KEYSLOT_SWITCH_5XNEWDEVICEKEYGENKEY 0xA + #define KEYSLOT_AES_MAX 0x10 #define KEYSLOT_RSA_MAX 0x2 diff --git a/exosphere/src/sealedkeys.c b/exosphere/src/sealedkeys.c index 2041ae79a..73a88bcc6 100644 --- a/exosphere/src/sealedkeys.c +++ b/exosphere/src/sealedkeys.c @@ -2,6 +2,7 @@ #include #include "utils.h" +#include "exocfg.h" #include "sealedkeys.h" #include "se.h" @@ -9,13 +10,24 @@ static const uint8_t g_titlekey_seal_key_source[0x10] = { 0xCB, 0xB7, 0x6E, 0x38, 0xA1, 0xCB, 0x77, 0x0F, 0xB2, 0xA5, 0xB2, 0x9D, 0xD8, 0x56, 0x9F, 0x76 }; -static const uint8_t g_seal_key_sources[CRYPTOUSECASE_MAX][0x10] = { +static const uint8_t g_seal_key_sources[CRYPTOUSECASE_MAX_5X][0x10] = { {0xF4, 0x0C, 0x16, 0x26, 0x0D, 0x46, 0x3B, 0xE0, 0x8C, 0x6A, 0x56, 0xE5, 0x82, 0xD4, 0x1B, 0xF6}, {0x7F, 0x54, 0x2C, 0x98, 0x1E, 0x54, 0x18, 0x3B, 0xBA, 0x63, 0xBD, 0x4C, 0x13, 0x5B, 0xF1, 0x06}, {0xC7, 0x3F, 0x73, 0x60, 0xB7, 0xB9, 0x9D, 0x74, 0x0A, 0xF8, 0x35, 0x60, 0x1A, 0x18, 0x74, 0x63}, - {0x0E, 0xE0, 0xC4, 0x33, 0x82, 0x66, 0xE8, 0x08, 0x39, 0x13, 0x41, 0x7D, 0x04, 0x64, 0x2B, 0x6D} + {0x0E, 0xE0, 0xC4, 0x33, 0x82, 0x66, 0xE8, 0x08, 0x39, 0x13, 0x41, 0x7D, 0x04, 0x64, 0x2B, 0x6D}, + {0xE1, 0xA8, 0xAA, 0x6A, 0x2D, 0x9C, 0xDE, 0x43, 0x0C, 0xDE, 0xC6, 0x17, 0xF6, 0xC7, 0xF1, 0xDE}, + {0x74, 0x20, 0xF6, 0x46, 0x77, 0xB0, 0x59, 0x2C, 0xE8, 0x1B, 0x58, 0x64, 0x47, 0x41, 0x37, 0xD9}, + {0xAA, 0x19, 0x0F, 0xFA, 0x4C, 0x30, 0x3B, 0x2E, 0xE8, 0x1B, 0x58, 0x64, 0x47, 0x41, 0x37, 0xD9} }; +bool usecase_is_invalid(unsigned int usecase) { + if (exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_500) { + return usecase >= CRYPTOUSECASE_MAX_5X; + } else { + return usecase >= CRYPTOUSECASE_MAX; + } +} + void seal_key_internal(void *dst, const void *src, const uint8_t *seal_key_source) { decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, KEYSLOT_SWITCH_SESSIONKEY, seal_key_source, 0x10); se_aes_128_ecb_encrypt_block(KEYSLOT_SWITCH_TEMPKEY, dst, 0x10, src, 0x10); @@ -46,7 +58,7 @@ void unseal_titlekey(unsigned int keyslot, const void *src, size_t src_size) { void seal_key(void *dst, size_t dst_size, const void *src, size_t src_size, unsigned int usecase) { - if (usecase >= CRYPTOUSECASE_MAX || dst_size != 0x10 || src_size != 0x10) { + if (usecase_is_invalid(usecase) || dst_size != 0x10 || src_size != 0x10) { generic_panic(); } @@ -55,7 +67,7 @@ void seal_key(void *dst, size_t dst_size, const void *src, size_t src_size, unsi } void unseal_key(unsigned int keyslot, const void *src, size_t src_size, unsigned int usecase) { - if (usecase >= CRYPTOUSECASE_MAX || src_size != 0x10) { + if (usecase_is_invalid(usecase) || src_size != 0x10) { generic_panic(); } diff --git a/exosphere/src/sealedkeys.h b/exosphere/src/sealedkeys.h index 78226c7e2..c73f148bd 100644 --- a/exosphere/src/sealedkeys.h +++ b/exosphere/src/sealedkeys.h @@ -9,8 +9,12 @@ #define CRYPTOUSECASE_RSAPRIVATE 1 #define CRYPTOUSECASE_SECUREEXPMOD 2 #define CRYPTOUSECASE_RSAOAEP 3 +#define CRYPTOUSECASE_RSAIMPORT 4 +#define CRYPTOUSECASE_UNK5 5 +#define CRYPTOUSECASE_UNK6 6 #define CRYPTOUSECASE_MAX 4 +#define CRYPTOUSECASE_MAX_5X 7 void seal_titlekey(void *dst, size_t dst_size, const void *src, size_t src_size); void unseal_titlekey(unsigned int keyslot, const void *src, size_t src_size); diff --git a/exosphere/src/smc_api.c b/exosphere/src/smc_api.c index 19411a917..a26f457d5 100644 --- a/exosphere/src/smc_api.c +++ b/exosphere/src/smc_api.c @@ -19,6 +19,7 @@ #include "userpage.h" #include "titlekey.h" #include "lp0.h" +#include "exocfg.h" #define SMC_USER_HANDLERS 0x13 #define SMC_PRIV_HANDLERS 0x9 @@ -43,6 +44,10 @@ uint32_t smc_unwrap_rsa_oaep_wrapped_titlekey(smc_args_t *args); uint32_t smc_load_titlekey(smc_args_t *args); uint32_t smc_unwrap_aes_wrapped_titlekey(smc_args_t *args); +/* 5.x SMC prototypes. */ +uint32_t smc_encrypt_rsa_key_for_import(smc_args_t *args); +uint32_t smc_decrypt_or_import_rsa_key(smc_args_t *args); + /* Privileged SMC prototypes */ uint32_t smc_cpu_suspend(smc_args_t *args); uint32_t smc_cpu_off(smc_args_t *args); @@ -114,6 +119,32 @@ static atomic_flag g_is_priv_smc_in_progress = ATOMIC_FLAG_INIT; /* Global for smc_configure_carveout. */ static bool g_configured_carveouts[2] = {false, false}; +void set_version_specific_smcs(void) { + switch (exosphere_get_target_firmware()) { + case EXOSPHERE_TARGET_FIRMWARE_100: + /* 1.0.0 doesn't have ConfigureCarveout or ReadWriteRegister. */ + g_smc_priv_table[7].handler = NULL; + g_smc_priv_table[8].handler = NULL; + /* 1.0.0 doesn't have UnwrapAesWrappedTitlekey. */ + g_smc_user_table[0x12].handler = NULL; + break; + case EXOSPHERE_TARGET_FIRMWARE_200: + case EXOSPHERE_TARGET_FIRMWARE_300: + case EXOSPHERE_TARGET_FIRMWARE_400: + /* Do nothing. */ + break; + case EXOSPHERE_TARGET_FIRMWARE_500: + /* No more LoadSecureExpModKey. */ + g_smc_user_table[0xE].handler = NULL; + g_smc_user_table[0xC].id = 0xC300D60C; + g_smc_user_table[0xC].handler = smc_encrypt_rsa_key_for_import; + g_smc_user_table[0xD].handler = smc_decrypt_or_import_rsa_key; + break; + default: + panic_predefined(0xF); + } +} + uintptr_t get_smc_core012_stack_address(void) { return TZRAM_GET_SEGMENT_ADDRESS(TZRAM_SEGMENT_ID_CORE012_STACK) + 0x1000; @@ -427,6 +458,15 @@ uint32_t smc_unwrap_aes_wrapped_titlekey(smc_args_t *args) { return smc_wrapper_sync(args, user_unwrap_aes_wrapped_titlekey); } +uint32_t smc_encrypt_rsa_key_for_import(smc_args_t *args) { + return smc_wrapper_sync(args, user_encrypt_rsa_key_for_import); +} + +uint32_t smc_decrypt_or_import_rsa_key(smc_args_t *args) { + return smc_wrapper_sync(args, user_decrypt_or_import_rsa_key); +} + + uint32_t smc_cpu_on(smc_args_t *args) { return cpu_on((uint32_t)args->X[1], args->X[2], args->X[3]); } @@ -496,7 +536,7 @@ uint32_t smc_read_write_register(smc_args_t *args) { } else { return 2; } - } else if (mkey_get_revision() >= MASTERKEY_REVISION_400_CURRENT && MMIO_GET_DEVICE_PA(MMIO_DEVID_MC) <= address && + } else if (exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_400 && MMIO_GET_DEVICE_PA(MMIO_DEVID_MC) <= address && address < MMIO_GET_DEVICE_PA(MMIO_DEVID_MC) + MMIO_GET_DEVICE_SIZE(MMIO_DEVID_MC)) { /* Memory Controller RW supported only on 4.0.0+ */ const uint8_t mc_whitelist[0x68] = { diff --git a/exosphere/src/smc_api.h b/exosphere/src/smc_api.h index 4d291cc58..3d2a46a79 100644 --- a/exosphere/src/smc_api.h +++ b/exosphere/src/smc_api.h @@ -20,6 +20,8 @@ void clear_priv_smc_in_progress(void); uintptr_t get_smc_core012_stack_address(void); uintptr_t get_exception_entry_stack_address(unsigned int core_id); +void set_version_specific_smcs(void); + void call_smc_handler(unsigned int handler_id, smc_args_t *args); #endif diff --git a/exosphere/src/smc_user.c b/exosphere/src/smc_user.c index 2e6566beb..1d649f773 100644 --- a/exosphere/src/smc_user.c +++ b/exosphere/src/smc_user.c @@ -14,14 +14,15 @@ #include "sealedkeys.h" #include "userpage.h" #include "titlekey.h" +#include "exocfg.h" /* Globals. */ static bool g_crypt_aes_done = false; static bool g_exp_mod_done = false; -static uint8_t g_secure_exp_mod_exponent[0x100]; -static uint8_t g_rsa_oaep_exponent[0x100]; +static uint8_t g_imported_exponents[4][0x100]; +static uint8_t g_rsausecase_to_cryptousecase[5] = {1, 2, 3, 5, 6}; void set_exp_mod_done(bool done) { g_exp_mod_done = done; @@ -125,12 +126,31 @@ uint32_t user_generate_aes_kek(smc_args_t *args) { uint8_t mask_id = (uint8_t)((packed_options >> 1) & 3); /* Switches the output based on how it will be used. */ - uint8_t usecase = (uint8_t)((packed_options >> 5) & 3); + uint8_t usecase = (uint8_t)((packed_options >> 5) & (exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_500 ? 7 : 3)); /* Switched the output based on whether it should be console unique. */ bool is_personalized = (int)(packed_options & 1); bool is_recovery_boot = configitem_is_recovery_boot(); + + /* 5.0.0+ Bounds checking. */ + if (exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_500) { + if (is_personalized) { + if (master_key_rev > MASTERKEY_REVISION_500_CURRENT || ((1 << (master_key_rev + 1)) & 0x33) == 0) { + return 2; + } + if (mask_id > 3 || usecase >= CRYPTOUSECASE_MAX_5X) { + return 2; + } + } else { + if (usecase >= CRYPTOUSECASE_UNK6) { + return 2; + } + if (usecase == CRYPTOUSECASE_UNK5 && mask_id >= 4) { + return 2; + } + } + } /* Mask 2 is only allowed when booted from recovery. */ if (mask_id == 2 && !is_recovery_boot) { @@ -163,8 +183,10 @@ 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() >= MASTERKEY_REVISION_400_CURRENT) { + /* Behavior changed in 4.0.0, and in 5.0.0. */ + if (exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_500) { + keyslot = devkey_get_keyslot(master_key_rev); + } else if (exosphere_get_target_firmware() == EXOSPHERE_TARGET_FIRMWARE_400) { if (master_key_rev >= 1) { keyslot = KEYSLOT_SWITCH_DEVICEKEY; /* New device key, 4.x. */ } else { @@ -246,7 +268,17 @@ uint32_t user_crypt_aes(smc_args_t *args) { size_t size = args->X[6]; if (size & 0xF) { - generic_panic(); + return 2; + } + + if (exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_500) { + /* Disallow dma lists outside of safe range. */ + if (in_ll_paddr - 0x80000000 >= 0x3FF7F5) { + return 2; + } + if (out_ll_paddr - 0x80000000 >= 0x3FF7F5) { + return 2; + } } set_crypt_aes_done(false); @@ -287,6 +319,9 @@ uint32_t user_generate_specific_aes_key(smc_args_t *args) { if (master_key_rev > 0) { master_key_rev -= 1; } + if (exosphere_get_target_firmware() < EXOSPHERE_TARGET_FIRMWARE_400) { + master_key_rev = 0; + } if (master_key_rev >= MASTERKEY_REVISION_MAX) { return 2; @@ -299,9 +334,11 @@ uint32_t user_generate_specific_aes_key(smc_args_t *args) { unsigned int keyslot; - /* Behavior changed in 4.0.0. */ - if (mkey_get_revision() >= MASTERKEY_REVISION_400_CURRENT) { - if (master_key_rev >= 2) { + /* Behavior changed in 5.0.0. */ + if (exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_500) { + keyslot = devkey_get_keyslot(master_key_rev); + } else if (exosphere_get_target_firmware() == EXOSPHERE_TARGET_FIRMWARE_400) { + if (master_key_rev >= 1) { keyslot = KEYSLOT_SWITCH_DEVICEKEY; /* New device key, 4.x. */ } else { keyslot = KEYSLOT_SWITCH_4XOLDDEVICEKEY; /* Old device key, 4.x. */ @@ -378,6 +415,10 @@ uint32_t user_load_rsa_oaep_key(smc_args_t *args) { size_t size; upage_ref_t page_ref; + /* This function no longer exists in 5.x+. */ + if (exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_500) { + generic_panic(); + } /* Copy keydata */ sealed_kek[0] = args->X[1]; @@ -405,11 +446,11 @@ uint32_t user_load_rsa_oaep_key(smc_args_t *args) { flush_dcache_range(user_data, user_data + size); /* Ensure that our private key is 0x100 bytes. */ - if (gcm_decrypt_key(user_data, size, user_data, size, sealed_kek, 0x10, wrapped_key, 0x10, CRYPTOUSECASE_RSAOAEP, is_personalized) < 0x100) { + if (gcm_decrypt_key(user_data, size, user_data, size, sealed_kek, 0x10, wrapped_key, 0x10, CRYPTOUSECASE_RSAOAEP, is_personalized, NULL) < 0x100) { return 2; } - memcpy(g_rsa_oaep_exponent, user_data, 0x100); + memcpy(g_imported_exponents[0], user_data, 0x100); return 0; } @@ -423,6 +464,10 @@ uint32_t user_decrypt_rsa_private_key(smc_args_t *args) { size_t size; upage_ref_t page_ref; + /* This function no longer exists in 5.x+. */ + if (exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_500) { + generic_panic(); + } /* Copy keydata */ sealed_kek[0] = args->X[1]; @@ -455,7 +500,7 @@ uint32_t user_decrypt_rsa_private_key(smc_args_t *args) { size_t out_size; - if ((out_size = gcm_decrypt_key(user_data, size, user_data, size, sealed_kek, 0x10, wrapped_key, 0x10, CRYPTOUSECASE_RSAPRIVATE, is_personalized)) == 0) { + if ((out_size = gcm_decrypt_key(user_data, size, user_data, size, sealed_kek, 0x10, wrapped_key, 0x10, CRYPTOUSECASE_RSAPRIVATE, is_personalized, NULL)) == 0) { return 2; } @@ -477,6 +522,10 @@ uint32_t user_load_secure_exp_mod_key(smc_args_t *args) { size_t size; upage_ref_t page_ref; + /* This function no longer exists in 5.x+. */ + if (exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_500) { + generic_panic(); + } /* Copy keydata */ sealed_kek[0] = args->X[1]; @@ -506,15 +555,15 @@ uint32_t user_load_secure_exp_mod_key(smc_args_t *args) { size_t out_size; /* Ensure that our key is non-zero bytes. */ - if ((out_size = gcm_decrypt_key(user_data, size, user_data, size, sealed_kek, 0x10, wrapped_key, 0x10, CRYPTOUSECASE_SECUREEXPMOD, is_personalized)) == 0) { + if ((out_size = gcm_decrypt_key(user_data, size, user_data, size, sealed_kek, 0x10, wrapped_key, 0x10, CRYPTOUSECASE_SECUREEXPMOD, is_personalized, NULL)) == 0) { return 2; } /* Copy key to global. */ if (out_size <= 0x100) { - memcpy(g_secure_exp_mod_exponent, user_data, out_size); + memcpy(g_imported_exponents[1], user_data, out_size); } else { - memcpy(g_secure_exp_mod_exponent, user_data, 0x100); + memcpy(g_imported_exponents[1], user_data, 0x100); } return 0; @@ -529,6 +578,23 @@ uint32_t user_secure_exp_mod(smc_args_t *args) { void *user_input = (void *)args->X[1]; void *user_modulus = (void *)args->X[2]; + unsigned int exponent_id = 1; + if (exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_500) { + switch (args->X[3]) { + case 0: + exponent_id = 1; + break; + case 1: + exponent_id = 2; + break; + case 2: + exponent_id = 3; + break; + default: + return 2; + } + } + /* Copy user data into secure memory. */ if (upage_init(&page_ref, user_input) == 0) { return 2; @@ -542,7 +608,7 @@ uint32_t user_secure_exp_mod(smc_args_t *args) { set_exp_mod_done(false); /* Hardcode RSA keyslot 0. */ - set_rsa_keyslot(0, modulus, 0x100, g_secure_exp_mod_exponent, 0x100); + set_rsa_keyslot(0, modulus, 0x100, g_imported_exponents[exponent_id], 0x100); se_exp_mod(0, input, 0x100, exp_mod_done_handler); return 0; @@ -562,7 +628,7 @@ uint32_t user_unwrap_rsa_oaep_wrapped_titlekey(smc_args_t *args) { master_key_rev -= 1; } - if (mkey_get_revision() > 0 && master_key_rev >= MASTERKEY_REVISION_MAX) { + if (exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_300 && master_key_rev >= MASTERKEY_REVISION_MAX) { return 2; } else { master_key_rev = 0; @@ -587,7 +653,7 @@ uint32_t user_unwrap_rsa_oaep_wrapped_titlekey(smc_args_t *args) { tkey_set_master_key_rev(master_key_rev); /* Hardcode RSA keyslot 0. */ - set_rsa_keyslot(0, modulus, 0x100, g_rsa_oaep_exponent, 0x100); + set_rsa_keyslot(0, modulus, 0x100, g_imported_exponents[0], 0x100); se_exp_mod(0, wrapped_key, 0x100, exp_mod_done_handler); return 0; @@ -624,7 +690,7 @@ uint32_t user_unwrap_aes_wrapped_titlekey(smc_args_t *args) { if (master_key_rev > 0) { master_key_rev -= 1; } - if (mkey_get_revision() > 0 && master_key_rev >= MASTERKEY_REVISION_MAX) { + if (exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_300 && master_key_rev >= MASTERKEY_REVISION_MAX) { return 2; } else { master_key_rev = 0; @@ -640,3 +706,145 @@ uint32_t user_unwrap_aes_wrapped_titlekey(smc_args_t *args) { return 0; /* FIXME: what should we return there */ } + + +uint32_t user_encrypt_rsa_key_for_import(smc_args_t *args) { + uint64_t in_sealed_kek[2]; + uint64_t out_sealed_kek[2]; + uint64_t in_wrapped_key[2]; + uint64_t out_wrapped_key[2]; + uint8_t usecase; + + uint8_t user_data[0x400]; + void *user_address; + void *user_in_kek; + void *user_out_kek; + void *user_in_key; + void *user_out_key; + size_t size; + upage_ref_t page_ref; + + /* Copy keydata */ + user_in_kek = (void *)args->X[1]; + user_out_kek = (void *)args->X[2]; + usecase = args->X[3] & 7; + user_address = (void *)args->X[4]; + size = (size_t)args->X[5]; + user_in_key = (void *)args->X[6]; + user_out_key = (void *)args->X[7]; + + if (usecase > CRYPTOUSECASE_RSAIMPORT) { + return 2; + } + if (usecase == 0) { + if (size < 0x31 || size > 0x240) { + return 2; + } + } else if (size < 0x130 || size > 0x240) { + return 2; + } + + if (upage_init(&page_ref, user_address) == 0 + || user_copy_to_secure(&page_ref, user_data, user_address, size) == 0 + || user_copy_to_secure(&page_ref, in_sealed_kek, user_in_kek, 0x10) == 0 + || user_copy_to_secure(&page_ref, out_sealed_kek, user_out_kek, 0x10) == 0 + || user_copy_to_secure(&page_ref, in_wrapped_key, user_in_key, 0x10) == 0 + || user_copy_to_secure(&page_ref, out_wrapped_key, user_out_key, 0x10) == 0) { + return 2; + } + + flush_dcache_range(user_data, user_data + size); + + size_t out_size; + + uint8_t device_id_high; + + if ((out_size = gcm_decrypt_key(user_data, size, user_data, size, in_sealed_kek, 0x10, in_wrapped_key, 0x10, CRYPTOUSECASE_RSAIMPORT, true, &device_id_high)) == 0) { + return 2; + } + + gcm_encrypt_key(user_data, size, user_data, size - 0x30, out_sealed_kek, 0x10, out_wrapped_key, 0x10, g_rsausecase_to_cryptousecase[usecase], device_id_high); + + if (secure_copy_to_user(&page_ref, user_address, user_data, size) == 0) { + return 2; + } + + return 0; +} + +uint32_t user_decrypt_or_import_rsa_key(smc_args_t *args) { + uint64_t sealed_kek[2]; + uint64_t wrapped_key[2]; + uint8_t usecase; + + uint8_t user_data[0x400]; + void *user_address; + size_t size; + upage_ref_t page_ref; + + /* This function no longer exists in 5.x+. */ + if (exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_500) { + generic_panic(); + } + + /* Copy keydata */ + sealed_kek[0] = args->X[1]; + sealed_kek[1] = args->X[2]; + usecase = args->X[3] & 7; + user_address = (void *)args->X[4]; + size = (size_t)args->X[5]; + wrapped_key[0] = args->X[6]; + wrapped_key[1] = args->X[7]; + + if (usecase > CRYPTOUSECASE_RSAIMPORT) { + return 2; + } + if (usecase == 0) { + if (size < 0x31 || size > 0x240) { + return 2; + } + } else if (size < 0x130 || size > 0x240) { + return 2; + } + + if (upage_init(&page_ref, user_address) == 0 || user_copy_to_secure(&page_ref, user_data, user_address, size) == 0) { + return 2; + } + + flush_dcache_range(user_data, user_data + size); + + size_t out_size; + + if ((out_size = gcm_decrypt_key(user_data, size, user_data, size, sealed_kek, 0x10, wrapped_key, 0x10, g_rsausecase_to_cryptousecase[usecase], true, NULL)) == 0) { + return 2; + } + + unsigned int exponent_id; + + switch (usecase) { + case 0: + if (secure_copy_to_user(&page_ref, user_address, user_data, size) == 0) { + return 2; + } + return 0; + case 1: + exponent_id = 1; + break; + case 2: + exponent_id = 0; + break; + case 3: + exponent_id = 2; + break; + case 4: + exponent_id = 3; + break; + default: + generic_panic(); + } + + /* Copy key to global. */ + memcpy(g_imported_exponents[exponent_id], user_data, 0x100); + return 0; + +} diff --git a/exosphere/src/smc_user.h b/exosphere/src/smc_user.h index 58deda4c6..a99add637 100644 --- a/exosphere/src/smc_user.h +++ b/exosphere/src/smc_user.h @@ -18,6 +18,9 @@ uint32_t user_unwrap_rsa_oaep_wrapped_titlekey(smc_args_t *args); uint32_t user_load_titlekey(smc_args_t *args); uint32_t user_unwrap_aes_wrapped_titlekey(smc_args_t *args); +uint32_t user_encrypt_rsa_key_for_import(smc_args_t *args); +uint32_t user_decrypt_or_import_rsa_key(smc_args_t *args); + void set_crypt_aes_done(bool done); bool get_crypt_aes_done(void); diff --git a/exosphere/src/start.s b/exosphere/src/start.s index 83c5af585..8703c97ce 100644 --- a/exosphere/src/start.s +++ b/exosphere/src/start.s @@ -242,6 +242,7 @@ g_coldboot_crt0_relocation_list: .global g_coldboot_crt0_main_func_list g_coldboot_crt0_main_func_list: .quad 4 /* Number of functions */ + .quad 0 /* Target firmware, overwritten in coldboot_init. */ /* Functions */ .quad init_dma_controllers .quad set_memory_registers_enable_mmu @@ -252,4 +253,4 @@ g_coldboot_crt0_main_func_list: .align 3 .global g_warmboot_crt0_main_func_list g_warmboot_crt0_main_func_list: - .space (4 * 8) + .space (6 * 8) diff --git a/exosphere/src/sysreg.h b/exosphere/src/sysreg.h index a592a64d9..7fc362004 100644 --- a/exosphere/src/sysreg.h +++ b/exosphere/src/sysreg.h @@ -14,6 +14,8 @@ #define MAKE_SYSREG(n) MAKE_REG32(SYSREG_BASE + n) #define MAKE_SB_REG(n) MAKE_REG32(SB_BASE + n) +#define AHB_ARBITRATION_DISABLE_0 MAKE_SYSREG(0x004) + #define SB_CSR_0 MAKE_SB_REG(0x00) #define SB_PIROM_START_0 MAKE_SB_REG(0x04) #define SB_PFCFG_0 MAKE_SB_REG(0x08) diff --git a/exosphere/src/utils.h b/exosphere/src/utils.h index fc98ba369..829bd6a51 100644 --- a/exosphere/src/utils.h +++ b/exosphere/src/utils.h @@ -28,9 +28,10 @@ /* For warmboot (and coldboot crt0) */ typedef struct { size_t nb_funcs; + uint64_t target_firmware; union { struct { - void (*init_dma_controllers)(void); + void (*init_dma_controllers)(unsigned int); void (*set_memory_registers_enable_mmu)(void); void (*flush_dcache_all)(void); void (*invalidate_icache_all)(void); diff --git a/exosphere/src/warmboot_init.c b/exosphere/src/warmboot_init.c index dea4165dc..86fadfb4b 100644 --- a/exosphere/src/warmboot_init.c +++ b/exosphere/src/warmboot_init.c @@ -3,6 +3,7 @@ #include "mc.h" #include "arm.h" #include "synchronization.h" +#include "exocfg.h" #undef MC_BASE #define MC_BASE (MMIO_GET_DEVICE_PA(MMIO_DEVID_MC)) @@ -29,39 +30,61 @@ void warmboot_crt0_critical_section_enter(volatile critical_section_t *critical_ critical_section_enter(critical_section); } -void init_dma_controllers(void) { - /* TODO: 4.x does slightly different init. How should we handle this? We can't detect master key revision yet. */ +void init_dma_controllers(unsigned int target_firmware) { + if (target_firmware >= EXOSPHERE_TARGET_FIRMWARE_400) { + /* Set some unknown registers in HOST1X. */ + MAKE_REG32(0x500038F8) &= 0xFFFFFFFE; + MAKE_REG32(0x50003300) = 0; - /* SYSCTR0_CNTCR_0 = ENABLE | HALT_ON_DEBUG (write-once init) */ - MAKE_REG32(0x700F0000) = 3; + /* AHB_MASTER_SWID_0 - Enable SWID[0] for all bits. */ + MAKE_REG32(0x6000C018) = 0xFFFFFFFF; - /* Set some unknown registers in HOST1X. */ - MAKE_REG32(0x500038F8) &= 0xFFFFFFFE; - MAKE_REG32(0x50003300) = 0; + /* AHB_MASTER_SWID_1 */ + MAKE_REG32(0x6000C038) = 0x0; - /* AHB_MASTER_SWID_0 */ - MAKE_REG32(0x6000C018) = 0; + /* MSELECT_CONFIG_0 |= WRAP_TO_INCR_SLAVE0(APC) | WRAP_TO_INCR_SLAVE1(PCIe) | WRAP_TO_INCR_SLAVE2(GPU) */ + MAKE_REG32(0x50060000) |= 0x38000000; - /* AHB_MASTER_SWID_1 - Makes USB1/USB2 use SWID[1] */ - MAKE_REG32(0x6000C038) = 0x40040; + /* AHB_ARBITRATION_DISABLE_0 - Disables USB and USB2 from arbitration */ + MAKE_REG32(0x6000C004) = 0x40040; - /* APBDMA_CHANNEL_SWID_0 = ~0 (SWID = 1 for all APB-DMA channels) */ - MAKE_REG32(0x6002003C) = 0xFFFFFFFF; + /* AHB_ARBITRATION_PRIORITY_CTRL_0 - Select high prio group with prio 7 */ + MAKE_REG32(0x6000C008) = 0xE0000001; - /* APBDMA_CHANNEL_SWID1_0 = 0 (See above) */ - MAKE_REG32(0x60020054) = 0; + /* AHB_GIZMO_TZRAM_0 |= DONT_SPLIT_AHB_WR */ + MAKE_REG32(0x6000C054) = 0x80; + } else { + /* SYSCTR0_CNTCR_0 = ENABLE | HALT_ON_DEBUG (write-once init) */ + MAKE_REG32(0x700F0000) = 3; - /* APBDMA_SECURITY_REG_0 = 0 (All APB-DMA channels non-secure) */ - MAKE_REG32(0x60020038) = 0; + /* Set some unknown registers in HOST1X. */ + MAKE_REG32(0x500038F8) &= 0xFFFFFFFE; + MAKE_REG32(0x50003300) = 0; - /* MSELECT_CONFIG_0 |= WRAP_TO_INCR_SLAVE0(APC) | WRAP_TO_INCR_SLAVE1(PCIe) | WRAP_TO_INCR_SLAVE2(GPU) */ - MAKE_REG32(0x50060000) |= 0x38000000; + /* AHB_MASTER_SWID_0 */ + MAKE_REG32(0x6000C018) = 0; - /* AHB_ARBITRATION_PRIORITY_CTRL_0 - Select high prio group with prio 7 */ - MAKE_REG32(0x6000C008) = 0xE0000001; + /* AHB_MASTER_SWID_1 - Makes USB1/USB2 use SWID[1] */ + MAKE_REG32(0x6000C038) = 0x40040; - /* AHB_GIZMO_TZRAM_0 |= DONT_SPLIT_AHB_WR */ - MAKE_REG32(0x6000C054) = 0x80; + /* APBDMA_CHANNEL_SWID_0 = ~0 (SWID = 1 for all APB-DMA channels) */ + MAKE_REG32(0x6002003C) = 0xFFFFFFFF; + + /* APBDMA_CHANNEL_SWID1_0 = 0 (See above) */ + MAKE_REG32(0x60020054) = 0; + + /* APBDMA_SECURITY_REG_0 = 0 (All APB-DMA channels non-secure) */ + MAKE_REG32(0x60020038) = 0; + + /* MSELECT_CONFIG_0 |= WRAP_TO_INCR_SLAVE0(APC) | WRAP_TO_INCR_SLAVE1(PCIe) | WRAP_TO_INCR_SLAVE2(GPU) */ + MAKE_REG32(0x50060000) |= 0x38000000; + + /* AHB_ARBITRATION_PRIORITY_CTRL_0 - Select high prio group with prio 7 */ + MAKE_REG32(0x6000C008) = 0xE0000001; + + /* AHB_GIZMO_TZRAM_0 |= DONT_SPLIT_AHB_WR */ + MAKE_REG32(0x6000C054) = 0x80; + } } void set_memory_registers_enable_mmu(void) { @@ -144,7 +167,7 @@ void warmboot_init(boot_func_list_t *func_list) { /* On warmboot (not cpu_on) only */ if (MC_SECURITY_CFG3_0 == 0) { - init_dma_controllers(); + init_dma_controllers(func_list->target_firmware); } identity_remap_tzram(); diff --git a/exosphere/src/warmboot_main.c b/exosphere/src/warmboot_main.c index 60404f55e..8c5103dbb 100644 --- a/exosphere/src/warmboot_main.c +++ b/exosphere/src/warmboot_main.c @@ -7,6 +7,7 @@ #include "masterkey.h" #include "bootup.h" #include "smc_api.h" +#include "exocfg.h" #include "se.h" #include "mc.h" @@ -45,7 +46,7 @@ void __attribute__((noreturn)) warmboot_main(void) { /* Make PMC (2.x+), MC (4.x+) registers secure-only */ secure_additional_devices(); - if (mkey_get_revision() < MASTERKEY_REVISION_400_CURRENT || configitem_get_hardware_type() == 0) { + if (exosphere_get_target_firmware() < EXOSPHERE_TARGET_FIRMWARE_400 || configitem_get_hardware_type() == 0) { /* Enable input to I2C1 */ PINMUX_AUX_GEN1_I2C_SCL_0 = 0x40; PINMUX_AUX_GEN1_I2C_SDA_0 = 0x40; @@ -58,9 +59,8 @@ void __attribute__((noreturn)) warmboot_main(void) { clear_user_smc_in_progress(); - if (mkey_get_revision() >= MASTERKEY_REVISION_400_CURRENT) { - setup_4x_mmio(); /* TODO */ - } + if (exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_400) { + setup_4x_mmio(); } } setup_current_core_state();