aboutsummaryrefslogtreecommitdiff
path: root/tftf/tests/runtime_services/standard_service/psci/system_tests/test_psci_system_suspend_stress.c
diff options
context:
space:
mode:
Diffstat (limited to 'tftf/tests/runtime_services/standard_service/psci/system_tests/test_psci_system_suspend_stress.c')
-rw-r--r--tftf/tests/runtime_services/standard_service/psci/system_tests/test_psci_system_suspend_stress.c301
1 files changed, 301 insertions, 0 deletions
diff --git a/tftf/tests/runtime_services/standard_service/psci/system_tests/test_psci_system_suspend_stress.c b/tftf/tests/runtime_services/standard_service/psci/system_tests/test_psci_system_suspend_stress.c
new file mode 100644
index 000000000..5b500beb7
--- /dev/null
+++ b/tftf/tests/runtime_services/standard_service/psci/system_tests/test_psci_system_suspend_stress.c
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch.h>
+#include <arch_helpers.h>
+#include <arm_gic.h>
+#include <assert.h>
+#include <debug.h>
+#include <events.h>
+#include <gic_v2.h>
+#include <irq.h>
+#include <plat_topology.h>
+#include <platform.h>
+#include <platform_def.h>
+#include <power_management.h>
+#include <psci.h>
+#include <sgi.h>
+#include <stdlib.h>
+#include <test_helpers.h>
+#include <tftf.h>
+#include <tftf_lib.h>
+#include <timer.h>
+
+#define MAX_TEST_ITERATIONS (100 * PLATFORM_CORE_COUNT)
+
+/* Number of iterations of the test */
+static int iteration_count;
+
+/* The CPU assigned the baton to drive the test */
+static u_register_t baton_cpu;
+
+/* Synchronization event which will be waited on by all the non-baton CPUs */
+static event_t sync_event;
+
+/* Global variables to synchronize participating CPUs on wake-up */
+static spinlock_t cpu_count_lock;
+static volatile int cpu_count;
+static volatile int participating_cpu_count;
+
+/* Variable to store the system suspend power state and its statistics */
+static int system_susp_pwr_state;
+static u_register_t susp_count;
+
+static test_result_t do_sys_susp_on_off_stress(void);
+
+/*
+ * Helper function to wait for participating CPUs participating to enter the
+ * test function.
+ */
+static void wait_for_cpus_to_enter_test(void)
+{
+ assert(participating_cpu_count <= PLATFORM_CORE_COUNT);
+ while (cpu_count != participating_cpu_count)
+ ;
+}
+
+/* Helper function to increment the cpu_count */
+static void inc_cpu_count(void)
+{
+ spin_lock(&cpu_count_lock);
+ cpu_count++;
+ spin_unlock(&cpu_count_lock);
+ assert(cpu_count <= PLATFORM_CORE_COUNT);
+}
+
+/* Helper function to decrement the cpu_count */
+static void dec_cpu_count(void)
+{
+ spin_lock(&cpu_count_lock);
+ cpu_count--;
+ spin_unlock(&cpu_count_lock);
+ assert(cpu_count >= 0);
+}
+
+/* Helper function to turn ON all the CPUs in the platform */
+static int try_cpu_on_all(void)
+{
+ int ret, cpu_node;
+ u_register_t cpu_mpid, current_cpu = read_mpidr_el1() & MPID_MASK;
+
+ /* Try to turn on all the non-lead CPUs */
+ for_each_cpu(cpu_node) {
+ cpu_mpid = tftf_get_mpidr_from_node(cpu_node);
+
+ /* Skip lead CPU, it is already powered on */
+ if (cpu_mpid == current_cpu)
+ continue;
+
+ do {
+ ret = tftf_try_cpu_on(cpu_mpid,
+ (uintptr_t) do_sys_susp_on_off_stress, 0);
+ if (ret != PSCI_E_SUCCESS && ret != PSCI_E_ON_PENDING &&
+ ret != PSCI_E_ALREADY_ON) {
+ ERROR("Unexpected return value 0x%x"
+ " from PSCI CPU ON\n", ret);
+ return -1;
+ }
+ } while (ret != PSCI_E_SUCCESS);
+ }
+ return 0;
+}
+
+/* Helper function function to get number of CPUs which are OFF in the system */
+static int get_off_cpu_count(void)
+{
+ int aff_off_cpus = 0;
+ u_register_t cpu_mpid, current_cpu = read_mpidr_el1() & MPID_MASK;
+ int cpu_node;
+
+ /* Query the number of OFF CPUs */
+ for_each_cpu(cpu_node) {
+ cpu_mpid = tftf_get_mpidr_from_node(cpu_node);
+ /* Skip lead CPU, it is already powered on */
+ if (cpu_mpid == current_cpu)
+ continue;
+
+ if (tftf_psci_affinity_info(cpu_mpid, MPIDR_AFFLVL0) ==
+ PSCI_STATE_OFF)
+ aff_off_cpus++;
+ }
+
+ return aff_off_cpus;
+}
+
+/*
+ * The main test function which will be executed by all CPUs.
+ * 1. The CPU holding the baton will first enter this function and then turns
+ * ON all other CPUs.
+ * 2. All the `non-baton` CPUs then wait for the `sync_event` to be signaled
+ * to turn themselves OFF.
+ * 3. Number of CPUs which are signaled via `sync_event` by baton CPU is
+ * random.
+ * 4. After signaled CPUs have turned themselves OFF, SYSTEM SUSPEND is
+ * issued by the baton CPU.
+ * 5. The return value of SYSTEM_SUSPEND is checked by the baton CPU.
+ * 6. The next baton CPU is chosen randomly and the test is handed over to this
+ * CPU.
+ */
+static test_result_t do_sys_susp_on_off_stress(void)
+{
+ int psci_ret, off_cpu_count;
+ u_register_t current_cpu;
+
+ inc_cpu_count();
+
+ current_cpu = read_mpidr_el1() & MPID_MASK;
+ if (current_cpu != baton_cpu) {
+ tftf_wait_for_event(&sync_event);
+ dec_cpu_count();
+ return TEST_RESULT_SUCCESS;
+ }
+
+ INFO("System suspend test: Baton holder CPU = 0x%llx\n",
+ (unsigned long long) current_cpu);
+ if (try_cpu_on_all() == -1) {
+ tftf_testcase_printf("CPU_ON of secondary CPUs failed.\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ wait_for_cpus_to_enter_test();
+
+ /* Turn off random number of cores 1 out of 3 times */
+ if (rand() % 3)
+ off_cpu_count = rand() % participating_cpu_count;
+ else
+ off_cpu_count = participating_cpu_count - 1;
+
+ /* Signal random number of CPUs to turn OFF */
+ tftf_send_event_to(&sync_event, off_cpu_count);
+
+ /* Wait for `off_cpu_count` CPUs to turn OFF */
+ while (get_off_cpu_count() != off_cpu_count)
+ ;
+
+ /* Program timer to fire after delay */
+ tftf_program_timer(PLAT_SUSPEND_ENTRY_TIME);
+
+ /* Issue SYSTEM SUSPEND */
+ psci_ret = tftf_system_suspend();
+ tftf_cancel_timer();
+
+ /* Check return value of SYSTEM SUSPEND API */
+ if (off_cpu_count == (participating_cpu_count - 1)) {
+ if (psci_ret != PSCI_E_SUCCESS) {
+ tftf_testcase_printf("SYSTEM SUSPEND did not succeed "
+ "where expected\n");
+ return TEST_RESULT_FAIL;
+ }
+ } else {
+ if (psci_ret != PSCI_E_DENIED) {
+ tftf_testcase_printf("SYSTEM SUSPEND did not fail "
+ "where expected\n");
+ return TEST_RESULT_FAIL;
+ }
+ }
+
+ /* Pass the baton top another CPU */
+ baton_cpu = tftf_find_random_cpu_other_than(current_cpu);
+
+ /* Unblock the waiting CPUs */
+ tftf_send_event_to(&sync_event,
+ (participating_cpu_count - 1) - off_cpu_count);
+
+ /* Wait for all CPUs other than current to turn OFF */
+ while (get_off_cpu_count() != (participating_cpu_count - 1))
+ ;
+
+ dec_cpu_count();
+
+ if (iteration_count++ < MAX_TEST_ITERATIONS) {
+ /* Hand over the test execution the new baton CPU */
+ psci_ret = tftf_cpu_on(baton_cpu,
+ (uintptr_t) do_sys_susp_on_off_stress, 0);
+ if (psci_ret != PSCI_E_SUCCESS)
+ return TEST_RESULT_FAIL;
+
+ /* Wait for new baton CPU to enter test */
+ while (cpu_count == 0)
+ ;
+ } else {
+ /*
+ * The test has completed. Print statistics if PSCI STAT COUNT
+ * is supported.
+ */
+ if (is_psci_stat_count_supported()) {
+ u_register_t count = tftf_psci_stat_count(baton_cpu,
+ system_susp_pwr_state);
+ tftf_testcase_printf("Iterated %d with %lld system"
+ " suspends\n", MAX_TEST_ITERATIONS,
+ (unsigned long long)(count - susp_count));
+ }
+ }
+
+ return TEST_RESULT_SUCCESS;
+}
+
+/*
+ * @Test_Aim@ Stress test PSCI SYSTEM SUSPEND API.
+ * This test iteratively issues PSCI SYSTEM SUSPEND on random cores after
+ * issuing turning OFF a random number of CPUs. The PSCI SYSTEM SUSPEND
+ * will only succeed if all the CPUs except the calling CPU is OFF.
+ */
+test_result_t psci_sys_susp_on_off_stress_test(void)
+{
+ unsigned int pstateid_idx[PLAT_MAX_PWR_LEVEL + 1];
+ unsigned int pwrlvl, susp_type, state_id;
+ int ret;
+
+ if (!is_psci_sys_susp_supported()) {
+ tftf_testcase_printf("System suspend is not supported "
+ "by the EL3 firmware\n");
+ return TEST_RESULT_SKIPPED;
+ }
+
+ SKIP_TEST_IF_LESS_THAN_N_CPUS(2);
+
+ INIT_PWR_LEVEL_INDEX(pstateid_idx);
+ tftf_init_event(&sync_event);
+ init_spinlock(&cpu_count_lock);
+
+ /* Initialize participating CPU count */
+ participating_cpu_count = tftf_get_total_cpus_count();
+ cpu_count = 0;
+
+ iteration_count = 0;
+
+ /*
+ * Assign a baton to the current CPU and it is in charge of driving
+ * the test.
+ */
+ baton_cpu = read_mpidr_el1() & MPID_MASK;
+
+ /* Print SYSTEM SUSPEND statistics if PSCI STAT is supported */
+ if (is_psci_stat_count_supported()) {
+ NOTICE("PSCI STAT COUNT supported\n");
+ tftf_set_deepest_pstate_idx(PLAT_MAX_PWR_LEVEL, pstateid_idx);
+
+ /* Check if the power state is valid */
+ ret = tftf_get_pstate_vars(&pwrlvl,
+ &susp_type,
+ &state_id,
+ pstateid_idx);
+ if (ret != PSCI_E_SUCCESS) {
+ tftf_testcase_printf("tftf_get_pstate_vars() failed"
+ " with ret = %x\n", ret);
+ return TEST_RESULT_FAIL;
+ }
+
+ assert(pwrlvl == PLAT_MAX_PWR_LEVEL);
+
+ system_susp_pwr_state = tftf_make_psci_pstate(pwrlvl,
+ susp_type, state_id);
+
+ susp_count = tftf_psci_stat_count(baton_cpu, system_susp_pwr_state);
+ }
+
+ return do_sys_susp_on_off_stress();
+}