Validate power_state and entrypoint when executing PSCI calls
This patch allows the platform to validate the power_state and
entrypoint information from the normal world early on in PSCI
calls so that we can return the error safely. New optional
pm_ops hooks `validate_power_state` and `validate_ns_entrypoint`
are introduced to do this.
As a result of these changes, all the other pm_ops handlers except
the PSCI_ON handler are expected to be successful. Also, the PSCI
implementation will now assert if a PSCI API is invoked without the
corresponding pm_ops handler being registered by the platform.
NOTE : PLATFORM PORTS WILL BREAK ON MERGE OF THIS COMMIT. The
pm hooks have 2 additional optional callbacks and the return type
of the other hooks have changed.
Fixes ARM-Software/tf-issues#229
Change-Id: I036bc0cff2349187c7b8b687b9ee0620aa7e24dc
diff --git a/services/std_svc/psci/psci_afflvl_off.c b/services/std_svc/psci/psci_afflvl_off.c
index 6a683cd..d1b7e88 100644
--- a/services/std_svc/psci/psci_afflvl_off.c
+++ b/services/std_svc/psci/psci_afflvl_off.c
@@ -31,55 +31,37 @@
#include <arch.h>
#include <arch_helpers.h>
#include <assert.h>
+#include <debug.h>
#include <string.h>
#include "psci_private.h"
-typedef int (*afflvl_off_handler_t)(aff_map_node_t *node);
+typedef void (*afflvl_off_handler_t)(aff_map_node_t *node);
/*******************************************************************************
* The next three functions implement a handler for each supported affinity
* level which is called when that affinity level is turned off.
******************************************************************************/
-static int psci_afflvl0_off(aff_map_node_t *cpu_node)
+static void psci_afflvl0_off(aff_map_node_t *cpu_node)
{
- int rc;
-
assert(cpu_node->level == MPIDR_AFFLVL0);
/*
- * Generic management: Get the index for clearing any lingering re-entry
- * information and allow the secure world to switch itself off
- */
-
- /*
- * Call the cpu off handler registered by the Secure Payload Dispatcher
- * to let it do any bookeeping. Assume that the SPD always reports an
- * E_DENIED error if SP refuse to power down
- */
- if (psci_spd_pm && psci_spd_pm->svc_off) {
- rc = psci_spd_pm->svc_off(0);
- if (rc)
- return rc;
- }
-
- /*
* Arch. management. Perform the necessary steps to flush all
* cpu caches.
*/
psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL0);
- if (!psci_plat_pm_ops->affinst_off)
- return PSCI_E_SUCCESS;
+ assert(psci_plat_pm_ops->affinst_off);
/*
* Plat. management: Perform platform specific actions to turn this
* cpu off e.g. exit cpu coherency, program the power controller etc.
*/
- return psci_plat_pm_ops->affinst_off(cpu_node->level,
- psci_get_phys_state(cpu_node));
+ psci_plat_pm_ops->affinst_off(cpu_node->level,
+ psci_get_phys_state(cpu_node));
}
-static int psci_afflvl1_off(aff_map_node_t *cluster_node)
+static void psci_afflvl1_off(aff_map_node_t *cluster_node)
{
/* Sanity check the cluster level */
assert(cluster_node->level == MPIDR_AFFLVL1);
@@ -90,19 +72,18 @@
*/
psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL1);
- if (!psci_plat_pm_ops->affinst_off)
- return PSCI_E_SUCCESS;
+ assert(psci_plat_pm_ops->affinst_off);
/*
* Plat. Management. Allow the platform to do its cluster
* specific bookeeping e.g. turn off interconnect coherency,
* program the power controller etc.
*/
- return psci_plat_pm_ops->affinst_off(cluster_node->level,
+ psci_plat_pm_ops->affinst_off(cluster_node->level,
psci_get_phys_state(cluster_node));
}
-static int psci_afflvl2_off(aff_map_node_t *system_node)
+static void psci_afflvl2_off(aff_map_node_t *system_node)
{
/* Cannot go beyond this level */
assert(system_node->level == MPIDR_AFFLVL2);
@@ -118,14 +99,13 @@
*/
psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL2);
- if (!psci_plat_pm_ops->affinst_off)
- return PSCI_E_SUCCESS;
+ assert(psci_plat_pm_ops->affinst_off);
/*
* Plat. Management : Allow the platform to do its bookeeping
* at this affinity level
*/
- return psci_plat_pm_ops->affinst_off(system_node->level,
+ psci_plat_pm_ops->affinst_off(system_node->level,
psci_get_phys_state(system_node));
}
@@ -140,11 +120,11 @@
* topology tree and calls the off handler for the corresponding affinity
* levels
******************************************************************************/
-static int psci_call_off_handlers(aff_map_node_t *mpidr_nodes[],
+static void psci_call_off_handlers(aff_map_node_t *mpidr_nodes[],
int start_afflvl,
int end_afflvl)
{
- int rc = PSCI_E_INVALID_PARAMS, level;
+ int level;
aff_map_node_t *node;
for (level = start_afflvl; level <= end_afflvl; level++) {
@@ -152,17 +132,8 @@
if (node == NULL)
continue;
- /*
- * TODO: In case of an error should there be a way
- * of restoring what we might have torn down at
- * lower affinity levels.
- */
- rc = psci_afflvl_off_handlers[level](node);
- if (rc != PSCI_E_SUCCESS)
- break;
+ psci_afflvl_off_handlers[level](node);
}
-
- return rc;
}
/*******************************************************************************
@@ -187,7 +158,7 @@
int psci_afflvl_off(int start_afflvl,
int end_afflvl)
{
- int rc = PSCI_E_SUCCESS;
+ int rc;
mpidr_aff_map_nodes_t mpidr_nodes;
unsigned int max_phys_off_afflvl;
@@ -195,14 +166,14 @@
* Collect the pointers to the nodes in the topology tree for
* each affinity instance in the mpidr. If this function does
* not return successfully then either the mpidr or the affinity
- * levels are incorrect. In either case, we cannot return back
- * to the caller as it would not know what to do.
+ * levels are incorrect. Either way, this an internal TF error
+ * therefore assert.
*/
rc = psci_get_aff_map_nodes(read_mpidr_el1() & MPIDR_AFFINITY_MASK,
start_afflvl,
end_afflvl,
mpidr_nodes);
- assert (rc == PSCI_E_SUCCESS);
+ assert(rc == PSCI_E_SUCCESS);
/*
* This function acquires the lock corresponding to each affinity
@@ -213,6 +184,18 @@
end_afflvl,
mpidr_nodes);
+
+ /*
+ * Call the cpu off handler registered by the Secure Payload Dispatcher
+ * to let it do any bookkeeping. Assume that the SPD always reports an
+ * E_DENIED error if SP refuse to power down
+ */
+ if (psci_spd_pm && psci_spd_pm->svc_off) {
+ rc = psci_spd_pm->svc_off(0);
+ if (rc)
+ goto exit;
+ }
+
/*
* This function updates the state of each affinity instance
* corresponding to the mpidr in the range of affinity levels
@@ -232,7 +215,7 @@
psci_set_max_phys_off_afflvl(max_phys_off_afflvl);
/* Perform generic, architecture and platform specific handling */
- rc = psci_call_off_handlers(mpidr_nodes,
+ psci_call_off_handlers(mpidr_nodes,
start_afflvl,
end_afflvl);
@@ -244,6 +227,7 @@
*/
psci_set_max_phys_off_afflvl(PSCI_INVALID_DATA);
+exit:
/*
* Release the locks corresponding to each affinity level in the
* reverse order to which they were acquired.