blob: 7d2fe7f35d717cd8a17e56ff1971f6d9de923e9b [file] [log] [blame]
/*
* Copyright (c) 2021-2022, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdlib.h>
#include <arch_helpers.h>
#include <cactus_test_cmds.h>
#include <debug.h>
#include <ffa_endpoints.h>
#include <ffa_svc.h>
#include <host_realm_helper.h>
#include <lib/events.h>
#include <lib/power_management.h>
#include <plat_topology.h>
#include <platform.h>
#include "rmi_spm_tests.h"
#include <smccc.h>
#include <test_helpers.h>
static test_result_t realm_multi_cpu_payload_del_undel(void);
#define ECHO_VAL1 U(0xa0a0a0a0)
#define ECHO_VAL2 U(0xb0b0b0b0)
#define ECHO_VAL3 U(0xc0c0c0c0)
#define MAX_REPEATED_TEST 3
/* Buffer to delegate and undelegate */
static char bufferdelegate[NUM_GRANULES * GRANULE_SIZE * PLATFORM_CORE_COUNT]
__aligned(GRANULE_SIZE);
static char bufferstate[NUM_GRANULES * PLATFORM_CORE_COUNT];
static int cpu_test_spm_rmi[PLATFORM_CORE_COUNT];
static event_t cpu_booted[PLATFORM_CORE_COUNT];
static unsigned int lead_mpid;
/*
* The following test conducts SPM(direct messaging) tests on a subset of selected CPUs while
* simultaneously performing another set of tests of the RMI(delegation)
* on the remaining CPU's to the full platform count. Once that test completes
* the same test is run again with a different assignment for what CPU does
* SPM versus RMI.
*/
/*
* Function that randomizes the CPU assignment of tests, SPM or RMI
*/
static void rand_cpu_spm_rmi(void)
{
int fentry;
int seln = 0;
for (int i = 0; i < PLATFORM_CORE_COUNT; i++) {
cpu_test_spm_rmi[i] = -1;
}
for (int i = 0; i < NUM_CPU_DED_SPM; i++) {
fentry = 0;
while (fentry == 0) {
#if (PLATFORM_CORE_COUNT > 1)
seln = (rand() % (PLATFORM_CORE_COUNT - 1)) + 1;
#endif
if (cpu_test_spm_rmi[seln] == -1) {
cpu_test_spm_rmi[seln] = 1;
fentry = 1;
}
}
}
for (int i = 0; i < PLATFORM_CORE_COUNT; i++) {
if (cpu_test_spm_rmi[i] == -1) {
cpu_test_spm_rmi[i] = 0;
}
}
}
/*
* Get function to determine what has been assigned to a given CPU
*/
static int spm_rmi_test(unsigned int mpidr)
{
return cpu_test_spm_rmi[platform_get_core_pos(mpidr)];
}
/*
* RMI function to randomize the initial state of granules allocated for the test.
* A certain subset will be delegated leaving the rest undelegated
*/
static test_result_t init_buffer_del_spm_rmi(void)
{
u_register_t retrmm;
for (int i = 0; i < (NUM_GRANULES * PLATFORM_CORE_COUNT) ; i++) {
if ((rand() % 2) == 0) {
retrmm = host_rmi_granule_delegate(
(u_register_t)&bufferdelegate[i * GRANULE_SIZE]);
bufferstate[i] = B_DELEGATED;
if (retrmm != 0UL) {
tftf_testcase_printf("Delegate operation returns 0x%lx\n",
retrmm);
return TEST_RESULT_FAIL;
}
} else {
bufferstate[i] = B_UNDELEGATED;
}
}
return TEST_RESULT_SUCCESS;
}
static test_result_t reset_buffer_del_spm_rmi(void)
{
u_register_t retrmm;
for (uint32_t i = 0U; i < (NUM_GRANULES * PLATFORM_CORE_COUNT) ; i++) {
if (bufferstate[i] == B_DELEGATED) {
retrmm = host_rmi_granule_undelegate(
(u_register_t)&bufferdelegate[i * GRANULE_SIZE]);
if (retrmm != 0UL) {
ERROR("Undelegate operation returns fail, %lx\n",
retrmm);
return TEST_RESULT_FAIL;
}
bufferstate[i] = B_UNDELEGATED;
}
}
return TEST_RESULT_SUCCESS;
}
/*
* Each CPU reaching this function will send a ready event to all other CPUs
* and wait for others CPUs before start executing its callback in parallel
* with all others CPUs
*/
static test_result_t wait_then_call(test_result_t (*callback)(void))
{
unsigned int mpidr, this_mpidr = read_mpidr_el1() & MPID_MASK;
unsigned int cpu_node, core_pos;
unsigned int this_core_pos = platform_get_core_pos(this_mpidr);
tftf_send_event_to_all(&cpu_booted[this_core_pos]);
for_each_cpu(cpu_node) {
mpidr = tftf_get_mpidr_from_node(cpu_node);
/* Ignore myself and the lead core */
if (mpidr == this_mpidr || mpidr == lead_mpid) {
continue;
}
core_pos = platform_get_core_pos(mpidr);
tftf_wait_for_event(&cpu_booted[core_pos]);
}
/* All cores reach this call in approximately "same" time */
return (*callback)();
}
/*
* Power on the given cpu and provide it with entrypoint to run and return result
*/
static test_result_t run_on_cpu(unsigned int mpidr, uintptr_t cpu_on_handler)
{
int32_t ret;
ret = tftf_cpu_on(mpidr, cpu_on_handler, 0U);
if (ret != PSCI_E_SUCCESS) {
ERROR("tftf_cpu_on mpidr 0x%x returns %d\n", mpidr, ret);
return TEST_RESULT_FAIL;
}
return TEST_RESULT_SUCCESS;
}
/*
* SPM functions for the direct messaging
*/
static const struct ffa_uuid expected_sp_uuids[] = {
{PRIMARY_UUID}, {SECONDARY_UUID}, {TERTIARY_UUID}
};
static test_result_t send_cactus_echo_cmd(ffa_id_t sender,
ffa_id_t dest,
uint64_t value)
{
struct ffa_value ret;
ret = cactus_echo_send_cmd(sender, dest, value);
/*
* Return responses may be FFA_MSG_SEND_DIRECT_RESP or FFA_INTERRUPT,
* but only expect the former. Expect SMC32 convention from SP.
*/
if (!is_ffa_direct_response(ret)) {
return TEST_RESULT_FAIL;
}
if (cactus_get_response(ret) != CACTUS_SUCCESS ||
cactus_echo_get_val(ret) != value) {
ERROR("Echo Failed!\n");
return TEST_RESULT_FAIL;
}
return TEST_RESULT_SUCCESS;
}
/**
* Handler that is passed during tftf_cpu_on to individual CPU cores.
* Runs a specific core and send a direct message request.
* Expects core_pos | SP_ID as a response.
*/
static test_result_t run_spm_direct_message(void)
{
unsigned int mpid = read_mpidr_el1() & MPID_MASK;
unsigned int core_pos = platform_get_core_pos(mpid);
test_result_t ret = TEST_RESULT_SUCCESS;
struct ffa_value ffa_ret;
/*
* Send a direct message request to SP1 (MP SP) from current physical
* CPU. Notice SP1 ECs are already woken as a result of the PSCI_CPU_ON
* invocation so they already reached the message loop.
* The SPMC uses the MP pinned context corresponding to the physical
* CPU emitting the request.
*/
ret = send_cactus_echo_cmd(HYP_ID, SP_ID(1), ECHO_VAL1);
if (ret != TEST_RESULT_SUCCESS) {
goto out;
}
/*
* Secure Partitions beyond the first SP only have their first
* EC (or vCPU0) woken up at boot time by the SPMC.
* Other ECs need one round of ffa_run to reach the message loop.
*/
ffa_ret = ffa_run(SP_ID(2), core_pos);
if (ffa_func_id(ffa_ret) != FFA_MSG_WAIT) {
ERROR("Failed to run SP%x on core %u\n", SP_ID(2),
core_pos);
ret = TEST_RESULT_FAIL;
goto out;
}
/*
* Send a direct message request to SP2 (MP SP) from current physical
* CPU. The SPMC uses the MP pinned context corresponding to the
* physical CPU emitting the request.
*/
ret = send_cactus_echo_cmd(HYP_ID, SP_ID(2), ECHO_VAL2);
if (ret != TEST_RESULT_SUCCESS) {
goto out;
}
/*
* Send a direct message request to SP3 (UP SP) from current physical CPU.
* The SPMC uses the single vCPU migrated to the new physical core.
* The single SP vCPU may receive requests from multiple physical CPUs.
* Thus it is possible one message is being processed on one core while
* another (or multiple) cores attempt sending a new direct message
* request. In such case the cores attempting the new request receive
* a busy response from the SPMC. To handle this case a retry loop is
* implemented permitting some fairness.
*/
uint32_t trial_loop = 5U;
while (trial_loop--) {
ffa_ret = cactus_echo_send_cmd(HYP_ID, SP_ID(3), ECHO_VAL3);
if ((ffa_func_id(ffa_ret) == FFA_ERROR) &&
(ffa_error_code(ffa_ret) == FFA_ERROR_BUSY)) {
VERBOSE("%s(%u) trial %u\n", __func__,
core_pos, trial_loop);
waitms(1);
continue;
}
if (is_ffa_direct_response(ffa_ret) == true) {
if (cactus_get_response(ffa_ret) != CACTUS_SUCCESS ||
cactus_echo_get_val(ffa_ret) != ECHO_VAL3) {
ERROR("Echo Failed!\n");
ret = TEST_RESULT_FAIL;
}
goto out;
}
}
ret = TEST_RESULT_FAIL;
out:
return ret;
}
/*
* Secondary core will perform sequentially a call to secure and realm worlds.
*/
static test_result_t non_secure_call_secure_and_realm(void)
{
test_result_t result = run_spm_direct_message();
if (result != TEST_RESULT_SUCCESS)
return result;
return realm_multi_cpu_payload_del_undel();
}
/*
* Non secure call secure synchronously in parallel
* with all other cores in this test
*/
static test_result_t non_secure_call_secure_multi_cpu_sync(void)
{
return wait_then_call(run_spm_direct_message);
}
/*
* Multi CPU testing of delegate and undelegate of granules
* The granules are first randomly initialized to either realm or non secure
* using the function init_buffer_del and then the function below
* assigns NUM_GRANULES to each CPU for delegation or undelgation
* depending upon the initial state
*/
static test_result_t realm_multi_cpu_payload_del_undel(void)
{
u_register_t retrmm;
unsigned int cpu_node;
cpu_node = platform_get_core_pos(read_mpidr_el1() & MPID_MASK);
for (int i = 0; i < NUM_GRANULES; i++) {
if (bufferstate[((cpu_node * NUM_GRANULES) + i)] == B_UNDELEGATED) {
retrmm = host_rmi_granule_delegate((u_register_t)
&bufferdelegate[((cpu_node *
NUM_GRANULES) + i) * GRANULE_SIZE]);
bufferstate[((cpu_node * NUM_GRANULES) + i)] = B_DELEGATED;
} else {
retrmm = host_rmi_granule_undelegate((u_register_t)
&bufferdelegate[((cpu_node *
NUM_GRANULES) + i) * GRANULE_SIZE]);
bufferstate[((cpu_node * NUM_GRANULES) + i)] = B_UNDELEGATED;
}
if (retrmm != 0UL) {
tftf_testcase_printf("Delegate operation returns fail, %lx\n",
retrmm);
return TEST_RESULT_FAIL;
}
}
return TEST_RESULT_SUCCESS;
}
/*
* Non secure call realm synchronously in parallel
* with all other cores in this test
*/
static test_result_t non_secure_call_realm_multi_cpu_sync(void)
{
return wait_then_call(realm_multi_cpu_payload_del_undel);
}
/*
* NS world communicate with S and RL worlds in series via SMC from a single core.
*/
test_result_t test_spm_rmm_serial_smc(void)
{
if (get_armv9_2_feat_rme_support() == 0U) {
return TEST_RESULT_SKIPPED;
}
lead_mpid = read_mpidr_el1() & MPID_MASK;
unsigned int mpidr;
/**********************************************************************
* Check SPMC has ffa_version and expected FFA endpoints are deployed.
**********************************************************************/
CHECK_SPMC_TESTING_SETUP(1, 0, expected_sp_uuids);
host_rmi_init_cmp_result();
/*
* Randomize the initial state of the RMI granules to realm or non-secure
*/
if (init_buffer_del_spm_rmi() == TEST_RESULT_FAIL) {
return TEST_RESULT_FAIL;
}
/*
* Preparation step:
* Find another CPU than the lead CPU and power it on.
*/
mpidr = tftf_find_any_cpu_other_than(lead_mpid);
assert(mpidr != INVALID_MPID);
/*
* Run SPM direct message call and RMI call in serial on a second core.
* wait for core power cycle between each call.
*/
for (size_t i = 0; i < MAX_REPEATED_TEST; i++) {
/* SPM FF-A direct message call */
if (TEST_RESULT_SUCCESS != run_on_cpu(mpidr,
(uintptr_t)non_secure_call_secure_and_realm)) {
return TEST_RESULT_FAIL;
}
/* Wait for the target CPU to finish the test execution */
wait_for_core_to_turn_off(mpidr);
}
if (reset_buffer_del_spm_rmi() != TEST_RESULT_SUCCESS) {
return TEST_RESULT_FAIL;
}
VERBOSE("Done exiting.\n");
/**********************************************************************
* Report register comparison result
**********************************************************************/
return host_cmp_result();
}
/*
* Test function to let NS world communicate with S and RL worlds in parallel
* via SMC using multiple cores
*/
test_result_t test_spm_rmm_parallel_smc(void)
{
if (get_armv9_2_feat_rme_support() == 0U) {
return TEST_RESULT_SKIPPED;
}
lead_mpid = read_mpidr_el1() & MPID_MASK;
unsigned int cpu_node, mpidr;
/**********************************************************************
* Check SPMC has ffa_version and expected FFA endpoints are deployed.
**********************************************************************/
CHECK_SPMC_TESTING_SETUP(1, 0, expected_sp_uuids);
host_rmi_init_cmp_result();
/*
* Randomize the initial state of the RMI granules to realm or non-secure
*/
if (init_buffer_del_spm_rmi() == TEST_RESULT_FAIL) {
return TEST_RESULT_FAIL;
}
/*
* Main test to run both SPM and RMM or TRP together in parallel
*/
for (int i = 0; i < MAX_REPEATED_TEST; i++) {
VERBOSE("Main test(%d) to run both SPM and RMM or\
TRP together in parallel...\n", i);
/* Reinitialize all CPUs event */
for (unsigned int i = 0U; i < PLATFORM_CORE_COUNT; i++) {
tftf_init_event(&cpu_booted[i]);
}
/*
* Randomise the assignment of the CPU's to either SPM or RMI
*/
rand_cpu_spm_rmi();
/*
* for each CPU we assign it randomly either spm or rmi test function
*/
for_each_cpu(cpu_node) {
mpidr = tftf_get_mpidr_from_node(cpu_node);
if (mpidr == lead_mpid) {
continue;
}
if (spm_rmi_test(mpidr) == 1) {
if (TEST_RESULT_SUCCESS != run_on_cpu(mpidr,
(uintptr_t)non_secure_call_secure_multi_cpu_sync)) {
return TEST_RESULT_FAIL;
}
} else {
if (TEST_RESULT_SUCCESS != run_on_cpu(mpidr,
(uintptr_t)non_secure_call_realm_multi_cpu_sync)) {
return TEST_RESULT_FAIL;
}
}
}
VERBOSE("Waiting for secondary CPUs to turn off ...\n");
wait_for_non_lead_cpus();
}
VERBOSE("Done exiting.\n");
if (reset_buffer_del_spm_rmi() != TEST_RESULT_SUCCESS) {
return TEST_RESULT_FAIL;
}
/**********************************************************************
* Report register comparison result
**********************************************************************/
return host_cmp_result();
}