Unify interrupt return paths from TSP into the TSPD
authorSoby Mathew <soby.mathew@arm.com>
Tue, 22 Sep 2015 11:01:18 +0000 (12:01 +0100)
committerSoby Mathew <soby.mathew@arm.com>
Fri, 4 Dec 2015 12:02:12 +0000 (12:02 +0000)
The TSP is expected to pass control back to EL3 if it gets preempted due to
an interrupt while handling a Standard SMC in the following scenarios:

1. An FIQ preempts Standard SMC execution and that FIQ is not a TSP Secure
   timer interrupt or is preempted by a higher priority interrupt by the time
   the TSP acknowledges it. In this case, the TSP issues an SMC with the ID
   as `TSP_EL3_FIQ`. Currently this case is never expected to happen as only
   the TSP Secure Timer is expected to generate FIQ.

2. An IRQ preempts Standard SMC execution and in this case the TSP issues
   an SMC with the ID as `TSP_PREEMPTED`.

In both the cases, the TSPD hands control back to the normal world and returns
returns an error code to the normal world to indicate that the standard SMC it
had issued has been preempted but not completed.

This patch unifies the handling of these two cases in the TSPD and ensures that
the TSP only uses TSP_PREEMPTED instead of separate SMC IDs. Also instead of 2
separate error codes, SMC_PREEMPTED and TSP_EL3_FIQ, only SMC_PREEMPTED is
returned as error code back to the normal world.

Background information: On a GICv3 system, when the secure world has affinity
routing enabled, in 2. an FIQ will preempt TSP execution instead of an IRQ. The
FIQ could be a result of a Group 0 or a Group 1 NS interrupt. In both case, the
TSPD passes control back to the normal world upon receipt of the TSP_PREEMPTED
SMC. A Group 0 interrupt will immediately preempt execution to EL3 where it
will be handled. This allows for unified interrupt handling in TSP for both
GICv3 and GICv2 systems.

Change-Id: I9895344db74b188021e3f6a694701ad272fb40d4

bl32/tsp/aarch64/tsp_exceptions.S
bl32/tsp/tsp_interrupt.c
bl32/tsp/tsp_private.h
include/bl32/tsp/tsp.h
services/spd/tspd/tspd_main.c

index 4c0d4361e76ad726d98dc4fd9a30e997e7e76b55..272d94b7b77d98f7b4984863e3fd431b83b247ac 100644 (file)
@@ -125,7 +125,7 @@ irq_sp_elx:
 
        save_caller_regs_and_lr
        /* We just update some statistics in the handler */
-       bl      tsp_irq_received
+       bl      tsp_handle_preemption
        /* Hand over control to the normal world to handle the IRQ */
        smc     #0
        /* The resume std smc starts from here */
index 139642d094be530c0f3547c7db3ac05cfb21d653..d5379cdc6a8bc877b585ed210cc48e992e33967d 100644 (file)
@@ -67,6 +67,25 @@ void tsp_update_sync_fiq_stats(uint32_t type, uint64_t elr_el3)
 #endif
 }
 
+/******************************************************************************
+ * This function is invoked when a non S-EL1 interrupt is received and causes
+ * the preemption of TSP. This function returns TSP_PREEMPTED and results
+ * in the control being handed over to EL3 for handling the interrupt.
+ *****************************************************************************/
+int32_t tsp_handle_preemption(void)
+{
+       uint32_t linear_id = plat_my_core_pos();
+
+       tsp_stats[linear_id].preempt_intr_count++;
+#if LOG_LEVEL >= LOG_LEVEL_VERBOSE
+       spin_lock(&console_lock);
+       VERBOSE("TSP: cpu 0x%lx: %d preempt interrupt requests\n",
+               read_mpidr(), tsp_stats[linear_id].preempt_intr_count);
+       spin_unlock(&console_lock);
+#endif
+       return TSP_PREEMPTED;
+}
+
 /*******************************************************************************
  * TSP FIQ handler called as a part of both synchronous and asynchronous
  * handling of FIQ interrupts. It returns 0 upon successfully handling a S-EL1
@@ -82,16 +101,21 @@ int32_t tsp_fiq_handler(void)
         * Get the highest priority pending interrupt id and see if it is the
         * secure physical generic timer interrupt in which case, handle it.
         * Otherwise throw this interrupt at the EL3 firmware.
+        *
+        * There is a small time window between reading the highest priority
+        * pending interrupt and acknowledging it during which another
+        * interrupt of higher priority could become the highest pending
+        * interrupt. This is not expected to happen currently for TSP.
         */
        id = plat_ic_get_pending_interrupt_id();
 
        /* TSP can only handle the secure physical timer interrupt */
        if (id != TSP_IRQ_SEC_PHY_TIMER)
