blob: eae5ccd8a2b872437e1ad5baac73cdd040ac19d6 [file] [log] [blame]
/*
* Copyright (c) 2018, 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_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 <test_helpers.h>
#include <tftf_lib.h>
#define TEST_VALUE_1 4
#define TEST_VALUE_2 6
#define TEST_SPURIOUS_ITERATIONS_COUNT 1000000
#define TEST_SPI_ID (MIN_SPI_ID + 2)
#define CPU_TARGET_FIELD ((1 << PLATFORM_CORE_COUNT) - 1)
static event_t cpu_ready[PLATFORM_CORE_COUNT];
static volatile int requested_irq_received[PLATFORM_CORE_COUNT];
static volatile int test_finished_flag;
static volatile int spurious_count[PLATFORM_CORE_COUNT];
static volatile int preempted_count[PLATFORM_CORE_COUNT];
/* 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;
}
/* Dummy handler that increases a variable to check if it has been called. */
static int test_spurious_handler(void *data)
{
u_register_t core_mpid = read_mpidr_el1() & MPID_MASK;
unsigned int core_pos = platform_get_core_pos(core_mpid);
spurious_count[core_pos]++;
return 0;
}
/* Helper function for test_multicore_spurious_interrupt. */
static test_result_t test_multicore_spurious_interrupt_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);
/* Signal to the lead CPU that the calling CPU has entered the test */
tftf_send_event(&cpu_ready[core_pos]);
while (test_finished_flag == 0) {
smc_args std_smc_args;
smc_ret_values smc_ret;
/* Invoke an STD SMC. */
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);
while (result != TEST_RESULT_FAIL) {
if (smc_ret.ret0 == 0) {
/* Verify result */
if ((smc_ret.ret1 != TEST_VALUE_1 * 2) ||
(smc_ret.ret2 != TEST_VALUE_2 * 2)) {
tftf_testcase_printf(
"SMC @ CPU %d returned 0x0 0x%llx 0x%llx instead of 0x0 0x%x 0x%x\n",
core_pos,
(unsigned long long)smc_ret.ret1,
(unsigned long long)smc_ret.ret2,
TEST_VALUE_1 * 2, TEST_VALUE_2 * 2);
result = TEST_RESULT_FAIL;
} else {
/* Correct, exit inner loop */
break;
}
} else if (smc_ret.ret0 == TSP_SMC_PREEMPTED) {
/* Resume the STD SMC. */
std_smc_args.fid = TSP_FID_RESUME;
smc_ret = tftf_smc(&std_smc_args);
preempted_count[core_pos]++;
} else {
/* Error */
tftf_testcase_printf(
"SMC @ lead CPU returned 0x%llx 0x%llx 0x%llx\n",
(unsigned long long)smc_ret.ret0,
(unsigned long long)smc_ret.ret1,
(unsigned long long)smc_ret.ret2);
mp_printf("Panic <others> %d\n", core_pos);
result = TEST_RESULT_FAIL;
}
}
if (result == TEST_RESULT_FAIL)
break;
}
dsbsy();
/* Signal to the lead CPU that the calling CPU has finished the test */
tftf_send_event(&cpu_ready[core_pos]);
return result;
}
/*
* @Test_Aim@ Test Spurious interrupt handling. GICv2 only. Only works if TF
* is compiled with TSP_NS_INTR_ASYNC_PREEMPT = 0.
*
* Steps: 1. Setup SPI handler and spurious interrupt handler on the lead CPU.
* 2. Redirect SPI interrupts to all CPUs.
* 3. Turn on secondary CPUs and make them invoke STD SMC all time.
* 4. The lead CPU starts a loop that triggers a SPI so that all CPUs
* will try to handle it.
* 5. The CPUs that can't handle the SPI will receive a spurious
* interrupt and increase a counter.
* 6. Check that there have been spurious interrupts. Not necessarily
* the number of (CPU - 1) * iterations as the SMC may need time to
* handle.
*
* Returns SUCCESS if all steps succeed, else failure.
*/
test_result_t test_multicore_spurious_interrupt(void)
{
int i, j;
u_register_t cpu_mpid;
unsigned int cpu_node, core_pos;
int psci_ret;
int 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();
if (is_gicv3_mode()) {
tftf_testcase_printf("Detected GICv3. Need GICv2.\n");
return TEST_RESULT_SKIPPED;
}
ret = tftf_irq_register_handler(GIC_SPURIOUS_INTERRUPT,
test_spurious_handler);
if (ret != 0) {
tftf_testcase_printf(
"Failed to register spurious handler. Error = %d\n",
ret);
return TEST_RESULT_SKIPPED;
}
/* Reset test variables and boot secondary cores. */
for (i = 0; i < PLATFORM_CORE_COUNT; i++) {
spurious_count[i] = 0;
preempted_count[i] = 0;
}
test_finished_flag = 0;
for (i = 0; i < PLATFORM_CORE_COUNT; i++)
tftf_init_event(&cpu_ready[i]);
dsbsy();
for_each_cpu(cpu_node) {
cpu_mpid = tftf_get_mpidr_from_node(cpu_node);
/* Skip lead CPU */
if (cpu_mpid == lead_mpid)
continue;
psci_ret = tftf_cpu_on(cpu_mpid,
(uintptr_t)test_multicore_spurious_interrupt_non_lead_fn, 0);
if (psci_ret != PSCI_E_SUCCESS) {
tftf_testcase_printf(
"Failed to power on CPU 0x%x (%d)\n",
(unsigned int)cpu_mpid, psci_ret);
test_finished_flag = 1;
return TEST_RESULT_SKIPPED;
}
mp_printf("CPU 0x%x powered on\n", (unsigned int)cpu_mpid);
}
/* Wait for non-lead CPUs to enter 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_ready[core_pos]);
tftf_init_event(&cpu_ready[core_pos]);
}
/* Wait until tftf_init_event is seen by all cores */
dsbsy();
/* Register SPI handler (for all CPUs) and enable it. */
ret = tftf_irq_register_handler(TEST_SPI_ID, test_handler);
if (ret != 0) {
tftf_testcase_printf(
"Failed to register SPI handler @ lead CPU. Error code = %d\n",
ret);
tftf_irq_unregister_handler(GIC_SPURIOUS_INTERRUPT);
test_finished_flag = 1;
return TEST_RESULT_SKIPPED;
}
/* Enable IRQ and route it to this CPU. */
tftf_irq_enable(TEST_SPI_ID, GIC_HIGHEST_NS_PRIORITY);
/* Route interrupts to all CPUs */
gicv2_set_itargetsr_value(TEST_SPI_ID, CPU_TARGET_FIELD);
for (j = 0; j < TEST_SPURIOUS_ITERATIONS_COUNT; j++) {
/* Clear handled flags. */
for (i = 0; i < PLATFORM_CORE_COUNT; i++)
requested_irq_received[i] = 0;
dsbsy();
/* Request SPI */
gicv2_gicd_set_ispendr(TEST_SPI_ID);
/* Wait until it is handled. */
int wait = 1;
while (wait) {
for (i = 0; i < PLATFORM_CORE_COUNT; i++) {
if (requested_irq_received[i])
wait = 0;
}
}
}
test_finished_flag = 1;
/* Make sure the flag update is seen by all cores */
dsbsy();
/* Wait for non-lead CPUs to finish 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_ready[core_pos]);
}
/* Cleanup. */
tftf_irq_disable(TEST_SPI_ID);
tftf_irq_unregister_handler(TEST_SPI_ID);
tftf_irq_unregister_handler(GIC_SPURIOUS_INTERRUPT);
/* Check results. */
unsigned int total_spurious_count = 0;
unsigned int total_preempted_count = 0;
for (i = 0; i < PLATFORM_CORE_COUNT; i++) {
total_spurious_count += spurious_count[i];
total_preempted_count += preempted_count[i];
}
mp_printf("Count Spurious= %d; Preempted= %d\n", total_spurious_count,
total_preempted_count);
/* Check that the test has tested the behaviour. */
if (total_spurious_count == 0) {
tftf_testcase_printf("No spurious interrupts were handled.\n"
"The TF-A must be compiled with TSP_NS_INTR_ASYNC_PREEMPT = 0\n");
/*
* Don't flag the test as failed in case the TF-A was compiled
* with TSP_NS_INTR_ASYNC_PREEMPT=1.
*/
return TEST_RESULT_SKIPPED;
}
if (total_preempted_count == 0) {
tftf_testcase_printf("No preempted STD SMCs.\n");
return TEST_RESULT_FAIL;
}
return TEST_RESULT_SUCCESS;
}