From ef3d1cd24e0a03a540c6f8225efe7f12198cb33b Mon Sep 17 00:00:00 2001 From: Antonio Nino Diaz Date: Tue, 11 Apr 2017 11:46:55 +0100 Subject: Add GICv2 spurious interrupt test This test verifies that spurious interrupts are handled correctly in platforms with GICv2. Test enabled for Juno only. Change-Id: I34051d4820574286dfd026d0cf9cc264075636c8 Signed-off-by: Antonio Nino Diaz --- .../trusted_os/tsp/test_irq_spurious_gicv2.c | 314 +++++++++++++++++++++ 1 file changed, 314 insertions(+) create mode 100644 tests/runtime_services/trusted_os/tsp/test_irq_spurious_gicv2.c (limited to 'tests/runtime_services') diff --git a/tests/runtime_services/trusted_os/tsp/test_irq_spurious_gicv2.c b/tests/runtime_services/trusted_os/tsp/test_irq_spurious_gicv2.c new file mode 100644 index 0000000..0db0780 --- /dev/null +++ b/tests/runtime_services/trusted_os/tsp/test_irq_spurious_gicv2.c @@ -0,0 +1,314 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TEST_VALUE_1 4 +#define TEST_VALUE_2 6 + +#define TEST_SPURIOUS_ITERATIONS_COUNT 1000000 + +#define TEST_SPI_ID (MIN_SPI_ID + 2) + +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) +{ + unsigned int core_pos = platform_get_core_pos(read_mpidr_el1()); + + spurious_count[core_pos]++; + + return 0; +} + +/* Helper function for test_juno_multicore_spurious_interrupt. */ +static test_result_t test_juno_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.arg0 = 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.arg0 = 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 %d\n", core_pos); + result = TEST_RESULT_FAIL; + } + } + + if (result == TEST_RESULT_FAIL) + break; + } + + /* 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_juno_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(); + + 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_juno_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; + } + } + + /* 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[i]); + } + + /* 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, 0xFF); + + 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; + + /* 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]; + } + + /* Check that the test has tested the behaviour. */ + if (total_spurious_count == 0) { + tftf_testcase_printf("No spurious interrupts were handled.\n" + "The TF must be compiled with TSP_NS_INTR_ASYNC_PREEMPT = 0\n"); + /* + * Don't flag the test as failed in case the TF 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; +} -- cgit v1.2.3