| /* |
| * Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #include <assert.h> |
| #include <inttypes.h> |
| #include <stdint.h> |
| |
| #include "../../services/std_svc/spm/el3_spmc/spmc.h" |
| #include "../../services/std_svc/spm/el3_spmc/spmc_shared_mem.h" |
| #include <arch_features.h> |
| #include <arch_helpers.h> |
| #include <bl32/tsp/tsp.h> |
| #include <common/bl_common.h> |
| #include <common/build_message.h> |
| #include <common/debug.h> |
| #include "ffa_helpers.h" |
| #include <lib/psci/psci.h> |
| #include <lib/spinlock.h> |
| #include <lib/xlat_tables/xlat_tables_defs.h> |
| #include <lib/xlat_tables/xlat_tables_v2.h> |
| #include <plat/common/platform.h> |
| #include <platform_tsp.h> |
| #include <services/ffa_svc.h> |
| #include "tsp_private.h" |
| |
| #include <platform_def.h> |
| |
| static ffa_endpoint_id16_t tsp_id, spmc_id; |
| uint8_t mem_region_buffer[4096 * 2] __aligned(PAGE_SIZE); |
| |
| /* Partition Mailbox. */ |
| static uint8_t send_page[PAGE_SIZE] __aligned(PAGE_SIZE); |
| static uint8_t recv_page[PAGE_SIZE] __aligned(PAGE_SIZE); |
| |
| /* |
| * Declare a global mailbox for use within the TSP. |
| * This will be initialized appropriately when the buffers |
| * are mapped with the SPMC. |
| */ |
| static struct mailbox mailbox; |
| |
| /******************************************************************************* |
| * This enum is used to handle test cases driven from the FF-A Test Driver. |
| ******************************************************************************/ |
| /* Keep in Sync with FF-A Test Driver. */ |
| enum message_t { |
| /* Partition Only Messages. */ |
| FF_A_RELAY_MESSAGE = 0, |
| |
| /* Basic Functionality. */ |
| FF_A_ECHO_MESSAGE, |
| FF_A_RELAY_MESSAGE_EL3, |
| |
| /* Memory Sharing. */ |
| FF_A_MEMORY_SHARE, |
| FF_A_MEMORY_SHARE_FRAGMENTED, |
| FF_A_MEMORY_LEND, |
| FF_A_MEMORY_LEND_FRAGMENTED, |
| |
| FF_A_MEMORY_SHARE_MULTI_ENDPOINT, |
| FF_A_MEMORY_LEND_MULTI_ENDPOINT, |
| |
| LAST, |
| FF_A_RUN_ALL = 255, |
| FF_A_OP_MAX = 256 |
| }; |
| |
| #if SPMC_AT_EL3 |
| extern void tsp_cpu_on_entry(void); |
| #endif |
| |
| /******************************************************************************* |
| * Test Functions. |
| ******************************************************************************/ |
| |
| /******************************************************************************* |
| * Enable the TSP to forward the received message to another partition and ask |
| * it to echo the value back in order to validate direct messages functionality. |
| ******************************************************************************/ |
| static int ffa_test_relay(uint64_t arg0, |
| uint64_t arg1, |
| uint64_t arg2, |
| uint64_t arg3, |
| uint64_t arg4, |
| uint64_t arg5, |
| uint64_t arg6, |
| uint64_t arg7) |
| { |
| smc_args_t ffa_forward_result; |
| ffa_endpoint_id16_t receiver = arg5; |
| |
| ffa_forward_result = ffa_msg_send_direct_req(tsp_id, |
| receiver, |
| FF_A_ECHO_MESSAGE, arg4, |
| 0, 0, 0); |
| return ffa_forward_result._regs[3]; |
| } |
| |
| /******************************************************************************* |
| * This function handles memory management tests, currently share and lend. |
| * This test supports the use of FRAG_RX to use memory descriptors that do not |
| * fit in a single 4KB buffer. |
| ******************************************************************************/ |
| static int test_memory_send(ffa_endpoint_id16_t sender, uint64_t handle, |
| ffa_mtd_flag32_t flags, bool multi_endpoint) |
| { |
| struct ffa_mtd *m; |
| struct ffa_emad_v1_0 *receivers; |
| struct ffa_comp_mrd *composite; |
| int ret, status = 0; |
| unsigned int mem_attrs; |
| char *ptr; |
| ffa_endpoint_id16_t source = sender; |
| uint32_t total_length, recv_length = 0; |
| |
| /* |
| * In the case that we're testing multiple endpoints choose a partition |
| * ID that resides in the normal world so the SPMC won't detect it as |
| * invalid. |
| * TODO: Should get endpoint receiver id and flag as input from NWd. |
| */ |
| uint32_t receiver_count = multi_endpoint ? 2 : 1; |
| ffa_endpoint_id16_t test_receivers[2] = { tsp_id, 0x10 }; |
| |
| /* Ensure that the sender ID resides in the normal world. */ |
| if (ffa_is_secure_world_id(sender)) { |
| ERROR("Invalid sender ID 0x%x.\n", sender); |
| return FFA_ERROR_DENIED; |
| } |
| |
| if (!memory_retrieve(&mailbox, &m, handle, source, test_receivers, |
| receiver_count, flags, &recv_length, |
| &total_length)) { |
| return FFA_ERROR_INVALID_PARAMETER; |
| } |
| |
| receivers = (struct ffa_emad_v1_0 *) |
| ((uint8_t *) m + m->emad_offset); |
| while (total_length != recv_length) { |
| smc_args_t ffa_return; |
| uint32_t frag_length; |
| |
| ffa_return = ffa_mem_frag_rx(handle, recv_length); |
| |
| if (ffa_return._regs[0] == FFA_ERROR) { |
| WARN("TSP: failed to resume mem with handle %lx\n", |
| handle); |
| return ffa_return._regs[2]; |
| } |
| frag_length = ffa_return._regs[3]; |
| |
| /* Validate frag_length is less than total_length and mailbox size. */ |
| if (frag_length > total_length || |
| frag_length > (mailbox.rxtx_page_count * PAGE_SIZE)) { |
| ERROR("Invalid parameters!\n"); |
| return FFA_ERROR_INVALID_PARAMETER; |
| } |
| |
| /* Validate frag_length is less than remaining mem_region_buffer size. */ |
| if (frag_length + recv_length >= REGION_BUF_SIZE) { |
| ERROR("Out of memory!\n"); |
| return FFA_ERROR_INVALID_PARAMETER; |
| } |
| |
| memcpy(&mem_region_buffer[recv_length], mailbox.rx_buffer, |
| frag_length); |
| |
| if (ffa_rx_release()) { |
| ERROR("Failed to release buffer!\n"); |
| return FFA_ERROR_DENIED; |
| } |
| |
| recv_length += frag_length; |
| |
| assert(recv_length <= total_length); |
| } |
| |
| composite = ffa_memory_region_get_composite(m, 0); |
| if (composite == NULL) { |
| WARN("Failed to get composite descriptor!\n"); |
| return FFA_ERROR_INVALID_PARAMETER; |
| } |
| |
| VERBOSE("Address: %p; page_count: %x %lx\n", |
| (void *)composite->address_range_array[0].address, |
| composite->address_range_array[0].page_count, PAGE_SIZE); |
| |
| /* This test is only concerned with RW permissions. */ |
| if (ffa_get_data_access_attr( |
| receivers[0].mapd.memory_access_permissions) != FFA_MEM_PERM_RW) { |
| ERROR("Data permission in retrieve response %x does not match share/lend %x!\n", |
| ffa_get_data_access_attr(receivers[0].mapd.memory_access_permissions), |
| FFA_MEM_PERM_RW); |
| return FFA_ERROR_INVALID_PARAMETER; |
| } |
| |
| mem_attrs = MT_RW_DATA | MT_EXECUTE_NEVER; |
| |
| /* Only expecting to be sent memory from NWd so map accordingly. */ |
| mem_attrs |= MT_NS; |
| |
| for (int32_t i = 0; i < (int32_t)composite->address_range_count; i++) { |
| size_t size = composite->address_range_array[i].page_count * PAGE_SIZE; |
| |
| ptr = (char *) composite->address_range_array[i].address; |
| ret = mmap_add_dynamic_region( |
| (uint64_t)ptr, |
| (uint64_t)ptr, |
| size, mem_attrs); |
| |
| if (ret != 0) { |
| ERROR("Failed [%d] mmap_add_dynamic_region %u (%lx) (%lx) (%x)!\n", |
| i, ret, |
| (uint64_t)composite->address_range_array[i].address, |
| size, mem_attrs); |
| |
| /* Remove mappings previously created in this transaction. */ |
| for (i--; i >= 0; i--) { |
| ret = mmap_remove_dynamic_region( |
| (uint64_t)composite->address_range_array[i].address, |
| composite->address_range_array[i].page_count * PAGE_SIZE); |
| |
| if (ret != 0) { |
| ERROR("Failed [%d] mmap_remove_dynamic_region!\n", i); |
| panic(); |
| } |
| } |
| |
| return FFA_ERROR_NO_MEMORY; |
| } |
| |
| /* Increment memory region for validation purposes. */ |
| ++(*ptr); |
| |
| /* |
| * Read initial magic number from memory region for |
| * validation purposes. |
| */ |
| if (!i) { |
| status = *ptr; |
| } |
| } |
| |
| for (uint32_t i = 0U; i < composite->address_range_count; i++) { |
| ret = mmap_remove_dynamic_region( |
| (uint64_t)composite->address_range_array[i].address, |
| composite->address_range_array[i].page_count * PAGE_SIZE); |
| |
| if (ret != 0) { |
| ERROR("Failed [%d] mmap_remove_dynamic_region!\n", i); |
| return FFA_ERROR_NO_MEMORY; |
| } |
| } |
| |
| if (!memory_relinquish((struct ffa_mem_relinquish_descriptor *)mailbox.tx_buffer, |
| m->handle, tsp_id)) { |
| ERROR("Failed to relinquish memory region!\n"); |
| return FFA_ERROR_INVALID_PARAMETER; |
| } |
| return status; |
| } |
| |
| static smc_args_t *send_ffa_pm_success(void) |
| { |
| return set_smc_args(FFA_MSG_SEND_DIRECT_RESP_SMC32, |
| ((tsp_id & FFA_DIRECT_MSG_ENDPOINT_ID_MASK) |
| << FFA_DIRECT_MSG_SOURCE_SHIFT) | spmc_id, |
| FFA_FWK_MSG_BIT | |
| (FFA_PM_MSG_PM_RESP & FFA_FWK_MSG_MASK), |
| 0, 0, 0, 0, 0); |
| } |
| |
| /******************************************************************************* |
| * This function performs any remaining book keeping in the test secure payload |
| * before this cpu is turned off in response to a psci cpu_off request. |
| ******************************************************************************/ |
| smc_args_t *tsp_cpu_off_main(uint64_t arg0, |
| uint64_t arg1, |
| uint64_t arg2, |
| uint64_t arg3, |
| uint64_t arg4, |
| uint64_t arg5, |
| uint64_t arg6, |
| uint64_t arg7) |
| { |
| uint32_t linear_id = plat_my_core_pos(); |
| |
| /* |
| * This cpu is being turned off, so disable the timer to prevent the |
| * secure timer interrupt from interfering with power down. A pending |
| * interrupt will be lost but we do not care as we are turning off. |
| */ |
| tsp_generic_timer_stop(); |
| |
| /* Update this cpu's statistics. */ |
| tsp_stats[linear_id].smc_count++; |
| tsp_stats[linear_id].eret_count++; |
| tsp_stats[linear_id].cpu_off_count++; |
| |
| VERBOSE("TSP: cpu 0x%lx off request\n", read_mpidr()); |
| VERBOSE("TSP: cpu 0x%lx: %d smcs, %d erets %d cpu off requests\n", |
| read_mpidr(), |
| tsp_stats[linear_id].smc_count, |
| tsp_stats[linear_id].eret_count, |
| tsp_stats[linear_id].cpu_off_count); |
| |
| return send_ffa_pm_success(); |
| } |
| |
| /******************************************************************************* |
| * This function performs any book keeping in the test secure payload before |
| * this cpu's architectural state is saved in response to an earlier psci |
| * cpu_suspend request. |
| ******************************************************************************/ |
| smc_args_t *tsp_cpu_suspend_main(uint64_t arg0, |
| uint64_t arg1, |
| uint64_t arg2, |
| uint64_t arg3, |
| uint64_t arg4, |
| uint64_t arg5, |
| uint64_t arg6, |
| uint64_t arg7) |
| { |
| uint32_t linear_id = plat_my_core_pos(); |
| |
| /* |
| * Save the time context and disable it to prevent the secure timer |
| * interrupt from interfering with wakeup from the suspend state. |
| */ |
| tsp_generic_timer_save(); |
| tsp_generic_timer_stop(); |
| |
| /* Update this cpu's statistics. */ |
| tsp_stats[linear_id].smc_count++; |
| tsp_stats[linear_id].eret_count++; |
| tsp_stats[linear_id].cpu_suspend_count++; |
| |
| VERBOSE("TSP: cpu 0x%lx: %d smcs, %d erets %d cpu suspend requests\n", |
| read_mpidr(), |
| tsp_stats[linear_id].smc_count, |
| tsp_stats[linear_id].eret_count, |
| tsp_stats[linear_id].cpu_suspend_count); |
| |
| return send_ffa_pm_success(); |
| } |
| |
| /******************************************************************************* |
| * This function performs any bookkeeping in the test secure payload after this |
| * cpu's architectural state has been restored after wakeup from an earlier psci |
| * cpu_suspend request. |
| ******************************************************************************/ |
| smc_args_t *tsp_cpu_resume_main(uint64_t max_off_pwrlvl, |
| uint64_t arg1, |
| uint64_t arg2, |
| uint64_t arg3, |
| uint64_t arg4, |
| uint64_t arg5, |
| uint64_t arg6, |
| uint64_t arg7) |
| { |
| uint32_t linear_id = plat_my_core_pos(); |
| |
| /* Restore the generic timer context. */ |
| tsp_generic_timer_restore(); |
| |
| /* Update this cpu's statistics. */ |
| tsp_stats[linear_id].smc_count++; |
| tsp_stats[linear_id].eret_count++; |
| tsp_stats[linear_id].cpu_resume_count++; |
| |
| VERBOSE("TSP: cpu 0x%lx resumed. maximum off power level %" PRId64 "\n", |
| read_mpidr(), max_off_pwrlvl); |
| VERBOSE("TSP: cpu 0x%lx: %d smcs, %d erets %d cpu resume requests\n", |
| read_mpidr(), |
| tsp_stats[linear_id].smc_count, |
| tsp_stats[linear_id].eret_count, |
| tsp_stats[linear_id].cpu_resume_count); |
| |
| return send_ffa_pm_success(); |
| } |
| |
| /******************************************************************************* |
| * This function handles framework messages. Currently only PM. |
| ******************************************************************************/ |
| static smc_args_t *handle_framework_message(uint64_t arg0, |
| uint64_t arg1, |
| uint64_t arg2, |
| uint64_t arg3, |
| uint64_t arg4, |
| uint64_t arg5, |
| uint64_t arg6, |
| uint64_t arg7) |
| { |
| /* Check if it is a power management message from the SPMC. */ |
| if (ffa_endpoint_source(arg1) != spmc_id) { |
| goto err; |
| } |
| |
| /* Check if it is a PM request message. */ |
| if ((arg2 & FFA_FWK_MSG_MASK) == FFA_FWK_MSG_PSCI) { |
| /* Check if it is a PSCI CPU_OFF request. */ |
| if (arg3 == PSCI_CPU_OFF) { |
| return tsp_cpu_off_main(arg0, arg1, arg2, arg3, |
| arg4, arg5, arg6, arg7); |
| } else if (arg3 == PSCI_CPU_SUSPEND_AARCH64) { |
| return tsp_cpu_suspend_main(arg0, arg1, arg2, arg3, |
| arg4, arg5, arg6, arg7); |
| } |
| } else if ((arg2 & FFA_FWK_MSG_MASK) == FFA_PM_MSG_WB_REQ) { |
| /* Check it is a PSCI Warm Boot request. */ |
| if (arg3 == FFA_WB_TYPE_NOTS2RAM) { |
| return tsp_cpu_resume_main(arg0, arg1, arg2, arg3, |
| arg4, arg5, arg6, arg7); |
| } |
| } |
| |
| err: |
| ERROR("%s: Unknown framework message!\n", __func__); |
| panic(); |
| } |
| |
| /******************************************************************************* |
| * Handles partition messages. Exercised from the FF-A Test Driver. |
| ******************************************************************************/ |
| static smc_args_t *handle_partition_message(uint64_t arg0, |
| uint64_t arg1, |
| uint64_t arg2, |
| uint64_t arg3, |
| uint64_t arg4, |
| uint64_t arg5, |
| uint64_t arg6, |
| uint64_t arg7) |
| { |
| uint16_t sender = ffa_endpoint_source(arg1); |
| uint16_t receiver = ffa_endpoint_destination(arg1); |
| int status = -1; |
| const bool multi_endpoint = true; |
| |
| switch (arg3) { |
| case FF_A_MEMORY_SHARE: |
| INFO("TSP Tests: Memory Share Request--\n"); |
| status = test_memory_send(sender, arg4, FFA_FLAG_SHARE_MEMORY, !multi_endpoint); |
| break; |
| |
| case FF_A_MEMORY_LEND: |
| INFO("TSP Tests: Memory Lend Request--\n"); |
| status = test_memory_send(sender, arg4, FFA_FLAG_LEND_MEMORY, !multi_endpoint); |
| break; |
| |
| case FF_A_MEMORY_SHARE_MULTI_ENDPOINT: |
| INFO("TSP Tests: Multi Endpoint Memory Share Request--\n"); |
| status = test_memory_send(sender, arg4, FFA_FLAG_SHARE_MEMORY, multi_endpoint); |
| break; |
| |
| case FF_A_MEMORY_LEND_MULTI_ENDPOINT: |
| INFO("TSP Tests: Multi Endpoint Memory Lend Request--\n"); |
| status = test_memory_send(sender, arg4, FFA_FLAG_LEND_MEMORY, multi_endpoint); |
| break; |
| case FF_A_RELAY_MESSAGE: |
| INFO("TSP Tests: Relaying message--\n"); |
| status = ffa_test_relay(arg0, arg1, arg2, arg3, arg4, |
| arg5, arg6, arg7); |
| break; |
| |
| case FF_A_ECHO_MESSAGE: |
| INFO("TSP Tests: echo message--\n"); |
| status = arg4; |
| break; |
| |
| default: |
| INFO("TSP Tests: Unknown request ID %d--\n", (int) arg3); |
| } |
| |
| /* Swap the sender and receiver in the response. */ |
| return ffa_msg_send_direct_resp(receiver, sender, status, 0, 0, 0, 0); |
| } |
| |
| /******************************************************************************* |
| * This function implements the event loop for handling FF-A ABI invocations. |
| ******************************************************************************/ |
| static smc_args_t *tsp_event_loop(uint64_t smc_fid, |
| uint64_t arg1, |
| uint64_t arg2, |
| uint64_t arg3, |
| uint64_t arg4, |
| uint64_t arg5, |
| uint64_t arg6, |
| uint64_t arg7) |
| { |
| /* Panic if the SPMC did not forward an FF-A call. */ |
| if (!is_ffa_fid(smc_fid)) { |
| ERROR("%s: Unknown SMC FID (0x%lx)\n", __func__, smc_fid); |
| panic(); |
| } |
| |
| switch (smc_fid) { |
| case FFA_INTERRUPT: |
| /* |
| * IRQs were enabled upon re-entry into the TSP. The interrupt |
| * must have been handled by now. Return to the SPMC indicating |
| * the same. |
| */ |
| return set_smc_args(FFA_MSG_WAIT, 0, 0, 0, 0, 0, 0, 0); |
| |
| case FFA_MSG_SEND_DIRECT_REQ_SMC64: |
| case FFA_MSG_SEND_DIRECT_REQ_SMC32: |
| /* Check if a framework message, handle accordingly. */ |
| if ((arg2 & FFA_FWK_MSG_BIT)) { |
| return handle_framework_message(smc_fid, arg1, arg2, arg3, |
| arg4, arg5, arg6, arg7); |
| } |
| return handle_partition_message(smc_fid, arg1, arg2, arg3, |
| arg4, arg5, arg6, arg7); |
| } |
| |
| ERROR("%s: Unsupported FF-A FID (0x%lx)\n", __func__, smc_fid); |
| panic(); |
| } |
| |
| static smc_args_t *tsp_loop(smc_args_t *args) |
| { |
| smc_args_t ret; |
| |
| do { |
| /* -------------------------------------------- |
| * Mask FIQ interrupts to avoid preemption |
| * in case EL3 SPMC delegates an IRQ next or a |
| * managed exit. Lastly, unmask IRQs so that |
| * they can be handled immediately upon re-entry. |
| * --------------------------------------------- |
| */ |
| write_daifset(DAIF_FIQ_BIT); |
| write_daifclr(DAIF_IRQ_BIT); |
| ret = smc_helper(args->_regs[0], args->_regs[1], args->_regs[2], |
| args->_regs[3], args->_regs[4], args->_regs[5], |
| args->_regs[6], args->_regs[7]); |
| args = tsp_event_loop(ret._regs[0], ret._regs[1], ret._regs[2], |
| ret._regs[3], ret._regs[4], ret._regs[5], |
| ret._regs[6], ret._regs[7]); |
| } while (1); |
| |
| /* Not Reached. */ |
| return NULL; |
| } |
| |
| /******************************************************************************* |
| * TSP main entry point where it gets the opportunity to initialize its secure |
| * state/applications. Once the state is initialized, it must return to the |
| * SPD with a pointer to the 'tsp_vector_table' jump table. |
| ******************************************************************************/ |
| uint64_t tsp_main(void) |
| { |
| smc_args_t smc_args = {0}; |
| |
| NOTICE("TSP: %s\n", build_version_string); |
| NOTICE("TSP: %s\n", build_message); |
| INFO("TSP: Total memory base : 0x%lx\n", (unsigned long) BL32_BASE); |
| INFO("TSP: Total memory size : 0x%lx bytes\n", BL32_TOTAL_SIZE); |
| uint32_t linear_id = plat_my_core_pos(); |
| |
| /* Initialize the platform. */ |
| tsp_platform_setup(); |
| |
| /* Initialize secure/applications state here. */ |
| tsp_generic_timer_start(); |
| |
| /* Register secondary entrypoint with the SPMC. */ |
| smc_args = smc_helper(FFA_SECONDARY_EP_REGISTER_SMC64, |
| (uint64_t) tsp_cpu_on_entry, |
| 0, 0, 0, 0, 0, 0); |
| if (smc_args._regs[SMC_ARG0] != FFA_SUCCESS_SMC32) { |
| ERROR("TSP could not register secondary ep (0x%lx)\n", |
| smc_args._regs[2]); |
| panic(); |
| } |
| /* Get TSP's endpoint id. */ |
| smc_args = smc_helper(FFA_ID_GET, 0, 0, 0, 0, 0, 0, 0); |
| if (smc_args._regs[SMC_ARG0] != FFA_SUCCESS_SMC32) { |
| ERROR("TSP could not get own ID (0x%lx) on core%d\n", |
| smc_args._regs[2], linear_id); |
| panic(); |
| } |
| |
| tsp_id = smc_args._regs[2]; |
| INFO("TSP FF-A endpoint id = 0x%x\n", tsp_id); |
| |
| /* Get the SPMC ID. */ |
| smc_args = smc_helper(FFA_SPM_ID_GET, 0, 0, 0, 0, 0, 0, 0); |
| if (smc_args._regs[SMC_ARG0] != FFA_SUCCESS_SMC32) { |
| ERROR("TSP could not get SPMC ID (0x%lx) on core%d\n", |
| smc_args._regs[2], linear_id); |
| panic(); |
| } |
| |
| spmc_id = smc_args._regs[2]; |
| |
| /* Call RXTX_MAP to map a 4k RX and TX buffer. */ |
| if (ffa_rxtx_map((uintptr_t) send_page, |
| (uintptr_t) recv_page, 1)) { |
| ERROR("TSP could not map it's RX/TX Buffers\n"); |
| panic(); |
| } |
| |
| mailbox.tx_buffer = send_page; |
| mailbox.rx_buffer = recv_page; |
| mailbox.rxtx_page_count = 1; |
| |
| /* Update this cpu's statistics. */ |
| tsp_stats[linear_id].smc_count++; |
| tsp_stats[linear_id].eret_count++; |
| tsp_stats[linear_id].cpu_on_count++; |
| |
| VERBOSE("TSP: cpu 0x%lx: %d smcs, %d erets %d cpu on requests\n", |
| read_mpidr(), |
| tsp_stats[linear_id].smc_count, |
| tsp_stats[linear_id].eret_count, |
| tsp_stats[linear_id].cpu_on_count); |
| |
| /* Tell SPMD that we are done initialising. */ |
| tsp_loop(set_smc_args(FFA_MSG_WAIT, 0, 0, 0, 0, 0, 0, 0)); |
| |
| /* Not reached. */ |
| return 0; |
| } |
| |
| /******************************************************************************* |
| * This function performs any remaining book keeping in the test secure payload |
| * after this cpu's architectural state has been setup in response to an earlier |
| * psci cpu_on request. |
| ******************************************************************************/ |
| smc_args_t *tsp_cpu_on_main(void) |
| { |
| uint32_t linear_id = plat_my_core_pos(); |
| |
| /* Initialize secure/applications state here. */ |
| tsp_generic_timer_start(); |
| |
| /* Update this cpu's statistics. */ |
| tsp_stats[linear_id].smc_count++; |
| tsp_stats[linear_id].eret_count++; |
| tsp_stats[linear_id].cpu_on_count++; |
| VERBOSE("TSP: cpu 0x%lx turned on\n", read_mpidr()); |
| VERBOSE("TSP: cpu 0x%lx: %d smcs, %d erets %d cpu on requests\n", |
| read_mpidr(), |
| tsp_stats[linear_id].smc_count, |
| tsp_stats[linear_id].eret_count, |
| tsp_stats[linear_id].cpu_on_count); |
| /* --------------------------------------------- |
| * Jump to the main event loop to return to EL3 |
| * and be ready for the next request on this cpu. |
| * --------------------------------------------- |
| */ |
| return tsp_loop(set_smc_args(FFA_MSG_WAIT, 0, 0, 0, 0, 0, 0, 0)); |
| } |