Demonstrate model for routing IRQs to EL3

This patch provides an option to specify a interrupt routing model
where non-secure interrupts (IRQs) are routed to EL3 instead of S-EL1.
When such an interrupt occurs, the TSPD arranges a return to
the normal world after saving any necessary context. The interrupt
routing model to route IRQs to EL3 is enabled only during STD SMC
processing. Thus the pre-emption of S-EL1 is disabled during Fast SMC
and Secure Interrupt processing.

A new build option TSPD_ROUTE_NS_INT_EL3 is introduced to change
the non secure interrupt target execution level to EL3.

Fixes ARM-software/tf-issues#225

Change-Id: Ia1e779fbbb6d627091e665c73fa6315637cfdd32
diff --git a/services/spd/tspd/tspd_main.c b/services/spd/tspd/tspd_main.c
index b8d4569..ee17483 100644
--- a/services/spd/tspd/tspd_main.c
+++ b/services/spd/tspd/tspd_main.c
@@ -48,6 +48,7 @@
 #include <platform.h>
 #include <runtime_svc.h>
 #include <stddef.h>
+#include <string.h>
 #include <tsp.h>
 #include <uuid.h>
 #include "tspd_private.h"
@@ -71,6 +72,24 @@
 
 int32_t tspd_init(void);
 
+uint64_t tspd_handle_sp_preemption(void *handle)
+{
+	cpu_context_t *ns_cpu_context;
+	assert(handle == cm_get_context(SECURE));
+	cm_el1_sysregs_context_save(SECURE);
+	/* Get a reference to the non-secure context */
+	ns_cpu_context = cm_get_context(NON_SECURE);
+	assert(ns_cpu_context);
+
+	/*
+	 * Restore non-secure state. The secure system
+	 * register context will be saved when required.
+	 */
+	cm_el1_sysregs_context_restore(NON_SECURE);
+	cm_set_next_eret_context(NON_SECURE);
+
+	SMC_RET1(ns_cpu_context, SMC_PREEMPTED);
+}
 /*******************************************************************************
  * This function is the handler registered for S-EL1 interrupts by the TSPD. It
  * validates the interrupt and upon success arranges entry into the TSP at
@@ -120,11 +139,16 @@
 						      CTX_SPSR_EL3);
 		tsp_ctx->saved_elr_el3 = SMC_GET_EL3(&tsp_ctx->cpu_ctx,
 						     CTX_ELR_EL3);
+#if TSPD_ROUTE_IRQ_TO_EL3
+		/*Need to save the previously interrupted secure context */
+		memcpy(&tsp_ctx->sp_ctx, &tsp_ctx->cpu_ctx, TSPD_SP_CTX_SIZE);
+#endif
 	}
 
 	cm_el1_sysregs_context_restore(SECURE);
 	cm_set_elr_spsr_el3(SECURE, (uint64_t) &tsp_vectors->fiq_entry,
 		    SPSR_64(MODE_EL1, MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS));
+
 	cm_set_next_eret_context(SECURE);
 
 	/*
@@ -136,6 +160,34 @@
 	 */
 	SMC_RET2(&tsp_ctx->cpu_ctx, TSP_HANDLE_FIQ_AND_RETURN, read_elr_el3());
 }
