Core: Initial implementation of sec IRQ handling

This commit makes possible for partitions to define IRQ handlers that
are executed in case of an interrupt is triggered, with the isolation
required by TFM_LVL settings.

Detailed changes:
 - Add template files to generate code for configuring IRQs, and set
   up IRQ handlers based on information provided in the partition's
   manifest
 - Add capability to Core to isolate the IRQ handlers
 - Add documentation

Change-Id: I0e46b9a41fb4e20ca4c398acf5ce1d4027e8597f
Signed-off-by: Mate Toth-Pal <mate.toth-pal@arm.com>
diff --git a/secure_fw/spm/spm_api.c b/secure_fw/spm/spm_api.c
index 78999a8..e138190 100644
--- a/secure_fw/spm/spm_api.c
+++ b/secure_fw/spm/spm_api.c
@@ -27,6 +27,24 @@
     TFM_INIT_FAILURE,
 } sp_error_type_t;
 
+/* The size of this struct must be multiple of 4 bytes as it is stacked to an
+ * uint32_t[] array
+ */
+struct interrupted_ctx_stack_frame_t {
+#if TFM_LVL != 1
+    uint32_t stack_ptr;
+#endif
+    uint32_t partition_state;
+};
+
+/* The size of this struct must be multiple of 4 bytes as it is stacked to an
+ * uint32_t[] array
+ */
+struct handler_ctx_stack_frame_t {
+    uint32_t partition_state;
+    uint32_t caller_partition_idx;
+};
+
 /*
  * This function is called when a secure partition causes an error.
  * In case of an error in the error handling, a non-zero value have to be
@@ -88,6 +106,10 @@
 {
     struct spm_partition_desc_t *part_ptr;
     enum spm_err_t err;
+    static uint32_t ns_interrupt_ctx_stack[
+           sizeof(struct interrupted_ctx_stack_frame_t)/sizeof(uint32_t)] = {0};
+    static uint32_t tfm_core_interrupt_ctx_stack[
+           sizeof(struct interrupted_ctx_stack_frame_t)/sizeof(uint32_t)] = {0};
 
     (void)tfm_memset (&g_spm_partition_db, 0, sizeof(g_spm_partition_db));
 
@@ -131,6 +153,7 @@
 #endif
 
     part_ptr->runtime_data.partition_state = SPM_PARTITION_STATE_UNINIT;
+    part_ptr->runtime_data.ctx_stack_ptr = ns_interrupt_ctx_stack;
     tfm_nspm_configure_clients();
     ++g_spm_partition_db.partition_count;
 
@@ -144,6 +167,7 @@
     part_ptr->static_data.partition_flags =
                     SPM_PART_FLAG_APP_ROT | SPM_PART_FLAG_PSA_ROT;
     part_ptr->runtime_data.partition_state = SPM_PARTITION_STATE_UNINIT;
+    part_ptr->runtime_data.ctx_stack_ptr = tfm_core_interrupt_ctx_stack;
     ++g_spm_partition_db.partition_count;
 
     err = add_user_defined_partitions();
@@ -199,6 +223,77 @@
         return SPM_ERR_PARTITION_NOT_AVAILABLE;
     }
 }
+
+void tfm_spm_partition_push_interrupted_ctx(uint32_t partition_idx)
+{
+    struct spm_partition_runtime_data_t *runtime_data =
+            &g_spm_partition_db.partitions[partition_idx].runtime_data;
+    struct interrupted_ctx_stack_frame_t *stack_frame =
+            (struct interrupted_ctx_stack_frame_t *)
+            runtime_data->ctx_stack_ptr;
+
+    stack_frame->partition_state = runtime_data->partition_state;
+#if TFM_LVL != 1
+    stack_frame->stack_ptr = runtime_data->stack_ptr;
+#endif
+    runtime_data->ctx_stack_ptr +=
+            sizeof(struct interrupted_ctx_stack_frame_t) / sizeof(uint32_t);
+
+}
+
+void tfm_spm_partition_pop_interrupted_ctx(uint32_t partition_idx)
+{
+    struct spm_partition_runtime_data_t *runtime_data =
+            &g_spm_partition_db.partitions[partition_idx].runtime_data;
+    struct interrupted_ctx_stack_frame_t *stack_frame;
+
+    runtime_data->ctx_stack_ptr -=
+            sizeof(struct interrupted_ctx_stack_frame_t) / sizeof(uint32_t);
+    stack_frame = (struct interrupted_ctx_stack_frame_t *)
+                   runtime_data->ctx_stack_ptr;
+    tfm_spm_partition_set_state(partition_idx, stack_frame->partition_state);
+    stack_frame->partition_state = 0;
+#if TFM_LVL != 1
+    tfm_spm_partition_set_stack(partition_idx, stack_frame->stack_ptr);
+    stack_frame->stack_ptr = 0;
+#endif
+}
+
+void tfm_spm_partition_push_handler_ctx(uint32_t partition_idx)
+{
+    struct spm_partition_runtime_data_t *runtime_data =
+            &g_spm_partition_db.partitions[partition_idx].runtime_data;
+    struct handler_ctx_stack_frame_t *stack_frame =
+            (struct handler_ctx_stack_frame_t *)
+            runtime_data->ctx_stack_ptr;
+
+    stack_frame->partition_state = runtime_data->partition_state;
+    stack_frame->caller_partition_idx = runtime_data->caller_partition_idx;
+
+    runtime_data->ctx_stack_ptr +=
+            sizeof(struct handler_ctx_stack_frame_t) / sizeof(uint32_t);
+}
+
+void tfm_spm_partition_pop_handler_ctx(uint32_t partition_idx)
+{
+    struct spm_partition_runtime_data_t *runtime_data =
+            &g_spm_partition_db.partitions[partition_idx].runtime_data;
+    struct handler_ctx_stack_frame_t *stack_frame;
+
+    runtime_data->ctx_stack_ptr -=
+            sizeof(struct handler_ctx_stack_frame_t) / sizeof(uint32_t);
+
+    stack_frame = (struct handler_ctx_stack_frame_t *)
+                  runtime_data->ctx_stack_ptr;
+
+    tfm_spm_partition_set_state(partition_idx, stack_frame->partition_state);
+    stack_frame->partition_state = 0;
+    tfm_spm_partition_set_caller_partition_idx(
+                              partition_idx, stack_frame->caller_partition_idx);
+    stack_frame->caller_partition_idx = 0;
+
+}
+
 #endif /* !defined(TFM_PSA_API) */
 
 #if (TFM_LVL != 1) || defined(TFM_PSA_API)
