From bda9dcbe732bd0a76adf7a8269bd331185423108 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 26 Feb 2018 02:00:02 -0800 Subject: [PATCH] Start implementing smcCpuSuspend --- exosphere/src/bpmp.h | 22 +++++++ exosphere/src/configitem.c | 12 ++-- exosphere/src/configitem.h | 1 + exosphere/src/cpu_context.c | 16 ++--- exosphere/src/cpu_context.h | 7 +- exosphere/src/flow.h | 46 +++++++++++++ exosphere/src/lp0.c | 128 ++++++++++++++++++++++++++++++++++++ exosphere/src/lp0.h | 10 +++ exosphere/src/pmc.h | 8 +++ exosphere/src/smc_api.c | 1 + exosphere/src/timers.h | 4 ++ exosphere/src/utils.h | 6 ++ 12 files changed, 241 insertions(+), 20 deletions(-) create mode 100644 exosphere/src/bpmp.h create mode 100644 exosphere/src/flow.h create mode 100644 exosphere/src/lp0.c create mode 100644 exosphere/src/lp0.h diff --git a/exosphere/src/bpmp.h b/exosphere/src/bpmp.h new file mode 100644 index 000000000..b71384ce3 --- /dev/null +++ b/exosphere/src/bpmp.h @@ -0,0 +1,22 @@ +#ifndef EXOSPHERE_BPMP_H +#define EXOSPHERE_BPMP_H + +#include +#include "memory_map.h" + +/* Exosphere register definitions for the Tegra X1 BPMP vectors. */ + +#define BPMP_VECTOR_BASE (mmio_get_device_address(MMIO_DEVID_EXCEPTION_VECTORS)) + + +#define BPMP_VECTOR_RESET (*((volatile uint32_t *)(BPMP_VECTOR_BASE + 0x200))) +#define BPMP_VECTOR_UNDEF (*((volatile uint32_t *)(BPMP_VECTOR_BASE + 0x204))) +#define BPMP_VECTOR_SWI (*((volatile uint32_t *)(BPMP_VECTOR_BASE + 0x208))) +#define BPMP_VECTOR_PREFETCH_ABORT (*((volatile uint32_t *)(BPMP_VECTOR_BASE + 0x20C))) +#define BPMP_VECTOR_DATA_ABORT (*((volatile uint32_t *)(BPMP_VECTOR_BASE + 0x210))) +#define BPMP_VECTOR_UNK (*((volatile uint32_t *)(BPMP_VECTOR_BASE + 0x214))) +#define BPMP_VECTOR_IRQ (*((volatile uint32_t *)(BPMP_VECTOR_BASE + 0x218))) +#define BPMP_VECTOR_FIQ (*((volatile uint32_t *)(BPMP_VECTOR_BASE + 0x21C))) + + +#endif \ No newline at end of file diff --git a/exosphere/src/configitem.c b/exosphere/src/configitem.c index 2e2923d82..fc9af2b88 100644 --- a/exosphere/src/configitem.c +++ b/exosphere/src/configitem.c @@ -8,15 +8,15 @@ #include "fuse.h" #include "utils.h" -static int g_battery_profile = 0; +static bool g_battery_profile = false; uint32_t configitem_set(enum ConfigItem item, uint64_t value) { if (item != CONFIGITEM_BATTERYPROFILE) { return 2; } - g_battery_profile = ((int)(value != 0)) & 1; - return 0; /* FIXME: what should we return there */ + g_battery_profile = (value != 0); + return 0; } bool configitem_is_recovery_boot(void) { @@ -37,6 +37,10 @@ bool configitem_is_retail(void) { return is_retail != 0; } +bool configitem_should_profile_battery(void) { + return g_battery_profile; +} + uint32_t configitem_get(enum ConfigItem item, uint64_t *p_outvalue) { uint32_t result = 0; switch (item) { @@ -80,7 +84,7 @@ uint32_t configitem_get(enum ConfigItem item, uint64_t *p_outvalue) { *p_outvalue = bootconfig_get_kernel_memory_configuration(); break; case CONFIGITEM_BATTERYPROFILE: - *p_outvalue = g_battery_profile; + *p_outvalue = (int)g_battery_profile; break; default: result = 2; diff --git a/exosphere/src/configitem.h b/exosphere/src/configitem.h index 65ba0c9e0..465ae4740 100644 --- a/exosphere/src/configitem.h +++ b/exosphere/src/configitem.h @@ -25,5 +25,6 @@ uint32_t configitem_get(enum ConfigItem item, uint64_t *p_outvalue); bool configitem_is_recovery_boot(void); bool configitem_is_retail(void); +bool configitem_should_profile_battery(void); #endif diff --git a/exosphere/src/cpu_context.c b/exosphere/src/cpu_context.c index d91829618..61996870c 100644 --- a/exosphere/src/cpu_context.c +++ b/exosphere/src/cpu_context.c @@ -6,12 +6,12 @@ static saved_cpu_context_t g_cpu_contexts[NUM_CPU_CORES] = {0}; -void set_core_entrypoint_and_context_id(uint32_t core, uint64_t entrypoint_addr, uint64_t context_id) { +void set_core_entrypoint_and_context_id(uint32_t core, uint64_t entrypoint_addr, uint64_t argument) { g_cpu_contexts[core].ELR_EL3 = entrypoint_addr; - g_cpu_contexts[core].context_id = context_id; + g_cpu_contexts[core].argument = argument; } -uint32_t cpu_on(uint32_t core, uint64_t entrypoint_addr, uint64_t context_id) { +uint32_t cpu_on(uint32_t core, uint64_t entrypoint_addr, uint64_t argument) { /* Is core valid? */ if (core >= NUM_CPU_CORES) { return 0xFFFFFFFE; @@ -22,7 +22,7 @@ uint32_t cpu_on(uint32_t core, uint64_t entrypoint_addr, uint64_t context_id) { return 0xFFFFFFFC; } - set_core_entrypoint_and_context_id(core, entrypoint_addr, context_id); + set_core_entrypoint_and_context_id(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}; @@ -60,11 +60,3 @@ uint32_t cpu_off(void) { return 0; /* TODO */ } - -uint32_t cpu_suspend(uint64_t power_state, uint64_t entrypoint_addr, uint64_t context_id) { - (void)power_state; - (void)entrypoint_addr; - (void)context_id; - return 0; - /* TODO */ -} diff --git a/exosphere/src/cpu_context.h b/exosphere/src/cpu_context.h index 85149eaf5..a1b0eb6ab 100644 --- a/exosphere/src/cpu_context.h +++ b/exosphere/src/cpu_context.h @@ -6,7 +6,7 @@ /* Exosphere CPU Management functionality. */ typedef struct { - uint64_t context_id; + uint64_t argument; uint64_t ELR_EL3; int is_active; int is_saved; @@ -44,11 +44,10 @@ typedef struct { #define NUM_CPU_CORES 4 -void set_core_entrypoint_and_context_id(uint32_t core, uint64_t entrypoint_addr, uint64_t context_id); +void set_core_entrypoint_and_argument(uint32_t core, uint64_t entrypoint_addr, uint64_t argument); -uint32_t cpu_on(uint32_t core, uint64_t entrypoint_addr, uint64_t context_id); +uint32_t cpu_on(uint32_t core, uint64_t entrypoint_addr, uint64_t argument); uint32_t cpu_off(void); /* TODO */ -uint32_t cpu_suspend(uint64_t power_state, uint64_t entrypoint_addr, uint64_t context_id); /* TODO */ #endif diff --git a/exosphere/src/flow.h b/exosphere/src/flow.h new file mode 100644 index 000000000..2ab161be5 --- /dev/null +++ b/exosphere/src/flow.h @@ -0,0 +1,46 @@ +#ifndef EXOSPHERE_FLOW_CTLR_H +#define EXOSPHERE_FLOW_CTLR_H + +#include +#include "cpu_context.h" +#include "memory_map.h" + +/* Exosphere register definitions for the Tegra X1 Flow Controller. */ + +#define FLOW_BASE (mmio_get_device_address(MMIO_DEVID_FLOWCTRL)) + +#define MAKE_FLOW_REG(ofs) (*((volatile uint32_t *)(FLOW_BASE + ofs))) + +#define FLOW_CTLR_HALT_COP_EVENTS_0 MAKE_FLOW_REG(0x004) +#define FLOW_CTLR_L2FLUSH_CONTROL_0 MAKE_FLOW_REG(0x094) + + +static const struct { + unsigned int CPUN_CSR_OFS; + unsigned int HALT_CPUN_EVENTS_OFS; + unsigned int CC4_COREN_CTRL_OFS; +} g_flow_core_offsets[NUM_CPU_CORES] = { + {0x008, 0x000, 0x06C}, + {0x018, 0x014, 0x070}, + {0x020, 0x01C, 0x074}, + {0x028, 0x024, 0x078}, +}; + +static inline void flow_set_cc4_ctrl(uint32_t core, uint32_t cc4_ctrl) { + MAKE_FLOW_REG(g_flow_core_offsets[core].CC4_COREN_CTRL_OFS) = cc4_ctrl; +} + +static inline void flow_set_halt_events(uint32_t core, uint32_t halt_events) { + MAKE_FLOW_REG(g_flow_core_offsets[core].HALT_CPUN_EVENTS_OFS) = halt_events; +} + +static inline void flow_set_csr(uint32_t core, uint32_t csr) { + MAKE_FLOW_REG(g_flow_core_offsets[core].CPUN_CSR_OFS) = csr; +} + +static inline void flow_clear_csr0_and_events(uint32_t core) { + MAKE_FLOW_REG(g_flow_core_offsets[core].CPUN_CSR_OFS) = 0; + MAKE_FLOW_REG(g_flow_core_offsets[core].HALT_CPUN_EVENTS_OFS) = 0; +} + +#endif \ No newline at end of file diff --git a/exosphere/src/lp0.c b/exosphere/src/lp0.c new file mode 100644 index 000000000..f4d10e1bd --- /dev/null +++ b/exosphere/src/lp0.c @@ -0,0 +1,128 @@ +#include +#include +#include + +#include "utils.h" + +#include "bpmp.h" +#include "configitem.h" +#include "flow.h" +#include "fuse.h" +#include "i2c.h" +#include "lp0.h" +#include "pmc.h" +#include "se.h" +#include "smc_api.h" +#include "timers.h" + +/* Save security engine, and go to sleep. */ +void save_se_and_power_down_cpu(void) { + clear_priv_smc_in_progress(); + /* TODO. */ +} + +uint32_t cpu_suspend(uint64_t power_state, uint64_t entrypoint, uint64_t argument) { + /* Ensure SMC call is to enter deep sleep. */ + if ((power_state & 0x17FFF) != 0x1001B) { + return 0xFFFFFFFD; + } + + unsigned int current_core = get_core_id(); + + /* TODO: Enable clock and reset for I2C1. */ + if (configitem_should_profile_battery() && !i2c_query_ti_charger_bit_7()) { + /* Profile the battery. */ + i2c_set_ti_charger_bit_7(); + uint32_t start_time = get_time(); + bool should_wait = true; + /* TODO: This is GPIO-6 GPIO_IN_1 */ + while ((*((volatile uint32_t *)(mmio_get_device_address(MMIO_DEVID_GPIO) + 0x634))) & 1) { + if (get_time() - start_time > 50000) { + should_wait = false; + break; + } + } + if (should_wait) { + wait(0x100); + } + } + /* TODO: Reset I2C1 controller. */ + + /* Enable LP0 Wake Event Detection. */ + wait(75); + APBDEV_PMC_CNTRL2_0 |= 0x200; /* Set WAKE_DET_EN. */ + wait(75); + APBDEV_PM_0 = 0xFFFFFFFF; /* Set all wake events. */ + APBDEV_PMC_WAKE2_STATUS_0 = 0xFFFFFFFF; /* Set all wake events. */ + wait(75); + + /* TODO: Enable I2C5 Clock/Reset. */ + if (fuse_get_bootrom_patch_version() >= 0x7F) { + i2c_send_pmic_cpu_shutdown_cmd(); + } + + /* Jamais Vu mitigation #1: Ensure all other cores are off. */ + if (APBDEV_PMC_PWRGATE_STATUS_0 & 0xE00) { + generic_panic(); + } + + /* Jamais Vu mitigation #2: Ensure the BPMP is halted. */ + if ((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(); + } + } else { + /* BPMP must be in never-woken-up halt mode, under normal conditions. */ + if (FLOW_CTLR_HALT_COP_EVENTS_0 != 0x40000000) { + generic_panic(); + } + } + + /* TODO: Jamais Vu mitigation #3: Ensure all relevant DMA controllers are held in reset. */ + /* This just requires checking CLK_RST_CONTROLLER_RST_DEVICES_H_0 & mask == 0x4000004. */ + + /* Signal to bootrom the next reset should be a warmboot. */ + APBDEV_PMC_SCRATCH0_0 = 1; + APBDEV_PMC_DPD_ENABLE_0 |= 2; + + /* Prepare to boot the BPMP running our deep sleep firmware. */ + + /* Mark PMC registers as not secure-world only, so BPMP can access them. */ + (*((volatile uint32_t *)(mmio_get_device_address(MMIO_DEVID_MISC) + 0xC00))) &= 0xFFFFDFFF; /* TODO: macro */ + + /* Setup BPMP vectors. */ + BPMP_VECTOR_RESET = 0x40003000; /* lp0_entry_firmware_crt0 */ + BPMP_VECTOR_UNDEF = 0x40003004; /* Reboot. */ + BPMP_VECTOR_SWI = 0x40003004; /* Reboot. */ + BPMP_VECTOR_PREFETCH_ABORT = 0x40003004; /* Reboot. */ + BPMP_VECTOR_DATA_ABORT = 0x40003004; /* Reboot. */ + BPMP_VECTOR_UNK = 0x40003004; /* Reboot. */ + BPMP_VECTOR_IRQ = 0x40003004; /* Reboot. */ + BPMP_VECTOR_FIQ = 0x40003004; /* Reboot. */ + + /* TODO: Hold the BPMP in reset. */ + uint8_t *lp0_entry_code = (uint8_t *)(lp0_get_plaintext_ram_segment_address(LP0_ENTRY_RAM_SEGMENT_ID_LP0_ENTRY_CODE)); + (void)(lp0_entry_code); + /* TODO: memcpy(lp0_entry_code, BPMP_FIRMWARE_ADDRESS, sizeof(BPMP_FIRMWARE)); */ + /* TODO: flush_dcache_range(lp0_entry_code, lp0_entry_code + sizeof(BPMP_FIRMWARE)); */ + /* TODO: Take the BPMP out of reset. */ + + /* Start executing BPMP firmware. */ + FLOW_CTLR_HALT_COP_EVENTS_0 = 0; + /* Prepare the current core for sleep. */ + flow_set_cc4_ctrl(current_core, 0); + flow_set_halt_events(current_core, 0); + FLOW_CTLR_L2FLUSH_CONTROL_0 = 0; + flow_set_csr(current_core, 2); + + /* Save core context. */ + set_core_entrypoint_and_argument(current_core, entrypoint, argument); + /* TODO: save_current_core_context(); */ + /* TODO: set_current_core_inacctive(); */ + /* TODO: set_current_core_saved(true); */ + /* TODO: call_with_stack_pointer(tzram_get_segment_address(TZRAM_SEGMENT_ID_CORE012_STACK) + 0x1000ULL, save_se_and_power_down_cpu); */ + + /* NOTE: This return never actually occurs. */ + return 0; +} \ No newline at end of file diff --git a/exosphere/src/lp0.h b/exosphere/src/lp0.h new file mode 100644 index 000000000..dd5e06468 --- /dev/null +++ b/exosphere/src/lp0.h @@ -0,0 +1,10 @@ +#ifndef EXOSPHERE_LP0_H +#define EXOSPHERE_LP0_H + +#include + +/* Exosphere Deep Sleep Entry implementation. */ + +uint32_t cpu_suspend(uint64_t power_state, uint64_t entrypoint, uint64_t argument); + +#endif \ No newline at end of file diff --git a/exosphere/src/pmc.h b/exosphere/src/pmc.h index 1ffb1bec5..db04ed4f2 100644 --- a/exosphere/src/pmc.h +++ b/exosphere/src/pmc.h @@ -8,7 +8,15 @@ #define PMC_BASE (mmio_get_device_address(MMIO_DEVID_RTC_PMC) + 0x400ULL) +#define APBDEV_PMC_DPD_ENABLE_0 (*((volatile uint32_t *)(PMC_BASE + 0x24))) + #define APBDEV_PMC_PWRGATE_TOGGLE_0 (*((volatile uint32_t *)(PMC_BASE + 0x30))) #define APBDEV_PMC_PWRGATE_STATUS_0 (*((volatile uint32_t *)(PMC_BASE + 0x38))) +#define APBDEV_PMC_SCRATCH0_0 (*((volatile uint32_t *)(PMC_BASE + 0x50))) + +#define APBDEV_PM_0 (*((volatile uint32_t *)(PMC_BASE + 0x14))) +#define APBDEV_PMC_WAKE2_STATUS_0 (*((volatile uint32_t *)(PMC_BASE + 0x168))) +#define APBDEV_PMC_CNTRL2_0 (*((volatile uint32_t *)(PMC_BASE + 0x440))) + #endif diff --git a/exosphere/src/smc_api.c b/exosphere/src/smc_api.c index 7be80b6c3..6e2a148ba 100644 --- a/exosphere/src/smc_api.c +++ b/exosphere/src/smc_api.c @@ -18,6 +18,7 @@ #include "se.h" #include "userpage.h" #include "titlekey.h" +#include "lp0.h" #define SMC_USER_HANDLERS 0x13 #define SMC_PRIV_HANDLERS 0x9 diff --git a/exosphere/src/timers.h b/exosphere/src/timers.h index db9559c90..65d4fb9bb 100644 --- a/exosphere/src/timers.h +++ b/exosphere/src/timers.h @@ -12,4 +12,8 @@ void wait(uint32_t microseconds); +static inline uint32_t get_time(void) { + return TIMERUS_CNTR_1US_0; +} + #endif diff --git a/exosphere/src/utils.h b/exosphere/src/utils.h index 3dc79bcee..08141623f 100644 --- a/exosphere/src/utils.h +++ b/exosphere/src/utils.h @@ -42,6 +42,12 @@ static inline unsigned int get_core_id(void) { return (unsigned int)core_id & 3; } +static inline uint64_t get_debug_authentication_status(void) { + uint64_t debug_auth; + __asm__ __volatile__ ("mrs %0, dbgauthstatus_el1" : "=r"(debug_auth)); + return debug_auth; +} + static inline bool check_32bit_additive_overflow(uint32_t a, uint32_t b) { return __builtin_add_overflow_p(a, b, (uint32_t)0); }