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/core/arch/tfm_arch_v8m_base.c b/secure_fw/core/arch/tfm_arch_v8m_base.c
index 35029b1..6aa969a 100644
--- a/secure_fw/core/arch/tfm_arch_v8m_base.c
+++ b/secure_fw/core/arch/tfm_arch_v8m_base.c
@@ -123,6 +123,53 @@
             [SVC_RET] "I" (TFM_SVC_SFN_RETURN)
         : "r0");
 }
+
+__attribute__((section("SFN"), naked))
+void priv_irq_handler_main(uint32_t partition_id,
+                                                  uint32_t unpriv_handler,
+                                                  uint32_t irq_signal,
+                                                  uint32_t irq_line)
+{
+    __ASM(
+          /* Save the callee saved registers*/
+          "PUSH   {r4-r7, lr}               \n"
+          "MOV    r4, r8                    \n"
+          "MOV    r5, r9                    \n"
+          "MOV    r6, r10                   \n"
+          "MOV    r7, r11                   \n"
+          "PUSH   {r4-r7}                   \n"
+          "MOV    r4, r12                   \n"
+          "PUSH   {r4}                      \n"
+          /* Request SVC to configure environment for the unpriv IRQ handler */
+          "SVC    %[SVC_REQ]                \n"
+          /* clear the callee saved registers to prevent information leak */
+          "MOVS   r4, #0                    \n"
+          "MOV    r5, r4                    \n"
+          "MOV    r6, r4                    \n"
+          "MOV    r7, r4                    \n"
+          "MOV    r8, r4                    \n"
+          "MOV    r9, r4                    \n"
+          "MOV    r10, r4                   \n"
+          "MOV    r11, r4                   \n"
+          /* Branch to the unprivileged handler */
+          "BLX    lr                        \n"
+          /* Request SVC to reconfigure the environment of the interrupted
+           * partition
+           */
+          "SVC    %[SVC_RET]                \n"
+          /* restore callee saved registers and return */
+          "POP    {r4}                      \n"
+          "MOV    r12, r4                   \n"
+          "POP    {r4-r7}                   \n"
+          "MOV    r8, r4                    \n"
+          "MOV    r9, r5                    \n"
+          "MOV    r10, r6                   \n"
+          "MOV    r11, r7                   \n"
+          "POP   {r4-r7, pc}                \n"
+          : : [SVC_REQ] "I" (TFM_SVC_DEPRIV_REQ)
+          , [SVC_RET] "I" (TFM_SVC_DEPRIV_RET)
+          : "r0");
+}
 #endif
 
 /**
@@ -145,19 +192,19 @@
 __attribute__((naked)) void SVC_Handler(void)
 {
     __ASM volatile(
-        ".syntax unified\n"
-        "MOVS    r0, #4            \n" /* Check store SP in thread mode to r0 */
-        "MOV     r1, lr            \n"
-        "TST     r0, r1            \n"
-        "BEQ     handler           \n"
-        "MRS     r0, PSP           \n" /* Coming from thread mode */
-        "B sp_stored               \n"
-        "handler:                  \n"
-        "BX      lr                \n" /* Coming from handler mode */
-        "sp_stored:                \n"
-        "MOV     r1, lr            \n"
-        "BL      SVCHandler_main   \n"
-        "BX      r0                \n"
+    ".syntax unified                        \n"
+    "MRS     r2, MSP                        \n"
+    "MOVS    r1, #4                         \n"
+    "MOV     r3, lr                         \n"
+    "MOV     r0, r2                         \n"
+    "TST     r1, r3                         \n"
+    "BEQ     handler                        \n"
+    /* If SVC was made from thread mode, overwrite r0 with PSP */
+    "MRS     r0, PSP                        \n"
+    "handler:                               \n"
+    "MOV     r1, lr                         \n"
+    "BL      SVCHandler_main                \n"
+    "BX      r0                             \n"
     );
 }
 
diff --git a/secure_fw/core/arch/tfm_arch_v8m_main.c b/secure_fw/core/arch/tfm_arch_v8m_main.c
index b27650d..3035d3c 100644
--- a/secure_fw/core/arch/tfm_arch_v8m_main.c
+++ b/secure_fw/core/arch/tfm_arch_v8m_main.c
@@ -108,6 +108,39 @@
             [SVC_RET] "I" (TFM_SVC_SFN_RETURN)
         : "r0");
 }
+
+__attribute__((section("SFN"), naked))
+void priv_irq_handler_main(uint32_t partition_id,
+                                                  uint32_t unpriv_handler,
+                                                  uint32_t irq_signal,
+                                                  uint32_t irq_line)
+{
+    __ASM(
+          /* Save the callee saved registers*/
+          "PUSH   {r4-r12, lr}              \n"
+          /* Request SVC to configure environment for the unpriv IRQ handler */
+          "SVC    %[SVC_REQ]                \n"
+          /* clear the callee saved registers to prevent information leak */
+          "MOV    r4, #0                    \n"
+          "MOV    r5, #0                    \n"
+          "MOV    r6, #0                    \n"
+          "MOV    r7, #0                    \n"
+          "MOV    r8, #0                    \n"
+          "MOV    r9, #0                    \n"
+          "MOV    r10, #0                   \n"
+          "MOV    r11, #0                   \n"
+          /* Branch to the unprivileged handler */
+          "BLX    lr                        \n"
+          /* Request SVC to reconfigure the environment of the interrupted
+           * partition
+           */
+          "SVC    %[SVC_RET]                \n"
+            /* restore callee saved registers and return */
+          "POP    {r4-r12, pc}              \n"
+          : : [SVC_REQ] "I" (TFM_SVC_DEPRIV_REQ)
+            , [SVC_RET] "I" (TFM_SVC_DEPRIV_RET)
+          : "r0");
+}
 #endif
 
 /**
@@ -152,13 +185,15 @@
 __attribute__((naked)) void SVC_Handler(void)
 {
     __ASM volatile(
-        "TST     lr, #4            \n" /* Check store SP in thread mode to r0 */
-        "IT      EQ                \n"
-        "BXEQ    lr                \n"
-        "MRS     r0, PSP           \n"
-        "MOV     r1, lr            \n"
-        "BL      SVCHandler_main   \n"
-        "BX      r0                \n"
+    "MRS     r2, MSP                        \n"
+    /* Check store SP in thread mode to r0 */
+    "TST     lr, #4                         \n"
+    "ITE     EQ                             \n"
+    "MOVEQ   r0, r2                         \n"
+    "MRSNE   r0, PSP                        \n"
+    "MOV     r1, lr                         \n"
+    "BL      SVCHandler_main                \n"
+    "BX      r0                             \n"
     );
 }
 
diff --git a/secure_fw/core/ipc/include/tfm_utils.h b/secure_fw/core/ipc/include/tfm_utils.h
index 18163a0..01462bc 100644
--- a/secure_fw/core/ipc/include/tfm_utils.h
+++ b/secure_fw/core/ipc/include/tfm_utils.h
@@ -26,6 +26,4 @@
 #define TFM_GET_CONTAINER_PTR(ptr, type, member) \
     (type *)((unsigned long)(ptr) - offsetof(type, member))
 
-int32_t tfm_bitcount(uint32_t n);
-
 #endif