@@ -306,7 +401,8 @@
 {
     g_spm_partition_db.partitions[partition_idx].runtime_data.partition_state =
             state;
-    if (state == SPM_PARTITION_STATE_RUNNING) {
+    if (state == SPM_PARTITION_STATE_RUNNING ||
+        state == SPM_PARTITION_STATE_HANDLING_IRQ) {
         g_spm_partition_db.running_partition_idx = partition_idx;
     }
 }
@@ -318,6 +414,13 @@
             caller_partition_idx = caller_partition_idx;
 }
 
+void tfm_spm_partition_set_signal_mask(uint32_t partition_idx,
+                                       uint32_t signal_mask)
+{
+    g_spm_partition_db.partitions[partition_idx].runtime_data.
+            signal_mask = signal_mask;
+}
+
 void tfm_spm_partition_set_caller_client_id(uint32_t partition_idx,
                                             int32_t caller_client_id)
 {
diff --git a/secure_fw/spm/spm_api.h b/secure_fw/spm/spm_api.h
index a15434d..d707346 100644
--- a/secure_fw/spm/spm_api.h
+++ b/secure_fw/spm/spm_api.h
@@ -32,6 +32,7 @@
     SPM_PARTITION_STATE_UNINIT = 0,
     SPM_PARTITION_STATE_IDLE,
     SPM_PARTITION_STATE_RUNNING,
+    SPM_PARTITION_STATE_HANDLING_IRQ,
     SPM_PARTITION_STATE_SUSPENDED,
     SPM_PARTITION_STATE_BLOCKED,
     SPM_PARTITION_STATE_CLOSED
@@ -74,6 +75,16 @@
                                       */
     struct iovec_args_t iovec_args;
     psa_outvec *orig_outvec;
+    uint32_t *ctx_stack_ptr;
+    /*
+     * FIXME: There is a 'signal_mask' defined in the structure
+     * 'tfm_spm_ipc_partition_t'. It should be eliminated, and the IPC
+     * implementation should use the 'signal_mask' define in this structure.
+     * However currently the content of 'spm_partition_runtime_data_t' structure
+     * is not maintained by the IPC implementation. This is to be fixed with the
+     * effort of restructuring common code among library and IPC model.
+     */
+    uint32_t signal_mask;
 };
 
 
