diff options
Diffstat (limited to 'tftf/tests/runtime_services/standard_service/psci/api_tests/affinity_info/test_psci_affinity_info.c')
-rw-r--r-- | tftf/tests/runtime_services/standard_service/psci/api_tests/affinity_info/test_psci_affinity_info.c | 434 |
1 files changed, 434 insertions, 0 deletions
diff --git a/tftf/tests/runtime_services/standard_service/psci/api_tests/affinity_info/test_psci_affinity_info.c b/tftf/tests/runtime_services/standard_service/psci/api_tests/affinity_info/test_psci_affinity_info.c new file mode 100644 index 000000000..5abb7f0d7 --- /dev/null +++ b/tftf/tests/runtime_services/standard_service/psci/api_tests/affinity_info/test_psci_affinity_info.c @@ -0,0 +1,434 @@ +/* + * 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 <events.h> +#include <irq.h> +#include <plat_topology.h> +#include <platform.h> +#include <power_management.h> +#include <psci.h> +#include <sgi.h> +#include <test_helpers.h> +#include <tftf_lib.h> + +/* Special value used to terminate the array of expected return values */ +#define END_OF_EXPECTED_VALUE 0xDEADBEEF +/* + * Event used by test test_affinity_info_level0_powerdown() to synchronise + * CPUs + */ +static event_t cpu_about_to_suspend; +static unsigned int psci_version; + +/* + * `expected_values` should contain an array of expected return values + * terminated by `END_OF_EXPECTED_VALUE`. If 'actual_value' exists in + * one of 'expected_values' then return a test success. + * Otherwise, print an error message in the test report and report a test + * failure. + */ +static test_result_t get_test_result(const int *expected_values, int actual_value) +{ + const int *expected_val_list; + + expected_val_list = expected_values; + while (*expected_val_list != END_OF_EXPECTED_VALUE) { + if (*expected_val_list == actual_value) + return TEST_RESULT_SUCCESS; + expected_val_list++; + } + + expected_val_list = expected_values; + tftf_testcase_printf("Unexpected return value: %i Expected values are:", + actual_value); + while (*expected_val_list != END_OF_EXPECTED_VALUE) { + tftf_testcase_printf("%i ", *expected_val_list); + expected_val_list++; + } + tftf_testcase_printf("\n"); + + return TEST_RESULT_FAIL; +} + +/* + * @Test_Aim@ Test PSCI AFFINITY_INFO targeted at affinity level 0 on online CPU + * + * Call PSCI AFFINITY_INFO targeted at affinity level 0 on lead CPU. + * Expect the PSCI implementation to report that the affinity instance is on. + */ +test_result_t test_affinity_info_level0_on(void) +{ + unsigned int mpid = read_mpidr_el1() & MPID_MASK; + int32_t aff_info; + int expected_values[] = {PSCI_STATE_ON, END_OF_EXPECTED_VALUE}; + + aff_info = tftf_psci_affinity_info(mpid, MPIDR_AFFLVL0); + return get_test_result(expected_values, aff_info); +} + +/* + * @Test_Aim@ Test PSCI AFFINITY_INFO targeted at affinity level 0 on offline CPU + * + * Call PSCI AFFINITY_INFO targeted at affinity level 0 on all non-lead CPUs. + * Expect the PSCI implementation to report that the affinity instances are off. + * + * This test needs 2 CPUs to run. It will be skipped on a single core platform. + */ +test_result_t test_affinity_info_level0_off(void) +{ + unsigned int lead_mpid = read_mpidr_el1() & MPID_MASK; + unsigned int target_mpid, target_node; + int32_t aff_info; + test_result_t ret = TEST_RESULT_SUCCESS; + int expected_values[] = {PSCI_STATE_OFF, END_OF_EXPECTED_VALUE}; + + SKIP_TEST_IF_LESS_THAN_N_CPUS(2); + + for_each_cpu(target_node) { + target_mpid = tftf_get_mpidr_from_node(target_node); + /* Skip lead CPU, as it is powered on */ + if (target_mpid == lead_mpid) + continue; + + aff_info = tftf_psci_affinity_info(target_mpid, MPIDR_AFFLVL0); + if (get_test_result(expected_values, aff_info) + == TEST_RESULT_FAIL) { + ret = TEST_RESULT_FAIL; + } + } + + return ret; +} + +/* + * @Test_Aim@ Test PSCI AFFINITY_INFO targeted at affinity level 1 on online cluster + * + * Call PSCI AFFINITY_INFO targeted at affinity level 1 on the lead cluster + * (i.e. the cluster to which the lead CPU belongs to). + * PSCI implementation prior to PSCI 1.0 needs to report that the cluster is on + * and others can also return INVALID_PARAMETERS. + */ +test_result_t test_affinity_info_level1_on(void) +{ + unsigned int lead_mpid = read_mpidr_el1() & MPID_MASK; + unsigned int target_mpid; + int32_t aff_info; + int expected_values[3]; + + /* + * Minimum version of PSCI is 0.2, uses this info to decide if + * tftf_get_psci_version() needs to be called or not. + */ + if (!psci_version) + psci_version = tftf_get_psci_version(); + + /* + * From PSCI version 1.0 onwards, Trusted Firmware-A may or may not + * track affinity levels greater than zero. + */ + if (!(psci_version & PSCI_MAJOR_VER_MASK)) { + expected_values[0] = PSCI_STATE_ON; + expected_values[1] = END_OF_EXPECTED_VALUE; + } else { + expected_values[0] = PSCI_STATE_ON; + expected_values[1] = PSCI_E_INVALID_PARAMS; + expected_values[2] = END_OF_EXPECTED_VALUE; + } + + /* + * Build an MPID corresponding to the lead cluster. Set the affinity + * level0 bits to some arbitrary value that doesn't correspond to any + * CPU on the platform. The PSCI implementation should ignore the + * affinity 0 field. + */ + target_mpid = (lead_mpid & MPIDR_CLUSTER_MASK) | 0xE1; + aff_info = tftf_psci_affinity_info(target_mpid, MPIDR_AFFLVL1); + return get_test_result(expected_values, aff_info); +} + +/* + * @Test_Aim@ Test PSCI AFFINITY_INFO targeted at affinity level 1 on offline cluster + * + * Call PSCI AFFINITY_INFO targeted at affinity level 1 on a non-lead cluster + * (i.e. another cluster than the one to which the lead CPU belongs to). + * PSCI implementation prior to PSCI 1.0 needs to report that the cluster is OFF + * and others can also return INVALID_PARAMETERS. + * + * This test needs 2 clusters to run. It will be skipped on a single cluster + * platform. + */ +test_result_t test_affinity_info_level1_off(void) +{ + unsigned int lead_mpid = read_mpidr_el1() & MPID_MASK; + unsigned int target_mpid; + int32_t aff_info; + unsigned int cluster_id; + int expected_values[3]; + + SKIP_TEST_IF_LESS_THAN_N_CLUSTERS(2); + + for (cluster_id = 0; + cluster_id < tftf_get_total_clusters_count(); + ++cluster_id) { + if (cluster_id != MPIDR_CLUSTER_ID(lead_mpid)) + break; + } + assert(cluster_id != tftf_get_total_clusters_count()); + + + /* + * Minimum version of PSCI is 0.2, uses this info to decide if + * tftf_get_psci_version() needs to be called or not. + */ + if (!psci_version) + psci_version = tftf_get_psci_version(); + + /* + * From PSCI version 1.0 onwards, Trusted Firmware-A may or may not + * track affinity levels greater than zero. + */ + if (!(psci_version & PSCI_MAJOR_VER_MASK)) { + expected_values[0] = PSCI_STATE_OFF; + expected_values[1] = END_OF_EXPECTED_VALUE; + } else { + expected_values[0] = PSCI_STATE_OFF; + expected_values[1] = PSCI_E_INVALID_PARAMS; + expected_values[2] = END_OF_EXPECTED_VALUE; + } + + /* + * Build an MPID corresponding to a non-lead cluster. Set the affinity + * level0 bits to some arbitrary value that doesn't correspond to any + * CPU on the platform. The PSCI implementation should ignore the + * affinity 0 field. + */ + target_mpid = make_mpid(cluster_id, 0xE1); + aff_info = tftf_psci_affinity_info(target_mpid, MPIDR_AFFLVL1); + return get_test_result(expected_values, aff_info); +} + +/* + * @Test_Aim@ Test PSCI AFFINITY_INFO targeted at affinity level 2 + * + * For PSCI implementations prior to PSCI 1.0 , the expected return value + * depends on the the maximum affinity level that the power management + * operations can apply to on this platform. + * - If the platform doesn't have an affinity level 2 then expect the PSCI + * implementation to report that it received invalid parameters. + * - If affinity level 2 exists then expect the PSCI implementation to report + * that the affinity instance is on. + * + * From PSCI 1.0 onwards, it can also return either INVALID_PARMETERS + */ +test_result_t test_affinity_info_level2(void) +{ + int expected_values[3]; + unsigned int target_mpid; + int32_t aff_info; + + /* + * Minimum version of PSCI is 0.2, uses this info to decide if + * tftf_get_psci_version() needs to be called or not. + */ + if (!psci_version) + psci_version = tftf_get_psci_version(); + + expected_values[0] = (PLATFORM_MAX_AFFLVL >= 2) + ? PSCI_STATE_ON + : PSCI_E_INVALID_PARAMS; + + /* + * From PSCI version 1.0 onwards, Trusted Firmware-A may or may not + * track affinity levels greater than zero. + */ + if (!(psci_version & PSCI_MAJOR_VER_MASK)) { + expected_values[1] = END_OF_EXPECTED_VALUE; + } else { + expected_values[1] = PSCI_E_INVALID_PARAMS; + expected_values[2] = END_OF_EXPECTED_VALUE; + } + + /* + * Build an MPID corresponding to the lead affinity instance at level 2. + * Set the affinity level0 & level1 bits to some arbitrary values that + * don't correspond to any affinity instance on the platform. The PSCI + * implementation should ignore the affinity 0 & 1 fields. + */ + target_mpid = read_mpidr_el1() & (MPIDR_AFFLVL_MASK << MPIDR_AFF_SHIFT(2)); + target_mpid |= 0xAB << MPIDR_AFF1_SHIFT; + target_mpid |= 0xE1 << MPIDR_AFF0_SHIFT; + + aff_info = tftf_psci_affinity_info(target_mpid, MPIDR_AFFLVL2); + return get_test_result(expected_values, aff_info); +} + +/* + * @Test_Aim@ Test PSCI AFFINITY_INFO targeted at affinity level 3 + * + * For PSCI implementations prior to PSCI 1.0 , the expected return value + * depends on the maximum affinity level that the power management + * operations can apply to on this platform. + * - If the platform doesn't have an affinity level 3 then expect the PSCI + * implementation to report that it received invalid parameters. + * - If affinity level 3 exists then expect the PSCI implementation to report + * that the affinity instance is on. + * + * From PSCI 1.0 onwards, it can also return INVALID_PARMETERS + */ +test_result_t test_affinity_info_level3(void) +{ +#ifndef AARCH32 + int expected_values[3]; + uint64_t target_mpid; + int32_t aff_info; + + /* + * Minimum version of PSCI is 0.2, uses this info to decide if + * tftf_get_psci_version() needs to be called or not. + */ + if (!psci_version) + psci_version = tftf_get_psci_version(); + + expected_values[0] = (PLATFORM_MAX_AFFLVL == 3) + ? PSCI_STATE_ON + : PSCI_E_INVALID_PARAMS; + + /* + * From PSCI version 1.0 onwards, Trusted Firmware-A may or may not + * track affinity levels greater than zero. + */ + if (!(psci_version & PSCI_MAJOR_VER_MASK)) { + expected_values[1] = END_OF_EXPECTED_VALUE; + } else { + expected_values[1] = PSCI_E_INVALID_PARAMS; + expected_values[2] = END_OF_EXPECTED_VALUE; + } + + /* + * Build an MPID corresponding to the lead affinity instance at level 3. + * Set the affinity level0/level1/level2 bits to some arbitrary values + * that don't correspond to any affinity instance on the platform. The + * PSCI implementation should ignore the affinity 0, 1 & 2 fields. + */ + target_mpid = read_mpidr_el1(); + target_mpid &= ((uint64_t) MPIDR_AFFLVL_MASK) << MPIDR_AFF_SHIFT(3); + target_mpid |= 0xD2 << MPIDR_AFF2_SHIFT; + target_mpid |= 0xAB << MPIDR_AFF1_SHIFT; + target_mpid |= 0xE1 << MPIDR_AFF0_SHIFT; + + aff_info = tftf_psci_affinity_info(target_mpid, MPIDR_AFFLVL3); + return get_test_result(expected_values, aff_info); +#else + return TEST_RESULT_SKIPPED; +#endif +} + +/* + * Suspend to powerdown the calling CPU. + * + * 1) Enable SGI #0. This SGI will be sent by the lead CPU to wake this CPU. + * 2) Suspend the CPU. + * 3) Report success/failure of the suspend operation. + */ +static test_result_t suspend_to_powerdown(void) +{ + uint32_t power_state, stateid; + int psci_ret, expected_return_val; + + /* + * Enable reception of SGI 0 on the calling CPU. + * SGI 0 will serve as the wake-up event to come out of suspend. + */ + tftf_irq_enable(IRQ_NS_SGI_0, GIC_HIGHEST_NS_PRIORITY); + + expected_return_val = tftf_psci_make_composite_state_id( + PSTATE_AFF_LVL_0, PSTATE_TYPE_POWERDOWN, &stateid); + + /* Need at least 1 power down state defined at level 0 */ + if (expected_return_val != PSCI_E_SUCCESS) + return TEST_RESULT_SKIPPED; + + /* + * Suspend the calling CPU to the desired affinity level and power state + */ + power_state = tftf_make_psci_pstate(PSTATE_AFF_LVL_0, + PSTATE_TYPE_POWERDOWN, + stateid); + + /* + * Notify the lead CPU that the calling CPU is about to suspend itself + */ + tftf_send_event(&cpu_about_to_suspend); + + psci_ret = tftf_cpu_suspend(power_state); + + tftf_irq_disable(IRQ_NS_SGI_0); + + if (psci_ret != PSCI_E_SUCCESS) { + tftf_testcase_printf("Failed to suspend (%i)\n", psci_ret); + return TEST_RESULT_FAIL; + } + + return TEST_RESULT_SUCCESS; +} + +/* + * @Test_Aim@ Test PSCI AFFINITY_INFO targeted at affinity level 0 on a + * suspended CPU + * + * A CPU that has been physically powered down as a result of a call to + * CPU_SUSPEND must be reported as ON by the AFFINITY_INFO call. This test + * aims at verifying this behaviour. + * + * This test needs 2 CPUs to run. It will be skipped on a single core platform. + * It will also be skipped if an error is encountered during the bring-up of the + * non-lead CPU. + */ +test_result_t test_affinity_info_level0_powerdown(void) +{ + unsigned int lead_mpid = read_mpidr_el1() & MPID_MASK; + unsigned int target_mpid, target_core_pos; + int psci_ret; + int32_t aff_info; + test_result_t ret; + int expected_values[] = {PSCI_STATE_ON, END_OF_EXPECTED_VALUE}; + + SKIP_TEST_IF_LESS_THAN_N_CPUS(2); + + /* + * Preparation step: + * Find another CPU than the lead CPU and power it on. + */ + target_mpid = tftf_find_any_cpu_other_than(lead_mpid); + assert(target_mpid != INVALID_MPID); + target_core_pos = platform_get_core_pos(target_mpid); + + psci_ret = tftf_cpu_on(target_mpid, (uintptr_t) suspend_to_powerdown, 0); + if (psci_ret != PSCI_E_SUCCESS) { + tftf_testcase_printf("Failed to power on CPU 0x%x (%d)\n", + target_mpid, psci_ret); + return TEST_RESULT_SKIPPED; + } + + /* Wait for the other CPU to initiate the suspend operation */ + tftf_wait_for_event(&cpu_about_to_suspend); + + /* Wait a bit for the CPU to really enter suspend state */ + waitms(PLAT_SUSPEND_ENTRY_TIME); + + /* Request status of the non-lead CPU while it is suspended */ + aff_info = tftf_psci_affinity_info(target_mpid, MPIDR_AFFLVL0); + ret = get_test_result(expected_values, aff_info); + + /* Wake up non-lead CPU */ + tftf_send_sgi(IRQ_NS_SGI_0, target_core_pos); + + return ret; +} |