summaryrefslogtreecommitdiff
path: root/tests/runtime_services
diff options
context:
space:
mode:
authorJeenu Viswambharan <jeenu.viswambharan@arm.com>2016-08-08 12:31:54 +0100
committerJeenu Viswambharan <jeenu.viswambharan@arm.com>2016-09-16 10:52:30 +0100
commit369d4f844224ea7123b57bdbdec0826c5967f9a9 (patch)
treea79eadc314e900f5513e3c40ff5bbde3c6004f0d /tests/runtime_services
parent620ee44566591a3ade345da5a3741867ae31b436 (diff)
Add test cases for PSCI NODE_HW_STATE
These test cases make NODE_HW_STATE PSCI calls for both online and offline CPUs and clusters, and verify that the returned state from power controller match expected values. These also perform parameter validation. Change-Id: I02a412571a584cafa36e8dfe0ddf6ed53760573b
Diffstat (limited to 'tests/runtime_services')
-rw-r--r--tests/runtime_services/standard_service/psci/api_tests/psci_node_hw_state/test_node_hw_state.c340
1 files changed, 340 insertions, 0 deletions
diff --git a/tests/runtime_services/standard_service/psci/api_tests/psci_node_hw_state/test_node_hw_state.c b/tests/runtime_services/standard_service/psci/api_tests/psci_node_hw_state/test_node_hw_state.c
new file mode 100644
index 0000000..01b6755
--- /dev/null
+++ b/tests/runtime_services/standard_service/psci/api_tests/psci_node_hw_state/test_node_hw_state.c
@@ -0,0 +1,340 @@
+/*
+ * Copyright (c) 2016, 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 <arch.h>
+#include <arch_helpers.h>
+#include <debug.h>
+#include <events.h>
+#include <plat_topology.h>
+#include <platform.h>
+#include <platform_def.h>
+#include <power_management.h>
+#include <psci.h>
+#include <tftf_lib.h>
+
+/* Invoke _func and verifies return value is TEST_RESULT_SUCCESS */
+#define TEST_FUNC(_func) { \
+ int ret = (_func)(); \
+ if (ret != TEST_RESULT_SUCCESS) { \
+ INFO("test_node_hw_state: function " #_func " failed!\n"); \
+ return ret; \
+ } \
+}
+
+/* Enable messages for debugging purposes */
+#if 0
+# define DBGMSG(...) INFO(__VA_ARGS__)
+#else
+# define DBGMSG(...)
+#endif
+
+#define INVALID_POWER_LEVEL (PLAT_MAX_PWR_LEVEL + 1)
+
+static event_t cpu_booted[PLATFORM_CORE_COUNT];
+static event_t cpu_continue[PLATFORM_CORE_COUNT];
+
+/* MPIDRs for CPU belonging to both own and foreign clusters */
+static int native_peer = INVALID_MPID;
+static int foreign_peer = INVALID_MPID;
+
+static test_result_t cpu_ping(void)
+{
+ unsigned int mpid = read_mpidr_el1() & MPID_MASK;
+ unsigned int core_pos = platform_get_core_pos(mpid);
+
+ /* Tell the lead CPU that the calling CPU has entered the test */
+ tftf_send_event(&cpu_booted[core_pos]);
+
+ /* Wait for flag to proceed */
+ tftf_wait_for_event(&cpu_continue[core_pos]);
+
+ /*
+ * When returning from the function, the TFTF framework will power CPUs
+ * down, without this test needing to do anything
+ */
+ return TEST_RESULT_SUCCESS;
+}
+
+/* Helper function to detect support for PSCI NODE_HW_STATE */
+static int is_psci_node_hw_state_supported(void)
+{
+ return (tftf_get_psci_feature_info(SMC_PSCI_CPU_HW_STATE64) ==
+ PSCI_E_NOT_SUPPORTED) ? 0 : 1;
+}
+
+/*
+ * @Test_Aim@ Call NODE_HW_STATE for the current CPU and make sure it returns
+ * PSCI_HW_STATE_ON
+ */
+static test_result_t test_self_cpu(void)
+{
+ if (tftf_psci_node_hw_state(read_mpidr_el1(), 0) != PSCI_HW_STATE_ON) {
+ DBGMSG("%s: failed\n", __func__);
+ return TEST_RESULT_FAIL;
+ } else {
+ return TEST_RESULT_SUCCESS;
+ }
+}
+
+/*
+ * @Test_Aim@ Call NODE_HW_STATE for the current cluster and make sure it
+ * returns PSCI_HW_STATE_ON
+ */
+static test_result_t test_self_cluster(void)
+{
+ if (tftf_psci_node_hw_state(read_mpidr_el1(), 1) != PSCI_HW_STATE_ON) {
+ DBGMSG("%s: failed\n", __func__);
+ return TEST_RESULT_FAIL;
+ } else {
+ return TEST_RESULT_SUCCESS;
+ }
+}
+
+/*
+ * @Test_Aim@ Call NODE_HW_STATE for a foreign CPU that's currently off. Make
+ * sure it returns PSCI_HW_STATE_OFF
+ */
+static test_result_t test_offline_cpu(void)
+{
+ assert(foreign_peer != INVALID_MPID);
+ if (tftf_psci_node_hw_state(foreign_peer, 0) != PSCI_HW_STATE_OFF) {
+ DBGMSG("%s: failed\n", __func__);
+ return TEST_RESULT_FAIL;
+ } else {
+ return TEST_RESULT_SUCCESS;
+ }
+}
+
+/*
+ * @Test_Aim@ Call NODE_HW_STATE for a cluster that's currently off. Make sure
+ * it returns PSCI_HW_STATE_OFF
+ */
+static test_result_t test_offline_cluster(void)
+{
+ assert(foreign_peer != INVALID_MPID);
+ if (tftf_psci_node_hw_state(foreign_peer, 1) != PSCI_HW_STATE_OFF) {
+ DBGMSG("%s: failed\n", __func__);
+ return TEST_RESULT_FAIL;
+ } else {
+ return TEST_RESULT_SUCCESS;
+ }
+}
+
+/*
+ * @Test_Aim@ Call NODE_HW_STATE with an invalid MPIDR. Make sure it returns
+ * invalid parameters
+ */
+static test_result_t test_invalid_mpidr(void)
+{
+ if (tftf_psci_node_hw_state(INVALID_MPID, 0) != PSCI_E_INVALID_PARAMS) {
+ DBGMSG("%s: failed\n", __func__);
+ return TEST_RESULT_FAIL;
+ } else {
+ return TEST_RESULT_SUCCESS;
+ }
+}
+
+/*
+ * @Test_Aim@ Call NODE_HW_STATE with an invalid power_level. Make sure it
+ * returns invalid parameters
+ */
+static test_result_t test_invalid_power_level(void)
+{
+ if (tftf_psci_node_hw_state(read_mpidr_el1(), INVALID_POWER_LEVEL) !=
+ PSCI_E_INVALID_PARAMS) {
+ DBGMSG("%s: failed\n", __func__);
+ return TEST_RESULT_FAIL;
+ } else {
+ return TEST_RESULT_SUCCESS;
+ }
+}
+
+/*
+ * @Test_Aim@ Call NODE_HW_STATE on all powered-down CPUs on the system. Verify
+ * that the state was PSCI_HW_STATE_OFF before, but is PSCI_HW_STATE_ON
+ * afterwards
+ */
+static test_result_t test_online_all(void)
+{
+ int cpu_node, mpidr, my_mpidr, pos;
+ int state, ret, i;
+
+ /* Initialize all events */
+ for (i = 0; i < ARRAY_SIZE(cpu_booted); i++)
+ tftf_init_event(&cpu_booted[i]);
+ for (i = 0; i < ARRAY_SIZE(cpu_continue); i++)
+ tftf_init_event(&cpu_continue[i]);
+
+ DBGMSG("%s: powering cores on...\n", __func__);
+ my_mpidr = read_mpidr_el1() & MPID_MASK;
+ DBGMSG("%s: my mpidr: %x\n", __func__, my_mpidr);
+ for_each_cpu(cpu_node) {
+ mpidr = tftf_get_mpidr_from_node(cpu_node);
+ if (mpidr == my_mpidr)
+ continue;
+
+ /* Verify that the other CPU is turned off */
+ state = tftf_psci_node_hw_state(mpidr, 0);
+ if (state != PSCI_HW_STATE_OFF) {
+ DBGMSG("%s: before: mpidr %x: state %u, expected %u\n",
+ __func__, mpidr, state,
+ PSCI_HW_STATE_OFF);
+ return TEST_RESULT_FAIL;
+ }
+
+ /* Power on the CPU and wait for its event */
+ pos = platform_get_core_pos(mpidr);
+ ret = tftf_cpu_on(mpidr, (uint64_t) cpu_ping, 0);
+ if (ret != PSCI_E_SUCCESS) {
+ DBGMSG("%s: powering on %x failed", __func__, mpidr);
+ return TEST_RESULT_FAIL;
+ }
+ tftf_wait_for_event(&cpu_booted[pos]);
+
+ /* Verify that the other CPU is turned on */
+ state = tftf_psci_node_hw_state(mpidr, 0);
+ if (state != PSCI_HW_STATE_ON) {
+ DBGMSG("%s: after: mpidr %x: state %u, expected %u\n",
+ __func__, mpidr, state,
+ PSCI_HW_STATE_ON);
+ return TEST_RESULT_FAIL;
+ }
+
+ /* Allow to the CPU to proceed to power down */
+ tftf_send_event(&cpu_continue[pos]);
+ }
+
+ /* Wait for other CPUs to power down */
+ INFO("%s: waiting for all other CPUs to power down\n", __func__);
+ for_each_cpu(cpu_node) {
+ mpidr = tftf_get_mpidr_from_node(cpu_node);
+ if (mpidr == my_mpidr)
+ continue;
+
+ /* Loop until other CPU is powered down */
+ while (tftf_psci_affinity_info(mpidr, MPIDR_AFFLVL0) !=
+ PSCI_STATE_OFF)
+ tftf_timer_sleep(10);
+ }
+
+ /* Now verify that all CPUs have powered off */
+ for_each_cpu(cpu_node) {
+ mpidr = tftf_get_mpidr_from_node(cpu_node);
+ if (mpidr == my_mpidr)
+ continue;
+
+ /* Verify that the other CPU is turned off */
+ state = tftf_psci_node_hw_state(mpidr, 0);
+ if (state != PSCI_HW_STATE_OFF) {
+ DBGMSG("%s: mpidr %x: state %u, expected %u\n",
+ __func__, mpidr, state,
+ PSCI_HW_STATE_OFF);
+ return TEST_RESULT_FAIL;
+ }
+ }
+
+ return TEST_RESULT_SUCCESS;
+}
+
+/*
+ * Find a peer CPU in the system. The 'foreign' argument specifies where to
+ * locate the peer CPU: value zero finds a CPU in the same cluster; non-zero
+ * argument finds CPU from a different cluster.
+ */
+static int find_peer(int foreign)
+{
+ int dmn, cpu, mpidr, my_mpidr;
+
+ my_mpidr = read_mpidr_el1() & MPID_MASK;
+ dmn = PWR_DOMAIN_INIT;
+ cpu = PWR_DOMAIN_INIT;
+ do {
+ dmn = tftf_get_next_peer_domain(dmn, foreign);
+ if (foreign) {
+ cpu = tftf_get_next_cpu_in_pwr_domain(dmn,
+ PWR_DOMAIN_INIT);
+ } else {
+ cpu = dmn;
+ }
+
+ assert(cpu != PWR_DOMAIN_INIT);
+ mpidr = tftf_get_mpidr_from_node(cpu);
+ assert(mpidr != INVALID_MPID);
+ } while (mpidr == my_mpidr && dmn != PWR_DOMAIN_INIT);
+
+ return mpidr;
+}
+
+/*
+ * @Test_Aim@ Validate PSCI NODE_HW_STATE API
+ */
+test_result_t test_psci_node_hw_state(void)
+{
+ DBGMSG("%s: begin\n", __func__);
+ if (!is_psci_node_hw_state_supported()) {
+ tftf_testcase_printf("PSCI NODE_HW_STATE is not supported\n");
+ return TEST_RESULT_SKIPPED;
+ }
+
+ TEST_FUNC(test_invalid_mpidr);
+ TEST_FUNC(test_invalid_power_level);
+ TEST_FUNC(test_self_cpu);
+ TEST_FUNC(test_self_cluster);
+ TEST_FUNC(test_online_all);
+
+ DBGMSG("%s: end\n", __func__);
+ return TEST_RESULT_SUCCESS;
+}
+
+/*
+ * @Test_Aim@ Validate PSCI NODE_HW_STATE API in sytems with more than one
+ * cluster
+ */
+test_result_t test_psci_node_hw_state_multi(void)
+{
+ SKIP_TEST_IF_LESS_THAN_N_CLUSTERS(2);
+
+ DBGMSG("%s: begin\n", __func__);
+ if (!is_psci_node_hw_state_supported()) {
+ tftf_testcase_printf("PSCI NODE_HW_STATE is not supported\n");
+ return TEST_RESULT_SKIPPED;
+ }
+
+ /* Initialize peer MPDIRs */
+ native_peer = find_peer(0);
+ foreign_peer = find_peer(1);
+ DBGMSG("native=%x foreign=%x\n", native_peer, foreign_peer);
+
+ TEST_FUNC(test_offline_cpu);
+ TEST_FUNC(test_offline_cluster);
+
+ DBGMSG("%s: end\n", __func__);
+ return TEST_RESULT_SUCCESS;
+}