diff --git a/secure_fw/core/ipc/tfm_spm.c b/secure_fw/core/ipc/tfm_spm.c
index c10bcc8..4f45070 100644
--- a/secure_fw/core/ipc/tfm_spm.c
+++ b/secure_fw/core/ipc/tfm_spm.c
@@ -22,6 +22,7 @@
 #include "tfm_pools.h"
 #include "tfm_spm.h"
 #include "tfm_spm_signal_defs.h"
+#include "tfm_irq_signal_defs.h"
 #include "tfm_thread.h"
 #include "region_defs.h"
 #include "tfm_nspm.h"
diff --git a/secure_fw/core/ipc/tfm_svcalls.c b/secure_fw/core/ipc/tfm_svcalls.c
index c02a5b8..07df935 100644
--- a/secure_fw/core/ipc/tfm_svcalls.c
+++ b/secure_fw/core/ipc/tfm_svcalls.c
@@ -22,8 +22,6 @@
 #include "tfm_memory_utils.h"
 #include "spm_api.h"
 
-#define PSA_TIMEOUT_MASK        PSA_BLOCK
-
 /************************* SVC handler for PSA Client APIs *******************/
 
 uint32_t tfm_svcall_psa_framework_version(void)
diff --git a/secure_fw/core/ipc/tfm_utils.c b/secure_fw/core/ipc/tfm_utils.c
index 591e243..dd15494 100644
--- a/secure_fw/core/ipc/tfm_utils.c
+++ b/secure_fw/core/ipc/tfm_utils.c
@@ -12,20 +12,3 @@
     while (1)
         ;
 }
-
-int32_t tfm_bitcount(uint32_t n)
-{
-    int32_t count = 0;
-    uint8_t tmp;
-
-    while (n) {
-        tmp = n & 0xFF;
-        while (tmp) {
-            count += tmp & 0x1;
-            tmp >>= 1;
-        }
-        n >>= 8;
-    }
-
-    return count;
-}
diff --git a/secure_fw/core/secure_utilities.h b/secure_fw/core/secure_utilities.h
index ce03b47..769d390 100644
--- a/secure_fw/core/secure_utilities.h
+++ b/secure_fw/core/secure_utilities.h
@@ -56,4 +56,6 @@
 #define ERROR_MSG(MSG) printf("[Sec Error] %s\r\n", MSG)
 #endif
 
+int32_t tfm_bitcount(uint32_t n);
+
 #endif /* __SECURE_UTILITIES_H__ */
diff --git a/secure_fw/core/tfm_core.c b/secure_fw/core/tfm_core.c
index 5ba4585..30dca20 100644
--- a/secure_fw/core/tfm_core.c
+++ b/secure_fw/core/tfm_core.c
@@ -16,6 +16,8 @@
 #include "secure_utilities.h"
 #include "secure_fw/spm/spm_api.h"
 #include "secure_fw/include/tfm_spm_services_api.h"
+#include "tfm_irq_signal_defs.h"
+#include "tfm_irq_list.h"
 #ifdef TFM_PSA_API
 #include "psa_client.h"
 #include "psa_service.h"
@@ -82,6 +84,8 @@
 
 int32_t tfm_core_init(void)
 {
+    size_t i;
+
     /* Enables fault handlers */
     enable_fault_handlers();
 
@@ -110,6 +114,15 @@
      * secure peripherals
      */
     nvic_interrupt_target_state_cfg();
+
+    for (i = 0; i < tfm_core_irq_signals_count; ++i) {
+        tfm_spm_hal_set_secure_irq_priority(
+                                          tfm_core_irq_signals[i].irq_line,
+                                          tfm_core_irq_signals[i].irq_priority);
+        tfm_spm_hal_set_irq_target_state(tfm_core_irq_signals[i].irq_line,
+                                         TFM_IRQ_TARGET_STATE_SECURE);
+    }
+
     /* Enable secure peripherals interrupts */
     nvic_interrupt_enable();
 
diff --git a/secure_fw/core/tfm_func_api.c b/secure_fw/core/tfm_func_api.c
index ffbd957..dcbce48 100644
--- a/secure_fw/core/tfm_func_api.c
+++ b/secure_fw/core/tfm_func_api.c
@@ -7,6 +7,7 @@
 
 #include <stdio.h>
 #include <string.h>
+#include <stdint.h>
 #include <stdbool.h>
 #include <arm_cmse.h>
 #include "tfm_secure_api.h"
@@ -17,8 +18,12 @@
 #include "region_defs.h"
 #include "tfm_api.h"
 #include "tfm_arch.h"
+#include "platform/include/tfm_spm_hal.h"
+#include "tfm_irq_list.h"
+#include "psa_service.h"
 
 #define EXC_RETURN_SECURE_FUNCTION 0xFFFFFFFD
+#define EXC_RETURN_SECURE_HANDLER  0xFFFFFFF1
 
 #ifndef TFM_LVL
 #error TFM_LVL is not defined!
@@ -100,6 +105,42 @@
     return dst;
 }
 
+/**
+ * \brief Create a stack frame that sets the execution environment to thread
+ *        mode on exception return.
+ *
+ * \param[in] svc_ctx         The stacked SVC context
+ * \param[in] unpriv_handler  The unprivileged IRQ handler to be called
+ * \param[in] dst             A pointer where the context is to be created. (the
+ *                            pointer is considered to be a stack pointer, and
+ *                            the frame is created below it)
+ *
+ * \return A pointer pointing at the created stack frame.
+ */
+static int32_t *prepare_partition_irq_ctx(
+                             const struct tfm_exc_stack_t *svc_ctx,
+                             sfn_t unpriv_handler,
+                             int32_t *dst)
+{
+    int i;
+
+    /* XPSR  = as was when called, but make sure it's thread mode */
+    *(--dst) = svc_ctx->XPSR & 0xFFFFFE00;
+    /* ReturnAddress = resume to the privileged handler code, but execute it
+     * unprivileged.
+     */
+    *(--dst) = svc_ctx->RetAddr;
+    /* LR = start address */
+    *(--dst) = (int32_t)unpriv_handler;
+
+    /* R12, R0-R3 unused arguments */
+    for (i = 0; i < 5; ++i) {
+        *(--dst) = 0;
+    }
+
+    return dst;
+}
+
 static void restore_caller_ctx(
             const struct tfm_exc_stack_t *svc_ctx,
             struct tfm_exc_stack_t *target_ctx)
