From f41aaccaa2bffc8468f3ec482c82063a4640b7e2 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 16 Aug 2018 18:45:38 -0700 Subject: [PATCH] exosphere: commit WIP warmboot progress -15+ bugs fixed -We now receive ~0x400 SMCs from user processes (including from psm) on wake-from-sleep --- exosphere/src/bootconfig.h | 1 - exosphere/src/bootup.c | 2 +- exosphere/src/bpmpfw_debug.h | 8 ++++++++ exosphere/src/car.h | 2 ++ exosphere/src/cpu_context.c | 24 ++++++++++++++++++++---- exosphere/src/lp0.c | 32 ++++++++++++++++++++++++-------- exosphere/src/package2.c | 30 ++++++++++++++++++++++++++---- exosphere/src/smc_api.c | 7 ++++++- exosphere/src/smc_api.h | 3 +++ exosphere/src/start.s | 6 ++++-- exosphere/src/warmboot_init.c | 7 ++++--- exosphere/src/warmboot_main.c | 8 +++++++- 12 files changed, 105 insertions(+), 25 deletions(-) create mode 100644 exosphere/src/bpmpfw_debug.h diff --git a/exosphere/src/bootconfig.h b/exosphere/src/bootconfig.h index f5505f4ab..823323a03 100644 --- a/exosphere/src/bootconfig.h +++ b/exosphere/src/bootconfig.h @@ -26,7 +26,6 @@ typedef struct { bootconfig_unsigned_config_t unsigned_config; uint8_t signature[0x100]; bootconfig_signed_config_t signed_config; - uint8_t unused_space[0x240]; /* remaining space in the evt page */ } bootconfig_t; static inline bootconfig_t *get_loaded_bootconfig(void) { diff --git a/exosphere/src/bootup.c b/exosphere/src/bootup.c index 90f590841..7a945ec8f 100644 --- a/exosphere/src/bootup.c +++ b/exosphere/src/bootup.c @@ -268,7 +268,7 @@ void setup_current_core_state(void) { uint64_t temp_reg; /* Setup system registers. */ - SET_SYSREG(spsr_el3, 0b1111 << 6 | 0b1001); /* use EL2h+DAIF set initially, may be overwritten later. Not in official code */ + SET_SYSREG(spsr_el3, 0b1111 << 6 | 0b0101); /* use EL2h+DAIF set initially, may be overwritten later. Not in official code */ SET_SYSREG(actlr_el3, 0x73ull); SET_SYSREG(actlr_el2, 0x73ull); diff --git a/exosphere/src/bpmpfw_debug.h b/exosphere/src/bpmpfw_debug.h new file mode 100644 index 000000000..c61d2d14c --- /dev/null +++ b/exosphere/src/bpmpfw_debug.h @@ -0,0 +1,8 @@ +#ifndef EXOSPHERE_BPMPFW_DEBUG_H +#define EXOSPHERE_BPMPFW_DEBUG_H + +static const unsigned char g_bpmpfw_for_debug[0x584] = { + /* ... */ +}; + +#endif \ No newline at end of file diff --git a/exosphere/src/car.h b/exosphere/src/car.h index 3fb511bc2..7d489d2c4 100644 --- a/exosphere/src/car.h +++ b/exosphere/src/car.h @@ -14,6 +14,8 @@ #define CLK_RST_CONTROLLER_MISC_CLK_ENB_0 MAKE_CAR_REG(0x048) #define CLK_RST_CONTROLLER_RST_DEVICES_H_0 MAKE_CAR_REG(0x008) #define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRD_0 MAKE_CAR_REG(0x3A4) +#define CLK_RST_CONTROLLER_RST_CPUG_CMPLX_SET_0 MAKE_CAR_REG(0x450) +#define CLK_RST_CONTROLLER_RST_CPUG_CMPLX_CLR_0 MAKE_CAR_REG(0x454) #define NUM_CAR_BANKS 7 diff --git a/exosphere/src/cpu_context.c b/exosphere/src/cpu_context.c index d952d7879..853519236 100644 --- a/exosphere/src/cpu_context.c +++ b/exosphere/src/cpu_context.c @@ -2,6 +2,8 @@ #include #include "arm.h" #include "cpu_context.h" +#include "car.h" +#include "exocfg.h" #include "flow.h" #include "pmc.h" #include "timers.h" @@ -18,7 +20,7 @@ #define RESTORE_SYSREG64(reg) do { temp_reg = g_cpu_contexts[current_core].reg; __asm__ __volatile__ ("msr " #reg ", %0" :: "r"(temp_reg) : "memory"); } while(false) #define RESTORE_SYSREG32(reg) RESTORE_SYSREG64(reg) #define RESTORE_BP_REG(i, _) RESTORE_SYSREG64(DBGBVR##i##_EL1); RESTORE_SYSREG64(DBGBCR##i##_EL1); -#define RESTORE_WP_REG(i, _) RESTORE_SYSREG64(DBGBVR##i##_EL1); RESTORE_SYSREG64(DBGBCR##i##_EL1); +#define RESTORE_WP_REG(i, _) RESTORE_SYSREG64(DBGWVR##i##_EL1); RESTORE_SYSREG64(DBGWCR##i##_EL1); /* start.s */ void __attribute__((noreturn)) __jump_to_lower_el(uint64_t arg, uintptr_t ep, uint32_t spsr); @@ -79,8 +81,13 @@ uint32_t cpu_on(uint32_t core, uintptr_t entrypoint_addr, uint64_t argument) { set_core_entrypoint_and_argument(core, entrypoint_addr, argument); - const uint32_t status_masks[NUM_CPU_CORES] = {0x4000, 0x200, 0x400, 0x800}; - const uint32_t toggle_vals[NUM_CPU_CORES] = {0xE, 0x9, 0xA, 0xB}; + static const uint32_t status_masks[NUM_CPU_CORES] = {0x4000, 0x200, 0x400, 0x800}; + static const uint32_t toggle_vals[NUM_CPU_CORES] = {0xE, 0x9, 0xA, 0xB}; + + if (exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_400) { + /* Reset the core */ + CLK_RST_CONTROLLER_RST_CPUG_CMPLX_SET_0 = (1 << (core + 0x10)) | (1 << core); + } /* Check if we're already in the correct state. */ if ((APBDEV_PMC_PWRGATE_STATUS_0 & status_masks[core]) != status_masks[core]) { @@ -91,7 +98,7 @@ uint32_t cpu_on(uint32_t core, uintptr_t entrypoint_addr, uint64_t argument) { wait(1); counter--; if (counter < 1) { - return 0; + goto CPU_ON_SUCCESS; } } @@ -108,6 +115,13 @@ uint32_t cpu_on(uint32_t core, uintptr_t entrypoint_addr, uint64_t argument) { counter--; } } + +CPU_ON_SUCCESS: + if (exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_400) { + /* Start the core */ + CLK_RST_CONTROLLER_RST_CPUG_CMPLX_CLR_0 = (1 << (core + 0x10)) | (1 << core); + } + return 0; } @@ -145,6 +159,7 @@ void save_current_core_context(void) { /* Save system registers. */ SAVE_SYSREG32(OSDTRRX_EL1); + SAVE_SYSREG32(OSDTRTX_EL1); SAVE_SYSREG32(MDSCR_EL1); SAVE_SYSREG32(OSECCR_EL1); SAVE_SYSREG32(MDCCINT_EL1); @@ -168,6 +183,7 @@ void restore_current_core_context(void) { if (g_cpu_contexts[current_core].is_saved) { RESTORE_SYSREG32(OSDTRRX_EL1); + RESTORE_SYSREG32(OSDTRTX_EL1); RESTORE_SYSREG32(MDSCR_EL1); RESTORE_SYSREG32(OSECCR_EL1); RESTORE_SYSREG32(MDCCINT_EL1); diff --git a/exosphere/src/lp0.c b/exosphere/src/lp0.c index 33041e9d9..e52e3ae52 100644 --- a/exosphere/src/lp0.c +++ b/exosphere/src/lp0.c @@ -23,16 +23,21 @@ #define u8 uint8_t #define u32 uint32_t -#include "bpmpfw_bin.h" +#include "bpmpfw_debug.h" #undef u8 #undef u32 /* Save security engine, and go to sleep. */ void save_se_and_power_down_cpu(void) { + /* TODO: Remove set suspend call once exo warmboots fully */ + set_suspend_for_debug(); uint32_t tzram_cmac[0x4] = {0}; uint8_t *tzram_encryption_dst = (uint8_t *)(LP0_ENTRY_GET_RAM_SEGMENT_ADDRESS(LP0_ENTRY_RAM_SEGMENT_ID_ENCRYPTED_TZRAM)); uint8_t *tzram_encryption_src = (uint8_t *)(LP0_ENTRY_GET_RAM_SEGMENT_ADDRESS(LP0_ENTRY_RAM_SEGMENT_ID_CURRENT_TZRAM)); + if (exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_500) { + tzram_encryption_src += 0x2000ull; + } uint8_t *tzram_store_address = (uint8_t *)(WARMBOOT_GET_RAM_SEGMENT_ADDRESS(WARMBOOT_RAM_SEGMENT_ID_TZRAM)); clear_priv_smc_in_progress(); @@ -45,17 +50,20 @@ void save_se_and_power_down_cpu(void) { flush_dcache_range(tzram_encryption_dst, tzram_encryption_dst + LP0_TZRAM_SAVE_SIZE); flush_dcache_range(tzram_encryption_src, tzram_encryption_src + LP0_TZRAM_SAVE_SIZE); - /* Use the all-zero cmac buffer as an IV. */ + /* Use the all-zero cmac buffer as an IV. */ se_aes_256_cbc_encrypt(KEYSLOT_SWITCH_LP0TZRAMKEY, tzram_encryption_dst, LP0_TZRAM_SAVE_SIZE, tzram_encryption_src, LP0_TZRAM_SAVE_SIZE, tzram_cmac); flush_dcache_range(tzram_encryption_dst, tzram_encryption_dst + LP0_TZRAM_SAVE_SIZE); /* Copy encrypted TZRAM from IRAM to DRAM. */ - memcpy(tzram_store_address, tzram_encryption_dst, LP0_TZRAM_SAVE_SIZE); + for (unsigned int i = 0; i < LP0_TZRAM_SAVE_SIZE; i += 4) { + write32le(tzram_store_address, i, read32le(tzram_encryption_dst, i)); + } + flush_dcache_range(tzram_store_address, tzram_store_address + LP0_TZRAM_SAVE_SIZE); /* Compute CMAC. */ se_compute_aes_256_cmac(KEYSLOT_SWITCH_LP0TZRAMKEY, tzram_cmac, sizeof(tzram_cmac), tzram_encryption_src, LP0_TZRAM_SAVE_SIZE); - + /* Write CMAC, lock registers. */ APBDEV_PMC_SECURE_SCRATCH112_0 = tzram_cmac[0]; APBDEV_PMC_SECURE_SCRATCH113_0 = tzram_cmac[1]; @@ -76,6 +84,8 @@ void save_se_and_power_down_cpu(void) { if (!configitem_is_retail()) { /* TODO: uart_log("OYASUMI"); */ } + + __dsb_sy(); finalize_powerdown(); } @@ -85,6 +95,7 @@ uint32_t cpu_suspend(uint64_t power_state, uint64_t entrypoint, uint64_t argumen if ((power_state & 0x17FFF) != 0x1001B) { return 0xFFFFFFFD; } + unsigned int current_core = get_core_id(); @@ -169,14 +180,17 @@ uint32_t cpu_suspend(uint64_t power_state, uint64_t entrypoint, uint64_t argumen BPMP_VECTOR_UNK = 0x40003004; /* Reboot. */ BPMP_VECTOR_IRQ = 0x40003004; /* Reboot. */ BPMP_VECTOR_FIQ = 0x40003004; /* Reboot. */ - + /* Hold the BPMP in reset. */ MAKE_CAR_REG(0x300) = 2; /* Copy BPMP firmware. */ uint8_t *lp0_entry_code = (uint8_t *)(LP0_ENTRY_GET_RAM_SEGMENT_ADDRESS(LP0_ENTRY_RAM_SEGMENT_ID_LP0_ENTRY_CODE)); - memcpy(lp0_entry_code, bpmpfw_bin, bpmpfw_bin_size); - flush_dcache_range(lp0_entry_code, lp0_entry_code + bpmpfw_bin_size); + for (unsigned int i = 0; i < sizeof(g_bpmpfw_for_debug); i += 4) { + write32le(lp0_entry_code, i, read32le(g_bpmpfw_for_debug, i)); + } + + flush_dcache_range(lp0_entry_code, lp0_entry_code + sizeof(g_bpmpfw_for_debug)); /* Take the BPMP out of reset. */ MAKE_CAR_REG(0x304) = 2; @@ -188,11 +202,13 @@ uint32_t cpu_suspend(uint64_t power_state, uint64_t entrypoint, uint64_t argumen flow_set_halt_events(current_core, false); FLOW_CTLR_L2FLUSH_CONTROL_0 = 0; flow_set_csr(current_core, 2); - + /* Save core context. */ set_core_entrypoint_and_argument(current_core, entrypoint, argument); save_current_core_context(); set_current_core_inactive(); + + call_with_stack_pointer(get_smc_core012_stack_address(), save_se_and_power_down_cpu); generic_panic(); diff --git a/exosphere/src/package2.c b/exosphere/src/package2.c index 507acb1fa..26db12510 100644 --- a/exosphere/src/package2.c +++ b/exosphere/src/package2.c @@ -382,13 +382,32 @@ static void load_package2_sections(package2_meta_t *metadata, uint32_t master_ke memset(load_buf, 0, PACKAGE2_SIZE_MAX); } +static void copy_warmboot_bin_to_dram() { + uint8_t *warmboot_src = (uint8_t *)0x4003B000; + uint8_t *warmboot_dst = (uint8_t *)0x8000D000; + const size_t warmboot_size = 0x2000; + + /* Flush cache, to ensure warmboot is where we need it to be. */ + flush_dcache_range(warmboot_src, warmboot_src + warmboot_size); + __dsb_sy(); + + /* Copy warmboot. */ + for (size_t i = 0; i < warmboot_size; i += sizeof(uint32_t)) { + write32le(warmboot_dst, i, read32le(warmboot_src, i)); + } + + /* Flush cache, to ensure warmboot is where we need it to be. */ + flush_dcache_range(warmboot_dst, warmboot_dst + warmboot_size); + __dsb_sy(); +} + static void sync_with_nx_bootloader(int state) { while (MAILBOX_NX_BOOTLOADER_SETUP_STATE < state) { wait(100); } } -static void indentity_unmap_dram(void) { +static void identity_unmap_dram(void) { uintptr_t *mmu_l1_tbl = (uintptr_t *)(TZRAM_GET_SEGMENT_ADDRESS(TZRAM_SEGEMENT_ID_SECMON_EVT) + 0x800 - 64); mmu_unmap_range(1, mmu_l1_tbl, IDENTITY_GET_MAPPING_ADDRESS(IDENTITY_MAPPING_DRAM), IDENTITY_GET_MAPPING_SIZE(IDENTITY_MAPPING_DRAM)); @@ -410,6 +429,7 @@ void load_package2(coldboot_crt0_reloc_list_t *reloc_list) { /* Perform initial PMC register writes, if relevant. */ if (exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_400) { + MAKE_REG32(PMC_BASE + 0x054) = 0x8000D000; MAKE_REG32(PMC_BASE + 0x0A0) &= 0xFFF3FFFF; MAKE_REG32(PMC_BASE + 0x818) &= 0xFFFFFFFE; MAKE_REG32(PMC_BASE + 0x334) |= 0x10; @@ -453,7 +473,7 @@ void load_package2(coldboot_crt0_reloc_list_t *reloc_list) { /* Synchronize with NX BOOTLOADER. */ 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(); */ + copy_warmboot_bin_to_dram(); sync_with_nx_bootloader(NX_BOOTLOADER_STATE_LOADED_PACKAGE2_4X); } else { sync_with_nx_bootloader(NX_BOOTLOADER_STATE_LOADED_PACKAGE2); @@ -461,7 +481,7 @@ void load_package2(coldboot_crt0_reloc_list_t *reloc_list) { /* Make PMC (2.x+), MC (4.x+) registers secure-only */ secure_additional_devices(); - + /* Remove the identity mapping for iRAM-C+D & TZRAM */ /* For our crt0 to work, this doesn't actually unmap TZRAM */ identity_unmap_iram_cd_tzram(); @@ -499,7 +519,9 @@ void load_package2(coldboot_crt0_reloc_list_t *reloc_list) { set_core_entrypoint_and_argument(0, DRAM_BASE_PHYSICAL + header.metadata.entrypoint, 0); /* Remove the DRAM identity mapping. */ - indentity_unmap_dram(); + if (0) { + identity_unmap_dram(); + } /* Synchronize with NX BOOTLOADER. */ if (exosphere_get_target_firmware() >= EXOSPHERE_TARGET_FIRMWARE_400) { diff --git a/exosphere/src/smc_api.c b/exosphere/src/smc_api.c index 23c5cb728..063611a0a 100644 --- a/exosphere/src/smc_api.c +++ b/exosphere/src/smc_api.c @@ -121,6 +121,11 @@ 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}; +static bool g_has_suspended = false; +void set_suspend_for_debug(void) { + g_has_suspended = true; +} + void set_version_specific_smcs(void) { switch (exosphere_get_target_firmware()) { case EXOSPHERE_TARGET_FIRMWARE_100: @@ -207,7 +212,7 @@ void call_smc_handler(uint32_t handler_id, smc_args_t *args) { unsigned char smc_id; unsigned int result; unsigned int (*smc_handler)(smc_args_t *args); - + /* Validate top-level handler. */ if (handler_id != SMC_HANDLER_USER && handler_id != SMC_HANDLER_PRIV) { generic_panic(); diff --git a/exosphere/src/smc_api.h b/exosphere/src/smc_api.h index 3d2a46a79..80eb5bcb3 100644 --- a/exosphere/src/smc_api.h +++ b/exosphere/src/smc_api.h @@ -22,6 +22,9 @@ uintptr_t get_exception_entry_stack_address(unsigned int core_id); void set_version_specific_smcs(void); + +void set_suspend_for_debug(void); + void call_smc_handler(unsigned int handler_id, smc_args_t *args); #endif diff --git a/exosphere/src/start.s b/exosphere/src/start.s index 38eb4ce34..185291859 100644 --- a/exosphere/src/start.s +++ b/exosphere/src/start.s @@ -223,8 +223,8 @@ __jump_to_main_warm: bl __set_exception_entry_stack_pointer mov w0, #0 /* use core0,1,2 stack bottom + 0x800 (VA of warmboot crt0 sp) temporarily */ - bl get_exception_entry_stack_address - add sp, x0, #0x800 + bl get_warmboot_main_stack_address + mov sp, x0 bl warmboot_main .section .text.__set_exception_entry_stack, "ax", %progbits @@ -248,12 +248,14 @@ __set_exception_entry_stack_pointer: .type __jump_to_lower_el, %function __jump_to_lower_el: /* x0: arg (context ID), x1: entrypoint, w2: spsr */ + mov x19, x0 mov w2, w2 msr elr_el3, x1 msr spsr_el3, x2 bl __set_exception_entry_stack_pointer + mov x0, x19 isb eret diff --git a/exosphere/src/warmboot_init.c b/exosphere/src/warmboot_init.c index 91897f3ca..d3d73374c 100644 --- a/exosphere/src/warmboot_init.c +++ b/exosphere/src/warmboot_init.c @@ -4,6 +4,7 @@ #include "arm.h" #include "synchronization.h" #include "exocfg.h" +#include "pmc.h" #undef MC_BASE #define MC_BASE (MMIO_GET_DEVICE_PA(MMIO_DEVID_MC)) @@ -24,7 +25,7 @@ uintptr_t get_warmboot_crt0_stack_address(void) { uintptr_t get_warmboot_crt0_stack_address_critsec_enter(void) { unsigned int core_id = get_core_id(); - if (core_id) { + if (core_id == 3) { return WARMBOOT_GET_TZRAM_SEGMENT_PA(TZRAM_SEGMENT_ID_CORE3_STACK) + 0x1000; } else { return WARMBOOT_GET_TZRAM_SEGMENT_PA(TZRAM_SEGEMENT_ID_SECMON_EVT) + 0x80 * (core_id + 1); @@ -180,12 +181,12 @@ void warmboot_init(void) { */ flush_dcache_all(); invalidate_icache_all(); - + /* On warmboot (not cpu_on) only */ if (MC_SECURITY_CFG3_0 == 0) { init_dma_controllers(g_exosphere_target_firmware_for_init); } - + /*identity_remap_tzram();*/ /* Nintendo pointlessly fully invalidate the TLB & invalidate the data cache on the modified ranges here */ if (g_exosphere_target_firmware_for_init < EXOSPHERE_TARGET_FIRMWARE_500) { diff --git a/exosphere/src/warmboot_main.c b/exosphere/src/warmboot_main.c index 01e127df6..1c9a9ad9e 100644 --- a/exosphere/src/warmboot_main.c +++ b/exosphere/src/warmboot_main.c @@ -16,6 +16,12 @@ #include "misc.h" #include "interrupt.h" +#include "pmc.h" + +uintptr_t get_warmboot_main_stack_address(void) { + return TZRAM_GET_SEGMENT_ADDRESS(TZRAM_SEGEMENT_ID_SECMON_EVT) + 0x780; +} + void __attribute__((noreturn)) warmboot_main(void) { /* This function and its callers are reached in either of the following events, under normal conditions: @@ -53,7 +59,7 @@ void __attribute__((noreturn)) warmboot_main(void) { PINMUX_AUX_GEN1_I2C_SCL_0 = 0x40; PINMUX_AUX_GEN1_I2C_SDA_0 = 0x40; - clkrst_enable(CARDEVICE_I2C1); + clkrst_reboot(CARDEVICE_I2C1); i2c_init(0); i2c_clear_ti_charger_bit_7(); clkrst_disable(CARDEVICE_I2C1);