diff --git a/exosphere/cpu_context.c b/exosphere/cpu_context.c new file mode 100644 index 000000000..7ac107051 --- /dev/null +++ b/exosphere/cpu_context.c @@ -0,0 +1,56 @@ +#include +#include "utils.h" +#include "pmc.h" + +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) { + g_cpu_contexts[core].ELR_EL3 = entrypoint_addr; + g_cpu_contexts[core].context_id = context_id; +} + +uint32_t cpu_on(uint32_t core, uint64_t entrypoint_addr, uint64_t context_id) { + /* Is core valid? */ + if (core >= NUM_CPU_CORES) { + return 0xFFFFFFFE; + } + + /* Is core already on? */ + if (g_cpu_contexts[core].is_active) { + return 0xFFFFFFFC; + } + + set_core_entrypoint_and_context_id(core, entrypoint_addr, context_id); + + const uint32_t status_masks[NUM_CPU_CORES] = {0x4000, 0x200, 0x400, 0x800}; + const uint32_t toggle_vals[NUM_CPU_CORES] = {0xE, 0x9, 0xA, 0xB}; + + /* Check if we're already in the correct state. */ + if ((APBDEV_PMC_PWRGATE_STATUS_0 & status_masks[core]) != status_masks[core]) { + uint32_t counter = 5001; + + /* Poll the start bit until 0 */ + while (APBDEV_PMC_PWRGATE_TOGGLE_0 & 0x100) { + wait(1); + counter--; + if (counter < 1) { + return 0; + } + } + + /* Program PWRGATE_TOGGLE with the START bit set to 1, selecting CE[N] */ + APBDEV_PMC_PWRGATE_TOGGLE_0 = toggle_vals[core] | 0x100; + + /* Poll until we're in the correct state. */ + counter = 5001; + while (counter > 0) { + if ((APBDEV_PMC_PWRGATE_STATUS_0 & status_masks[core]) == status_masks[core]) { + break; + } + wait(1); + counter--; + } + } + return 0; +} + diff --git a/exosphere/cpu_context.h b/exosphere/cpu_context.h new file mode 100644 index 000000000..e9af79a78 --- /dev/null +++ b/exosphere/cpu_context.h @@ -0,0 +1,54 @@ +#ifndef EXOSPHERE_CPU_CTX_H +#define EXOSPHERE_CPU_CTX_H + +#include + +/* Exosphere CPU Management functionality. */ + +typedef struct { + uint64_t context_id; + uint64_t ELR_EL3; + int is_active; + int is_saved; + uint32_t OSDTRRX_EL1; + uint32_t OSDTRTX_EL1; + uint32_t MDSCR_EL1; + uint32_t OSECCR_EL1; + uint32_t MDCCINT_EL1; + uint32_t DBGCLAIMCLR_EL1; + uint32_t DBGVCR32_EL2; + uint32_t SDER32_EL3; + uint32_t MDCR_EL2; + uint32_t MDCR_EL3; + uint64_t DBGBVR0_EL1; + uint64_t DBGBCR0_EL1; + uint64_t DBGBVR1_EL1; + uint64_t DBGBCR1_EL1; + uint64_t DBGBVR2_EL1; + uint64_t DBGBCR2_EL1; + uint64_t DBGBVR3_EL1; + uint64_t DBGBCR3_EL1; + uint64_t DBGBVR4_EL1; + uint64_t DBGBCR4_EL1; + uint64_t DBGBVR5_EL1; + uint64_t DBGBCR5_EL1; + uint64_t DBGWVR0_EL1; + uint64_t DBGWCR0_EL1; + uint64_t DBGWVR1_EL1; + uint64_t DBGWCR1_EL1; + uint64_t DBGWVR2_EL1; + uint64_t DBGWCR2_EL1; + uint64_t DBGWVR3_EL1; + uint64_t DBGWCR3_EL1; +} saved_cpu_context_t; + +#define NUM_CPU_CORES 4 + +void set_core_entrypoint_and_context_id(uint64_t entrypoint_addr, uint64_t context_id); + +uint32_t cpu_on(uint32_t core, uint64_t entrypoint_addr, uint64_t context_id); +uint32_t cpu_off(void); +uint32_t cpu_suspend(uint64_t power_state, uint64_t entrypoint_addr, uint64_t context_id); + + +#endif \ No newline at end of file diff --git a/exosphere/pmc.c b/exosphere/pmc.c new file mode 100644 index 000000000..afc0fd4a9 --- /dev/null +++ b/exosphere/pmc.c @@ -0,0 +1,11 @@ +#include "pmc.h" + +volatile void *g_pmc_registers = NULL; + +void set_pmc_address(void *pmc_base) { + g_pmc_registers = pmc_base; +} + +inline void *get_pmc_address(void) { + return g_pmc_registers; +} \ No newline at end of file diff --git a/exosphere/pmc.h b/exosphere/pmc.h new file mode 100644 index 000000000..8140bfc4b --- /dev/null +++ b/exosphere/pmc.h @@ -0,0 +1,14 @@ +#ifndef EXOSPHERE_PMC_H +#define EXOSPHERE_PMC_H + +#include + +/* Exosphere register definitions for the Tegra X1 PMC. */ + +void set_pmc_address(void *pmc_base); +void *get_pmc_address(void); /* This is inlined in pmc.c */ + +#define APBDEV_PMC_PWRGATE_TOGGLE_0 (*((volatile uint32_t *)(get_pmc_address() + 0x430))) +#define APBDEV_PMC_PWRGATE_STATUS_0 (*((volatile uint32_t *)(get_pmc_address() + 0x438))) + +#endif \ No newline at end of file diff --git a/exosphere/se.c b/exosphere/se.c index f2b40a00e..034bb8570 100644 --- a/exosphere/se.c +++ b/exosphere/se.c @@ -7,7 +7,7 @@ void trigger_se_rsa_op(void *buf, unsigned int size); void trigger_se_aes_op(unsigned int op, char *dst, unsigned int dst_size, const unsigned char *src, unsigned int src_size); /* Globals for driver. */ -security_engine_t *g_security_engine; +volatile security_engine_t *g_security_engine; unsigned int (*g_se_callback)(void); @@ -19,6 +19,11 @@ void set_security_engine_address(security_engine_t *security_engine) { g_security_engine = security_engine; } +/* Get the global security engine pointer. */ +security_engine_t *get_security_engine_address(void) { + return g_security_engine; +} + void set_security_engine_callback(unsigned int (*callback)(void)) { if (callback == NULL || g_se_callback != NULL) { panic(); diff --git a/exosphere/se.h b/exosphere/se.h index f2732ff50..817178743 100644 --- a/exosphere/se.h +++ b/exosphere/se.h @@ -92,7 +92,8 @@ typedef struct security_engine { -void set_security_engine_address(void *security_engine); +void set_security_engine_address(security_engine_t *security_engine); +security_engine_t *get_security_engine_address(void); void set_aes_keyslot_flags(unsigned int keyslot, unsigned int flags); void set_rsa_keyslot_flags(unsigned int keyslot, unsigned int flags); diff --git a/exosphere/smc_api.c b/exosphere/smc_api.c index 0e0306319..573cc3d64 100644 --- a/exosphere/smc_api.c +++ b/exosphere/smc_api.c @@ -1,6 +1,7 @@ #include #include "utils.h" +#include "cpu_context.h" #include "smc_api.h" #include "smc_user.h" #include "se.h" @@ -223,5 +224,23 @@ uint32_t smc_get_result(smc_args_t *) { } uint32_t smc_load_aes_key(smc_args_t *args) { - smc_wrapper_sync(args, user_load_aes_key); + return smc_wrapper_sync(args, user_load_aes_key); +} + + +uint32_t smc_cpu_on(smc_args_t *args) { + return cpu_on((uint32_t)args->X[1], args->X[2], args->X[3]); +} + +uint32_t smc_cpu_off(smc_args_t *args) { + return cpu_off(); +} + +/* Wrapper for cpu_suspend */ +uint32_t cpu_suspend_wrapper(smc_args_t *args) { + return cpu_suspend(args->X[1], args->X[2], args->X[3]); +} + +uint32_t smc_cpu_suspend(smc_args_t *args) { + return smc_wrapper_sync(args, cpu_suspend_wrapper); } \ No newline at end of file diff --git a/exosphere/timers.c b/exosphere/timers.c new file mode 100644 index 000000000..38b2026ad --- /dev/null +++ b/exosphere/timers.c @@ -0,0 +1,18 @@ +#include "timers.h" + +volatile void *g_timer_registers = NULL; + +void set_timer_address(void *timer_base) { + g_timer_registers = timer_base; +} + +inline void *get_timer_address(void) { + return g_timer_registers; +} + +void wait(uint32_t microseconds) { + uint32_t old_time = TIMERUS_CNTR_1US_0; + while (TIMERUS_CNTR_1US_0 - old_time <= result) { + /* Spin-lock. */ + } +} \ No newline at end of file diff --git a/exosphere/timers.h b/exosphere/timers.h new file mode 100644 index 000000000..db82ce7af --- /dev/null +++ b/exosphere/timers.h @@ -0,0 +1,15 @@ +#ifndef EXOSPHERE_TIMERS_H +#define EXOSPHERE_TIMERS_H + +#include + +/* Exosphere driver for the Tegra X1 Timers. */ + +void set_timer_address(void *timer_base); +void *get_timer_address(void); /* This is inlined in timers.c */ + +#define TIMERUS_CNTR_1US_0 (*((volatile uint32_t *)(get_timer_address() + 0x10))) + +void wait(uint32_t microseconds); + +#endif \ No newline at end of file diff --git a/exosphere/utils.h b/exosphere/utils.h index 3ba7bd2f6..9dc9664e5 100644 --- a/exosphere/utils.h +++ b/exosphere/utils.h @@ -1,9 +1,17 @@ #ifndef EXOSPHERE_UTILS_H #define EXOSPHERE_UTILS_H +#include + void panic(void); unsigned int read32le(const unsigned char *dword, unsigned int offset); unsigned int read32be(const unsigned char *dword, unsigned int offset); +static inline uint32_t get_core_id(void) { + uint32_t core_id; + asm volatile("mrs %0, MPIDR_EL1" : "=r"(core_id)); + return core_id; +} + #endif \ No newline at end of file