@@ -259,9 +300,10 @@
     }
 
     if (curr_partition_state == SPM_PARTITION_STATE_RUNNING ||
+        curr_partition_state == SPM_PARTITION_STATE_HANDLING_IRQ ||
         curr_partition_state == SPM_PARTITION_STATE_SUSPENDED ||
         curr_partition_state == SPM_PARTITION_STATE_BLOCKED) {
-        /* Recursion is not permitted! */
+        /* Active partitions cannot be called! */
         return TFM_ERROR_PARTITION_NON_REENTRANT;
     } else if (curr_partition_state != SPM_PARTITION_STATE_IDLE) {
         /* The partition to be called is not in a proper state */
@@ -271,6 +313,27 @@
 }
 
 /**
+ * \brief Check whether the partitions for the secure function call are in a
+ *        proper state
+ *
+ * \param[in] called_partition_state    State of the partition to be called
+ *
+ * \return \ref TFM_SUCCESS if the check passes, error otherwise.
+ */
+static int32_t check_irq_partition_state(
+                                   enum spm_part_state_t called_partition_state)
+{
+    if (called_partition_state == SPM_PARTITION_STATE_IDLE ||
+        called_partition_state == SPM_PARTITION_STATE_RUNNING ||
+        called_partition_state == SPM_PARTITION_STATE_HANDLING_IRQ ||
+        called_partition_state == SPM_PARTITION_STATE_SUSPENDED ||
+        called_partition_state == SPM_PARTITION_STATE_BLOCKED) {
+        return TFM_SUCCESS;
+    }
+    return TFM_SECURE_LOCK_FAILED;
+}
+
+/**
  * \brief Calculate the address where the iovec parameters are to be saved for
  *        the called partition.
  *
@@ -438,6 +501,89 @@
     return TFM_SUCCESS;
 }
 
+static int32_t tfm_start_partition_for_irq_handling(
+                                                uint32_t excReturn,
+                                                struct tfm_exc_stack_t *svc_ctx)
+{
+    uint32_t handler_partition_id = svc_ctx->R0;
+    sfn_t unpriv_handler = (sfn_t)svc_ctx->R1;
+    uint32_t irq_signal = svc_ctx->R2;
+    uint32_t irq_line = svc_ctx->R3;
+    int32_t res;
+    uint32_t psp = __get_PSP();
+#if (TFM_LVL != 1)
+    uint32_t handler_partition_psplim;
+#endif
+    uint32_t handler_partition_psp;
+    enum spm_part_state_t handler_partition_state;
+    uint32_t interrupted_partition_idx =
+            tfm_spm_partition_get_running_partition_idx();
+    const struct spm_partition_runtime_data_t *handler_part_data;
+    uint32_t handler_partition_idx;
+
+    handler_partition_idx = get_partition_idx(handler_partition_id);
+    handler_part_data = tfm_spm_partition_get_runtime_data(
+                                                         handler_partition_idx);
+    handler_partition_state = handler_part_data->partition_state;
+
+    res = check_irq_partition_state(handler_partition_state);
+    if (res != TFM_SUCCESS) {
+        return res;
+    }
+
+    /* set mask for the partition */
+    tfm_spm_partition_set_signal_mask(
+                                   handler_partition_idx,
+                                   handler_part_data->signal_mask | irq_signal);
+
+    tfm_spm_hal_disable_irq(irq_line);
+
+    /* save the current context of the interrupted partition */
+    tfm_spm_partition_push_interrupted_ctx(interrupted_partition_idx);
+
+#if (TFM_LVL != 1)
+    /* Save the psp as it was when the interrupt happened */
+    tfm_spm_partition_set_stack(interrupted_partition_idx, psp);
+
+    handler_partition_psp = handler_part_data->stack_ptr;
+    handler_partition_psplim =
+            tfm_spm_partition_get_stack_bottom(handler_partition_idx);
+#else /* TFM_LVL != 1 */
+    handler_partition_psp = psp;
+#endif /* TFM_LVL != 1 */
+
+    /* save the current context of the handler partition */
+    tfm_spm_partition_push_handler_ctx(handler_partition_idx);
+
+    /* Store caller for the partition */
+    tfm_spm_partition_set_caller_partition_idx(handler_partition_idx,
+                                               interrupted_partition_idx);
+
+#if TFM_LVL == 3
+    /* Dynamic partitioning is only done is TFM level 3 */
+    tfm_spm_partition_sandbox_deconfig(interrupted_partition_idx);
+
+    /* Configure partition execution environment */
+    if (tfm_spm_partition_sandbox_config(handler_partition_idx) != SPM_ERR_OK) {
+        ERROR_MSG("Failed to configure sandbox for partition!");
+        tfm_secure_api_error_handler();
+    }
+#endif /* TFM_LVL == 3 */
+
+    psp = (uint32_t)prepare_partition_irq_ctx(svc_ctx, unpriv_handler,
+                                              (int32_t *)handler_partition_psp);
+    __set_PSP(psp);
+#if (TFM_LVL != 1)
+    __set_PSPLIM(handler_partition_psplim);
+#endif /* TFM_LVL != 1 */
+    tfm_spm_partition_set_state(interrupted_partition_idx,
+                                SPM_PARTITION_STATE_SUSPENDED);
+    tfm_spm_partition_set_state(handler_partition_idx,
+                                SPM_PARTITION_STATE_HANDLING_IRQ);
+
+    return TFM_SUCCESS;
+}
+
 static int32_t tfm_return_from_partition(uint32_t *excReturn)
 {
     uint32_t current_partition_idx =
@@ -565,6 +711,79 @@
     return TFM_SUCCESS;
 }
 