@@ -215,6 +226,46 @@
 
 #ifndef TFM_PSA_API
 /**
+ * \brief Save interrupted partition context on ctx stack
+ *
+ * \param[in] partition_idx  Partition index
+ *
+ * \note This function doesn't check if partition_idx is valid.
+ * \note This function doesn't whether the ctx stack overflows.
+ */
+void tfm_spm_partition_push_interrupted_ctx(uint32_t partition_idx);
+
+/**
+ * \brief Restores interrupted partition context on ctx stack
+ *
+ * \param[in] partition_idx  Partition index
+ *
+ * \note This function doesn't check if partition_idx is valid.
+ * \note This function doesn't whether the ctx stack underflows.
+ */
+void tfm_spm_partition_pop_interrupted_ctx(uint32_t partition_idx);
+
+/**
+ * \brief Save handler partition context on ctx stack
+ *
+ * \param[in] partition_idx  Partition index
+ *
+ * \note This function doesn't check if partition_idx is valid.
+ * \note This function doesn't whether the ctx stack overflows.
+ */
+void tfm_spm_partition_push_handler_ctx(uint32_t partition_idx);
+
+/**
+ * \brief Restores handler partition context on ctx stack
+ *
+ * \param[in] partition_idx  Partition index
+ *
+ * \note This function doesn't check if partition_idx is valid.
+ * \note This function doesn't whether the ctx stack underflows.
+ */
+void tfm_spm_partition_pop_handler_ctx(uint32_t partition_idx);
+
+/**
  * \brief Get the current runtime data of a partition
  *
  * \param[in] partition_idx     Partition index
@@ -329,6 +380,17 @@
  * \note This function doesn't check if partition_idx is valid.
  */
 void tfm_spm_partition_cleanup_context(uint32_t partition_idx);
+
+/**
+ * \brief Set the signal mask for a given partition
+ *
+ * \param[in] partition_idx        Partition index
+ * \param[in] signal_mask          The signal mask to be set for the partition
+ *
+ * \note This function doesn't check if any of the partition_idxs are valid.
+ */
+void tfm_spm_partition_set_signal_mask(uint32_t partition_idx,
+                                       uint32_t signal_mask);
 #endif /* !defined(TFM_PSA_API) */
 
 /**
diff --git a/secure_fw/spm/spm_db_setup.h b/secure_fw/spm/spm_db_setup.h
index db06154..31696dc 100644
--- a/secure_fw/spm/spm_db_setup.h
+++ b/secure_fw/spm/spm_db_setup.h
@@ -56,22 +56,43 @@
     } while (0)
 #endif
 
+/* The max size of the context stack can be calculated as a function of the IRQ
+ * count of the secure partition:
+ *
+ * max_stack_size = intr_ctx_size + (IRQ_CNT * (intr_ctx_size + hndl_ctx_size))
+ *
+ * where:
+ *   intr_ctx: Frame pushed when the partition is interrupted
+ *   hndl_ctx: Frame pushed when the partition is handling an interrupt
+ */
+#define DECLARE_CONTEXT_STACK(partition)                            \
+        static uint32_t ctx_stack_ptr_##partition[                  \
+            (sizeof(struct interrupted_ctx_stack_frame_t) +         \
+                (TFM_PARTITION_##partition##_IRQ_COUNT) * (         \
+                    sizeof(struct interrupted_ctx_stack_frame_t) +  \
+                    sizeof(struct handler_ctx_stack_frame_t)        \
+            )) / sizeof(uint32_t)]
+
 #if TFM_LVL == 1
-#define PARTITION_INIT_RUNTIME_DATA(data, partition)            \
-    do {                                                        \
-        data.partition_state      = SPM_PARTITION_STATE_UNINIT; \
+#define PARTITION_INIT_RUNTIME_DATA(data, partition)                \
+    do {                                                            \
+        DECLARE_CONTEXT_STACK(partition);                           \
+        data.partition_state = SPM_PARTITION_STATE_UNINIT;          \
+        data.ctx_stack_ptr = ctx_stack_ptr_##partition;             \
     } while (0)
 #else
 #define PARTITION_INIT_RUNTIME_DATA(data, partition)                \
     do {                                                            \
-        data.partition_state      = SPM_PARTITION_STATE_UNINIT;     \
+        DECLARE_CONTEXT_STACK(partition);                           \
+        data.partition_state = SPM_PARTITION_STATE_UNINIT;          \
         /* The top of the stack is reserved for the iovec        */ \
         /* parameters of the service called. That's why in       */ \
         /* data.stack_ptr we extract sizeof(struct iovec_args_t) */ \
         /* from the limit.                                       */ \
-        data.stack_ptr            =                                 \
+        data.stack_ptr =                                            \
                 PART_REGION_ADDR(partition, _STACK$$ZI$$Limit -     \
                                   sizeof(struct iovec_args_t));     \
+        data.ctx_stack_ptr = ctx_stack_ptr_##partition;             \
     } while (0)
 #endif