+
+#if TSPD_ROUTE_IRQ_TO_EL3
+/*******************************************************************************
+ * This function is the handler registered for S-EL1 interrupts by the TSPD. It
+ * validates the interrupt and upon success arranges entry into the TSP at
+ * 'tsp_fiq_entry()' for handling the interrupt.
+ ******************************************************************************/
+static uint64_t tspd_ns_interrupt_handler(uint32_t id,
+					    uint32_t flags,
+					    void *handle,
+					    void *cookie)
+{
+	/* Check the security state when the exception was generated */
+	assert(get_interrupt_src_ss(flags) == SECURE);
+
+#if IMF_READ_INTERRUPT_ID
+	/* Check the security status of the interrupt */
+	assert(plat_ic_get_interrupt_type(id) == INTR_TYPE_NS);
+#endif
+	/*
+	 * Disable the routing of NS interrupts from secure world to EL3 while
+	 * interrupted on this core.
+	 */
+	disable_intr_rm_local(INTR_TYPE_NS, SECURE);
+
+	return tspd_handle_sp_preemption(handle);
+}
+#endif
 
 /*******************************************************************************
  * Secure Payload Dispatcher setup. The SPD finds out the SP entrypoint and type
@@ -270,21 +322,7 @@
 		if (ns)
 			SMC_RET1(handle, SMC_UNK);
 
-		assert(handle == cm_get_context(SECURE));
-		cm_el1_sysregs_context_save(SECURE);
-		/* Get a reference to the non-secure context */
-		ns_cpu_context = cm_get_context(NON_SECURE);
-		assert(ns_cpu_context);
-
-		/*
-		 * Restore non-secure state. There is no need to save the
-		 * secure system register context since the TSP was supposed
-		 * to preserve it during S-EL1 interrupt handling.
-		 */
-		cm_el1_sysregs_context_restore(NON_SECURE);
-		cm_set_next_eret_context(NON_SECURE);
-
-		SMC_RET1(ns_cpu_context, SMC_PREEMPTED);
+		return tspd_handle_sp_preemption(handle);
 
 	/*
 	 * This function ID is used only by the TSP to indicate that it has
@@ -308,6 +346,14 @@
 			SMC_SET_EL3(&tsp_ctx->cpu_ctx,
 				    CTX_ELR_EL3,
 				    tsp_ctx->saved_elr_el3);
+#if TSPD_ROUTE_IRQ_TO_EL3
+			/*
+			 * Need to restore the previously interrupted
+			 * secure context.
+			 */
+			memcpy(&tsp_ctx->cpu_ctx, &tsp_ctx->sp_ctx,
+				TSPD_SP_CTX_SIZE);
+#endif
 		}
 
 		/* Get a reference to the non-secure context */
@@ -389,6 +435,28 @@
 						flags);
 			if (rc)
 				panic();
+
+#if TSPD_ROUTE_IRQ_TO_EL3
+			/*
+			 * Register an interrupt handler for NS interrupts when
+			 * generated during code executing in secure state are
+			 * routed to EL3.
+			 */
+			flags = 0;
+			set_interrupt_rm_flag(flags, SECURE);
+
+			rc = register_interrupt_type_handler(INTR_TYPE_NS,
+						tspd_ns_interrupt_handler,
+						flags);
+			if (rc)
+				panic();
+
+			/*
+			 * Disable the interrupt NS locally since it will be enabled globally
+			 * within cm_init_context.
+			 */
+			disable_intr_rm_local(INTR_TYPE_NS, SECURE);
+#endif
 		}
 
 
@@ -507,6 +575,13 @@
 				set_std_smc_active_flag(tsp_ctx->state);
 				cm_set_elr_el3(SECURE, (uint64_t)
 						&tsp_vectors->std_smc_entry);
+#if TSPD_ROUTE_IRQ_TO_EL3
+				/*
+				 * Enable the routing of NS interrupts to EL3
+				 * during STD SMC processing on this core.
+				 */
+				enable_intr_rm_local(INTR_TYPE_NS, SECURE);
+#endif
 			}
 
 			cm_el1_sysregs_context_restore(SECURE);
@@ -529,8 +604,18 @@
 			/* Restore non-secure state */
 			cm_el1_sysregs_context_restore(NON_SECURE);
 			cm_set_next_eret_context(NON_SECURE);
-			if (GET_SMC_TYPE(smc_fid) == SMC_TYPE_STD)
+			if (GET_SMC_TYPE(smc_fid) == SMC_TYPE_STD) {
 				clr_std_smc_active_flag(tsp_ctx->state);
+#if TSPD_ROUTE_IRQ_TO_EL3
+				/*
+				 * Disable the routing of NS interrupts to EL3
+				 * after STD SMC processing is finished on this
+				 * core.
+				 */
+				disable_intr_rm_local(INTR_TYPE_NS, SECURE);
+#endif
+			}
+
 			SMC_RET3(ns_cpu_context, x1, x2, x3);
 		}
 
@@ -564,6 +649,15 @@
 		 * We are done stashing the non-secure context. Ask the
 		 * secure payload to do the work now.
 		 */
+#if TSPD_ROUTE_IRQ_TO_EL3
+		/*
+		 * Enable the routing of NS interrupts to EL3 during resumption
+		 * of STD SMC call on this core.
+		 */
+		enable_intr_rm_local(INTR_TYPE_NS, SECURE);
+#endif
+
+
 
 		/* We just need to return to the preempted point in
 		 * TSP and the execution will resume as normal.