+static int32_t tfm_return_from_partition_irq_handling(uint32_t *excReturn)
+{
+    uint32_t handler_partition_idx =
+            tfm_spm_partition_get_running_partition_idx();
+    const struct spm_partition_runtime_data_t *handler_part_data;
+#if TFM_LVL != 1
+    const struct spm_partition_runtime_data_t *interrupted_part_data;
+    uint32_t interrupted_partition_psplim;
+#endif /* TFM_LVL != 1 */
+    uint32_t interrupted_partition_idx;
+    uint32_t psp = __get_PSP();
+    struct tfm_exc_stack_t *svc_ctx = (struct tfm_exc_stack_t *)psp;
+
+    if (handler_partition_idx == SPM_INVALID_PARTITION_IDX) {
+        return TFM_SECURE_UNLOCK_FAILED;
+    }
+
+    handler_part_data = tfm_spm_partition_get_runtime_data(
+                                                         handler_partition_idx);
+    interrupted_partition_idx = handler_part_data->caller_partition_idx;
+
+    if (interrupted_partition_idx == SPM_INVALID_PARTITION_IDX) {
+        return TFM_SECURE_UNLOCK_FAILED;
+    }
+
+#if TFM_LVL != 1
+    interrupted_part_data = tfm_spm_partition_get_runtime_data(
+            interrupted_partition_idx);
+
+#if TFM_LVL == 3
+    /* Deconfigure completed partition environment */
+    tfm_spm_partition_sandbox_deconfig(handler_partition_idx);
+
+    /* Configure the caller partition environment */
+    if (tfm_spm_partition_sandbox_config(interrupted_partition_idx)
+        != SPM_ERR_OK) {
+        ERROR_MSG("Failed to configure sandbox for partition!");
+        tfm_secure_api_error_handler();
+    }
+#endif /* TFM_LVL == 3 */
+
+    /* Restore caller context */
+    *excReturn = svc_ctx->RetAddr;
+
+    if (psp+sizeof(struct tfm_exc_stack_t) !=  handler_part_data->stack_ptr) {
+        ERROR_MSG("The interrupt handler unfolded its stack improperly!");
+        tfm_secure_api_error_handler();
+    }
+
+    psp = interrupted_part_data->stack_ptr;
+#else /* TFM_LVL != 1 */
+    /* For level 1, modify PSP, so that the SVC stack frame disappears,
+     * and return to the privileged handler using the stack frame still on the
+     * MSP stack.
+     */
+    *excReturn = svc_ctx->RetAddr;
+    psp += sizeof(struct tfm_exc_stack_t);
+#endif /* TFM_LVL != 1 */
+
+    tfm_spm_partition_pop_handler_ctx(handler_partition_idx);
+    tfm_spm_partition_pop_interrupted_ctx(interrupted_partition_idx);
+
+#if TFM_LVL != 1
+    interrupted_partition_psplim =
+        tfm_spm_partition_get_stack_bottom(interrupted_partition_idx);
+
+    __set_PSPLIM(interrupted_partition_psplim);
+#endif /* TFM_LVL != 1 */
+    __set_PSP(psp);
+
+    return TFM_SUCCESS;
+}
+
 static int32_t tfm_check_sfn_req_integrity(const struct tfm_sfn_req_s *desc_ptr)
 {
     if ((desc_ptr == NULL) ||
@@ -720,8 +939,12 @@
     uint32_t caller_partition_flags =
             tfm_spm_partition_get_flags(curr_part_data->caller_partition_idx);
 
-    if (!(running_partition_flags & SPM_PART_FLAG_APP_ROT))  {
+    if (!(running_partition_flags & SPM_PART_FLAG_APP_ROT) ||
+        curr_part_data->partition_state == SPM_PARTITION_STATE_HANDLING_IRQ ||
+        curr_part_data->partition_state == SPM_PARTITION_STATE_SUSPENDED)  {
         /* This handler shouldn't be called from outside partition context.
+         * Also if the current partition is handling IRQ, the caller partition
+         * index might not be valid;
          * Partitions are only allowed to run while S domain is locked.
          */
         svc_args[0] = TFM_ERROR_INVALID_PARAMETER;
@@ -802,8 +1025,12 @@
             tfm_spm_partition_get_runtime_data(running_partition_idx);
     int res = 0;
 
-    if (!(running_partition_flags & SPM_PART_FLAG_APP_ROT))  {
+    if (!(running_partition_flags & SPM_PART_FLAG_APP_ROT) ||
+        curr_part_data->partition_state == SPM_PARTITION_STATE_HANDLING_IRQ ||
+        curr_part_data->partition_state == SPM_PARTITION_STATE_SUSPENDED)  {
         /* This handler shouldn't be called from outside partition context.
+         * Also if the current partition is handling IRQ, the caller partition
+         * index might not be valid;
          * Partitions are only allowed to run while S domain is locked.
          */
         svc_args[0] = TFM_ERROR_INVALID_PARAMETER;
@@ -954,6 +1181,29 @@
     return EXC_RETURN_SECURE_FUNCTION;
 }
 
+/* This SVC handler is called, if a thread mode execution environment is to
+ * be set up, to run an unprivileged IRQ handler
+ */
+uint32_t tfm_core_depriv_req_handler(uint32_t *svc_args, uint32_t excReturn)
+{
+    struct tfm_exc_stack_t *svc_ctx = (struct tfm_exc_stack_t *)svc_args;
+
+    int32_t res;
+
+    if (excReturn & EXC_RETURN_STACK_PROCESS) {
+        /* FixMe: error severity TBD */
+        ERROR_MSG("Partition request SVC called with PSP active!");
+        tfm_secure_api_error_handler();
+    }
+
+    res = tfm_start_partition_for_irq_handling(excReturn, svc_ctx);
+    if (res != TFM_SUCCESS) {
+        /* FixMe: consider possible fault scenarios */
+        return excReturn;
+    }
+    return EXC_RETURN_SECURE_FUNCTION;
+}
+
 /* This SVC handler is called when sfn returns */
 uint32_t tfm_core_partition_return_handler(uint32_t lr)
 {
@@ -997,6 +1247,38 @@
     return lr;
 }
 
+/* This SVC handler is called if a deprivileged IRQ handler was executed, and
+ * the execution environment is to be set back for the privileged handler mode
+ */
+uint32_t tfm_core_depriv_return_handler(uint32_t *irq_svc_args, uint32_t lr)
+{
+    struct tfm_exc_stack_t *irq_svc_ctx =
+                                         (struct tfm_exc_stack_t *)irq_svc_args;
+
+    if (!(lr & EXC_RETURN_STACK_PROCESS)) {
+        /* Partition request SVC called with MSP active.
+         * FixMe: error severity TBD
+         */
+        ERROR_MSG("Partition request SVC called with MSP active!");
+        tfm_secure_api_error_handler();
+    }
+
+    int32_t res;
+
+    res = tfm_return_from_partition_irq_handling(&lr);
+    if (res != TFM_SUCCESS) {
+        /* Unlock errors indicate ctx database corruption or unknown anomalies
+         * Halt execution
+         */
+        ERROR_MSG("Secure API error during unlock!");
+        tfm_secure_api_error_handler();
+    }
+
+    irq_svc_ctx->RetAddr = lr;
+
+    return EXC_RETURN_SECURE_HANDLER;
+}
+
 void tfm_core_set_buffer_area_handler(uint32_t *args)
 {
     /* r0 is stored in args[0] in exception stack frame
@@ -1016,8 +1298,13 @@
      /* tfm_core_set_buffer_area() returns int32_t */
     int32_t *res_ptr = (int32_t *)&args[0];
 
-    if (!(running_partition_flags & SPM_PART_FLAG_APP_ROT)) {
-        /* This handler should only be called from a secure partition. */
+    if (!(running_partition_flags & SPM_PART_FLAG_APP_ROT) ||
+        curr_part_data->partition_state == SPM_PARTITION_STATE_HANDLING_IRQ ||
+        curr_part_data->partition_state == SPM_PARTITION_STATE_SUSPENDED) {
+        /* This handler shouldn't be called from outside partition context.
+         * Also if the current partition is handling IRQ, the caller partition
+         * index might not be valid;
+         */
         *res_ptr = TFM_ERROR_INVALID_PARAMETER;
         return;
     }
@@ -1046,3 +1333,149 @@
     return;
 }
 
+/* FIXME: get_irq_line_for_signal is also implemented in the ipc folder. */
+/**
+ * \brief Return the IRQ line number associated with a signal
+ *
+ * \param[in] partition_id    The ID of the partition in which we look for the
+ *                            signal
+ * \param[in] signal          The signal we do the query for
+ *
+ * \retval >=0     The IRQ line number associated with a signal in the partition
+ * \retval <0      error
+ */
+static int32_t get_irq_line_for_signal(int32_t partition_id,
+                                       psa_signal_t signal)
+{
+    size_t i;
+
+    for (i = 0; i < tfm_core_irq_signals_count; ++i) {
+        if (tfm_core_irq_signals[i].partition_id == partition_id &&
+            tfm_core_irq_signals[i].signal_value == signal) {
+            return tfm_core_irq_signals[i].irq_line;
+        }
+    }
+    return -1;
+}
+
+/* FIXME: tfm_core_psa_eoi, tfm_core_enable_irq_handler and
+ * tfm_core_disable_irq_handler function has an implementation in
+ * tfm_svcalls.c for the IPC model.
+ * The two implementations should be merged as part of restructuring common code
+ * among library and IPC model.
+ */
+void tfm_core_enable_irq_handler(uint32_t *svc_args)
+{
+    struct tfm_exc_stack_t *svc_ctx = (struct tfm_exc_stack_t *)svc_args;
+    psa_signal_t irq_signal = svc_ctx->R0;
+    uint32_t running_partition_idx =
+                      tfm_spm_partition_get_running_partition_idx();
+    uint32_t running_partition_id =
+                      tfm_spm_partition_get_partition_id(running_partition_idx);
+    int32_t irq_line;
+
+    /* Only a single signal is allowed */
+    if (tfm_bitcount(irq_signal) != 1) {
+        /* FixMe: error severity TBD */
+        tfm_secure_api_error_handler();
+    }
+
+    irq_line = get_irq_line_for_signal(running_partition_id, irq_signal);
+
+    if (irq_line < 0) {
+        /* FixMe: error severity TBD */
+        tfm_secure_api_error_handler();
+    }
+
+    tfm_spm_hal_enable_irq(irq_line);
+}
+
+void tfm_core_disable_irq_handler(uint32_t *svc_args)
+{
+    struct tfm_exc_stack_t *svc_ctx = (struct tfm_exc_stack_t *)svc_args;
+    psa_signal_t irq_signal = svc_ctx->R0;
+    uint32_t running_partition_idx =
+                      tfm_spm_partition_get_running_partition_idx();
+    uint32_t running_partition_id =
+                      tfm_spm_partition_get_partition_id(running_partition_idx);
+    int32_t irq_line;
+
+    /* Only a single signal is allowed */
+    if (tfm_bitcount(irq_signal) != 1) {
+        /* FixMe: error severity TBD */
+        tfm_secure_api_error_handler();
+    }
+
+    irq_line = get_irq_line_for_signal(running_partition_id, irq_signal);
+
+    if (irq_line < 0) {
+        /* FixMe: error severity TBD */
+        tfm_secure_api_error_handler();
+    }
+
+    tfm_spm_hal_disable_irq(irq_line);
+}
+
+void tfm_core_psa_wait(uint32_t *svc_args)
+{
+    /* Look for partition that is ready for run */
+    struct tfm_exc_stack_t *svc_ctx = (struct tfm_exc_stack_t *)svc_args;
+    uint32_t running_partition_idx;
+    const struct spm_partition_runtime_data_t *curr_part_data;
+
+    psa_signal_t signal_mask = svc_ctx->R0;
+    uint32_t timeout = svc_ctx->R1;
+
+    /*
+     * Timeout[30:0] are reserved for future use.
+     * SPM must ignore the value of RES.
+     */
+    timeout &= PSA_TIMEOUT_MASK;
+
+    running_partition_idx = tfm_spm_partition_get_running_partition_idx();
+    curr_part_data = tfm_spm_partition_get_runtime_data(running_partition_idx);
+
+    if (timeout == PSA_BLOCK) {
+        /* FIXME: Scheduling is not available in library model, and busy wait is
+         * also not possible as this code is running in SVC context, and it
+         * cannot be pre-empted by interrupts. So do nothing here for now
+         */
+        (void) signal_mask;
+    }
+
+    svc_ctx->R0 = curr_part_data->signal_mask;
+}
+
+void tfm_core_psa_eoi(uint32_t *svc_args)
+{
+    struct tfm_exc_stack_t *svc_ctx = (struct tfm_exc_stack_t *)svc_args;
+    psa_signal_t irq_signal =  svc_ctx->R0;
+    uint32_t signal_mask;
+    uint32_t running_partition_idx;
+    uint32_t running_partition_id;
+    const struct spm_partition_runtime_data_t *curr_part_data;
+    int32_t irq_line;
+
+    running_partition_idx = tfm_spm_partition_get_running_partition_idx();
+    running_partition_id =
+                      tfm_spm_partition_get_partition_id(running_partition_idx);
+    curr_part_data = tfm_spm_partition_get_runtime_data(running_partition_idx);
+
+    /* Only a single signal is allowed */
+    if (tfm_bitcount(irq_signal) != 1) {
+        tfm_secure_api_error_handler();
+    }
+
+    irq_line = get_irq_line_for_signal(running_partition_id, irq_signal);
+
+    if (irq_line < 0) {
+        /* FixMe: error severity TBD */
+        tfm_secure_api_error_handler();
+    }
+
+    tfm_spm_hal_clear_pending_irq(irq_line);
+    tfm_spm_hal_enable_irq(irq_line);
+
+    signal_mask = curr_part_data->signal_mask & ~irq_signal;
+    tfm_spm_partition_set_signal_mask(running_partition_idx, signal_mask);
+}
diff --git a/secure_fw/core/tfm_handler.c b/secure_fw/core/tfm_handler.c
index 89b0008..5be8ba7 100644
--- a/secure_fw/core/tfm_handler.c
+++ b/secure_fw/core/tfm_handler.c
@@ -12,10 +12,14 @@
 #include "tfm_svc.h"
 #include "tfm_secure_api.h"
 #include "region_defs.h"
+#include "spm_partition_defs.h"
 #include "tfm_api.h"
 #include "tfm_internal.h"
 #include "tfm_memory_utils.h"
 #include "tfm_arch.h"
+#include "tfm_irq_signal_defs.h"
+#include "tfm_peripherals_def.h"
+#include "tfm_irq_list.h"
 #ifdef TFM_PSA_API
 #include <stdbool.h>
 #include "tfm_svcalls.h"
@@ -29,7 +33,13 @@
 extern void tfm_psa_ipc_request_handler(const uint32_t svc_args[]);
 #endif
 
-uint32_t SVCHandler_main(uint32_t *svc_args, uint32_t lr)
+
+/* Include the definitions of the privileged IRQ handlers, and the declarations
+ * of the unprivileged handlers.
+ */
+#include "tfm_secure_irq_handlers.inc"
+
+uint32_t SVCHandler_main(uint32_t *svc_args, uint32_t lr, uint32_t *msp)
 {
     uint8_t svc_number;
     /*
@@ -95,6 +105,24 @@
     case TFM_SVC_SET_SHARE_AREA:
         tfm_core_set_buffer_area_handler(svc_args);
         break;
+    case TFM_SVC_DEPRIV_REQ:
+        lr = tfm_core_depriv_req_handler(svc_args, lr);
+        break;
+    case TFM_SVC_DEPRIV_RET:
+        lr = tfm_core_depriv_return_handler(msp, lr);
+        break;
+    case TFM_SVC_PSA_WAIT:
+        tfm_core_psa_wait(svc_args);
+        break;
+    case TFM_SVC_PSA_EOI:
+        tfm_core_psa_eoi(svc_args);
+        break;
+    case TFM_SVC_ENABLE_IRQ:
+        tfm_core_enable_irq_handler(svc_args);
+        break;
+    case TFM_SVC_DISABLE_IRQ:
+        tfm_core_disable_irq_handler(svc_args);
+        break;
 #endif
     case TFM_SVC_PRINT:
         printf("\033[1;34m[Sec Thread] %s\033[0m\r\n", (char *)svc_args[0]);
diff --git a/secure_fw/core/tfm_internal.h b/secure_fw/core/tfm_internal.h
index dad36ad..d73b078 100644
--- a/secure_fw/core/tfm_internal.h
+++ b/secure_fw/core/tfm_internal.h
@@ -97,4 +97,36 @@
  */
 void tfm_core_validate_boot_data(void);
 
+/**
+ * \brief Handle deprivileged request
+ */
+extern uint32_t tfm_core_depriv_req_handler(uint32_t *svc_args,
+                                            uint32_t excReturn);
+
+/**
+ * \brief Handle request to return to privileged
+ */
+uint32_t tfm_core_depriv_return_handler(uint32_t *irq_svc_args, uint32_t lr);
+
+/**
+ * \brief Handle IRQ enable request
+ */
+void tfm_core_enable_irq_handler(uint32_t *svc_args);
+
+/**
+ * \brief Handle IRQ disable request
+ */
+void tfm_core_disable_irq_handler(uint32_t *svc_args);
+
+/**
+ * \brief Handle signal wait request
+ */
+void tfm_core_psa_wait(uint32_t *svc_args);
+
+/**
+ * \brief Handle request to record IRQ processed
+ */
+void tfm_core_psa_eoi(uint32_t *svc_args);
+
+
 #endif /* __TFM_INTERNAL_H__ */
diff --git a/secure_fw/core/tfm_irq_list.h b/secure_fw/core/tfm_irq_list.h
new file mode 100644
index 0000000..3dcfd2f
--- /dev/null
+++ b/secure_fw/core/tfm_irq_list.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __TFM_IRQ_LIST_H__
+#define __TFM_IRQ_LIST_H__
+
+#include "psa_service.h"
+
+#define TFM_DEFAULT_SECURE_IRQ_PRIOTITY 128
+
+struct tfm_core_irq_signal_data_t {
+    int32_t partition_id;
+    psa_signal_t signal_value;
+    int32_t irq_line;
+    uint32_t irq_priority;
+};
+
+extern const struct tfm_core_irq_signal_data_t tfm_core_irq_signals[];
+extern const size_t tfm_core_irq_signals_count;
+
+#endif /* __TFM_IRQ_LIST_H__ */
diff --git a/secure_fw/core/tfm_irq_signal_defs.h b/secure_fw/core/tfm_irq_signal_defs.h
new file mode 100644
index 0000000..e851776
--- /dev/null
+++ b/secure_fw/core/tfm_irq_signal_defs.h
@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef __TFM_IRQ_SIGNAL_DEFS_H__
+#define __TFM_IRQ_SIGNAL_DEFS_H__
+
+
+#endif /* __TFM_IRQ_SIGNAL_DEFS_H__ */
diff --git a/secure_fw/core/tfm_secure_api.c b/secure_fw/core/tfm_secure_api.c
index 104958e..a27aa8a 100644
--- a/secure_fw/core/tfm_secure_api.c
+++ b/secure_fw/core/tfm_secure_api.c
@@ -37,6 +37,23 @@
  */
 int32_t tfm_secure_lock;
 
+int32_t tfm_bitcount(uint32_t n)
+{
+    int32_t count = 0;
+    uint8_t tmp;
+
+    while (n) {
+        tmp = n & 0xFF;
+        while (tmp) {
+            count += tmp & 0x1;
+            tmp >>= 1;
+        }
+        n >>= 8;
+    }
+
+    return count;
+}
+
 /**
  * \brief Check whether a memory range is inside a memory region.
  *
diff --git a/secure_fw/core/tfm_secure_api.h b/secure_fw/core/tfm_secure_api.h
index 5a35fc4..d3b20c4 100644
--- a/secure_fw/core/tfm_secure_api.h
+++ b/secure_fw/core/tfm_secure_api.h
@@ -14,6 +14,7 @@
 #include "tfm_core.h"
 #include "tfm_api.h"
 #include "bl2/include/tfm_boot_status.h"
+#include "psa_service.h"
 
 /*!
  * \def __tfm_secure_gateway_attributes__
@@ -130,6 +131,9 @@
                                             uint32_t ns_caller,
                                             uint32_t privileged);
 
+void tfm_enable_irq(psa_signal_t irq_signal);
+void tfm_disable_irq(psa_signal_t irq_signal);
+
 #ifdef TFM_PSA_API
 /* The following macros are only valid if secure services can be called
  * using veneer functions. This is not the case if IPC messaging is enabled
diff --git a/secure_fw/core/tfm_secure_irq_handlers.inc b/secure_fw/core/tfm_secure_irq_handlers.inc
new file mode 100644
index 0000000..5ff583a
--- /dev/null
+++ b/secure_fw/core/tfm_secure_irq_handlers.inc
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+/*********** WARNING: This is an auto-generated file. Do not edit! ***********/
+
+#include "secure_fw/services/tfm_partition_defs.inc"
+
+/* Definitions of the signals of the IRQs */
+const struct tfm_core_irq_signal_data_t tfm_core_irq_signals[] = {
+};
+
+const size_t tfm_core_irq_signals_count = sizeof(tfm_core_irq_signals) /
+                                          sizeof(*tfm_core_irq_signals);
+
+extern void priv_irq_handler_main(uint32_t partition_id,
+                                  uint32_t unpriv_handler,
+                                  uint32_t irq_signal,
+                                  uint32_t irq_line);
+
+/* Forward declarations of unpriv IRQ handlers*/
+
+/* Definitions of privileged IRQ handlers */
diff --git a/secure_fw/core/tfm_secure_irq_handlers.inc.template b/secure_fw/core/tfm_secure_irq_handlers.inc.template
new file mode 100644
index 0000000..b28407b
--- /dev/null
+++ b/secure_fw/core/tfm_secure_irq_handlers.inc.template
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+{{utilities.donotedit_warning}}
+{% macro _irq_record(partition_name, signal, line, priority) -%}
+{ {{ partition_name }}_ID, {{ signal }}, {{ line }}, {{ priority }} },
+{%- endmacro %}
+
+#include "secure_fw/services/tfm_partition_defs.inc"
+
+/* Definitions of the signals of the IRQs */
+const struct tfm_core_irq_signal_data_t tfm_core_irq_signals[] = {
+{% for manifest in manifests %}
+    {% if manifest.manifest.irqs %}
+        {% if manifest.attr.conditional %}
+#ifdef {{manifest.attr.conditional}}
+        {% endif %}
+        {% for handler in manifest.manifest.irqs %}
+            {% set irq_data = namespace() %}
+            {% if handler.line_num %}
+                {% set irq_data.line = handler.line_num %}
+            {% elif handler.line_name %}
+                {% set irq_data.line = handler.line_name %}
+            {% else %}
+#error "Neither line_num nor line_name is provided for 'irqs' in partition {{manifest.manifest.name}}"
+            {% endif %}
+            {% if handler.tfm_irq_priority %}
+                {% set irq_data.priority = handler.tfm_irq_priority %}
+            {% else %}
+                {% set irq_data.priority = "TFM_DEFAULT_SECURE_IRQ_PRIOTITY" %}
+            {% endif %}
+    {{ _irq_record(manifest.manifest.name, handler.signal, irq_data.line, irq_data.priority) }}
+        {% endfor %}
+        {% if manifest.attr.conditional %}
+#endif /* {{manifest.attr.conditional}} */
+        {% endif %}
+   {% endif %}
+{% endfor %}
+};
+
+const size_t tfm_core_irq_signals_count = sizeof(tfm_core_irq_signals) /
+                                          sizeof(*tfm_core_irq_signals);
+
+extern void priv_irq_handler_main(uint32_t partition_id,
+                                  uint32_t unpriv_handler,
+                                  uint32_t irq_signal,
+                                  uint32_t irq_line);
+
+/* Forward declarations of unpriv IRQ handlers*/
+{% for manifest in manifests %}
+    {% if manifest.manifest.irqs %}
+        {% if manifest.attr.conditional %}
+#ifdef {{manifest.attr.conditional}}
+        {% endif %}
+        {% for handler in manifest.manifest.irqs %}
+extern void {{handler.signal}}_isr(void);
+        {% endfor %}
+        {% if manifest.attr.conditional %}
+#endif /* {{manifest.attr.conditional}} */
+        {% endif %}
+
+    {% endif %}
+{% endfor %}
+
+/* Definitions of privileged IRQ handlers */
+{% for manifest in manifests %}
+    {% if manifest.manifest.irqs %}
+        {% if manifest.attr.conditional %}
+#ifdef {{manifest.attr.conditional}}
+        {% endif %}
+        {% for handler in manifest.manifest.irqs %}
+            {% if handler.line_num %}
+void irq_{{handler.line_num}}_Handler(void)
+            {% elif handler.line_name %}
+void {{handler.line_name}}_Handler(void)
+            {% else %}
+#error "Neither line_num nor line_name is provided for 'irqs' in partition {{manifest.manifest.name}}"
+            {% endif %}
+{
+            {% if handler.line_num %}
+    priv_irq_handler_main({{manifest.manifest.name}}_ID,
+                          (uint32_t){{handler.signal}}_isr,
+                          {{handler.signal}},
+                          {{handler.line_num}});
+            {% elif handler.line_name %}
+    priv_irq_handler_main({{manifest.manifest.name}}_ID,
+                          (uint32_t){{handler.signal}}_isr,
+                          {{handler.signal}},
+                          {{handler.line_name}});
+            {% else %}
+#error "Neither line_num nor line_name is provided for 'irqs' in partition {{manifest.manifest.name}}"
+            {% endif %}
+}
+
+        {% endfor %}
+        {% if manifest.attr.conditional %}
+#endif /* {{manifest.attr.conditional}} */
+        {% endif %}
+
+    {% endif %}
+{% endfor %}
diff --git a/secure_fw/core/tfm_spm_services.c b/secure_fw/core/tfm_spm_services.c
index fabffbd..f580833 100644
--- a/secure_fw/core/tfm_spm_services.c
+++ b/secure_fw/core/tfm_spm_services.c
@@ -12,6 +12,7 @@
 #include "tfm_internal.h"
 #include "secure_fw/include/tfm_spm_services_api.h"
 #include "spm_api.h"
+#include "psa_service.h"
 
 uint8_t *tfm_scratch_area;
 uint32_t tfm_scratch_area_size;
@@ -99,3 +100,58 @@
         "BX     lr\n"
         : : "I" (TFM_SVC_GET_BOOT_DATA));
 }
+
+__attribute__((naked))
+void tfm_enable_irq(psa_signal_t irq_signal)
+{
+    __ASM("SVC %0\n"
+          "BX LR\n"
+          : : "I" (TFM_SVC_ENABLE_IRQ));
+}
+
+__attribute__((naked))
+void tfm_disable_irq(psa_signal_t irq_signal)
+{
+    __ASM("SVC %0\n"
+          "BX LR\n"
+          : : "I" (TFM_SVC_DISABLE_IRQ));
+}
+
+#ifndef TFM_PSA_API
+
+__attribute__((naked))
+static psa_signal_t psa_wait_internal(psa_signal_t signal_mask,
+                                      uint32_t timeout)
+{
+    __ASM("SVC %0\n"
+          "BX LR\n"
+          : : "I" (TFM_SVC_PSA_WAIT));
+}
+
+psa_signal_t psa_wait(psa_signal_t signal_mask, uint32_t timeout)
+{
+    /* FIXME: By using the 'WFI' instruction this function blocks until an
+     * interrupt happens. It is necessary to do this here as tfm_core_psa_wait
+     * runs with the priority of the SVC, so it cannot be interrupted, so
+     * waiting in it for the required interrupt to happen is not an option.
+     */
+    psa_signal_t actual_signal_mask;
+
+    while (1) {
+        actual_signal_mask = psa_wait_internal(signal_mask, timeout);
+        if ((actual_signal_mask & signal_mask) != 0) {
+            return actual_signal_mask;
+        }
+        __WFI();
+    }
+}
+
+__attribute__((naked))
+void psa_eoi(psa_signal_t irq_signal)
+{
+    __ASM("SVC %0\n"
+          "BX LR\n"
+          : : "I" (TFM_SVC_PSA_EOI));
+}
+
+#endif
diff --git a/secure_fw/core/tfm_svc.h b/secure_fw/core/tfm_svc.h
index e0c1102..d52e3d3 100644
--- a/secure_fw/core/tfm_svc.h
+++ b/secure_fw/core/tfm_svc.h
@@ -20,6 +20,12 @@
     TFM_SVC_SPM_REQUEST,
     TFM_SVC_PRINT,
     TFM_SVC_GET_BOOT_DATA,
+    TFM_SVC_DEPRIV_REQ,
+    TFM_SVC_DEPRIV_RET,
+    TFM_SVC_ENABLE_IRQ,
+    TFM_SVC_DISABLE_IRQ,
+    TFM_SVC_PSA_WAIT,
+    TFM_SVC_PSA_EOI,
 #ifdef TFM_PSA_API
     TFM_SVC_IPC_REQUEST,
     TFM_SVC_SCHEDULE,
@@ -31,7 +37,6 @@
     TFM_SVC_PSA_CALL,
     TFM_SVC_PSA_CLOSE,
     /* PSA Service SVC */
-    TFM_SVC_PSA_WAIT,
     TFM_SVC_PSA_GET,
     TFM_SVC_PSA_SET_RHANDLE,
     TFM_SVC_PSA_READ,
@@ -40,7 +45,6 @@
     TFM_SVC_PSA_REPLY,
     TFM_SVC_PSA_NOTIFY,
     TFM_SVC_PSA_CLEAR,
-    TFM_SVC_PSA_EOI,
 #endif
 } tfm_svc_number_t;
 
diff --git a/secure_fw/services/tfm_partition_list.inc b/secure_fw/services/tfm_partition_list.inc
index 2cc4818..90344e2 100644
--- a/secure_fw/services/tfm_partition_list.inc
+++ b/secure_fw/services/tfm_partition_list.inc
@@ -7,19 +7,23 @@
 
 /*********** WARNING: This is an auto-generated file. Do not edit! ***********/
 
+#include "tfm_partition_defs.inc"
+
 #ifndef __TFM_PARTITION_LIST_INC__
 #define __TFM_PARTITION_LIST_INC__
 
 /******** TFM_SP_STORAGE ********/
+#define TFM_PARTITION_TFM_SP_STORAGE_IRQ_COUNT 0
 PARTITION_DECLARE(TFM_SP_STORAGE, 0
     | SPM_PART_FLAG_IPC
-    , "PSA-ROT", 0x00000100, NORMAL);
+    , "PSA-ROT", TFM_SP_STORAGE_ID, NORMAL);
 PARTITION_ADD_INIT_FUNC(TFM_SP_STORAGE, tfm_sst_req_mngr_init);
 
 #ifdef TFM_PARTITION_AUDIT_LOG
 /******** TFM_SP_AUDIT_LOG ********/
+#define TFM_PARTITION_TFM_SP_AUDIT_LOG_IRQ_COUNT 0
 PARTITION_DECLARE(TFM_SP_AUDIT_LOG, 0
-    , "PSA-ROT", 0x00000101, NORMAL);
+    , "PSA-ROT", TFM_SP_AUDIT_LOG_ID, NORMAL);
 PARTITION_ADD_INIT_FUNC(TFM_SP_AUDIT_LOG, audit_core_init);
 #ifdef AUDIT_UART_REDIRECTION
 PARTITION_ADD_PERIPHERAL(TFM_SP_AUDIT_LOG, TFM_PERIPHERAL_UART1);
@@ -27,63 +31,71 @@
 #endif /* TFM_PARTITION_AUDIT_LOG */
 
 /******** TFM_SP_CRYPTO ********/
+#define TFM_PARTITION_TFM_SP_CRYPTO_IRQ_COUNT 0
 PARTITION_DECLARE(TFM_SP_CRYPTO, 0
     | SPM_PART_FLAG_IPC
-    , "PSA-ROT", 0x00000102, NORMAL);
+    , "PSA-ROT", TFM_SP_CRYPTO_ID, NORMAL);
 PARTITION_ADD_INIT_FUNC(TFM_SP_CRYPTO, tfm_crypto_init);
 
 #ifdef TFM_PARTITION_PLATFORM
 /******** TFM_SP_PLATFORM ********/
+#define TFM_PARTITION_TFM_SP_PLATFORM_IRQ_COUNT 0
 PARTITION_DECLARE(TFM_SP_PLATFORM, 0
-    , "PSA-ROT", 0x00000102, NORMAL);
+    , "PSA-ROT", TFM_SP_PLATFORM_ID, NORMAL);
 PARTITION_ADD_INIT_FUNC(TFM_SP_PLATFORM, platform_sp_init);
 #endif /* TFM_PARTITION_PLATFORM */
 
 /******** TFM_SP_INITIAL_ATTESTATION ********/
