blob: 3af35ad024280fad664358a10986574a43887aee [file] [log] [blame]
/*
* Copyright (c) 2018-2025, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch_helpers.h>
#include <debug.h>
#include <drivers/arm/arm_gic.h>
#include <drivers/arm/gic_v2v3_common.h>
#include <drivers/arm/gic_v2.h>
#include <events.h>
#include <irq.h>
#include <plat_topology.h>
#include <platform.h>
#include <power_management.h>
#include <smccc.h>
#include <string.h>
#include <test_helpers.h>
#include <tftf_lib.h>
#define TEST_ITERATIONS_COUNT 1000
#define SUSPEND_TIME_1_SEC 1000
#define TEST_VALUE_1 4
#define TEST_VALUE_2 6
static event_t cpu_has_entered_test[PLATFORM_CORE_COUNT];
static event_t cpu_has_finished_test[PLATFORM_CORE_COUNT];
static volatile int requested_irq_received[PLATFORM_CORE_COUNT];
static volatile int wakeup_irq_received[PLATFORM_CORE_COUNT];
static volatile int individual_test_failed[PLATFORM_CORE_COUNT];
static volatile int pwr_level_being_tested;
static volatile int test_finished_flag;
/* Dummy timer handler that sets a flag to check it has been called. */
static int suspend_wakeup_handler(void *data)
{
u_register_t core_mpid = read_mpidr_el1() & MPID_MASK;
unsigned int core_pos = platform_get_core_pos(core_mpid);
assert(wakeup_irq_received[core_pos] == 0);
wakeup_irq_received[core_pos] = 1;
return 0;
}
/* Dummy handler that sets a flag so as to check it has been called. */
static int test_handler(void *data)
{
u_register_t core_mpid = read_mpidr_el1() & MPID_MASK;
unsigned int core_pos = platform_get_core_pos(core_mpid);
assert(requested_irq_received[core_pos] == 0);
requested_irq_received[core_pos] = 1;
return 0;
}
/* Register a dummy handler for SGI #0 and enable it. Returns 0 if success. */
static int register_and_enable_test_sgi_handler(unsigned int core_pos)
{
/* SGIs #0 - #6 are freely available. */
int ret = tftf_irq_register_handler_sgi(IRQ_NS_SGI_0, test_handler);
if (ret != 0) {
tftf_testcase_printf(
"Failed to register SGI handler @ CPU %d (rc = %d)\n",
core_pos, ret);
return -1;
}
tftf_irq_enable_sgi(IRQ_NS_SGI_0, GIC_HIGHEST_NS_PRIORITY);
return 0;
}
/* Disable and unregister the dummy handler for SGI #0. */
static void unregister_and_disable_test_sgi_handler(void)
{
tftf_irq_disable_sgi(IRQ_NS_SGI_0);
tftf_irq_unregister_handler_sgi(IRQ_NS_SGI_0);
}
/*
* Generate a pre-empted STD SMC on the CPU who called this function. Steps:
* 1. IRQs are disabled.
* 2. An SGI is sent to itself. It cannot be handled because IRQs are disabled.
* 3. Invoke an STD SMC on the TSP, which is preempted by the pending SGI.
* 4. IRQs are enabled, the SGI is handled.
* 5. This function is exited with a preempted STD SMC waiting to be resumed.
*/
static int preempt_std_smc_on_this_cpu(void)
{
smc_args std_smc_args;
smc_ret_values smc_ret;
int result = TEST_RESULT_SUCCESS;
u_register_t core_mpid = read_mpidr_el1() & MPID_MASK;
unsigned int core_pos = platform_get_core_pos(core_mpid);
if (register_and_enable_test_sgi_handler(core_pos) != 0) {
return TEST_RESULT_FAIL;
}
/* Set PSTATE.I to 0. */
disable_irq();
/*
* Send SGI to itself. It can't be handled because the
* interrupts are disabled.
*/
requested_irq_received[core_pos] = 0;
tftf_send_sgi(IRQ_NS_SGI_0, core_pos);
/*
* Invoke an STD SMC. Should be pre-empted because of the SGI
* that is waiting.
*/
std_smc_args.fid = TSP_STD_FID(TSP_ADD);
std_smc_args.arg1 = TEST_VALUE_1;
std_smc_args.arg2 = TEST_VALUE_2;
smc_ret = tftf_smc(&std_smc_args);
if (smc_ret.ret0 != TSP_SMC_PREEMPTED) {
tftf_testcase_printf("SMC @ CPU %d returned 0x%llX.\n", core_pos,
(unsigned long long)smc_ret.ret0);
result = TEST_RESULT_FAIL;
}
/* Set PSTATE.I to 1. Let the SGI be handled. */
enable_irq();
/* Cleanup. Disable and unregister SGI handler. */
unregister_and_disable_test_sgi_handler();
/*
* Check that the SGI has been handled, but don't fail if it hasn't
* because there is no guarantee that it will have actually happened at
* this point.
*/
if (requested_irq_received[core_pos] == 0) {
VERBOSE("SGI not handled @ CPU %d\n", core_pos);
}
return result;
}
/* Resume a pre-empted STD SMC on the CPU who called this function. */
static int resume_std_smc_on_this_cpu(void)
{
smc_args std_smc_args;
smc_ret_values smc_ret;
u_register_t core_mpid = read_mpidr_el1() & MPID_MASK;
unsigned int core_pos = platform_get_core_pos(core_mpid);
/* Resume the STD SMC. Verify result. */
std_smc_args.fid = TSP_FID_RESUME;
smc_ret = tftf_smc(&std_smc_args);
if ((smc_ret.ret0 != 0) || (smc_ret.ret1 != TEST_VALUE_1 * 2)
|| (smc_ret.ret2 != TEST_VALUE_2 * 2)) {
tftf_testcase_printf(
"SMC @ CPU %d returned 0x%llX 0x%llX 0x%llX instead of 0x0 0x%X 0x%X\n",
core_pos, (unsigned long long)smc_ret.ret0,
(unsigned long long)smc_ret.ret1,
(unsigned long long)smc_ret.ret2,
TEST_VALUE_1 * 2, TEST_VALUE_2 * 2);
return TEST_RESULT_FAIL;
}
return TEST_RESULT_SUCCESS;
}
/*
* Try to resume a pre-empted STD SMC on the CPU who called this function,
* but check for SMC_UNKNOWN as a result.
*/
static int resume_fail_std_smc_on_this_cpu(void)
{
smc_args std_smc_args;
smc_ret_values smc_ret;
u_register_t core_mpid = read_mpidr_el1() & MPID_MASK;
unsigned int core_pos = platform_get_core_pos(core_mpid);
/* Resume the STD SMC. Verify result. */
std_smc_args.fid = TSP_FID_RESUME;
smc_ret = tftf_smc(&std_smc_args);
if (smc_ret.ret0 != SMC_UNKNOWN) {
tftf_testcase_printf(
"SMC @ CPU %d returned 0x%llX 0x%llX 0x%llX instead of SMC_UNKNOWN\n",
core_pos, (unsigned long long)smc_ret.ret0,
(unsigned long long)smc_ret.ret1,
(unsigned long long)smc_ret.ret2);
return TEST_RESULT_FAIL;
}
return TEST_RESULT_SUCCESS;
}
/*******************************************************************************
* Test pre-emption during STD SMCs.
******************************************************************************/
/* Test routine for test_irq_preempted_std_smc. */
static test_result_t test_irq_preempted_std_smc_fn(void)
{
u_register_t cpu_mpid = read_mpidr_el1() & MPID_MASK;
unsigned int core_pos = platform_get_core_pos(cpu_mpid);
tftf_send_event(&cpu_has_entered_test[core_pos]);
for (unsigned int i = 0; i < TEST_ITERATIONS_COUNT; i++) {
if (preempt_std_smc_on_this_cpu() != TEST_RESULT_SUCCESS)
return TEST_RESULT_FAIL;
if (resume_std_smc_on_this_cpu() != TEST_RESULT_SUCCESS)
return TEST_RESULT_FAIL;
}
return TEST_RESULT_SUCCESS;
}
/*
* @Test_Aim@ Multicore preemption test. Tests IRQ preemption during STD SMC
* from multiple cores. Uses an SGI to trigger the preemption. TSP should be
* present.
*
* Steps: 1. Invoke Standard SMC on the TSP and try to preempt it via IRQ.
* 2. Resume the preempted SMC and verify the result.
*
* Returns SUCCESS if above 2 steps are performed correctly in every CPU else
* failure.
*/
test_result_t test_irq_preempted_std_smc(void)
{
u_register_t cpu_mpid;
unsigned int cpu_node, core_pos;
int psci_ret;
u_register_t lead_mpid = read_mpidr_el1() & MPID_MASK;
SKIP_TEST_IF_TSP_NOT_PRESENT();
for (int i = 0; i < PLATFORM_CORE_COUNT; i++) {
tftf_init_event(&cpu_has_entered_test[i]);
}
/* Power on all CPUs */
for_each_cpu(cpu_node) {
cpu_mpid = tftf_get_mpidr_from_node(cpu_node);
/* Skip lead CPU as it is already powered on */
if (cpu_mpid == lead_mpid) {
continue;
}
core_pos = platform_get_core_pos(cpu_mpid);
psci_ret = tftf_cpu_on(cpu_mpid,
(uintptr_t)test_irq_preempted_std_smc_fn, 0);
if (psci_ret != PSCI_E_SUCCESS) {
tftf_testcase_printf(
"Failed to power on CPU %d (rc = %d)\n",
core_pos, psci_ret);
return TEST_RESULT_FAIL;
}
}
/* Wait until all CPUs have started the test. */
for_each_cpu(cpu_node) {
cpu_mpid = tftf_get_mpidr_from_node(cpu_node);
/* Skip lead CPU */
if (cpu_mpid == lead_mpid) {
continue;
}
core_pos = platform_get_core_pos(cpu_mpid);
tftf_wait_for_event(&cpu_has_entered_test[core_pos]);
}
/* Enter the test on lead CPU and return the result. */
return test_irq_preempted_std_smc_fn();
}
/*
* Test routine for non-lead CPUs for test_resume_preempted_std_smc_other_cpus.
*/
static test_result_t test_resume_preempted_std_smc_other_cpus_non_lead_fn(void)
{
test_result_t result = TEST_RESULT_SUCCESS;
u_register_t mpid = read_mpidr_el1() & MPID_MASK;
unsigned int core_pos = platform_get_core_pos(mpid);
/*
* Try to resume the STD SMC invoked from the lead CPU. It shouldn't be
* able to do it.
*/
smc_args std_smc_args;
std_smc_args.fid = TSP_FID_RESUME;
smc_ret_values smc_ret = tftf_smc(&std_smc_args);
if (smc_ret.ret0 != SMC_UNKNOWN) {
tftf_testcase_printf(
"SMC @ lead CPU returned 0x%llX 0x%llX 0x%llX instead of SMC_UNKNOWN\n",
(unsigned long long)smc_ret.ret0,
(unsigned long long)smc_ret.ret1,
(unsigned long long)smc_ret.ret2);
result = TEST_RESULT_FAIL;
}
/* Signal to the lead CPU that the calling CPU has finished the test */
tftf_send_event(&cpu_has_finished_test[core_pos]);
return result;
}
/*
* @Test_Aim@ Multicore preemption test. For a MP Secure Payload, the
* pre-emption on one CPU should not affect the other CPU. Trying to resume
* one STD SMC that was preempted on one CPU shouldn't be possible from any
* other CPU.
*
* Steps: 1. Issue Standard SMC and try preempting it via IRQ on lead CPU.
* 2. Try to resume it from the rest of the CPUs sequentially.
* 3. Resume the preempted SMC from the lead CPU and verify the result.
*
* Returns SUCCESS if step 2 fails and steps 1 and 3 succeed, else failure.
*/
test_result_t test_resume_preempted_std_smc_other_cpus(void)
{
int i;
u_register_t cpu_mpid;
unsigned int cpu_node, core_pos;
int psci_ret;
u_register_t lead_mpid = read_mpidr_el1() & MPID_MASK;
SKIP_TEST_IF_LESS_THAN_N_CPUS(2);
SKIP_TEST_IF_TSP_NOT_PRESENT();
/*
* Invoke a STD SMC that will be pre-empted.
*/
if (preempt_std_smc_on_this_cpu() != TEST_RESULT_SUCCESS) {
return TEST_RESULT_FAIL;
}
/*
* Try to resume the STD SMC from the rest of CPUs. It shouldn't be
* possible.
*/
for (i = 0; i < PLATFORM_CORE_COUNT; i++) {
tftf_init_event(&cpu_has_finished_test[i]);
}
/* Power on all CPUs and perform test sequentially. */
for_each_cpu(cpu_node) {
cpu_mpid = tftf_get_mpidr_from_node(cpu_node);
/* Skip lead CPU as it's the one with the pre-empted STD SMC. */
if (cpu_mpid == lead_mpid) {
continue;
}
core_pos = platform_get_core_pos(cpu_mpid);
psci_ret = tftf_cpu_on(cpu_mpid,
(uintptr_t)test_resume_preempted_std_smc_other_cpus_non_lead_fn, 0);
if (psci_ret != PSCI_E_SUCCESS) {
tftf_testcase_printf(
"Failed to power on CPU %d (rc = %d)\n",
core_pos, psci_ret);
return TEST_RESULT_FAIL;
}
/* Wait until the test is finished to begin with the next CPU. */
tftf_wait_for_event(&cpu_has_finished_test[core_pos]);
}
/*
* Try to resume the STD SMC from the lead CPU. It should be able to do
* it and to return the correct result.
*/
return resume_std_smc_on_this_cpu();
}
/* Test routine for secondary CPU for test_resume_different_cpu_preempted_std_smc */
static test_result_t test_resume_different_cpu_preempted_std_smc_non_lead_fn(void)
{
smc_args std_smc_args;
smc_ret_values smc_ret;
u_register_t mpid = read_mpidr_el1() & MPID_MASK;
unsigned int core_pos = platform_get_core_pos(mpid);
/* Signal to the lead CPU that the calling CPU has entered the test */
tftf_send_event(&cpu_has_entered_test[core_pos]);
/* Register and enable SGI. SGIs #0 - #6 are freely available. */
if (register_and_enable_test_sgi_handler(core_pos) != 0) {
/* Signal to the lead CPU that the calling CPU has finished */
tftf_send_event(&cpu_has_finished_test[core_pos]);
return TEST_RESULT_FAIL;
}
/* Set PSTATE.I to 0. */
disable_irq();
/*
* Send SGI to itself. It can't be handled because the interrupts are
* disabled.
*/
requested_irq_received[core_pos] = 0;
tftf_send_sgi(IRQ_NS_SGI_0, core_pos);
/*
* Invoke an STD SMC. Should be pre-empted because of the SGI that is
* waiting. It has to be different than the one invoked from the lead
* CPU.
*/
std_smc_args.fid = TSP_STD_FID(TSP_MUL);
std_smc_args.arg1 = TEST_VALUE_1;
std_smc_args.arg2 = TEST_VALUE_2;
smc_ret = tftf_smc(&std_smc_args);
if (smc_ret.ret0 != TSP_SMC_PREEMPTED) {
tftf_testcase_printf(
"SMC @ CPU %d returned 0x%llX instead of TSP_SMC_PREEMPTED.\n",
core_pos, (unsigned long long)smc_ret.ret0);
enable_irq();
unregister_and_disable_test_sgi_handler();
/* Signal to the lead CPU that the calling CPU has finished */
tftf_send_event(&cpu_has_finished_test[core_pos]);
return TEST_RESULT_FAIL;
}
/* Set PSTATE.I to 1. Let the SGI be handled. */
enable_irq();
/* Cleanup. Disable and unregister SGI handler. */
unregister_and_disable_test_sgi_handler();
/*
* Check that the SGI has been handled, but don't fail if it hasn't
* because there is no guarantee that it will have actually happened at
* this point.
*/
if (requested_irq_received[core_pos] == 0) {
VERBOSE("SGI not handled @ CPU %d\n", core_pos);
}
/* Resume the STD SMC. Verify result. */
std_smc_args.fid = TSP_FID_RESUME;
smc_ret = tftf_smc(&std_smc_args);
if ((smc_ret.ret0 != 0) || (smc_ret.ret1 != TEST_VALUE_1*TEST_VALUE_1)
|| (smc_ret.ret2 != TEST_VALUE_2*TEST_VALUE_2)) {
tftf_testcase_printf(
"SMC @ CPU %d returned 0x%llX 0x%llX 0x%llX instead of 0x0 0x%X 0x%X\n",
core_pos, (unsigned long long)smc_ret.ret0,
(unsigned long long)smc_ret.ret1,
(unsigned long long)smc_ret.ret2,
TEST_VALUE_1*2, TEST_VALUE_2*2);
/* Signal to the lead CPU that the calling CPU has finished */
tftf_send_event(&cpu_has_finished_test[core_pos]);
return TEST_RESULT_FAIL;
}
/* Try to resume the lead CPU STD SMC. Verify result. */
std_smc_args.fid = TSP_FID_RESUME;
smc_ret = tftf_smc(&std_smc_args);
if (smc_ret.ret0 != SMC_UNKNOWN) {
tftf_testcase_printf(
"SMC @ CPU %d returned 0x%llX 0x%llX 0x%llX instead of SMC_UNKNOWN\n",
core_pos, (unsigned long long)smc_ret.ret0,
(unsigned long long)smc_ret.ret1,
(unsigned long long)smc_ret.ret2);
/* Signal to the lead CPU that the calling CPU has finished */
tftf_send_event(&cpu_has_finished_test[core_pos]);
return TEST_RESULT_FAIL;
}
/* Signal to the lead CPU that the calling CPU has finished the test */
tftf_send_event(&cpu_has_finished_test[core_pos]);
return TEST_RESULT_SUCCESS;
}
/*
* @Test_Aim@ Multicore preemption test. For a MP Secure Payload, the
* pre-emption on one CPU should not affect the other CPU. Trying to resume
* one STD SMC pre-empted on one CPU shouldn't be possible from any other CPU
* involved in the test, and the STD SMC that is resumed from each CPU should
* be the same one that was invoked from it.
*
* Steps: 1. Lead and secondary CPUs set different preempted STD SMCs.
* 2. Resume the preempted SMC from secondary CPU. Verify the result.
* 3. Try to resume again to check if it can resume the lead SMC.
* 4. Resume the preempted SMC from lead CPU. Verify the result.
*
* Returns SUCCESS if steps 1, 2 and 4 succeed and step 3 fails, else failure.
*/
test_result_t test_resume_different_cpu_preempted_std_smc(void)
{
smc_args std_smc_args;
smc_ret_values smc_ret;
u_register_t cpu_mpid;
unsigned int core_pos;
int psci_ret;
u_register_t lead_mpid = read_mpidr_el1() & MPID_MASK;
unsigned int lead_pos = platform_get_core_pos(lead_mpid);
SKIP_TEST_IF_LESS_THAN_N_CPUS(2);
SKIP_TEST_IF_TSP_NOT_PRESENT();
/*
* Generate a SGI on the lead CPU that can't be handled because the
* interrupts are disabled.
*/
register_and_enable_test_sgi_handler(lead_mpid);
disable_irq();
requested_irq_received[lead_pos] = 0;
tftf_send_sgi(IRQ_NS_SGI_0, lead_pos);
/*
* Invoke an STD SMC. Should be pre-empted because of the SGI that is
* waiting.
*/
std_smc_args.fid = TSP_STD_FID(TSP_ADD);
std_smc_args.arg1 = TEST_VALUE_1;
std_smc_args.arg2 = TEST_VALUE_2;
smc_ret = tftf_smc(&std_smc_args);
if (smc_ret.ret0 != TSP_SMC_PREEMPTED) {
tftf_testcase_printf(
"SMC @ lead CPU returned 0x%llX instead of TSP_SMC_PREEMPTED.\n",
(unsigned long long)smc_ret.ret0);
enable_irq();
unregister_and_disable_test_sgi_handler();
return TEST_RESULT_FAIL;
}
/* Set PSTATE.I to 1. Let the SGI be handled. */
enable_irq();
/* Cleanup. Disable and unregister SGI handler. */
unregister_and_disable_test_sgi_handler();
/*
* Check that the SGI has been handled, but don't fail if it hasn't
* because there is no guarantee that it will have actually happened at
* this point.
*/
if (requested_irq_received[lead_pos] == 0) {
VERBOSE("SGI not handled @ lead CPU.\n");
}
/* Generate a preempted SMC in a secondary CPU. */
cpu_mpid = tftf_find_any_cpu_other_than(lead_mpid);
if (cpu_mpid == INVALID_MPID) {
tftf_testcase_printf("Couldn't find another CPU.\n");
return TEST_RESULT_FAIL;
}
core_pos = platform_get_core_pos(cpu_mpid);
tftf_init_event(&cpu_has_finished_test[core_pos]);
psci_ret = tftf_cpu_on(cpu_mpid, (uintptr_t)
test_resume_different_cpu_preempted_std_smc_non_lead_fn, 0);
if (psci_ret != PSCI_E_SUCCESS) {
tftf_testcase_printf("Failed to power on CPU %d (rc = %d)\n",
core_pos, psci_ret);
return TEST_RESULT_FAIL;
}
/* Wait until the test is finished to continue. */
tftf_wait_for_event(&cpu_has_finished_test[core_pos]);
/*
* Try to resume the STD SMC from the lead CPU. It should be able resume
* the one it generated before and to return the correct result.
*/
std_smc_args.fid = TSP_FID_RESUME;
smc_ret = tftf_smc(&std_smc_args);
if ((smc_ret.ret0 != 0) || (smc_ret.ret1 != TEST_VALUE_1 * 2) ||
(smc_ret.ret2 != TEST_VALUE_2 * 2)) {
tftf_testcase_printf(
"SMC @ lead CPU returned 0x%llX 0x%llX 0x%llX instead of 0x0 0x%X 0x%X\n",
(unsigned long long)smc_ret.ret0,
(unsigned long long)smc_ret.ret1,
(unsigned long long)smc_ret.ret2,
TEST_VALUE_1*2, TEST_VALUE_2*2);
return TEST_RESULT_FAIL;
}
return TEST_RESULT_SUCCESS;
}
/*******************************************************************************
* Test PSCI APIs while preempted.
******************************************************************************/
/*
* First part of the test routine for test_psci_cpu_on_off_preempted.
* Prepare a pre-empted STD SMC.
*/
static test_result_t test_psci_cpu_on_off_preempted_non_lead_fn_1(void)
{
test_result_t result = TEST_RESULT_SUCCESS;
u_register_t mpid = read_mpidr_el1() & MPID_MASK;
unsigned int core_pos = platform_get_core_pos(mpid);
if (preempt_std_smc_on_this_cpu() != TEST_RESULT_SUCCESS) {
return TEST_RESULT_FAIL;
}
/*
* Signal to the lead CPU that the calling CPU has entered the test
* conditions for the second part.
*/
tftf_send_event(&cpu_has_entered_test[core_pos]);
/*
* Now this CPU has to be turned off. Since this is not a lead CPU, it
* will be done in run_tests(). If it was done here, cpus_cnt wouldn't
* decrement and the tftf would think there is still a CPU running, so
* it wouldn't finish.
*
* The result will be overwritten when the second part of the test is
* executed.
*/
return result;
}
/*
* Second part of the test routine for test_psci_cpu_on_off_preempted.
* Try to resume the previously pre-empted STD SMC.
*/
static test_result_t test_psci_cpu_on_off_preempted_non_lead_fn_2(void)
{
test_result_t result;
u_register_t mpid = read_mpidr_el1() & MPID_MASK;
unsigned int core_pos = platform_get_core_pos(mpid);
/* Try to resume the STD SMC. Check that it fails. */
result = resume_fail_std_smc_on_this_cpu();
/* Signal to the lead CPU that the calling CPU has finished the test */
tftf_send_event(&cpu_has_finished_test[core_pos]);
return result;
}
/*
* @Test_Aim@ Resume preempted STD SMC after PSCI CPU OFF/ON cycle.
*
* Steps: 1. Each CPU sets a preempted STD SMC.
* 2. They send an event to the lead CPU and call PSCI CPU OFF.
* 3. The lead CPU invokes PSCI CPU ON for the secondaries (warm boot).
* 4. Try to resume the preempted STD SMC on secondary CPUs.
*
* Returns SUCCESS if steps 1, 2 or 3 succeed and step 4 fails, else failure.
*/
test_result_t test_psci_cpu_on_off_preempted_std_smc(void)
{
int i;
int all_powered_down;
u_register_t cpu_mpid;
unsigned int cpu_node, core_pos;
int psci_ret;
u_register_t lead_mpid = read_mpidr_el1() & MPID_MASK;
SKIP_TEST_IF_LESS_THAN_N_CPUS(2);
SKIP_TEST_IF_TSP_NOT_PRESENT();
for (i = 0; i < PLATFORM_CORE_COUNT; i++) {
tftf_init_event(&cpu_has_entered_test[i]);
tftf_init_event(&cpu_has_finished_test[i]);
}
/* Power on all CPUs */
for_each_cpu(cpu_node) {
cpu_mpid = tftf_get_mpidr_from_node(cpu_node);
/* Skip lead CPU as it is already powered on */
if (cpu_mpid == lead_mpid) {
continue;
}
core_pos = platform_get_core_pos(cpu_mpid);
psci_ret = tftf_cpu_on(cpu_mpid,
(uintptr_t)test_psci_cpu_on_off_preempted_non_lead_fn_1, 0);
if (psci_ret != PSCI_E_SUCCESS) {
tftf_testcase_printf("Failed to power on CPU %d (rc = %d)\n",
core_pos, psci_ret);
return TEST_RESULT_FAIL;
}
}
/* Wait for non-lead CPUs to exit the first part of the test */
for_each_cpu(cpu_node) {
cpu_mpid = tftf_get_mpidr_from_node(cpu_node);
/* Skip lead CPU */
if (cpu_mpid == lead_mpid) {
continue;
}
core_pos = platform_get_core_pos(cpu_mpid);
tftf_wait_for_event(&cpu_has_entered_test[core_pos]);
}
/* Check that all secondary CPUs are powered off. */
all_powered_down = 0;
while (all_powered_down == 0) {
all_powered_down = 1;
for_each_cpu(cpu_node) {
cpu_mpid = tftf_get_mpidr_from_node(cpu_node);
if (cpu_mpid == lead_mpid) {
continue;
}
if (tftf_is_cpu_online(cpu_mpid) != 0) {
all_powered_down = 0;
}
}
}
/* Start the second part of the test */
for_each_cpu(cpu_node) {
cpu_mpid = tftf_get_mpidr_from_node(cpu_node);
/* Skip lead CPU as it is already powered on */
if (cpu_mpid == lead_mpid) {
continue;
}
core_pos = platform_get_core_pos(cpu_mpid);
psci_ret = tftf_cpu_on(cpu_mpid,
(uintptr_t)test_psci_cpu_on_off_preempted_non_lead_fn_2, 0);
if (psci_ret != PSCI_E_SUCCESS) {
tftf_testcase_printf("Failed to power on CPU 0x%x (rc = %d)\n",
core_pos, psci_ret);
return TEST_RESULT_FAIL;
}
}
/* Wait for non-lead CPUs to finish the second part of the test. */
for_each_cpu(cpu_node) {
cpu_mpid = tftf_get_mpidr_from_node(cpu_node);
/* Skip lead CPU */
if (cpu_mpid == lead_mpid) {
continue;
}
core_pos = platform_get_core_pos(cpu_mpid);
tftf_wait_for_event(&cpu_has_finished_test[core_pos]);
}
return TEST_RESULT_SUCCESS;
}
/******************************************************************************/
/*
* @Test_Aim@ Resume preempted STD SMC after PSCI SYSTEM SUSPEND (in case it is
* supported).
*
* Steps: 1. The lead CPU sets a preempted STD SMC.
* 2. It calls PSCI SYSTEM SUSPEND with a wakeup timer for 1 sec.
* 3. Try to resume the preempted STD SMC.
*
* Returns SUCCESS if steps 1 and 2 succeed and step 3 fails.
*/
test_result_t test_psci_system_suspend_preempted_std_smc(void)
{
int psci_ret;
int result = TEST_RESULT_SUCCESS;
u_register_t lead_mpid = read_mpidr_el1() & MPID_MASK;
unsigned int lead_pos = platform_get_core_pos(lead_mpid);
SKIP_TEST_IF_TSP_NOT_PRESENT();
if (!is_psci_sys_susp_supported()) {
tftf_testcase_printf(
"SYSTEM_SUSPEND is not supported.\n");
return TEST_RESULT_SKIPPED;
}
if (preempt_std_smc_on_this_cpu() != TEST_RESULT_SUCCESS) {
return TEST_RESULT_FAIL;
}
if (!is_sys_suspend_state_ready()) {
result = TEST_RESULT_FAIL;
}
/* Prepare wakeup timer. IRQs need to be enabled. */
wakeup_irq_received[lead_pos] = 0;
tftf_timer_register_handler(suspend_wakeup_handler);
/* Program timer to fire interrupt after timer expires */
tftf_program_timer(SUSPEND_TIME_1_SEC);
/* Issue PSCI_SYSTEM_SUSPEND. */
psci_ret = tftf_system_suspend();
while (!wakeup_irq_received[lead_pos])
;
if (psci_ret != PSCI_E_SUCCESS) {
mp_printf("SYSTEM_SUSPEND from lead CPU failed. ret: 0x%x\n",
psci_ret);
result = TEST_RESULT_FAIL;
}
/* Remove timer after waking up.*/
tftf_cancel_timer();
tftf_timer_unregister_handler();
if (resume_fail_std_smc_on_this_cpu() != TEST_RESULT_SUCCESS) {
result = TEST_RESULT_FAIL;
}
return result;
}