Integrate 5.x SMC API changes, add 4.x specific setup, implement target firmware selection

This commit is contained in:
Michael Scire 2018-03-25 15:05:08 -06:00
parent c6ee1bffb7
commit c2eed3caf6
34 changed files with 734 additions and 80 deletions

View file

@ -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);

View file

@ -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);
}

View file

@ -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);

View file

@ -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 {

View file

@ -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

View file

@ -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();
}

View file

@ -22,6 +22,7 @@ typedef enum {
CARDEVICE_UARTB = 7,
CARDEVICE_I2C1 = 12,
CARDEVICE_I2C5 = 47,
CARDEVICE_ACTMON = 119,
CARDEVICE_BPMP = 1
} CarDevice;

View file

@ -5,6 +5,7 @@
#include "arm.h"
#include "package2.h"
#include "timers.h"
#include "exocfg.h"
#undef MAILBOX_NX_BOOTLOADER_BASE
#undef TIMERS_BASE
@ -150,6 +151,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 */
/* This at least copies .warm_crt0 to its VMA. */
@ -158,9 +160,12 @@ void coldboot_init(coldboot_crt0_reloc_list_t *reloc_list, boot_func_list_t *fun
}
/* 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();

View file

@ -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;

View file

@ -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);

37
exosphere/src/exocfg.c Normal file
View file

@ -0,0 +1,37 @@
#include <stdint.h>
#include <stdbool.h>
#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;
}

41
exosphere/src/exocfg.h Normal file
View file

@ -0,0 +1,41 @@
#ifndef EXOSPHERE_EXOSPHERE_CONFIG_H
#define EXOSPHERE_EXOSPHERE_CONFIG_H
#include <stdint.h>
#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

View file

@ -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;

View file

@ -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);

View file

@ -1,6 +1,7 @@
#include <stdint.h>
#include <string.h>
#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) {
@ -164,7 +165,47 @@ size_t gcm_decrypt_key(void *dst, size_t dst_size, const void *src, size_t src_s
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));
}

View file

@ -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

View file

@ -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();

View file

@ -1,5 +1,6 @@
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#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;
}
}

View file

@ -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

View file

@ -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)

View file

@ -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,13 +471,16 @@ 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());
}

View file

@ -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

View file

@ -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

View file

@ -2,6 +2,7 @@
#include <string.h>
#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();
}

View file

@ -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);

View file

@ -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] = {

View file

@ -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

View file

@ -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,13 +126,32 @@ 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) {
return 2;
@ -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;
}

View file

@ -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);

View file

@ -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)

View file

@ -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)

View file

@ -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);

View file

@ -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,9 +30,30 @@ 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;
/* AHB_MASTER_SWID_0 - Enable SWID[0] for all bits. */
MAKE_REG32(0x6000C018) = 0xFFFFFFFF;
/* AHB_MASTER_SWID_1 */
MAKE_REG32(0x6000C038) = 0x0;
/* MSELECT_CONFIG_0 |= WRAP_TO_INCR_SLAVE0(APC) | WRAP_TO_INCR_SLAVE1(PCIe) | WRAP_TO_INCR_SLAVE2(GPU) */
MAKE_REG32(0x50060000) |= 0x38000000;
/* AHB_ARBITRATION_DISABLE_0 - Disables USB and USB2 from arbitration */
MAKE_REG32(0x6000C004) = 0x40040;
/* 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;
} else {
/* SYSCTR0_CNTCR_0 = ENABLE | HALT_ON_DEBUG (write-once init) */
MAKE_REG32(0x700F0000) = 3;
@ -62,6 +84,7 @@ void init_dma_controllers(void) {
/* 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();

View file

@ -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();