From b234b2c4a06169aa965b77dd40c17be454a9f609 Mon Sep 17 00:00:00 2001
From: Soby Mathew <soby.mathew@arm.com>
Date: Thu, 15 Jan 2015 11:49:49 +0000
Subject: [PATCH] Verify capabilities before handling PSCI calls

This patch implements conditional checks in psci_smc_handler() to verify
that the psci function invoked by the caller is supported by the platform
or SPD implementation. The level of support is saved in the 'psci_caps'
variable. This check allows the PSCI implementation to return an error
early.

As a result of the above verification, the checks performed within the psci
handlers for the pm hooks are now removed and replaced with assertions.

Change-Id: I9b5b646a01d8566dc28c4d77dd3aa54e9bf3981a
---
 services/std_svc/psci/psci_afflvl_off.c     | 12 ++++++------
 services/std_svc/psci/psci_afflvl_on.c      | 18 +++++++-----------
 services/std_svc/psci/psci_afflvl_suspend.c | 18 +++++++-----------
 services/std_svc/psci/psci_main.c           |  4 ++++
 services/std_svc/psci/psci_system_off.c     | 17 +++++------------
 5 files changed, 29 insertions(+), 40 deletions(-)

diff --git a/services/std_svc/psci/psci_afflvl_off.c b/services/std_svc/psci/psci_afflvl_off.c
index ceb51f83e..7eb968899 100644
--- a/services/std_svc/psci/psci_afflvl_off.c
+++ b/services/std_svc/psci/psci_afflvl_off.c
@@ -51,8 +51,6 @@ static void psci_afflvl0_off(aff_map_node_t *cpu_node)
 	 */
 	psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL0);
 
-	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.
@@ -72,8 +70,6 @@ static void psci_afflvl1_off(aff_map_node_t *cluster_node)
 	 */
 	psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL1);
 
-	assert(psci_plat_pm_ops->affinst_off);
-
 	/*
 	 * Plat. Management. Allow the platform to do its cluster
 	 * specific bookeeping e.g. turn off interconnect coherency,
@@ -99,8 +95,6 @@ static void psci_afflvl2_off(aff_map_node_t *system_node)
 	 */
 	psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL2);
 
-	assert(psci_plat_pm_ops->affinst_off);
-
 	/*
 	 * Plat. Management : Allow the platform to do its bookeeping
 	 * at this affinity level
@@ -162,6 +156,12 @@ int psci_afflvl_off(int start_afflvl,
 	mpidr_aff_map_nodes_t mpidr_nodes;
 	unsigned int max_phys_off_afflvl;
 
+	/*
+	 * This function must only be called on platforms where the
+	 * CPU_OFF platform hooks have been implemented.
+	 */
+	assert(psci_plat_pm_ops->affinst_off);
+
 	/*
 	 * Collect the pointers to the nodes in the topology tree for
 	 * each affinity instance in the mpidr. If this function does
diff --git a/services/std_svc/psci/psci_afflvl_on.c b/services/std_svc/psci/psci_afflvl_on.c
index ad212b652..0ee03cb5e 100644
--- a/services/std_svc/psci/psci_afflvl_on.c
+++ b/services/std_svc/psci/psci_afflvl_on.c
@@ -75,8 +75,6 @@ static int psci_afflvl0_on(unsigned long target_cpu,
 	/* Set the secure world (EL3) re-entry point after BL1 */
 	psci_entrypoint = (unsigned long) psci_aff_on_finish_entry;
 
-	assert(psci_plat_pm_ops->affinst_on);
-
 	/*
 	 * Plat. management: Give the platform the current state
 	 * of the target cpu to allow it to perform the necessary
@@ -107,8 +105,6 @@ static int psci_afflvl1_on(unsigned long target_cpu,
 
 	/* State management: Is not required while turning a cluster on */
 
