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.