-               return TSP_EL3_FIQ;
+               return tsp_handle_preemption();
 
        /*
-        * Handle the interrupt. Also sanity check if it has been preempted by
-        * another secure interrupt through an assertion.
+        * Acknowledge and handle the secure timer interrupt. Also sanity check
+        * if it has been preempted by another interrupt through an assertion.
         */
        id = plat_ic_acknowledge_interrupt();
        assert(id == TSP_IRQ_SEC_PHY_TIMER);
@@ -110,18 +134,3 @@ int32_t tsp_fiq_handler(void)
 #endif
        return 0;
 }
-
-int32_t tsp_irq_received(void)
-{
-       uint32_t linear_id = plat_my_core_pos();
-
-       tsp_stats[linear_id].irq_count++;
-#if LOG_LEVEL >= LOG_LEVEL_VERBOSE
-       spin_lock(&console_lock);
-       VERBOSE("TSP: cpu 0x%lx received irq\n", read_mpidr());
-       VERBOSE("TSP: cpu 0x%lx: %d irq requests\n",
-               read_mpidr(), tsp_stats[linear_id].irq_count);
-       spin_unlock(&console_lock);
-#endif
-       return TSP_PREEMPTED;
-}
index 39fb5f663a785284293b0a407ec4435d14226fc8..346351c0c47eabcd8afd0cfd1c2e9fc0ad6e59df 100644 (file)
 
 typedef struct work_statistics {
        uint32_t fiq_count;             /* Number of FIQs on this cpu */
-       uint32_t irq_count;             /* Number of IRQs on this cpu */
        uint32_t sync_fiq_count;        /* Number of sync. fiqs on this cpu */
        uint32_t sync_fiq_ret_count;    /* Number of fiq returns on this cpu */
+       /* Number of non s-el1 interrupts on this cpu which preempted TSP */
+       uint32_t preempt_intr_count;
        uint32_t smc_count;             /* Number of returns on this cpu */
        uint32_t eret_count;            /* Number of entries on this cpu */
        uint32_t cpu_on_count;          /* Number of cpu on requests */
index c6578b788c35b9a622ea504e62f13fa43ef5c778..2286b3f19b3c43eff118b57537a23ba1a2e46370 100644 (file)
@@ -50,7 +50,6 @@
  * the TSPD after handling the interrupt else execution can remain in the TSP.
  */
 #define TSP_HANDLED_S_EL1_FIQ          0xf2000006
-#define TSP_EL3_FIQ                    0xf2000007
 
 /* SMC function ID that TSP uses to request service from secure monitor */
 #define TSP_GET_ARGS           0xf2001000
index 622316014990a24134c52994c69b80fcd28d9bdc..0d6e0d227e2db24b21df188c5c18e418994bc95e 100644 (file)
@@ -72,9 +72,16 @@ DEFINE_SVC_UUID(tsp_uuid,
 
 int32_t tspd_init(void);
 
+/*
+ * This helper function handles Secure EL1 preemption. The preemption could be
+ * due Non Secure interrupts or EL3 interrupts. In both the cases we context
+ * switch to the normal world and in case of EL3 interrupts, it will again be
+ * routed to EL3 which will get handled at the exception vectors.
+ */
 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 */
@@ -88,8 +95,16 @@ uint64_t tspd_handle_sp_preemption(void *handle)
        cm_el1_sysregs_context_restore(NON_SECURE);
        cm_set_next_eret_context(NON_SECURE);
 
+       /*
+        * We need to restore non secure context according to
+        * the SEL1 context which got preempted and currently
+        * TSP can only be preempted when a STD SMC is ongoing.
+        * Return SMC_PREEMPTED in x0 and restore non secure
+        * context.
+        */
        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
@@ -356,35 +371,6 @@ uint64_t tspd_smc_handler(uint32_t smc_fid,
 
                SMC_RET0((uint64_t) ns_cpu_context);
 
-
-       /*
-        * This function ID is used only by the TSP to indicate that it was
-        * interrupted due to a EL3 FIQ interrupt. Execution should resume
-        * in the normal world.
-        */
-       case TSP_EL3_FIQ:
-               if (ns)
-                       SMC_RET1(handle, SMC_UNK);
-
-               assert(handle == cm_get_context(SECURE));
-
-               /* Assert that standard SMC execution has been preempted */
-               assert(get_std_smc_active_flag(tsp_ctx->state));
-
-               /* Save the secure system register state */
-               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 */
-               cm_el1_sysregs_context_restore(NON_SECURE);
-               cm_set_next_eret_context(NON_SECURE);
-
-               SMC_RET1(ns_cpu_context, TSP_EL3_FIQ);
-
-
        /*
         * This function ID is used only by the SP to indicate it has
         * finished initialising itself after a cold boot
@@ -438,8 +424,7 @@ uint64_t tspd_smc_handler(uint32_t smc_fid,
                                panic();
 
                        /*
-                        * Disable the interrupt NS locally since it will be enabled globally
-                        * within cm_init_my_context.
+                        * Disable the NS interrupt locally.
                         */
                        disable_intr_rm_local(INTR_TYPE_NS, SECURE);
 #endif