/* * Initialize the EL3 exception handling. */ void ehf_init(void) { unsigned int flags = 0; int ret __unused; /* Ensure EL3 interrupts are supported */ assert(plat_ic_has_interrupt_type(INTR_TYPE_EL3)); /* * Make sure that priority water mark has enough bits to represent the * whole priority array. */ assert(exception_data.num_priorities <= (sizeof(ehf_pri_bits_t) * 8)); assert(exception_data.ehf_priorities); /* * Bit 7 of GIC priority must be 0 for secure interrupts. This means * platforms must use at least 1 of the remaining 7 bits. */ assert((exception_data.pri_bits >= 1) || (exception_data.pri_bits < 8)); /* Route EL3 interrupts when in Secure and Non-secure. */ set_interrupt_rm_flag(flags, NON_SECURE); set_interrupt_rm_flag(flags, SECURE); /* Register handler for EL3 interrupts */ ret = register_interrupt_type_handler(INTR_TYPE_EL3, ehf_el3_interrupt_handler, flags); assert(ret == 0); }
void mvebu_pmu_interrupt_enable(void) { unsigned int idx; uint32_t flags; int32_t rc; /* Reset PIC */ mmio_write_32(A7K8K_PIC_CAUSE_REG, A7K8K_PIC_MAX_IRQ_MASK); /* Unmask PMU overflow IRQ in PIC0 */ mmio_clrbits_32(A7K8K_PIC0_MASK_REG, A7K8K_PIC_PMUOF_IRQ_MASK); /* Configure ODMI Frame IRQs as edge triggered */ for (idx = 0; idx < PLATFORM_CORE_COUNT; idx++) gicv2_interrupt_set_cfg(A7K8K_ODMI_PMU_GIC_IRQ(idx), GIC_INTR_CFG_EDGE); /* * Register IRQ handler as INTR_TYPE_S_EL1 as its the only valid type * for GICv2 in ARM-TF. */ flags = 0U; set_interrupt_rm_flag((flags), (NON_SECURE)); rc = register_interrupt_type_handler(INTR_TYPE_S_EL1, a7k8k_pmu_interrupt_handler, flags); if (rc != 0) panic(); }
void bl31_plat_runtime_setup(void) { uint64_t flags = 0; uint64_t rc; set_interrupt_rm_flag(flags, NON_SECURE); rc = register_interrupt_type_handler(INTR_TYPE_EL3, rdo_el3_interrupt_handler, flags); if (rc) panic(); }
/******************************************************************************* * 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 fiqd_setup() which can be directly used. * It also assumes that a valid non-secure context has been initialised by PSCI * so it does not need to save and restore any non-secure state. This function * performs a synchronous entry into the Secure payload. The SP passes control * back to this routine through a SMC. ******************************************************************************/ int32_t fiqd_init(void) { uint32_t flags; uint64_t rc; /* * Register an interrupt handler for S-EL1 interrupts when generated * during code executing in the non-secure state. */ flags = 0; set_interrupt_rm_flag(flags, NON_SECURE); rc = register_interrupt_type_handler(INTR_TYPE_S_EL1, fiqd_sel1_interrupt_handler, flags); if (rc) panic(); return rc; }
void configure_tbase(uint64_t x1, uint64_t x2) { uint32_t w1 = maskSWdRegister(x1); DBG_PRINTF( "tbase_fastcall_handler TBASE_SMC_FASTCALL_CONFIG_OK\n\r"); if (TBASE_SMC_FASTCALL_CONFIG_VECTOR==w1) { tbaseEntryBase = maskSWdRegister(x2); tbaseInitStatus = TBASE_INIT_CONFIG_OK; DBG_PRINTF("tbase config ok %llx %x\n\r", tbaseEntryBase, tbaseInitStatus); // Register an FIQ handler when executing in the non-secure state. uint32_t flags = 0; set_interrupt_rm_flag(flags, NON_SECURE); uint32_t rc = register_interrupt_type_handler(INTR_TYPE_S_EL1, tbase_fiq_handler, flags); if (rc!=0) { DBG_PRINTF( "tbase_fastcall_setup FIQ register failed.\n\r"); } } else { // Just to keep compatibility for a minute tbaseEntryBase = w1; tbaseInitStatus = TBASE_INIT_CONFIG_OK; } }
/******************************************************************************* * 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. ******************************************************************************/ uint64_t tspd_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, void *cookie, void *handle, uint64_t flags) { cpu_context_t *ns_cpu_context; uint32_t linear_id = plat_my_core_pos(), ns; tsp_context_t *tsp_ctx = &tspd_sp_context[linear_id]; uint64_t rc; #if TSP_INIT_ASYNC entry_point_info_t *next_image_info; #endif /* Determine which security state this SMC originated from */ ns = is_caller_non_secure(flags); switch (smc_fid) { /* * This function ID is used by TSP to indicate that it was * preempted by a normal world IRQ. * */ case TSP_PREEMPTED: if (ns) SMC_RET1(handle, SMC_UNK); return tspd_handle_sp_preemption(handle); /* * This function ID is used only by the TSP to indicate that it has * finished handling a S-EL1 FIQ interrupt. Execution should resume * in the normal world. */ case TSP_HANDLED_S_EL1_FIQ: if (ns) SMC_RET1(handle, SMC_UNK); assert(handle == cm_get_context(SECURE)); /* * Restore the relevant EL3 state which saved to service * this SMC. */ if (get_std_smc_active_flag(tsp_ctx->state)) { SMC_SET_EL3(&tsp_ctx->cpu_ctx, CTX_SPSR_EL3, tsp_ctx->saved_spsr_el3); SMC_SET_EL3(&tsp_ctx->cpu_ctx, CTX_ELR_EL3, tsp_ctx->saved_elr_el3); #if TSPD_ROUTE_IRQ_TO_EL3 /* * Need to restore the previously interrupted * secure context. */ memcpy(&tsp_ctx->cpu_ctx, &tsp_ctx->sp_ctx, TSPD_SP_CTX_SIZE); #endif } /* 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 TSP 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((uint64_t) ns_cpu_context); /* * This function ID is used only by the TSP to indicate that it was * interrupted due to a EL3 FIQ interrupt. Execution should resume * in the normal world. */ case TSP_EL3_FIQ: if (ns) SMC_RET1(handle, SMC_UNK); assert(handle == cm_get_context(SECURE)); /* Assert that standard SMC execution has been preempted */ assert(get_std_smc_active_flag(tsp_ctx->state)); /* Save the secure system register state */ 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 */ cm_el1_sysregs_context_restore(NON_SECURE); cm_set_next_eret_context(NON_SECURE); SMC_RET1(ns_cpu_context, TSP_EL3_FIQ); /* * This function ID is used only by the SP to indicate it has * finished initialising itself after a cold boot */ case TSP_ENTRY_DONE: if (ns) SMC_RET1(handle, SMC_UNK); /* * Stash the SP entry points information. This is done * only once on the primary cpu */ assert(tsp_vectors == NULL); tsp_vectors = (tsp_vectors_t *) x1; if (tsp_vectors) { set_tsp_pstate(tsp_ctx->state, TSP_PSTATE_ON); /* * TSP has been successfully initialized. Register power * managemnt hooks with PSCI */ psci_register_spd_pm_hook(&tspd_pm); /* * Register an interrupt handler for S-EL1 interrupts * when generated during code executing in the * non-secure state. */ flags = 0; set_interrupt_rm_flag(flags, NON_SECURE); rc = register_interrupt_type_handler(INTR_TYPE_S_EL1, tspd_sel1_interrupt_handler, flags); if (rc) panic(); #if TSPD_ROUTE_IRQ_TO_EL3 /* * Register an interrupt handler for NS interrupts when * generated during code executing in secure state are * routed to EL3. */ flags = 0; set_interrupt_rm_flag(flags, SECURE); rc = register_interrupt_type_handler(INTR_TYPE_NS, tspd_ns_interrupt_handler, flags); if (rc) panic(); /* * Disable the interrupt NS locally since it will be enabled globally * within cm_init_my_context. */ disable_intr_rm_local(INTR_TYPE_NS, SECURE); #endif } #if TSP_INIT_ASYNC /* Save the Secure EL1 system register context */ assert(cm_get_context(SECURE) == &tsp_ctx->cpu_ctx); cm_el1_sysregs_context_save(SECURE); /* Program EL3 registers to enable entry into the next EL */ next_image_info = bl31_plat_get_next_image_ep_info(NON_SECURE); assert(next_image_info); assert(NON_SECURE == GET_SECURITY_STATE(next_image_info->h.attr)); cm_init_my_context(next_image_info); cm_prepare_el3_exit(NON_SECURE); SMC_RET0(cm_get_context(NON_SECURE)); #else /* * SP 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. */ tspd_synchronous_sp_exit(tsp_ctx, x1); #endif /* * These function IDs is used only by the SP to indicate it has * finished: * 1. turning itself on in response to an earlier psci * cpu_on request * 2. resuming itself after an earlier psci cpu_suspend * request. */ case TSP_ON_DONE: case TSP_RESUME_DONE: /* * These function IDs is used only by the SP to indicate it has * finished: * 1. suspending itself after an earlier psci cpu_suspend * request. * 2. turning itself off in response to an earlier psci * cpu_off request. */ case TSP_OFF_DONE: case TSP_SUSPEND_DONE: case TSP_SYSTEM_OFF_DONE: case TSP_SYSTEM_RESET_DONE: if (ns) SMC_RET1(handle, SMC_UNK); /* * SP 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, and pass x1 as * return value to the caller */ tspd_synchronous_sp_exit(tsp_ctx, x1); /* * Request from non-secure client to perform an * arithmetic operation or response from secure * payload to an earlier request. */ case TSP_FAST_FID(TSP_ADD): case TSP_FAST_FID(TSP_SUB): case TSP_FAST_FID(TSP_MUL): case TSP_FAST_FID(TSP_DIV): case TSP_STD_FID(TSP_ADD): case TSP_STD_FID(TSP_SUB): case TSP_STD_FID(TSP_MUL): case TSP_STD_FID(TSP_DIV): if (ns) { /* * 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 preempted */ if (get_std_smc_active_flag(tsp_ctx->state)) SMC_RET1(handle, SMC_UNK); cm_el1_sysregs_context_save(NON_SECURE); /* Save x1 and x2 for use by TSP_GET_ARGS call below */ store_tsp_args(tsp_ctx, x1, x2); /* * We are done stashing the non-secure context. Ask the * secure payload to do the work now. */ /* * Verify if there is a valid context to use, copy the * operation type and parameters to the secure context * and jump to the fast smc entry point in the secure * payload. Entry into S-EL1 will take place upon exit * from this function. */ assert(&tsp_ctx->cpu_ctx == cm_get_context(SECURE)); /* Set appropriate entry for SMC. * We expect the TSP to manage the PSTATE.I and PSTATE.F * flags as appropriate. */ if (GET_SMC_TYPE(smc_fid) == SMC_TYPE_FAST) { cm_set_elr_el3(SECURE, (uint64_t) &tsp_vectors->fast_smc_entry); } else { set_std_smc_active_flag(tsp_ctx->state); cm_set_elr_el3(SECURE, (uint64_t) &tsp_vectors->std_smc_entry); #if TSPD_ROUTE_IRQ_TO_EL3 /* * Enable the routing of NS interrupts to EL3 * during STD SMC processing on this core. */ enable_intr_rm_local(INTR_TYPE_NS, SECURE); #endif } cm_el1_sysregs_context_restore(SECURE); cm_set_next_eret_context(SECURE); SMC_RET3(&tsp_ctx->cpu_ctx, smc_fid, x1, x2); } else { /* * This is the result from the secure client of an * earlier request. The results are in x1-x3. Copy it * into the non-secure context, save the secure state * and return to the non-secure state. */ 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 */ cm_el1_sysregs_context_restore(NON_SECURE); cm_set_next_eret_context(NON_SECURE); if (GET_SMC_TYPE(smc_fid) == SMC_TYPE_STD) { clr_std_smc_active_flag(tsp_ctx->state); #if TSPD_ROUTE_IRQ_TO_EL3 /* * Disable the routing of NS interrupts to EL3 * after STD SMC processing is finished on this * core. */ disable_intr_rm_local(INTR_TYPE_NS, SECURE); #endif } SMC_RET3(ns_cpu_context, x1, x2, x3); } break; /* * Request from non secure world to resume the preempted * Standard SMC call. */ case TSP_FID_RESUME: /* RESUME should be invoked only by normal world */ if (!ns) { assert(0); break; } /* * This is a resume request from the non-secure client. * 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 preempted before resume */ if (!get_std_smc_active_flag(tsp_ctx->state)) SMC_RET1(handle, SMC_UNK); cm_el1_sysregs_context_save(NON_SECURE); /* * We are done stashing the non-secure context. Ask the * secure payload to do the work now. */ #if TSPD_ROUTE_IRQ_TO_EL3 /* * Enable the routing of NS interrupts to EL3 during resumption * of STD SMC call on this core. */ enable_intr_rm_local(INTR_TYPE_NS, SECURE); #endif /* We just need to return to the preempted point in * TSP and the execution will resume as normal. */ cm_el1_sysregs_context_restore(SECURE); cm_set_next_eret_context(SECURE); SMC_RET0(&tsp_ctx->cpu_ctx); /* * This is a request from the secure payload for more arguments * for an ongoing arithmetic operation requested by the * non-secure world. Simply return the arguments from the non- * secure client in the original call. */ case TSP_GET_ARGS: if (ns) SMC_RET1(handle, SMC_UNK); get_tsp_args(tsp_ctx, x1, x2); SMC_RET2(handle, x1, x2); case TOS_CALL_COUNT: /* * Return the number of service function IDs implemented to * provide service to non-secure */ SMC_RET1(handle, TSP_NUM_FID); case TOS_UID: /* Return TSP UID to the caller */ SMC_UUID_RET(handle, tsp_uuid); case TOS_CALL_VERSION: /* Return the version of current implementation */ SMC_RET2(handle, TSP_VERSION_MAJOR, TSP_VERSION_MINOR); default: break; } SMC_RET1(handle, SMC_UNK); }
/******************************************************************************* * 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 OPTEE needs to do * the work assigned to it. ******************************************************************************/ uint64_t opteed_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, void *cookie, void *handle, uint64_t flags) { cpu_context_t *ns_cpu_context; unsigned long mpidr = read_mpidr(); uint32_t linear_id = platform_get_core_pos(mpidr); optee_context_t *optee_ctx = &opteed_sp_context[linear_id]; uint64_t rc; /* * Determine which security state this SMC originated from */ if (is_caller_non_secure(flags)) { /* * 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)); cm_el1_sysregs_context_save(NON_SECURE); /* * We are done stashing the non-secure context. Ask the * OPTEE to do the work now. */ /* * Verify if there is a valid context to use, copy the * operation type and parameters to the secure context * and jump to the fast smc entry point in the secure * payload. Entry into S-EL1 will take place upon exit * from this function. */ assert(&optee_ctx->cpu_ctx == cm_get_context(SECURE)); /* Set appropriate entry for SMC. * We expect OPTEE to manage the PSTATE.I and PSTATE.F * flags as appropriate. */ if (GET_SMC_TYPE(smc_fid) == SMC_TYPE_FAST) { cm_set_elr_el3(SECURE, (uint64_t) &optee_vectors->fast_smc_entry); } else { cm_set_elr_el3(SECURE, (uint64_t) &optee_vectors->std_smc_entry); } cm_el1_sysregs_context_restore(SECURE); cm_set_next_eret_context(SECURE); /* Propagate hypervisor client ID */ write_ctx_reg(get_gpregs_ctx(&optee_ctx->cpu_ctx), CTX_GPREG_X7, read_ctx_reg(get_gpregs_ctx(handle), CTX_GPREG_X7)); SMC_RET4(&optee_ctx->cpu_ctx, smc_fid, x1, x2, x3); } /* * Returning from OPTEE */ switch (smc_fid) { /* * OPTEE has finished initialising itself after a cold boot */ case TEESMC_OPTEED_RETURN_ENTRY_DONE: /* * Stash the OPTEE entry points information. This is done * only once on the primary cpu */ assert(optee_vectors == NULL); optee_vectors = (optee_vectors_t *) x1; if (optee_vectors) { set_optee_pstate(optee_ctx->state, OPTEE_PSTATE_ON); /* * OPTEE has been successfully initialized. * Register power management hooks with PSCI */ psci_register_spd_pm_hook(&opteed_pm); /* * Register an interrupt handler for S-EL1 interrupts * when generated during code executing in the * non-secure state. */ flags = 0; set_interrupt_rm_flag(flags, NON_SECURE); rc = register_interrupt_type_handler(INTR_TYPE_S_EL1, opteed_sel1_interrupt_handler, flags); if (rc) panic(); } /* * OPTEE reports completion. The OPTEED must have initiated * the original request through a synchronous entry into * OPTEE. Jump back to the original C runtime context. */ opteed_synchronous_sp_exit(optee_ctx, x1); /* * These function IDs is used only by OP-TEE to indicate it has * finished: * 1. turning itself on in response to an earlier psci * cpu_on request * 2. resuming itself after an earlier psci cpu_suspend * request. */ case TEESMC_OPTEED_RETURN_ON_DONE: case TEESMC_OPTEED_RETURN_RESUME_DONE: /* * These function IDs is used only by the SP to indicate it has * finished: * 1. suspending itself after an earlier psci cpu_suspend * request. * 2. turning itself off in response to an earlier psci * cpu_off request. */ case TEESMC_OPTEED_RETURN_OFF_DONE: case TEESMC_OPTEED_RETURN_SUSPEND_DONE: case TEESMC_OPTEED_RETURN_SYSTEM_OFF_DONE: case TEESMC_OPTEED_RETURN_SYSTEM_RESET_DONE: /* * OPTEE reports completion. The OPTEED must have initiated the * original request through a synchronous entry into OPTEE. * Jump back to the original C runtime context, and pass x1 as * return value to the caller */ opteed_synchronous_sp_exit(optee_ctx, x1); /* * OPTEE is returning from a call or being preempted from a call, in * either case execution should resume in the normal world. */ case TEESMC_OPTEED_RETURN_CALL_DONE: /* * This is the result from the secure client of an * earlier request. The results are in x0-x3. Copy it * into the non-secure context, save the secure state * and return to the non-secure state. */ 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 */ cm_el1_sysregs_context_restore(NON_SECURE); cm_set_next_eret_context(NON_SECURE); SMC_RET4(ns_cpu_context, x1, x2, x3, x4); /* * OPTEE has finished handling a S-EL1 FIQ interrupt. Execution * should resume in the normal world. */ case TEESMC_OPTEED_RETURN_FIQ_DONE: /* 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 OPTEE 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((uint64_t) ns_cpu_context); default: panic(); } }
/******************************************************************************* * Initialize contexts of all Secure Partitions. ******************************************************************************/ int32_t spm_setup(void) { int rc; sp_context_t *ctx; void *sp_base, *rd_base; size_t sp_size, rd_size; uint64_t flags = 0U; /* Disable MMU at EL1 (initialized by BL2) */ disable_mmu_icache_el1(); /* * Non-blocking services can be interrupted by Non-secure interrupts. * Register an interrupt handler for NS interrupts when generated while * the CPU is in secure state. They are routed to EL3. */ set_interrupt_rm_flag(flags, SECURE); uint64_t rc_int = register_interrupt_type_handler(INTR_TYPE_NS, spm_ns_interrupt_handler, flags); if (rc_int) { ERROR("SPM: Failed to register NS interrupt handler with rc = %llx\n", rc_int); panic(); } /* Setup shim layer */ spm_exceptions_xlat_init_context(); /* * Setup all Secure Partitions. */ unsigned int i = 0U; while (1) { rc = plat_spm_sp_get_next_address(&sp_base, &sp_size, &rd_base, &rd_size); if (rc < 0) { /* Reached the end of the package. */ break; } if (i >= PLAT_SPM_MAX_PARTITIONS) { ERROR("Too many partitions in the package.\n"); panic(); } ctx = &sp_ctx_array[i]; assert(ctx->is_present == 0); /* Initialize context of the SP */ INFO("Secure Partition %u context setup start...\n", i); /* Save location of the image in physical memory */ ctx->image_base = (uintptr_t)sp_base; ctx->image_size = sp_size; rc = plat_spm_sp_rd_load(&ctx->rd, rd_base, rd_size); if (rc < 0) { ERROR("Error while loading RD blob.\n"); panic(); } spm_sp_setup(ctx); ctx->is_present = 1; INFO("Secure Partition %u setup done.\n", i); i++; } if (i == 0U) { ERROR("No present partitions in the package.\n"); panic(); } /* Register init function for deferred init. */ bl31_register_bl32_init(&spm_init); return 0; }