+#define TFM_PARTITION_TFM_SP_INITIAL_ATTESTATION_IRQ_COUNT 0
 PARTITION_DECLARE(TFM_SP_INITIAL_ATTESTATION, 0
     | SPM_PART_FLAG_IPC
-    , "PSA-ROT", 0x00000103, NORMAL);
+    , "PSA-ROT", TFM_SP_INITIAL_ATTESTATION_ID, NORMAL);
 PARTITION_ADD_INIT_FUNC(TFM_SP_INITIAL_ATTESTATION, attest_partition_init);
 
 #ifdef TFM_PARTITION_TEST_CORE
 /******** TFM_SP_CORE_TEST ********/
+#define TFM_PARTITION_TFM_SP_CORE_TEST_IRQ_COUNT 0
 PARTITION_DECLARE(TFM_SP_CORE_TEST, 0
     | SPM_PART_FLAG_IPC
-    , "APPLICATION-ROT", 0x00000002, NORMAL);
+    , "APPLICATION-ROT", TFM_SP_CORE_TEST_ID, NORMAL);
 PARTITION_ADD_INIT_FUNC(TFM_SP_CORE_TEST, core_test_init);
 PARTITION_ADD_PERIPHERAL(TFM_SP_CORE_TEST, TFM_PERIPHERAL_FPGA_IO);
 #endif /* TFM_PARTITION_TEST_CORE */
 
 #ifdef TFM_PARTITION_TEST_CORE
 /******** TFM_SP_CORE_TEST_2 ********/
