Trusted Firmware-A Tests, version 2.0
This is the first public version of the tests for the Trusted
Firmware-A project. Please see the documentation provided in the
source tree for more details.
Change-Id: I6f3452046a1351ac94a71b3525c30a4ca8db7867
Signed-off-by: Sandrine Bailleux <sandrine.bailleux@arm.com>
Co-authored-by: amobal01 <amol.balasokamble@arm.com>
Co-authored-by: Antonio Nino Diaz <antonio.ninodiaz@arm.com>
Co-authored-by: Asha R <asha.r@arm.com>
Co-authored-by: Chandni Cherukuri <chandni.cherukuri@arm.com>
Co-authored-by: David Cunado <david.cunado@arm.com>
Co-authored-by: Dimitris Papastamos <dimitris.papastamos@arm.com>
Co-authored-by: Douglas Raillard <douglas.raillard@arm.com>
Co-authored-by: dp-arm <dimitris.papastamos@arm.com>
Co-authored-by: Jeenu Viswambharan <jeenu.viswambharan@arm.com>
Co-authored-by: Jonathan Wright <jonathan.wright@arm.com>
Co-authored-by: Kévin Petit <kevin.petit@arm.com>
Co-authored-by: Roberto Vargas <roberto.vargas@arm.com>
Co-authored-by: Sathees Balya <sathees.balya@arm.com>
Co-authored-by: Shawon Roy <Shawon.Roy@arm.com>
Co-authored-by: Soby Mathew <soby.mathew@arm.com>
Co-authored-by: Thomas Abraham <thomas.abraham@arm.com>
Co-authored-by: Vikram Kanigiri <vikram.kanigiri@arm.com>
Co-authored-by: Yatharth Kochar <yatharth.kochar@arm.com>
diff --git a/lib/psci/psci.c b/lib/psci/psci.c
new file mode 100644
index 0000000..520c724
--- /dev/null
+++ b/lib/psci/psci.c
@@ -0,0 +1,341 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <arm_gic.h>
+#include <debug.h>
+#include <irq.h>
+#include <platform.h>
+#include <power_management.h>
+#include <psci.h>
+#include <sgi.h>
+#include <tftf.h>
+#include <tftf_lib.h>
+
+static unsigned int pstate_format_detected;
+static unsigned int pstate_format;
+static unsigned int is_state_id_null;
+
+const psci_function_t psci_functions[PSCI_NUM_CALLS] = {
+ DEFINE_PSCI_FUNC(PSCI_FEATURES, true),
+ DEFINE_PSCI_FUNC(PSCI_VERSION, true),
+ DEFINE_PSCI_FUNC(PSCI_CPU_SUSPEND_AARCH32, true),
+ DEFINE_PSCI_FUNC(PSCI_CPU_SUSPEND_AARCH64, true),
+ DEFINE_PSCI_FUNC(PSCI_CPU_OFF, true),
+ DEFINE_PSCI_FUNC(PSCI_CPU_ON_AARCH32, true),
+ DEFINE_PSCI_FUNC(PSCI_CPU_ON_AARCH64, true),
+ DEFINE_PSCI_FUNC(PSCI_AFFINITY_INFO_AARCH32, true),
+ DEFINE_PSCI_FUNC(PSCI_AFFINITY_INFO_AARCH64, true),
+ DEFINE_PSCI_FUNC(PSCI_SYSTEM_OFF, true),
+ DEFINE_PSCI_FUNC(PSCI_SYSTEM_RESET, true),
+ DEFINE_PSCI_FUNC(PSCI_MIG_INFO_TYPE, false),
+ DEFINE_PSCI_FUNC(PSCI_MIG_INFO_UP_CPU_AARCH32, false),
+ DEFINE_PSCI_FUNC(PSCI_MIG_INFO_UP_CPU_AARCH64, false),
+ DEFINE_PSCI_FUNC(PSCI_MIG_AARCH32, false),
+ DEFINE_PSCI_FUNC(PSCI_MIG_AARCH64, false),
+ DEFINE_PSCI_FUNC(PSCI_CPU_FREEZE, false),
+ DEFINE_PSCI_FUNC(PSCI_CPU_DEFAULT_SUSPEND32, false),
+ DEFINE_PSCI_FUNC(PSCI_CPU_DEFAULT_SUSPEND64, false),
+ DEFINE_PSCI_FUNC(PSCI_CPU_HW_STATE32, false),
+ DEFINE_PSCI_FUNC(PSCI_CPU_HW_STATE64, false),
+ DEFINE_PSCI_FUNC(PSCI_SYSTEM_SUSPEND32, false),
+ DEFINE_PSCI_FUNC(PSCI_SYSTEM_SUSPEND64, false),
+ DEFINE_PSCI_FUNC(PSCI_SET_SUSPEND_MODE, false),
+ DEFINE_PSCI_FUNC(PSCI_STAT_RESIDENCY32, false),
+ DEFINE_PSCI_FUNC(PSCI_STAT_RESIDENCY64, false),
+ DEFINE_PSCI_FUNC(PSCI_STAT_COUNT32, false),
+ DEFINE_PSCI_FUNC(PSCI_STAT_COUNT64, false),
+ DEFINE_PSCI_FUNC(PSCI_MEM_PROTECT, false),
+ DEFINE_PSCI_FUNC(PSCI_MEM_PROTECT_CHECK_RANGE32, false),
+ DEFINE_PSCI_FUNC(PSCI_MEM_PROTECT_CHECK_RANGE64, false),
+ DEFINE_PSCI_FUNC(PSCI_RESET2_AARCH32, false),
+ DEFINE_PSCI_FUNC(PSCI_RESET2_AARCH64, false),
+};
+
+int32_t tftf_psci_cpu_on(u_register_t target_cpu,
+ uintptr_t entry_point_address,
+ u_register_t context_id)
+{
+ smc_args args = {
+ SMC_PSCI_CPU_ON,
+ target_cpu,
+ entry_point_address,
+ context_id
+ };
+ smc_ret_values ret_vals;
+
+ ret_vals = tftf_smc(&args);
+
+ return ret_vals.ret0;
+}
+
+int32_t tftf_psci_cpu_off(void)
+{
+ smc_args args = { SMC_PSCI_CPU_OFF };
+ smc_ret_values ret_vals;
+
+ ret_vals = tftf_smc(&args);
+ return ret_vals.ret0;
+}
+
+
+u_register_t tftf_psci_stat_residency(u_register_t target_cpu,
+ uint32_t power_state)
+{
+ smc_args args = {
+ SMC_PSCI_STAT_RESIDENCY,
+ target_cpu,
+ power_state,
+ };
+ smc_ret_values ret_vals;
+
+ ret_vals = tftf_smc(&args);
+ return ret_vals.ret0;
+}
+
+u_register_t tftf_psci_stat_count(u_register_t target_cpu,
+ uint32_t power_state)
+{
+ smc_args args = {
+ SMC_PSCI_STAT_COUNT,
+ target_cpu,
+ power_state,
+ };
+ smc_ret_values ret_vals;
+
+ ret_vals = tftf_smc(&args);
+ return ret_vals.ret0;
+}
+
+int32_t tftf_psci_affinity_info(u_register_t target_affinity,
+ uint32_t lowest_affinity_level)
+{
+ smc_ret_values ret_vals;
+
+ smc_args args = {
+ SMC_PSCI_AFFINITY_INFO,
+ target_affinity,
+ lowest_affinity_level
+ };
+
+ ret_vals = tftf_smc(&args);
+ return ret_vals.ret0;
+}
+
+int32_t tftf_psci_node_hw_state(u_register_t target_cpu, uint32_t power_level)
+{
+ smc_args args = {
+ SMC_PSCI_CPU_HW_STATE,
+ target_cpu,
+ power_level
+ };
+ smc_ret_values ret;
+
+ ret = tftf_smc(&args);
+ return ret.ret0;
+}
+
+int32_t tftf_get_psci_feature_info(uint32_t psci_func_id)
+{
+ smc_args args = {
+ SMC_PSCI_FEATURES,
+ psci_func_id
+ };
+ smc_ret_values ret;
+
+ ret = tftf_smc(&args);
+ return ret.ret0;
+}
+
+int tftf_psci_make_composite_state_id(uint32_t affinity_level,
+ uint32_t state_type, uint32_t *state_id)
+{
+ unsigned int found_entry, i;
+ int ret = PSCI_E_SUCCESS;
+ const plat_state_prop_t *state_prop;
+
+ assert(state_id);
+
+ *state_id = 0;
+ for (i = 0; i <= affinity_level; i++) {
+ state_prop = plat_get_state_prop(i);
+ if (!state_prop) {
+ *state_id |= psci_make_local_state_id(i,
+ PLAT_PSCI_DUMMY_STATE_ID);
+ ret = PSCI_E_INVALID_PARAMS;
+ continue;
+ }
+ found_entry = 0;
+
+ while (state_prop->state_ID) {
+ if (state_type == state_prop->is_pwrdown) {
+ *state_id |= psci_make_local_state_id(i,
+ state_prop->state_ID);
+ found_entry = 1;
+ break;
+ }
+ state_prop++;
+ }
+ if (!found_entry) {
+ *state_id |= psci_make_local_state_id(i,
+ PLAT_PSCI_DUMMY_STATE_ID);
+ ret = PSCI_E_INVALID_PARAMS;
+ }
+ }
+
+ return ret;
+}
+
+static unsigned int tftf_psci_get_pstate_format(void)
+{
+ int ret;
+
+ ret = tftf_get_psci_feature_info(SMC_PSCI_CPU_SUSPEND);
+
+ /*
+ * If error is returned, then it probably means that the PSCI version
+ * is less than 1.0 and only the original format is supported. In case
+ * the version is 1.0 or higher, then PSCI FEATURES which is a
+ * mandatory API is not implemented which implies that only the
+ * original format is supported.
+ */
+ if (ret == PSCI_E_NOT_SUPPORTED)
+ return CPU_SUSPEND_FEAT_PSTATE_FORMAT_ORIGINAL;
+
+ /* Treat the invalid return value as PSCI FEATURES not supported */
+ if ((ret & ~CPU_SUSPEND_FEAT_VALID_MASK) != 0)
+ return CPU_SUSPEND_FEAT_PSTATE_FORMAT_ORIGINAL;
+
+ return (ret >> CPU_SUSPEND_FEAT_PSTATE_FORMAT_SHIFT) & 0x1;
+}
+
+/* Make the power state in the original format */
+uint32_t tftf_make_psci_pstate(uint32_t affinity_level,
+ uint32_t state_type,
+ uint32_t state_id)
+{
+ uint32_t power_state;
+
+ assert(psci_state_type_valid(state_type));
+
+ assert(pstate_format_detected);
+
+ if (pstate_format == CPU_SUSPEND_FEAT_PSTATE_FORMAT_EXTENDED) {
+ assert(psci_state_id_ext_valid(state_id));
+ power_state = (state_type << PSTATE_TYPE_SHIFT_EXT)
+ | (state_id << PSTATE_ID_SHIFT_EXT);
+ } else {
+ assert(psci_affinity_level_valid(affinity_level));
+ assert(psci_state_id_valid(state_id));
+ power_state = (affinity_level << PSTATE_AFF_LVL_SHIFT)
+ | (state_type << PSTATE_TYPE_SHIFT);
+ if (!is_state_id_null)
+ power_state |= (state_id << PSTATE_ID_SHIFT);
+ }
+
+ return power_state;
+}
+
+
+void tftf_detect_psci_pstate_format(void)
+{
+ uint32_t power_state;
+ unsigned int ret;
+
+ pstate_format = tftf_psci_get_pstate_format();
+
+ /*
+ * If the power state format is extended format, then the state-ID
+ * must use recommended encoding.
+ */
+ if (pstate_format == CPU_SUSPEND_FEAT_PSTATE_FORMAT_EXTENDED) {
+ pstate_format_detected = 1;
+ INFO("Extended PSCI power state format detected\n");
+ return;
+ }
+
+ tftf_irq_enable(IRQ_NS_SGI_0, GIC_HIGHEST_NS_PRIORITY);
+
+ /*
+ * Mask IRQ to prevent the interrupt handler being invoked
+ * and clearing the interrupt. A pending interrupt will cause this
+ * CPU to wake-up from suspend.
+ */
+ disable_irq();
+
+ /* Configure an SGI to wake-up from suspend */
+ tftf_send_sgi(IRQ_NS_SGI_0,
+ platform_get_core_pos(read_mpidr_el1() & MPID_MASK));
+
+
+ /*
+ * Try to detect if the platform uses NULL State-ID encoding by sending
+ * PSCI_SUSPEND call with the NULL State-ID encoding. If the call
+ * succeeds then the platform uses NULL State-ID encoding. Else it
+ * uses the recommended encoding for State-ID.
+ */
+ power_state = (PSTATE_AFF_LVL_0 << PSTATE_AFF_LVL_SHIFT)
+ | (PSTATE_TYPE_STANDBY << PSTATE_TYPE_SHIFT);
+
+ ret = tftf_cpu_suspend(power_state);
+
+ /* Unmask the IRQ to let the interrupt handler to execute */
+ enable_irq();
+ isb();
+
+ tftf_irq_disable(IRQ_NS_SGI_0);
+
+ /*
+ * The NULL State-ID returned SUCCESS. Hence State-ID is NULL
+ * for the power state format.
+ */
+ if (ret == PSCI_E_SUCCESS) {
+ is_state_id_null = 1;
+ INFO("Original PSCI power state format with NULL State-ID detected\n");
+ } else
+ INFO("Original PSCI power state format detected\n");
+
+
+ pstate_format_detected = 1;
+}
+
+unsigned int tftf_is_psci_state_id_null(void)
+{
+ assert(pstate_format_detected);
+
+ if (pstate_format == CPU_SUSPEND_FEAT_PSTATE_FORMAT_ORIGINAL)
+ return is_state_id_null;
+ else
+ return 0; /* Extended state ID does not support null format */
+}
+
+unsigned int tftf_is_psci_pstate_format_original(void)
+{
+ assert(pstate_format_detected);
+
+ return pstate_format == CPU_SUSPEND_FEAT_PSTATE_FORMAT_ORIGINAL;
+}
+
+unsigned int tftf_get_psci_version(void)
+{
+ smc_args args = { SMC_PSCI_VERSION };
+ smc_ret_values ret;
+
+ ret = tftf_smc(&args);
+
+ return ret.ret0;
+}
+
+int tftf_is_valid_psci_version(unsigned int version)
+{
+ if (version != PSCI_VERSION(1, 1) &&
+ version != PSCI_VERSION(1, 0) &&
+ version != PSCI_VERSION(0, 2) &&
+ version != PSCI_VERSION(0, 1)) {
+ return 0;
+ }
+ return 1;
+}