diff options
Diffstat (limited to 'services/spd/tlkd')
-rw-r--r-- | services/spd/tlkd/tlkd.mk | 14 | ||||
-rw-r--r-- | services/spd/tlkd/tlkd_common.c | 165 | ||||
-rw-r--r-- | services/spd/tlkd/tlkd_helpers.S | 80 | ||||
-rw-r--r-- | services/spd/tlkd/tlkd_main.c | 546 | ||||
-rw-r--r-- | services/spd/tlkd/tlkd_pm.c | 109 | ||||
-rw-r--r-- | services/spd/tlkd/tlkd_private.h | 124 |
6 files changed, 1038 insertions, 0 deletions
diff --git a/services/spd/tlkd/tlkd.mk b/services/spd/tlkd/tlkd.mk new file mode 100644 index 0000000..56de0a6 --- /dev/null +++ b/services/spd/tlkd/tlkd.mk @@ -0,0 +1,14 @@ +# +# Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +ifeq (${ERROR_DEPRECATED},0) +SPD_INCLUDES := -Iinclude/bl32/payloads +endif + +SPD_SOURCES := services/spd/tlkd/tlkd_common.c \ + services/spd/tlkd/tlkd_helpers.S \ + services/spd/tlkd/tlkd_main.c \ + services/spd/tlkd/tlkd_pm.c diff --git a/services/spd/tlkd/tlkd_common.c b/services/spd/tlkd/tlkd_common.c new file mode 100644 index 0000000..820bd8a --- /dev/null +++ b/services/spd/tlkd/tlkd_common.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <string.h> + +#include <arch_helpers.h> +#include <common/bl_common.h> +#include <lib/el3_runtime/context_mgmt.h> + +#include "tlkd_private.h" + +#define AT_MASK 3 + +/******************************************************************************* + * This function helps the SP to translate NS/S virtual addresses. + ******************************************************************************/ +uint64_t tlkd_va_translate(uintptr_t va, int type) +{ + uint64_t pa; + + if (type & TLK_TRANSLATE_NS_VADDR) { + + /* save secure context */ + cm_el1_sysregs_context_save(SECURE); + + /* restore non-secure context */ + cm_el1_sysregs_context_restore(NON_SECURE); + + /* switch NS bit to start using 64-bit, non-secure mappings */ + write_scr(cm_get_scr_el3(NON_SECURE)); + isb(); + } + + int at = type & AT_MASK; + switch (at) { + case 0: + AT(ats12e1r, va); + break; + case 1: + AT(ats12e1w, va); + break; + case 2: + AT(ats12e0r, va); + break; + case 3: + AT(ats12e0w, va); + break; + default: + assert(0); /* Unreachable */ + break; + } + + /* get the (NS/S) physical address */ + isb(); + pa = read_par_el1(); + + /* Restore secure state */ + if (type & TLK_TRANSLATE_NS_VADDR) { + + /* restore secure context */ + cm_el1_sysregs_context_restore(SECURE); + + /* switch NS bit to start using 32-bit, secure mappings */ + write_scr(cm_get_scr_el3(SECURE)); + isb(); + } + + return pa; +} + +/******************************************************************************* + * Given a secure payload entrypoint, register width, cpu id & pointer to a + * context data structure, this function will create a secure context ready for + * programming an entry into the secure payload. + ******************************************************************************/ +void tlkd_init_tlk_ep_state(struct entry_point_info *tlk_entry_point, + uint32_t rw, + uint64_t pc, + tlk_context_t *tlk_ctx) +{ + uint32_t ep_attr, spsr; + + /* Passing a NULL context is a critical programming error */ + assert(tlk_ctx); + assert(tlk_entry_point); + assert(pc); + + /* Associate this context with the cpu specified */ + tlk_ctx->mpidr = read_mpidr_el1(); + clr_yield_smc_active_flag(tlk_ctx->state); + cm_set_context(&tlk_ctx->cpu_ctx, SECURE); + + if (rw == SP_AARCH64) + spsr = SPSR_64(MODE_EL1, MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS); + else + spsr = SPSR_MODE32(MODE32_svc, + SPSR_T_ARM, + read_sctlr_el3() & SCTLR_EE_BIT, + DISABLE_ALL_EXCEPTIONS); + + /* initialise an entrypoint to set up the CPU context */ + ep_attr = SECURE | EP_ST_ENABLE; + if (read_sctlr_el3() & SCTLR_EE_BIT) + ep_attr |= EP_EE_BIG; + SET_PARAM_HEAD(tlk_entry_point, PARAM_EP, VERSION_1, ep_attr); + + tlk_entry_point->pc = pc; + tlk_entry_point->spsr = spsr; +} + +/******************************************************************************* + * This function takes a TLK context pointer and: + * 1. Applies the S-EL1 system register context from tlk_ctx->cpu_ctx. + * 2. Saves the current C runtime state (callee saved registers) on the stack + * frame and saves a reference to this state. + * 3. Calls el3_exit() so that the EL3 system and general purpose registers + * from the tlk_ctx->cpu_ctx are used to enter the secure payload image. + ******************************************************************************/ +uint64_t tlkd_synchronous_sp_entry(tlk_context_t *tlk_ctx) +{ + uint64_t rc; + + /* Passing a NULL context is a critical programming error */ + assert(tlk_ctx); + + /* Apply the Secure EL1 system register context and switch to it */ + assert(cm_get_context(SECURE) == &tlk_ctx->cpu_ctx); + cm_el1_sysregs_context_restore(SECURE); + cm_set_next_eret_context(SECURE); + + rc = tlkd_enter_sp(&tlk_ctx->c_rt_ctx); +#if ENABLE_ASSERTIONS + tlk_ctx->c_rt_ctx = 0; +#endif + + return rc; +} + +/******************************************************************************* + * This function takes a TLK context pointer and: + * 1. Saves the S-EL1 system register context to tlk_ctx->cpu_ctx. + * 2. Restores the current C runtime state (callee saved registers) from the + * stack frame using reference to this state saved in tlkd_enter_sp(). + * 3. It does not need to save any general purpose or EL3 system register state + * as the generic smc entry routine should have saved those. + ******************************************************************************/ +void tlkd_synchronous_sp_exit(tlk_context_t *tlk_ctx, uint64_t ret) +{ + /* Passing a NULL context is a critical programming error */ + assert(tlk_ctx); + + /* Save the Secure EL1 system register context */ + assert(cm_get_context(SECURE) == &tlk_ctx->cpu_ctx); + cm_el1_sysregs_context_save(SECURE); + + assert(tlk_ctx->c_rt_ctx != 0); + tlkd_exit_sp(tlk_ctx->c_rt_ctx, ret); + + /* Should never reach here */ + assert(0); +} diff --git a/services/spd/tlkd/tlkd_helpers.S b/services/spd/tlkd/tlkd_helpers.S new file mode 100644 index 0000000..6e616a6 --- /dev/null +++ b/services/spd/tlkd/tlkd_helpers.S @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <asm_macros.S> +#include "tlkd_private.h" + + .global tlkd_enter_sp + .global tlkd_exit_sp + + /* --------------------------------------------- + * This function is called with SP_EL0 as stack. + * Here we stash our EL3 callee-saved registers + * on to the stack as a part of saving the C + * runtime and enter the secure payload. + * 'x0' contains a pointer to the memory where + * the address of the C runtime context is to be + * saved. + * --------------------------------------------- + */ +func tlkd_enter_sp + /* Make space for the registers that we're going to save */ + mov x3, sp + str x3, [x0, #0] + sub sp, sp, #TLKD_C_RT_CTX_SIZE + + /* Save callee-saved registers on to the stack */ + stp x19, x20, [sp, #TLKD_C_RT_CTX_X19] + stp x21, x22, [sp, #TLKD_C_RT_CTX_X21] + stp x23, x24, [sp, #TLKD_C_RT_CTX_X23] + stp x25, x26, [sp, #TLKD_C_RT_CTX_X25] + stp x27, x28, [sp, #TLKD_C_RT_CTX_X27] + stp x29, x30, [sp, #TLKD_C_RT_CTX_X29] + + /* ---------------------------------------------- + * Everything is setup now. el3_exit() will + * use the secure context to restore to the + * general purpose and EL3 system registers to + * ERET into the secure payload. + * ---------------------------------------------- + */ + b el3_exit +endfunc tlkd_enter_sp + + /* ---------------------------------------------- + * This function is called with 'x0' pointing to + * a C runtime context saved in tlkd_enter_sp(). + * It restores the saved registers and jumps to + * that runtime with 'x0' as the new sp. This + * destroys the C runtime context that had been + * built on the stack below the saved context by + * the caller. Later the second parameter 'x1' + * is passed as return value to the caller + * ---------------------------------------------- + */ +func tlkd_exit_sp + /* Restore the previous stack */ + mov sp, x0 + + /* Restore callee-saved registers on to the stack */ + ldp x19, x20, [x0, #(TLKD_C_RT_CTX_X19 - TLKD_C_RT_CTX_SIZE)] + ldp x21, x22, [x0, #(TLKD_C_RT_CTX_X21 - TLKD_C_RT_CTX_SIZE)] + ldp x23, x24, [x0, #(TLKD_C_RT_CTX_X23 - TLKD_C_RT_CTX_SIZE)] + ldp x25, x26, [x0, #(TLKD_C_RT_CTX_X25 - TLKD_C_RT_CTX_SIZE)] + ldp x27, x28, [x0, #(TLKD_C_RT_CTX_X27 - TLKD_C_RT_CTX_SIZE)] + ldp x29, x30, [x0, #(TLKD_C_RT_CTX_X29 - TLKD_C_RT_CTX_SIZE)] + + /* ------------------------------------------------ + * This should take us back to the instruction + * after the call to the last tlkd_enter_sp(). + * Place the second parameter to x0 so that the + * caller will see it as a return value from the + * original entry call + * ------------------------------------------------ + */ + mov x0, x1 + ret +endfunc tlkd_exit_sp diff --git a/services/spd/tlkd/tlkd_main.c b/services/spd/tlkd/tlkd_main.c new file mode 100644 index 0000000..ecac435 --- /dev/null +++ b/services/spd/tlkd/tlkd_main.c @@ -0,0 +1,546 @@ +/* + * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2020, NVIDIA Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/******************************************************************************* + * This is the Secure Payload Dispatcher (SPD). The dispatcher is meant to be a + * plug-in component to the Secure Monitor, registered as a runtime service. The + * SPD is expected to be a functional extension of the Secure Payload (SP) that + * executes in Secure EL1. The Secure Monitor will delegate all SMCs targeting + * the Trusted OS/Applications range to the dispatcher. The SPD will either + * handle the request locally or delegate it to the Secure Payload. It is also + * responsible for initialising and maintaining communication with the SP. + ******************************************************************************/ +#include <assert.h> +#include <bl31/interrupt_mgmt.h> +#include <errno.h> +#include <stddef.h> + +#include <arch_helpers.h> +#include <bl31/bl31.h> +#include <bl32/payloads/tlk.h> +#include <common/bl_common.h> +#include <common/debug.h> +#include <common/runtime_svc.h> +#include <lib/el3_runtime/context_mgmt.h> +#include <plat/common/platform.h> +#include <tools_share/uuid.h> + +#include "tlkd_private.h" + +extern const spd_pm_ops_t tlkd_pm_ops; + +/******************************************************************************* + * Per-cpu Secure Payload state + ******************************************************************************/ +tlk_context_t tlk_ctx; + +/******************************************************************************* + * CPU number on which TLK booted up + ******************************************************************************/ +static uint32_t boot_cpu; + +/* TLK UID: RFC-4122 compliant UUID (version-5, sha-1) */ +DEFINE_SVC_UUID2(tlk_uuid, + 0xc9e911bd, 0xba2b, 0xee52, 0xb1, 0x72, + 0x46, 0x1f, 0xba, 0x97, 0x7f, 0x63); + +static int32_t tlkd_init(void); + +/******************************************************************************* + * Secure Payload Dispatcher's timer interrupt handler + ******************************************************************************/ +static uint64_t tlkd_interrupt_handler(uint32_t id, + uint32_t flags, + void *handle, + void *cookie) +{ + cpu_context_t *s_cpu_context; + int irq = plat_ic_get_pending_interrupt_id(); + + /* acknowledge the interrupt and mark it complete */ + (void)plat_ic_acknowledge_interrupt(); + plat_ic_end_of_interrupt(irq); + + /* + * Disable the routing of NS interrupts from secure world to + * EL3 while interrupted on this core. + */ + disable_intr_rm_local(INTR_TYPE_S_EL1, SECURE); + + /* Check the security state when the exception was generated */ + assert(get_interrupt_src_ss(flags) == NON_SECURE); + assert(handle == cm_get_context(NON_SECURE)); + + /* Save non-secure state */ + cm_el1_sysregs_context_save(NON_SECURE); + + /* Get a reference to the secure context */ + s_cpu_context = cm_get_context(SECURE); + assert(s_cpu_context); + + /* + * Restore non-secure state. There is no need to save the + * secure system register context since the SP was supposed + * to preserve it during S-EL1 interrupt handling. + */ + cm_el1_sysregs_context_restore(SECURE); + cm_set_next_eret_context(SECURE); + + /* Provide the IRQ number to the SPD */ + SMC_RET4(s_cpu_context, (uint32_t)TLK_IRQ_FIRED, 0, (uint32_t)irq, 0); +} + +/******************************************************************************* + * Secure Payload Dispatcher setup. The SPD finds out the SP entrypoint and type + * (aarch32/aarch64) if not already known and initialises the context for entry + * into the SP for its initialisation. + ******************************************************************************/ +static int32_t tlkd_setup(void) +{ + entry_point_info_t *tlk_ep_info; + uint32_t flags; + int32_t ret; + + /* + * Get information about the Secure Payload (BL32) image. Its + * absence is a critical failure. + */ + tlk_ep_info = bl31_plat_get_next_image_ep_info(SECURE); + if (!tlk_ep_info) { + WARN("No SP provided. Booting device without SP" + " initialization. SMC`s destined for SP" + " will return SMC_UNK\n"); + return 1; + } + + /* + * If there's no valid entry point for SP, we return a non-zero value + * signalling failure initializing the service. We bail out without + * registering any handlers + */ + if (!tlk_ep_info->pc) + return 1; + + /* + * Inspect the SP image's SPSR and determine it's execution state + * i.e whether AArch32 or AArch64. + */ + tlkd_init_tlk_ep_state(tlk_ep_info, + (tlk_ep_info->spsr >> MODE_RW_SHIFT) & MODE_RW_MASK, + tlk_ep_info->pc, + &tlk_ctx); + + /* get a list of all S-EL1 IRQs from the platform */ + + /* register interrupt handler */ + flags = 0; + set_interrupt_rm_flag(flags, NON_SECURE); + ret = register_interrupt_type_handler(INTR_TYPE_S_EL1, + tlkd_interrupt_handler, + flags); + if (ret != 0) { + ERROR("failed to register tlkd interrupt handler (%d)\n", ret); + } + + /* + * All TLK SPD initialization done. Now register our init function + * with BL31 for deferred invocation + */ + bl31_register_bl32_init(&tlkd_init); + + return 0; +} + +/******************************************************************************* + * This function passes control to the Secure Payload image (BL32) for the first + * time on the primary cpu after a cold boot. It assumes that a valid secure + * context has already been created by tlkd_setup() which can be directly + * used. This function performs a synchronous entry into the Secure payload. + * The SP passes control back to this routine through a SMC. + ******************************************************************************/ +static int32_t tlkd_init(void) +{ + entry_point_info_t *tlk_entry_point; + + /* + * Get information about the Secure Payload (BL32) image. Its + * absence is a critical failure. + */ + tlk_entry_point = bl31_plat_get_next_image_ep_info(SECURE); + assert(tlk_entry_point); + + cm_init_my_context(tlk_entry_point); + + /* + * TLK runs only on a single CPU. Store the value of the boot + * CPU for sanity checking later. + */ + boot_cpu = plat_my_core_pos(); + + /* + * Arrange for an entry into the test secure payload. + */ + return tlkd_synchronous_sp_entry(&tlk_ctx); +} + +/******************************************************************************* + * This function is responsible for handling all SMCs in the Trusted OS/App + * range from the non-secure state as defined in the SMC Calling Convention + * Document. It is also responsible for communicating with the Secure payload + * to delegate work and return results back to the non-secure state. Lastly it + * will also return any information that the secure payload needs to do the + * work assigned to it. + ******************************************************************************/ +static uintptr_t tlkd_smc_handler(uint32_t smc_fid, + u_register_t x1, + u_register_t x2, + u_register_t x3, + u_register_t x4, + void *cookie, + void *handle, + u_register_t flags) +{ + cpu_context_t *ns_cpu_context; + gp_regs_t *gp_regs; + uint32_t ns; + uint64_t par; + + /* Passing a NULL context is a critical programming error */ + assert(handle); + + /* These SMCs are only supported by a single CPU */ + if (boot_cpu != plat_my_core_pos()) + SMC_RET1(handle, SMC_UNK); + + /* Determine which security state this SMC originated from */ + ns = is_caller_non_secure(flags); + + switch (smc_fid) { + + /* + * This function ID is used by SP to indicate that it was + * preempted by a non-secure world IRQ. + */ + case TLK_PREEMPTED: + + if (ns) + SMC_RET1(handle, SMC_UNK); + + assert(handle == cm_get_context(SECURE)); + cm_el1_sysregs_context_save(SECURE); + + /* Get a reference to the non-secure context */ + ns_cpu_context = cm_get_context(NON_SECURE); + assert(ns_cpu_context); + + /* + * Restore non-secure state. There is no need to save the + * secure system register context since the SP was supposed + * to preserve it during S-EL1 interrupt handling. + */ + cm_el1_sysregs_context_restore(NON_SECURE); + cm_set_next_eret_context(NON_SECURE); + + SMC_RET1(ns_cpu_context, x1); + + /* + * This is a request from the non-secure context to: + * + * a. register shared memory with the SP for storing it's + * activity logs. + * b. register shared memory with the SP for passing args + * required for maintaining sessions with the Trusted + * Applications. + * c. register shared persistent buffers for secure storage + * d. register NS DRAM ranges passed by Cboot + * e. register Root of Trust parameters from Cboot for Verified Boot + * f. open/close sessions + * g. issue commands to the Trusted Apps + * h. resume the preempted yielding SMC call. + */ + case TLK_REGISTER_LOGBUF: + case TLK_REGISTER_REQBUF: + case TLK_SS_REGISTER_HANDLER: + case TLK_REGISTER_NS_DRAM_RANGES: + case TLK_SET_ROOT_OF_TRUST: + case TLK_OPEN_TA_SESSION: + case TLK_CLOSE_TA_SESSION: + case TLK_TA_LAUNCH_OP: + case TLK_TA_SEND_EVENT: + case TLK_RESUME_FID: + case TLK_SET_BL_VERSION: + case TLK_LOCK_BL_INTERFACE: + case TLK_BL_RPMB_SERVICE: + + if (!ns) + SMC_RET1(handle, SMC_UNK); + + /* + * This is a fresh request from the non-secure client. + * The parameters are in x1 and x2. Figure out which + * registers need to be preserved, save the non-secure + * state and send the request to the secure payload. + */ + assert(handle == cm_get_context(NON_SECURE)); + + /* + * Check if we are already processing a yielding SMC + * call. Of all the supported fids, only the "resume" + * fid expects the flag to be set. + */ + if (smc_fid == TLK_RESUME_FID) { + if (!get_yield_smc_active_flag(tlk_ctx.state)) + SMC_RET1(handle, SMC_UNK); + } else { + if (get_yield_smc_active_flag(tlk_ctx.state)) + SMC_RET1(handle, SMC_UNK); + } + + cm_el1_sysregs_context_save(NON_SECURE); + + /* + * Verify if there is a valid context to use. + */ + assert(&tlk_ctx.cpu_ctx == cm_get_context(SECURE)); + + /* + * Mark the SP state as active. + */ + set_yield_smc_active_flag(tlk_ctx.state); + + /* + * We are done stashing the non-secure context. Ask the + * secure payload to do the work now. + */ + cm_el1_sysregs_context_restore(SECURE); + cm_set_next_eret_context(SECURE); + + /* + * TLK is a 32-bit Trusted OS and so expects the SMC + * arguments via r0-r7. TLK expects the monitor frame + * registers to be 64-bits long. Hence, we pass x0 in + * r0-r1, x1 in r2-r3, x3 in r4-r5 and x4 in r6-r7. + * + * As smc_fid is a uint32 value, r1 contains 0. + */ + gp_regs = get_gpregs_ctx(&tlk_ctx.cpu_ctx); + write_ctx_reg(gp_regs, CTX_GPREG_X4, (uint32_t)x2); + write_ctx_reg(gp_regs, CTX_GPREG_X5, (uint32_t)(x2 >> 32)); + write_ctx_reg(gp_regs, CTX_GPREG_X6, (uint32_t)x3); + write_ctx_reg(gp_regs, CTX_GPREG_X7, (uint32_t)(x3 >> 32)); + SMC_RET4(&tlk_ctx.cpu_ctx, smc_fid, 0, (uint32_t)x1, + (uint32_t)(x1 >> 32)); + + /* + * Translate NS/EL1-S virtual addresses. + * + * x1 = virtual address + * x3 = type (NS/S) + * + * Returns PA:lo in r0, PA:hi in r1. + */ + case TLK_VA_TRANSLATE: + + /* Should be invoked only by secure world */ + if (ns) + SMC_RET1(handle, SMC_UNK); + + /* NS virtual addresses are 64-bit long */ + if (x3 & TLK_TRANSLATE_NS_VADDR) + x1 = (uint32_t)x1 | (x2 << 32); + + if (!x1) + SMC_RET1(handle, SMC_UNK); + + /* + * TODO: Sanity check x1. This would require platform + * support. + */ + + /* virtual address and type: ns/s */ + par = tlkd_va_translate(x1, x3); + + /* return physical address in r0-r1 */ + SMC_RET4(handle, (uint32_t)par, (uint32_t)(par >> 32), 0, 0); + + /* + * This is a request from the SP to mark completion of + * a yielding function ID. + */ + case TLK_REQUEST_DONE: + if (ns) + SMC_RET1(handle, SMC_UNK); + + /* + * Mark the SP state as inactive. + */ + clr_yield_smc_active_flag(tlk_ctx.state); + + /* Get a reference to the non-secure context */ + ns_cpu_context = cm_get_context(NON_SECURE); + assert(ns_cpu_context); + + /* + * This is a request completion SMC and we must switch to + * the non-secure world to pass the result. + */ + cm_el1_sysregs_context_save(SECURE); + + /* + * We are done stashing the secure context. Switch to the + * non-secure context and return the result. + */ + cm_el1_sysregs_context_restore(NON_SECURE); + cm_set_next_eret_context(NON_SECURE); + SMC_RET1(ns_cpu_context, x1); + + /* + * This function ID is used only by the SP to indicate it has + * finished initialising itself after a cold boot + */ + case TLK_ENTRY_DONE: + if (ns) + SMC_RET1(handle, SMC_UNK); + + /* + * SP has been successfully initialized. Register power + * management hooks with PSCI + */ + psci_register_spd_pm_hook(&tlkd_pm_ops); + + /* + * TLK reports completion. The SPD must have initiated + * the original request through a synchronous entry + * into the SP. Jump back to the original C runtime + * context. + */ + tlkd_synchronous_sp_exit(&tlk_ctx, x1); + break; + + /* + * These function IDs are used only by TLK to indicate it has + * finished: + * 1. suspending itself after an earlier psci cpu_suspend + * request. + * 2. resuming itself after an earlier psci cpu_suspend + * request. + * 3. powering down after an earlier psci system_off/system_reset + * request. + */ + case TLK_SUSPEND_DONE: + case TLK_RESUME_DONE: + + if (ns) + SMC_RET1(handle, SMC_UNK); + + /* + * TLK reports completion. TLKD must have initiated the + * original request through a synchronous entry into the SP. + * Jump back to the original C runtime context, and pass x1 as + * return value to the caller + */ + tlkd_synchronous_sp_exit(&tlk_ctx, x1); + break; + + /* + * This function ID is used by SP to indicate that it has completed + * handling the secure interrupt. + */ + case TLK_IRQ_DONE: + + if (ns) + SMC_RET1(handle, SMC_UNK); + + assert(handle == cm_get_context(SECURE)); + + /* save secure world context */ + cm_el1_sysregs_context_save(SECURE); + + /* Get a reference to the non-secure context */ + ns_cpu_context = cm_get_context(NON_SECURE); + assert(ns_cpu_context); + + /* + * Restore non-secure state. There is no need to save the + * secure system register context since the SP was supposed + * to preserve it during S-EL1 interrupt handling. + */ + cm_el1_sysregs_context_restore(NON_SECURE); + cm_set_next_eret_context(NON_SECURE); + + SMC_RET0(ns_cpu_context); + + /* + * Return the number of service function IDs implemented to + * provide service to non-secure + */ + case TOS_CALL_COUNT: + SMC_RET1(handle, TLK_NUM_FID); + + /* + * Return TLK's UID to the caller + */ + case TOS_UID: + SMC_UUID_RET(handle, tlk_uuid); + + /* + * Return the version of current implementation + */ + case TOS_CALL_VERSION: + SMC_RET2(handle, TLK_VERSION_MAJOR, TLK_VERSION_MINOR); + + default: + WARN("%s: Unhandled SMC: 0x%x\n", __func__, smc_fid); + break; + } + + SMC_RET1(handle, SMC_UNK); +} + +/* Define a SPD runtime service descriptor for fast SMC calls */ +DECLARE_RT_SVC( + tlkd_tos_fast, + + OEN_TOS_START, + OEN_TOS_END, + SMC_TYPE_FAST, + tlkd_setup, + tlkd_smc_handler +); + +/* Define a SPD runtime service descriptor for yielding SMC calls */ +DECLARE_RT_SVC( + tlkd_tos_std, + + OEN_TOS_START, + OEN_TOS_END, + SMC_TYPE_YIELD, + NULL, + tlkd_smc_handler +); + +/* Define a SPD runtime service descriptor for fast SMC calls */ +DECLARE_RT_SVC( + tlkd_tap_fast, + + OEN_TAP_START, + OEN_TAP_END, + SMC_TYPE_FAST, + NULL, + tlkd_smc_handler +); + +/* Define a SPD runtime service descriptor for yielding SMC calls */ +DECLARE_RT_SVC( + tlkd_tap_std, + + OEN_TAP_START, + OEN_TAP_END, + SMC_TYPE_YIELD, + NULL, + tlkd_smc_handler +); diff --git a/services/spd/tlkd/tlkd_pm.c b/services/spd/tlkd/tlkd_pm.c new file mode 100644 index 0000000..ed5bf77 --- /dev/null +++ b/services/spd/tlkd/tlkd_pm.c @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2020, NVIDIA Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> + +#include <arch_helpers.h> +#include <bl32/payloads/tlk.h> +#include <common/bl_common.h> +#include <common/debug.h> +#include <lib/el3_runtime/context_mgmt.h> +#include <lib/psci/psci.h> + +#include "tlkd_private.h" + +extern tlk_context_t tlk_ctx; + +#define MPIDR_CPU0 0x80000000 + +/******************************************************************************* + * Return the type of payload TLKD is dealing with. Report the current + * resident cpu (mpidr format) if it is a UP/UP migratable payload. + ******************************************************************************/ +static int32_t cpu_migrate_info(u_register_t *resident_cpu) +{ + /* the payload runs only on CPU0 */ + *resident_cpu = MPIDR_CPU0; + + /* Uniprocessor, not migrate capable payload */ + return PSCI_TOS_NOT_UP_MIG_CAP; +} + +/******************************************************************************* + * This cpu is being suspended. Inform TLK of the SYSTEM_SUSPEND event, so + * that it can pass this information to its Trusted Apps. + ******************************************************************************/ +static void cpu_suspend_handler(u_register_t suspend_level) +{ + gp_regs_t *gp_regs; + int cpu = read_mpidr() & MPIDR_CPU_MASK; + int32_t rc = 0; + + /* + * TLK runs only on CPU0 and suspends its Trusted Apps during + * SYSTEM_SUSPEND. It has no role to play during CPU_SUSPEND. + */ + if ((cpu != 0) || (suspend_level != PLAT_MAX_PWR_LVL)) + return; + + /* pass system suspend event to TLK */ + gp_regs = get_gpregs_ctx(&tlk_ctx.cpu_ctx); + write_ctx_reg(gp_regs, CTX_GPREG_X0, TLK_SYSTEM_SUSPEND); + + /* Program the entry point and enter TLK */ + rc = tlkd_synchronous_sp_entry(&tlk_ctx); + + /* + * Read the response from TLK. A non-zero return means that + * something went wrong while communicating with it. + */ + if (rc != 0) + panic(); +} + +/******************************************************************************* + * This cpu is being resumed. Inform TLK of the SYSTEM_SUSPEND exit, so + * that it can pass this information to its Trusted Apps. + ******************************************************************************/ +static void cpu_resume_handler(u_register_t suspend_level) +{ + gp_regs_t *gp_regs; + int cpu = read_mpidr() & MPIDR_CPU_MASK; + int32_t rc = 0; + + /* + * TLK runs only on CPU0 and resumes its Trusted Apps during + * SYSTEM_SUSPEND exit. It has no role to play during CPU_SUSPEND + * exit. + */ + if ((cpu != 0) || (suspend_level != PLAT_MAX_PWR_LVL)) + return; + + /* pass system resume event to TLK */ + gp_regs = get_gpregs_ctx(&tlk_ctx.cpu_ctx); + write_ctx_reg(gp_regs, CTX_GPREG_X0, TLK_SYSTEM_RESUME); + + /* Program the entry point and enter TLK */ + rc = tlkd_synchronous_sp_entry(&tlk_ctx); + + /* + * Read the response from TLK. A non-zero return means that + * something went wrong while communicating with it. + */ + if (rc != 0) + panic(); +} + +/******************************************************************************* + * Structure populated by the Dispatcher to be given a chance to perform any + * bookkeeping before PSCI executes a power mgmt. operation. + ******************************************************************************/ +const spd_pm_ops_t tlkd_pm_ops = { + .svc_migrate_info = cpu_migrate_info, + .svc_suspend = cpu_suspend_handler, + .svc_suspend_finish = cpu_resume_handler, +}; diff --git a/services/spd/tlkd/tlkd_private.h b/services/spd/tlkd/tlkd_private.h new file mode 100644 index 0000000..ad36f5e --- /dev/null +++ b/services/spd/tlkd/tlkd_private.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef TLKD_PRIVATE_H +#define TLKD_PRIVATE_H + +#include <platform_def.h> + +#include <arch.h> +#include <bl31/interrupt_mgmt.h> +#include <context.h> +#include <lib/psci/psci.h> + +/* + * This flag is used by the TLKD to determine if the SP is servicing a yielding + * SMC request prior to programming the next entry into the SP e.g. if SP + * execution is preempted by a non-secure interrupt and handed control to the + * normal world. If another request which is distinct from what the SP was + * previously doing arrives, then this flag will be help the TLKD to either + * reject the new request or service it while ensuring that the previous context + * is not corrupted. + */ +#define YIELD_SMC_ACTIVE_FLAG_SHIFT 2 +#define YIELD_SMC_ACTIVE_FLAG_MASK 1 +#define get_yield_smc_active_flag(state) \ + (((state) >> YIELD_SMC_ACTIVE_FLAG_SHIFT) \ + & YIELD_SMC_ACTIVE_FLAG_MASK) +#define set_yield_smc_active_flag(state) ((state) |= \ + (1 << YIELD_SMC_ACTIVE_FLAG_SHIFT)) +#define clr_yield_smc_active_flag(state) ((state) &= \ + ~(YIELD_SMC_ACTIVE_FLAG_MASK \ + << YIELD_SMC_ACTIVE_FLAG_SHIFT)) + +/******************************************************************************* + * Translate virtual address received from the NS world + ******************************************************************************/ +#define TLK_TRANSLATE_NS_VADDR 4 + +/******************************************************************************* + * Secure Payload execution state information i.e. aarch32 or aarch64 + ******************************************************************************/ +#define SP_AARCH32 MODE_RW_32 +#define SP_AARCH64 MODE_RW_64 + +/******************************************************************************* + * Number of cpus that the present on this platform. TODO: Rely on a topology + * tree to determine this in the future to avoid assumptions about mpidr + * allocation + ******************************************************************************/ +#define TLKD_CORE_COUNT PLATFORM_CORE_COUNT + +/******************************************************************************* + * Constants that allow assembler code to preserve callee-saved registers of the + * C runtime context while performing a security state switch. + ******************************************************************************/ +#define TLKD_C_RT_CTX_X19 0x0 +#define TLKD_C_RT_CTX_X20 0x8 +#define TLKD_C_RT_CTX_X21 0x10 +#define TLKD_C_RT_CTX_X22 0x18 +#define TLKD_C_RT_CTX_X23 0x20 +#define TLKD_C_RT_CTX_X24 0x28 +#define TLKD_C_RT_CTX_X25 0x30 +#define TLKD_C_RT_CTX_X26 0x38 +#define TLKD_C_RT_CTX_X27 0x40 +#define TLKD_C_RT_CTX_X28 0x48 +#define TLKD_C_RT_CTX_X29 0x50 +#define TLKD_C_RT_CTX_X30 0x58 +#define TLKD_C_RT_CTX_SIZE 0x60 +#define TLKD_C_RT_CTX_ENTRIES (TLKD_C_RT_CTX_SIZE >> DWORD_SHIFT) + +#ifndef __ASSEMBLER__ + +#include <stdint.h> + +#include <lib/cassert.h> + +/* AArch64 callee saved general purpose register context structure. */ +DEFINE_REG_STRUCT(c_rt_regs, TLKD_C_RT_CTX_ENTRIES); + +/* + * Compile time assertion to ensure that both the compiler and linker + * have the same double word aligned view of the size of the C runtime + * register context. + */ +CASSERT(TLKD_C_RT_CTX_SIZE == sizeof(c_rt_regs_t), + assert_tlkd_c_rt_regs_size_mismatch); + +/******************************************************************************* + * Structure which helps the SPD to maintain the per-cpu state of the SP. + * 'state' - collection of flags to track SP state e.g. on/off + * 'mpidr' - mpidr to associate a context with a cpu + * 'c_rt_ctx' - stack address to restore C runtime context from after + * returning from a synchronous entry into the SP. + * 'cpu_ctx' - space to maintain SP architectural state + * 'saved_tsp_args' - space to store arguments for TSP arithmetic operations + * which will queried using the TSP_GET_ARGS SMC by TSP. + ******************************************************************************/ +typedef struct tlk_context { + uint32_t state; + uint64_t mpidr; + uint64_t c_rt_ctx; + cpu_context_t cpu_ctx; +} tlk_context_t; + +/******************************************************************************* + * Function & Data prototypes + ******************************************************************************/ +uint64_t tlkd_va_translate(uintptr_t va, int type); +uint64_t tlkd_enter_sp(uint64_t *c_rt_ctx); +void __dead2 tlkd_exit_sp(uint64_t c_rt_ctx, uint64_t ret); +uint64_t tlkd_synchronous_sp_entry(tlk_context_t *tlk_ctx); +void __dead2 tlkd_synchronous_sp_exit(tlk_context_t *tlk_ctx, + uint64_t ret); +void tlkd_init_tlk_ep_state(struct entry_point_info *tlk_entry_point, + uint32_t rw, + uint64_t pc, + tlk_context_t *tlk_ctx); + +#endif /*__ASSEMBLER__*/ + +#endif /* TLKD_PRIVATE_H */ |