aboutsummaryrefslogtreecommitdiff
path: root/tftf/tests/runtime_services/standard_service/sdei/system_tests/test_sdei.c
diff options
context:
space:
mode:
Diffstat (limited to 'tftf/tests/runtime_services/standard_service/sdei/system_tests/test_sdei.c')
-rw-r--r--tftf/tests/runtime_services/standard_service/sdei/system_tests/test_sdei.c447
1 files changed, 447 insertions, 0 deletions
diff --git a/tftf/tests/runtime_services/standard_service/sdei/system_tests/test_sdei.c b/tftf/tests/runtime_services/standard_service/sdei/system_tests/test_sdei.c
new file mode 100644
index 000000000..dc357c16a
--- /dev/null
+++ b/tftf/tests/runtime_services/standard_service/sdei/system_tests/test_sdei.c
@@ -0,0 +1,447 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <debug.h>
+#include <events.h>
+#include <plat_topology.h>
+#include <platform.h>
+#include <power_management.h>
+#include <private_timer.h>
+#include <sdei.h>
+#include <tftf_lib.h>
+#include <timer.h>
+
+#define EV_COOKIE 0xDEADBEEF
+#define TIMER_TIMEO_MS 10
+
+extern sdei_handler_t sdei_entrypoint;
+extern sdei_handler_t sdei_entrypoint_resume;
+
+/*
+ * the bound event number as returned from sdei_interrupt_bind(), passed
+ * to the per-cpu SDEI test function
+ */
+static int bound_ev;
+/* true if the test is using a private interrupt source, false otherwise. */
+static int private_interrupt;
+
+static spinlock_t cpu_count_lock;
+static volatile int cpu_count;
+static volatile int participating_cpu_count;
+
+/* Helper function to wait for CPUs participating in the test. */
+static void wait_for_participating_cpus(void)
+{
+ assert(participating_cpu_count <= PLATFORM_CORE_COUNT);
+
+ spin_lock(&cpu_count_lock);
+ cpu_count++;
+ spin_unlock(&cpu_count_lock);
+
+ assert(cpu_count <= PLATFORM_CORE_COUNT);
+
+ while (cpu_count != participating_cpu_count)
+ continue;
+}
+
+void sdei_trigger_event(void)
+{
+ printf("%s: triggering SDEI event\n", __func__);
+ if (private_interrupt)
+ private_timer_start(TIMER_TIMEO_MS);
+ else
+ tftf_program_timer(TIMER_TIMEO_MS);
+}
+
+static test_result_t sdei_event(void)
+{
+ long long ret;
+
+ wait_for_participating_cpus();
+
+ printf("%s: mpidr = 0x%llx\n", __func__,
+ (unsigned long long)read_mpidr_el1());
+
+ ret = sdei_event_register(bound_ev, sdei_entrypoint_resume, EV_COOKIE,
+ SDEI_REGF_RM_PE, read_mpidr_el1());
+ if (ret < 0) {
+ tftf_testcase_printf("SDEI event register failed: 0x%llx\n",
+ ret);
+ return TEST_RESULT_FAIL;
+ }
+
+ ret = sdei_event_enable(bound_ev);
+ if (ret < 0) {
+ tftf_testcase_printf("SDEI event enable failed: 0x%llx\n", ret);
+ goto err0;
+ }
+
+ ret = sdei_pe_unmask();
+ if (ret < 0) {
+ tftf_testcase_printf("SDEI pe unmask failed: 0x%llx\n", ret);
+ goto err1;
+ }
+
+ sdei_trigger_event();
+
+ sdei_handler_done();
+
+ sdei_pe_mask();
+
+err1:
+ sdei_event_disable(bound_ev);
+err0:
+ sdei_event_unregister(bound_ev);
+
+ if (ret < 0)
+ return TEST_RESULT_FAIL;
+
+ return TEST_RESULT_SUCCESS;
+}
+
+int sdei_event_handler(int ev, unsigned long long arg)
+{
+ printf("%s: handler fired\n", __func__);
+ assert(arg == EV_COOKIE);
+ if (private_interrupt)
+ private_timer_stop();
+ else
+ tftf_cancel_timer();
+ return 0;
+}
+
+/* Handle an SDEI event on all cores in sequence. */
+test_result_t test_sdei_event_serial(void)
+{
+ struct sdei_intr_ctx intr_ctx;
+ u_register_t lead_mpid, target_mpid;
+ int cpu_node;
+ long long ret;
+
+ lead_mpid = read_mpidr_el1() & MPID_MASK;
+ participating_cpu_count = 1;
+ init_spinlock(&cpu_count_lock);
+ cpu_count = 0;
+
+ ret = sdei_version();
+ if (ret != MAKE_SDEI_VERSION(1, 0, 0)) {
+ tftf_testcase_printf("Unexpected SDEI version: 0x%llx\n", ret);
+ return TEST_RESULT_SKIPPED;
+ }
+
+ disable_irq();
+ bound_ev = sdei_interrupt_bind(tftf_get_timer_irq(), &intr_ctx);
+ if (bound_ev < 0) {
+ tftf_testcase_printf("SDEI interrupt bind failed: %x\n",
+ bound_ev);
+ return TEST_RESULT_FAIL;
+ }
+
+ /* use a shared interrupt source for this test-case */
+ private_interrupt = 0;
+
+ for_each_cpu(cpu_node) {
+ target_mpid = tftf_get_mpidr_from_node(cpu_node) & MPID_MASK;
+ if (lead_mpid == target_mpid)
+ continue;
+ ret = tftf_cpu_on(target_mpid,
+ (uintptr_t)sdei_event, 0);
+ if (ret != PSCI_E_SUCCESS) {
+ ERROR("CPU ON failed for 0x0x%llx\n",
+ (unsigned long long)target_mpid);
+ goto err0;
+ }
+ while (tftf_psci_affinity_info(target_mpid, MPIDR_AFFLVL0) !=
+ PSCI_STATE_OFF)
+ continue;
+ cpu_count--;
+ }
+
+ assert(cpu_count == 0);
+
+ if (sdei_event() != TEST_RESULT_SUCCESS)
+ goto err0;
+
+ cpu_count--;
+ assert(cpu_count == 0);
+
+ sdei_interrupt_release(bound_ev, &intr_ctx);
+ enable_irq();
+
+ return TEST_RESULT_SUCCESS;
+
+err0:
+ sdei_private_reset();
+ sdei_shared_reset();
+ sdei_interrupt_release(bound_ev, &intr_ctx);
+ enable_irq();
+ return TEST_RESULT_FAIL;
+}
+
+/* Handle an SDEI event on all cores in parallel. */
+test_result_t test_sdei_event_parallel(void)
+{
+ struct sdei_intr_ctx intr_ctx;
+ u_register_t lead_mpid, target_mpid;
+ int cpu_node;
+ long long ret;
+
+ lead_mpid = read_mpidr_el1() & MPID_MASK;
+ participating_cpu_count = tftf_get_total_cpus_count();
+ init_spinlock(&cpu_count_lock);
+ cpu_count = 0;
+
+ ret = sdei_version();
+ if (ret != MAKE_SDEI_VERSION(1, 0, 0)) {
+ tftf_testcase_printf("Unexpected SDEI version: 0x%llx\n", ret);
+ return TEST_RESULT_SKIPPED;
+ }
+
+ disable_irq();
+ bound_ev = sdei_interrupt_bind(IRQ_PCPU_HP_TIMER, &intr_ctx);
+ if (bound_ev < 0) {
+ tftf_testcase_printf("SDEI interrupt bind failed: %x\n",
+ bound_ev);
+ return TEST_RESULT_FAIL;
+ }
+
+ /* use a private interrupt source for this test-case */
+ private_interrupt = 1;
+
+ for_each_cpu(cpu_node) {
+ target_mpid = tftf_get_mpidr_from_node(cpu_node) & MPID_MASK;
+ if (lead_mpid == target_mpid)
+ continue;
+ ret = tftf_cpu_on(target_mpid,
+ (uintptr_t)sdei_event, 0);
+ if (ret != PSCI_E_SUCCESS) {
+ ERROR("CPU ON failed for 0x0x%llx\n",
+ (unsigned long long)target_mpid);
+ goto err0;
+ }
+ }
+
+ if (sdei_event() != TEST_RESULT_SUCCESS)
+ goto err0;
+
+ for_each_cpu(cpu_node) {
+ target_mpid = tftf_get_mpidr_from_node(cpu_node) & MPID_MASK;
+ if (lead_mpid == target_mpid)
+ continue;
+ while (tftf_psci_affinity_info(target_mpid, MPIDR_AFFLVL0) !=
+ PSCI_STATE_OFF)
+ continue;
+ cpu_count--;
+ }
+
+ cpu_count--;
+ assert(cpu_count == 0);
+
+ sdei_interrupt_release(bound_ev, &intr_ctx);
+ enable_irq();
+
+ return TEST_RESULT_SUCCESS;
+err0:
+ sdei_private_reset();
+ sdei_shared_reset();
+ sdei_interrupt_release(bound_ev, &intr_ctx);
+ enable_irq();
+ return TEST_RESULT_FAIL;
+}
+
+static test_result_t sdei_event_signal_self(void)
+{
+ long long ret;
+
+ ret = sdei_event_register(0, sdei_entrypoint_resume, EV_COOKIE,
+ SDEI_REGF_RM_PE, read_mpidr_el1());
+ if (ret < 0) {
+ tftf_testcase_printf("SDEI event register failed: 0x%llx\n",
+ ret);
+ return TEST_RESULT_FAIL;
+ }
+
+ ret = sdei_event_enable(0);
+ if (ret < 0) {
+ tftf_testcase_printf("SDEI event enable failed: 0x%llx\n", ret);
+ goto err0;
+ }
+
+ ret = sdei_pe_unmask();
+ if (ret < 0) {
+ tftf_testcase_printf("SDEI pe unmask failed: 0x%llx\n", ret);
+ goto err1;
+ }
+
+ ret = sdei_event_signal(read_mpidr_el1());
+ if (ret < 0) {
+ tftf_testcase_printf("SDEI event signal failed: 0x%llx\n", ret);
+ goto err2;
+ }
+
+ sdei_handler_done();
+
+err2:
+ sdei_pe_mask();
+err1:
+ sdei_event_disable(0);
+err0:
+ sdei_event_unregister(0);
+
+ if (ret < 0)
+ return TEST_RESULT_FAIL;
+
+ return TEST_RESULT_SUCCESS;
+}
+
+/* Each core signals itself using SDEI event signalling. */
+test_result_t test_sdei_event_signal_serial(void)
+{
+ u_register_t lead_mpid, target_mpid;
+ int cpu_node;
+ long long ret;
+
+ lead_mpid = read_mpidr_el1() & MPID_MASK;
+
+ ret = sdei_version();
+ if (ret != MAKE_SDEI_VERSION(1, 0, 0)) {
+ tftf_testcase_printf("Unexpected SDEI version: 0x%llx\n", ret);
+ return TEST_RESULT_SKIPPED;
+ }
+
+ disable_irq();
+ for_each_cpu(cpu_node) {
+ target_mpid = tftf_get_mpidr_from_node(cpu_node) & MPID_MASK;
+ if (lead_mpid == target_mpid)
+ continue;
+ ret = tftf_cpu_on(target_mpid,
+ (uintptr_t)sdei_event_signal_self, 0);
+ if (ret != PSCI_E_SUCCESS) {
+ ERROR("CPU ON failed for 0x0x%llx\n",
+ (unsigned long long)target_mpid);
+ ret = -1;
+ goto err0;
+ }
+ while (tftf_psci_affinity_info(target_mpid, MPIDR_AFFLVL0) !=
+ PSCI_STATE_OFF)
+ continue;
+ }
+
+ if (sdei_event_signal_self() != TEST_RESULT_SUCCESS) {
+ ret = -1;
+ goto err0;
+ }
+
+err0:
+ enable_irq();
+ if (ret < 0)
+ return TEST_RESULT_FAIL;
+ return TEST_RESULT_SUCCESS;
+}
+
+static event_t cpu_ready[PLATFORM_CORE_COUNT];
+
+static test_result_t sdei_wait_for_event_signal(void)
+{
+ int core_pos;
+ long long ret;
+
+ ret = sdei_event_register(0, sdei_entrypoint_resume, EV_COOKIE,
+ SDEI_REGF_RM_PE, read_mpidr_el1());
+ if (ret < 0) {
+ tftf_testcase_printf("SDEI event register failed: 0x%llx\n",
+ ret);
+ return TEST_RESULT_FAIL;
+ }
+
+ ret = sdei_event_enable(0);
+ if (ret < 0) {
+ tftf_testcase_printf("SDEI event enable failed: 0x%llx\n", ret);
+ goto err0;
+ }
+
+ ret = sdei_pe_unmask();
+ if (ret < 0) {
+ tftf_testcase_printf("SDEI pe unmask failed: 0x%llx\n", ret);
+ goto err1;
+ }
+
+ core_pos = platform_get_core_pos(read_mpidr_el1());
+ tftf_send_event(&cpu_ready[core_pos]);
+
+ sdei_handler_done();
+
+ sdei_pe_mask();
+err1:
+ sdei_event_disable(0);
+err0:
+ sdei_event_unregister(0);
+
+ if (ret < 0)
+ return TEST_RESULT_FAIL;
+
+ return TEST_RESULT_SUCCESS;
+}
+
+/*
+ * The primary core will signal all other cores
+ * use SDEI event signalling.
+ */
+test_result_t test_sdei_event_signal_all(void)
+{
+ u_register_t lead_mpid, target_mpid;
+ int cpu_node, core_pos;
+ int i;
+ long long ret;
+
+ for (i = 0; i < PLATFORM_CORE_COUNT; i++)
+ tftf_init_event(&cpu_ready[i]);
+
+ lead_mpid = read_mpidr_el1() & MPID_MASK;
+
+ ret = sdei_version();
+ if (ret != MAKE_SDEI_VERSION(1, 0, 0)) {
+ tftf_testcase_printf("Unexpected SDEI version: 0x%llx\n", ret);
+ return TEST_RESULT_SKIPPED;
+ }
+
+ disable_irq();
+ for_each_cpu(cpu_node) {
+ target_mpid = tftf_get_mpidr_from_node(cpu_node) & MPID_MASK;
+ if (lead_mpid == target_mpid)
+ continue;
+ ret = tftf_cpu_on(target_mpid,
+ (uintptr_t)sdei_wait_for_event_signal, 0);
+ if (ret != PSCI_E_SUCCESS) {
+ ERROR("CPU ON failed for 0x0x%llx\n",
+ (unsigned long long)target_mpid);
+ ret = -1;
+ goto err0;
+ }
+ core_pos = platform_get_core_pos(target_mpid);
+ tftf_wait_for_event(&cpu_ready[core_pos]);
+ }
+
+ for_each_cpu(cpu_node) {
+ target_mpid = tftf_get_mpidr_from_node(cpu_node) & MPID_MASK;
+ if (lead_mpid == target_mpid)
+ continue;
+ ret = sdei_event_signal(target_mpid);
+ if (ret < 0) {
+ tftf_testcase_printf("SDEI event signal failed: 0x%llx\n",
+ ret);
+ ret = -1;
+ goto err0;
+ }
+ }
+
+err0:
+ enable_irq();
+ if (ret < 0)
+ return TEST_RESULT_FAIL;
+ return TEST_RESULT_SUCCESS;
+}