-	assert(psci_plat_pm_ops->affinst_on);
-
 	/*
 	 * Plat. management: Give the platform the current state
 	 * of the target cpu to allow it to perform the necessary
@@ -141,8 +137,6 @@ static int psci_afflvl2_on(unsigned long target_cpu,
 
 	/* State management: Is not required while turning a system on */
 
-	assert(psci_plat_pm_ops->affinst_on);
-
 	/*
 	 * Plat. management: Give the platform the current state
 	 * of the target cpu to allow it to perform the necessary
@@ -218,6 +212,13 @@ int psci_afflvl_on(unsigned long target_cpu,
 	int rc;
 	mpidr_aff_map_nodes_t target_cpu_nodes;
 
+	/*
+	 * This function must only be called on platforms where the
+	 * CPU_ON platform hooks have been implemented.
+	 */
+	assert(psci_plat_pm_ops->affinst_on &&
+			psci_plat_pm_ops->affinst_on_finish);
+
 	/*
 	 * Collect the pointers to the nodes in the topology tree for
 	 * each affinity instance in the mpidr. If this function does
@@ -313,7 +314,6 @@ static void psci_afflvl0_on_finish(aff_map_node_t *cpu_node)
 	 * register. The actual state of this cpu has already been
 	 * changed.
 	 */
-	assert(psci_plat_pm_ops->affinst_on_finish);
 
 	/* Get the physical state of this cpu */
 	plat_state = get_phys_state(state);
@@ -357,8 +357,6 @@ static void psci_afflvl1_on_finish(aff_map_node_t *cluster_node)
 
 	assert(cluster_node->level == MPIDR_AFFLVL1);
 
-	assert(psci_plat_pm_ops->affinst_on_finish);
-
 	/*
 	 * Plat. management: Perform the platform specific actions
 	 * as per the old state of the cluster e.g. enabling
@@ -380,8 +378,6 @@ static void psci_afflvl2_on_finish(aff_map_node_t *system_node)
 	/* Cannot go beyond this affinity level */
 	assert(system_node->level == MPIDR_AFFLVL2);
 
-	assert(psci_plat_pm_ops->affinst_on_finish);
-
 	/*
 	 * Currently, there are no architectural actions to perform
 	 * at the system level.
diff --git a/services/std_svc/psci/psci_afflvl_suspend.c b/services/std_svc/psci/psci_afflvl_suspend.c
index 9ede65d01..dad0cefd8 100644
--- a/services/std_svc/psci/psci_afflvl_suspend.c
+++ b/services/std_svc/psci/psci_afflvl_suspend.c
@@ -119,8 +119,6 @@ static void psci_afflvl0_suspend(aff_map_node_t *cpu_node)
 	 */
 	psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL0);
 
-	assert(psci_plat_pm_ops->affinst_suspend);
-
 	/*
 	 * Plat. management: Allow the platform to perform the
 	 * necessary actions to turn off this cpu e.g. set the
@@ -146,8 +144,6 @@ static void psci_afflvl1_suspend(aff_map_node_t *cluster_node)
 	 */
 	psci_do_pwrdown_cache_maintenance(MPIDR_AFFLVL1);
 
-	assert(psci_plat_pm_ops->affinst_suspend);
-
 	/*
 	 * Plat. Management. Allow the platform to do its cluster specific
 	 * bookeeping e.g. turn off interconnect coherency, program the power
@@ -188,7 +184,6 @@ static void psci_afflvl2_suspend(aff_map_node_t *system_node)
 	 * Plat. Management : Allow the platform to do its bookeeping
 	 * at this affinity level
 	 */
-	assert(psci_plat_pm_ops->affinst_suspend);
 
 	/*
 	 * Sending the psci entrypoint is currently redundant
@@ -261,6 +256,13 @@ void psci_afflvl_suspend(entry_point_info_t *ep,
 	mpidr_aff_map_nodes_t mpidr_nodes;
 	unsigned int max_phys_off_afflvl;
 
+	/*
+	 * This function must only be called on platforms where the
+	 * CPU_SUSPEND platform hooks have been implemented.
+	 */
+	assert(psci_plat_pm_ops->affinst_suspend &&
+			psci_plat_pm_ops->affinst_suspend_finish);
+
 	/*
 	 * Collect the pointers to the nodes in the topology tree for
 	 * each affinity instance in the mpidr. If this function does
@@ -370,8 +372,6 @@ static void psci_afflvl0_suspend_finish(aff_map_node_t *cpu_node)
 	 * situation.
 	 */
 