+#define TFM_PARTITION_TFM_SP_CORE_TEST_2_IRQ_COUNT 0
 PARTITION_DECLARE(TFM_SP_CORE_TEST_2, 0
     | SPM_PART_FLAG_IPC
-    , "APPLICATION-ROT", 0x00000003, NORMAL);
+    , "APPLICATION-ROT", TFM_SP_CORE_TEST_2_ID, NORMAL);
 PARTITION_ADD_INIT_FUNC(TFM_SP_CORE_TEST_2, core_test_2_init);
 #endif /* TFM_PARTITION_TEST_CORE */
 
 #ifdef TFM_PARTITION_TEST_SECURE_SERVICES
 /******** TFM_SP_SECURE_TEST_PARTITION ********/
+#define TFM_PARTITION_TFM_SP_SECURE_TEST_PARTITION_IRQ_COUNT 0
 PARTITION_DECLARE(TFM_SP_SECURE_TEST_PARTITION, 0
     | SPM_PART_FLAG_IPC
-    , "PSA-ROT", 0x00000005, NORMAL);
+    , "PSA-ROT", TFM_SP_SECURE_TEST_PARTITION_ID, NORMAL);
 PARTITION_ADD_INIT_FUNC(TFM_SP_SECURE_TEST_PARTITION, tfm_secure_client_service_init);
 PARTITION_ADD_PERIPHERAL(TFM_SP_SECURE_TEST_PARTITION, TFM_PERIPHERAL_STD_UART);
 #endif /* TFM_PARTITION_TEST_SECURE_SERVICES */
 
 #ifdef TFM_PARTITION_TEST_CORE_IPC
 /******** TFM_SP_IPC_SERVICE_TEST ********/
+#define TFM_PARTITION_TFM_SP_IPC_SERVICE_TEST_IRQ_COUNT 0
 PARTITION_DECLARE(TFM_SP_IPC_SERVICE_TEST, 0
     | SPM_PART_FLAG_IPC
-    , "PSA-ROT", 0x00000007, HIGH);
+    , "PSA-ROT", TFM_SP_IPC_SERVICE_TEST_ID, HIGH);
 PARTITION_ADD_INIT_FUNC(TFM_SP_IPC_SERVICE_TEST, ipc_service_test_main);
 #endif /* TFM_PARTITION_TEST_CORE_IPC */
 
 #ifdef TFM_PARTITION_TEST_CORE_IPC
 /******** TFM_SP_IPC_CLIENT_TEST ********/
+#define TFM_PARTITION_TFM_SP_IPC_CLIENT_TEST_IRQ_COUNT 0
 PARTITION_DECLARE(TFM_SP_IPC_CLIENT_TEST, 0
     | SPM_PART_FLAG_IPC
-    , "APPLICATION-ROT", 0x00000006, NORMAL);
+    , "APPLICATION-ROT", TFM_SP_IPC_CLIENT_TEST_ID, NORMAL);
 PARTITION_ADD_INIT_FUNC(TFM_SP_IPC_CLIENT_TEST, ipc_client_test_main);
 #endif /* TFM_PARTITION_TEST_CORE_IPC */
 