-	assert(psci_plat_pm_ops->affinst_suspend_finish);
-
 	/* Get the physical state of this cpu */
 	plat_state = get_phys_state(state);
 	psci_plat_pm_ops->affinst_suspend_finish(cpu_node->level,
@@ -428,8 +428,6 @@ static void psci_afflvl1_suspend_finish(aff_map_node_t *cluster_node)
 	 * situation.
 	 */
 
-	assert(psci_plat_pm_ops->affinst_suspend_finish);
-
 	/* Get the physical state of this cpu */
 	plat_state = psci_get_phys_state(cluster_node);
 	psci_plat_pm_ops->affinst_suspend_finish(cluster_node->level,
@@ -458,8 +456,6 @@ static void psci_afflvl2_suspend_finish(aff_map_node_t *system_node)
 	 * situation.
 	 */
 
-	assert(psci_plat_pm_ops->affinst_suspend_finish);
-
 	/* Get the physical state of the system */
 	plat_state = psci_get_phys_state(system_node);
 	psci_plat_pm_ops->affinst_suspend_finish(system_node->level,
diff --git a/services/std_svc/psci/psci_main.c b/services/std_svc/psci/psci_main.c
index 0e10ac050..d8a00097b 100644
--- a/services/std_svc/psci/psci_main.c
+++ b/services/std_svc/psci/psci_main.c
@@ -321,6 +321,10 @@ uint64_t psci_smc_handler(uint32_t smc_fid,
 	if (is_caller_secure(flags))
 		SMC_RET1(handle, SMC_UNK);
 
+	/* Check the fid against the capabilities */
+	if (!(psci_caps & define_psci_cap(smc_fid)))
+		SMC_RET1(handle, SMC_UNK);
+
 	if (((smc_fid >> FUNCID_CC_SHIFT) & FUNCID_CC_MASK) == SMC_32) {
 		/* 32-bit PSCI function, clear top parameter bits */
 
diff --git a/services/std_svc/psci/psci_system_off.c b/services/std_svc/psci/psci_system_off.c
index f2520b6dd..970d4bb50 100644
--- a/services/std_svc/psci/psci_system_off.c
+++ b/services/std_svc/psci/psci_system_off.c
@@ -30,20 +30,17 @@
 
 #include <stddef.h>
 #include <arch_helpers.h>
+#include <assert.h>
 #include <debug.h>
 #include <platform.h>
 #include "psci_private.h"
 
 void psci_system_off(void)
 {
-	/* Check platform support */
-	if (!psci_plat_pm_ops->system_off) {
-		ERROR("Platform has not exported a PSCI System Off hook.\n");
-		panic();
-	}
-
 	psci_print_affinity_map();
 
+	assert(psci_plat_pm_ops->system_off);
+
 	/* Notify the Secure Payload Dispatcher */
 	if (psci_spd_pm && psci_spd_pm->svc_system_off) {
 		psci_spd_pm->svc_system_off();
@@ -57,14 +54,10 @@ void psci_system_off(void)
 
 void psci_system_reset(void)
 {
-	/* Check platform support */
-	if (!psci_plat_pm_ops->system_reset) {
-		ERROR("Platform has not exported a PSCI System Reset hook.\n");
-		panic();
-	}
-
 	psci_print_affinity_map();
 
+	assert(psci_plat_pm_ops->system_reset);
+
 	/* Notify the Secure Payload Dispatcher */
 	if (psci_spd_pm && psci_spd_pm->svc_system_reset) {
 		psci_spd_pm->svc_system_reset();
-- 
GitLab