diff --git a/secure_fw/services/tfm_partition_list.inc.template b/secure_fw/services/tfm_partition_list.inc.template
index e314c23..02be6d7 100644
--- a/secure_fw/services/tfm_partition_list.inc.template
+++ b/secure_fw/services/tfm_partition_list.inc.template
@@ -7,6 +7,8 @@
 
 {{utilities.donotedit_warning}}
 
+#include "tfm_partition_defs.inc"
+
 #ifndef __TFM_PARTITION_LIST_INC__
 #define __TFM_PARTITION_LIST_INC__
 
@@ -15,11 +17,16 @@
 #ifdef {{manifest.attr.conditional}}
     {% endif %}
 /******** {{manifest.manifest.name}} ********/
+    {% if manifest.manifest.irqs %}
+#define TFM_PARTITION_{{manifest.manifest.name}}_IRQ_COUNT {{manifest.manifest.irqs | length() }}
+    {% else %}
+#define TFM_PARTITION_{{manifest.manifest.name}}_IRQ_COUNT 0
+    {% endif %}
 PARTITION_DECLARE({{manifest.manifest.name}}, 0
     {% if manifest.attr.tfm_partition_ipc %}
     | SPM_PART_FLAG_IPC
     {% endif %}
-    , "{{manifest.manifest.type}}", {{manifest.manifest.id}}, {{manifest.manifest.priority}});
+    , "{{manifest.manifest.type}}", {{manifest.manifest.name}}_ID, {{manifest.manifest.priority}});
 PARTITION_ADD_INIT_FUNC({{manifest.manifest.name}}, {{manifest.manifest.entry_point}});
     {% if manifest.manifest.mmio_regions %}
         {% for region in manifest.manifest.mmio_regions %}
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