Core: Fundamental components for secure IPC

This patch provides Thread, Wait and ARCH related APIs
for PSA secure IPC implementation.

Change-Id: I338cd82563d20d75db4fd60441896f76dc85c6e2
Signed-off-by: Ken Liu <ken.liu@arm.com>
diff --git a/secure_fw/core/ipc/CMakeLists.inc b/secure_fw/core/ipc/CMakeLists.inc
index 87437ae..ff8e633 100644
--- a/secure_fw/core/ipc/CMakeLists.inc
+++ b/secure_fw/core/ipc/CMakeLists.inc
@@ -36,6 +36,10 @@
 	set (SS_IPC_C_SRC "${SS_IPC_DIR}/tfm_svcalls.c"
 			"${SS_IPC_DIR}/psa_service.c"
 			"${SS_IPC_DIR}/psa_client.c"
+			"${SS_IPC_DIR}/tfm_arch_v8m.c"
+			"${SS_IPC_DIR}/tfm_thread.c"
+			"${SS_IPC_DIR}/tfm_wait.c"
+			"${SS_IPC_DIR}/tfm_utils.c"
 			"${SS_IPC_DIR}/../tfm_core.c"
 			"${SS_IPC_DIR}/../tfm_secure_api.c"
 			"${SS_IPC_DIR}/../tfm_spm_services.c"
diff --git a/secure_fw/core/ipc/include/tfm_arch_v8m.h b/secure_fw/core/ipc/include/tfm_arch_v8m.h
new file mode 100644
index 0000000..96914c0
--- /dev/null
+++ b/secure_fw/core/ipc/include/tfm_arch_v8m.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef __TFM_ARCH_V8M_H__
+#define __TFM_ARCH_V8M_H__
+
+#include "cmsis.h"
+
+#define XPSR_T32            0x01000000
+#define LR_UNPRIVILEGED     0xfffffffd
+
+/* This header file collects the ARCH related operations. */
+struct tfm_state_context_base {
+    uint32_t    r0;
+    uint32_t    r1;
+    uint32_t    r2;
+    uint32_t    r3;
+    uint32_t    r12;
+    uint32_t    ra_lr;
+    uint32_t    ra;
+    uint32_t    xpsr;
+};
+
+struct tfm_state_context_ext {
+    uint32_t    r4;
+    uint32_t    r5;
+    uint32_t    r6;
+    uint32_t    r7;
+    uint32_t    r8;
+    uint32_t    r9;
+    uint32_t    r10;
+    uint32_t    r11;
+    uint32_t    sp;
+    uint32_t    sp_limit;
+    uint32_t    dummy;
+    uint32_t    lr;
+};
+
+struct tfm_state_context {
+    struct tfm_state_context_ext     ctxb;
+};
+
+#define TFM_STATE_1ST_ARG(ctx)     \
+                    (((struct tfm_state_context_base *)(ctx)->ctxb.sp)->r0)
+#define TFM_STATE_2ND_ARG(ctx)     \
+                    (((struct tfm_state_context_base *)(ctx)->ctxb.sp)->r1)
+#define TFM_STATE_3RD_ARG(ctx)     \
+                    (((struct tfm_state_context_base *)(ctx)->ctxb.sp)->r2)
+#define TFM_STATE_4TH_ARG(ctx)     \
+                    (((struct tfm_state_context_base *)(ctx)->ctxb.sp)->r3)
+#define TFM_STATE_RET_VAL(ctx)     \
+                    (((struct tfm_state_context_base *)(ctx)->ctxb.sp)->r0)
+
+__STATIC_INLINE void tfm_trigger_pendsv(void)
+{
+    SCB->ICSR = SCB_ICSR_PENDSVSET_Msk;
+}
+
+void tfm_initialize_context(struct tfm_state_context *ctx,
+                            uint32_t r0, uint32_t ra,
+                            uint32_t sp, uint32_t sp_limit);
+
+#endif
diff --git a/secure_fw/core/ipc/include/tfm_thread.h b/secure_fw/core/ipc/include/tfm_thread.h
new file mode 100644
index 0000000..8a6d863
--- /dev/null
+++ b/secure_fw/core/ipc/include/tfm_thread.h
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef __TFM_THREAD_H__
+#define __TFM_THREAD_H__
+
+#include "tfm_arch_v8m.h"
+#include "cmsis_compiler.h"
+
+/* Status code */
+#define THRD_STAT_CREATING        0
+#define THRD_STAT_RUNNING         1
+#define THRD_STAT_BLOCK           2
+#define THRD_STAT_DETACH          3
+#define THRD_STAT_INVALID         4
+
+/* Security attribute - default as security */
+#define THRD_ATTR_SECURE_OFFSET   16
+#define THRD_ATTR_SECURE          (0)
+#define THRD_ATTR_NON_SECURE      (1 << THRD_ATTR_SECURE_OFFSET)
+
+/* Lower value has higher priority */
+#define THRD_PRIOR_MASK           0xFF
+#define THRD_PRIOR_HIGHEST        0x0
+#define THRD_PRIOR_MEDIUM         0x7F
+#define THRD_PRIOR_LOWEST         0xFF
+
+/* Error code */
+#define THRD_SUCCESS              0
+#define THRD_ERR_INVALID_PARAM    1
+
+/* Thread entry function type */
+typedef void *(*tfm_thrd_func_t)(void *);
+
+/* Thread context */
+struct tfm_thrd_ctx {
+    tfm_thrd_func_t pfn;          /* entry function      */
+    void            *param;       /* entry parameter     */
+    uint8_t         *sp_base;     /* stack bottom        */
+    uint8_t         *sp_top;      /* stack top           */
+    uint32_t        prior;        /* priority            */
+    uint32_t        status;       /* status              */
+
+    struct tfm_state_context state_ctx; /* State context */
+    struct tfm_thrd_ctx *next;    /* next thread in list */
+};
+
+/*
+ * Initialize a thread context with the necessary info.
+ *
+ * Parameters :
+ *  pth         -    pointer of caller provided thread context
+ *  pfn         -    thread entry function
+ *  param       -    thread entry function parameter
+ *  sp_base     -    stack pointer base (higher address)
+ *  sp_top      -    stack pointer top (lower address)
+ *
+ * Notes :
+ *  Thread contex rely on caller allocated memory; initialize members in
+ *  context. This function does not insert thread into schedulable list.
+ */
+void tfm_thrd_init(struct tfm_thrd_ctx *pth,
+                   tfm_thrd_func_t pfn, void *param,
+                   uint8_t *sp_base, uint8_t *sp_top);
+
+/* Set thread priority.
+ *
+ * Parameters :
+ *  pth         -     pointer of thread context
+ *  prior       -     priority value (0~255)
+ *
+ * Notes :
+ *  Set thread priority. Priority is set to THRD_PRIOR_MEDIUM in
+ *  tfm_thrd_init().
+ */
+void __STATIC_INLINE tfm_thrd_priority(struct tfm_thrd_ctx *pth,
+                                       uint32_t prior)
+{
+    pth->prior &= ~THRD_PRIOR_MASK;
+    pth->prior |= prior & THRD_PRIOR_MASK;
+}
+
+/*
+ * Set thread security attribute.
+ *
+ * Parameters :
+ *  pth         -     pointer of thread context
+ *  attr_secure -     THRD_ATTR_SECURE or THRD_ATTR_NON_SECURE
+ *
+ * Notes
+ *  Reuse prior of thread context to shift down non-secure thread priority.
+ */
+void __STATIC_INLINE tfm_thrd_secure(struct tfm_thrd_ctx *pth,
+                                     uint32_t attr_secure)
+{
+    pth->prior &= ~THRD_ATTR_NON_SECURE;
+    pth->prior |= attr_secure;
+}
+
+/*
+ * Set thread status.
+ *
+ * Parameters :
+ *  pth         -     pointer of thread context
+ *  new_status  -     new status of thread
+ *
+ * Return :
+ *  None
+ *
+ * Notes :
+ *  Thread status is not changed if invalid status value inputed.
+ */
+void tfm_thrd_set_status(struct tfm_thrd_ctx *pth, uint32_t new_status);
+
+/*
+ * Get thread status.
+ *
+ * Parameters :
+ *  pth         -     pointer of thread context
+ *
+ * Return :
+ *  Status of thread
+ */
+uint32_t __STATIC_INLINE tfm_thrd_get_status(struct tfm_thrd_ctx *pth)
+{
+    return pth->status;
+}
+
+/*
+ * Set thread state return value.
+ *
+ * Parameters :
+ *  pth         -     pointer of thread context
+ *  retval      -     return value to be set for thread state
+ *
+ * Notes :
+ *  This API is useful for blocked syscall blocking thread. Syscall
+ *  could set its return value to the caller before caller goes.
+ */
+void __STATIC_INLINE tfm_thrd_set_retval(struct tfm_thrd_ctx *pth,
+                                         uint32_t retval)
+{
+    TFM_STATE_RET_VAL(&pth->state_ctx) = retval;
+}
+
+/*
+ * Validate thread context and insert it into schedulable list.
+ *
+ * Parameters :
+ *  pth         -     pointer of thread context
+ *
+ * Return :
+ *  THRD_SUCCESS for success. Or an error is returned.
+ *
+ * Notes :
+ *  This function validates thread info. It returns error if thread info
+ *  is not correct. Thread is avaliable after successful tfm_thrd_start().
+ */
+uint32_t tfm_thrd_start(struct tfm_thrd_ctx *pth);
+
+/*
+ * Get current running thread.
+ *
+ * Return :
+ *  Current running thread context pointer.
+ */
+struct tfm_thrd_ctx *tfm_thrd_curr_thread(void);
+
+/*
+ * Get next running thread in list.
+ *
+ * Return :
+ *  Pointer of next thread to be run.
+ */
+struct tfm_thrd_ctx *tfm_thrd_next_thread(void);
+
+/*
+ * Activate a scheduling action after exception.
+ *
+ * Notes :
+ *  This function could be called multiple times before scheduling.
+ */
+void tfm_thrd_activate_schedule(void);
+
+/*
+ * Save current context into 'prev' thread and switch to 'next'.
+ *
+ * Parameters :
+ *  ctxb        -     latest caller context
+ *  prev        -     previous thread to be switched out
+ *  next        -     thread to be run
+ *
+ * Notes :
+ *  This function could be called multiple times before scheduling.
+ */
+void tfm_thrd_context_switch(struct tfm_state_context_ext *ctxb,
+                             struct tfm_thrd_ctx *prev,
+                             struct tfm_thrd_ctx *next);
+
+/*
+ * Exit current running thread.
+ *
+ * Notes :
+ *  Remove current thread out of schedulable list.
+ */
+void tfm_thrd_do_exit(void);
+
+/*
+ * PendSV specified function.
+ *
+ * Parameters :
+ *  ctxb        -    State context storage pointer
+ *
+ * Notes:
+ *  This is a staging API. Scheduler should be called in SPM finally and
+ *  this function will be obsoleted later.
+ */
+void tfm_pendsv_do_schedule(struct tfm_state_context_ext *ctxb);
+
+#endif
diff --git a/secure_fw/core/ipc/include/tfm_utils.h b/secure_fw/core/ipc/include/tfm_utils.h
new file mode 100644
index 0000000..70707b8
--- /dev/null
+++ b/secure_fw/core/ipc/include/tfm_utils.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef __TFM_UTILS_H__
+#define __TFM_UTILS_H__
+
+/* CPU spin here */
+void tfm_panic(void);
+
+/* Assert and spin */
+#define TFM_ASSERT(cond)                                            \
+            do {                                                    \
+                if (!(cond)) {                                      \
+                    printf("Assert:%s:%d", __FUNCTION__, __LINE__); \
+                    while (1)                                       \
+                        ;                                           \
+                }                                                   \
+            } while (0)
+
+#endif
diff --git a/secure_fw/core/ipc/include/tfm_wait.h b/secure_fw/core/ipc/include/tfm_wait.h
new file mode 100644
index 0000000..2bed0ae
--- /dev/null
+++ b/secure_fw/core/ipc/include/tfm_wait.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef __TFM_WAIT_H__
+#define __TFM_WAIT_H__
+
+#include "cmsis_compiler.h"
+
+#define EVENT_MAGIC               0x65766e74
+#define EVENT_STAT_WAITED         0x0
+#define EVENT_STAT_SIGNALED       0x1
+
+struct tfm_event_ctx {
+    uint32_t magic;               /* 'evnt'              */
+    struct tfm_thrd_ctx *owner;   /* waiting thread      */
+    uint32_t status;              /* status              */
+    uint32_t retval;              /* return value        */
+};
+
+/*
+ * Initialize an event context.
+ *
+ * Parameters :
+ *  pevt        -    pointer of event context caller provided
+ *  stat        -    initial status (EVENT_STAT_WAITED or EVENT_STAT_SIGNALED)
+ */
+void __STATIC_INLINE tfm_event_init(struct tfm_event_ctx *pevt, uint32_t stat)
+{
+    pevt->magic = EVENT_MAGIC;
+    pevt->status = stat;
+    pevt->owner = NULL;
+    pevt->retval = 0;
+}
+
+/*
+ * Wait on an event.
+ *
+ * Parameters :
+ *  pevt        -    pointer of event context
+ *
+ * Notes :
+ *  Thread is blocked if event is not signaled.
+ */
+void tfm_event_wait(struct tfm_event_ctx *pevt);
+
+/*
+ * Signal an event.
+ *
+ * Parameters :
+ *  pevt        -    pointer of event context
+ *
+ * Notes :
+ *  Waiting thread on this event will be running.
+ */
+void tfm_event_signal(struct tfm_event_ctx *pevt);
+
+/*
+ * Peek an event status.
+ *
+ * Parameters :
+ *  pevt        -    pointer of event context
+ *
+ * Return :
+ *  Status of event.
+ *
+ * Notes :
+ *  This function is used for getting event status without blocking thread.
+ */
+uint32_t tfm_event_peek(struct tfm_event_ctx *pevt);
+
+/*
+ * Set event owner return value.
+ *
+ * Parameters :
+ *  pevt        -    pointer of event context
+ *  retval      -    return value of blocked owner thread
+ *
+ * Notes :
+ *  Thread return value is set while thread is to be running.
+ */
+void tfm_event_owner_retval(struct tfm_event_ctx *pevt, uint32_t retval);
+
+#endif
diff --git a/secure_fw/core/ipc/tfm_arch_v8m.c b/secure_fw/core/ipc/tfm_arch_v8m.c
new file mode 100644
index 0000000..5c50636
--- /dev/null
+++ b/secure_fw/core/ipc/tfm_arch_v8m.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#include <inttypes.h>
+#include <stdio.h>
+
+#include "tfm_arch_v8m.h"
+#include "cmsis.h"
+#include "psa_client.h"
+#include "psa_service.h"
+#include "secure_utilities.h"
+#include "tfm_utils.h"
+#include "tfm_thread.h"
+
+/* This file contains the ARCH code for ARM V8M */
+
+/*
+ * Thread exit zone.
+ * This function is set as the return address of thread entry and only
+ * privileged thread could return here. Un-privileged thread triggers
+ * fault if it tries to jump here and it gets exit by fault handler.
+ *
+ * The reason of putting this function here is for fault handler checking.
+ * Function address could be checked in fault handler to know it is a REAL
+ * thread exit or just an exception.
+ */
+static void exit_zone(void)
+{
+    tfm_thrd_do_exit();
+}
+
+void tfm_initialize_context(struct tfm_state_context *ctx,
+                            uint32_t r0, uint32_t ra,
+                            uint32_t sp, uint32_t sp_limit)
+{
+    /*
+     * For security consideration, set unused registers into ZERO;
+     * and only necessary registers are set here.
+     */
+    struct tfm_state_context_base *p_ctxa =
+                            (struct tfm_state_context_base *)sp;
+
+    /*
+     * Shift back SP to leave space for holding base context
+     * since thread is kicked off through exception return.
+     */
+    p_ctxa--;
+
+    /* Basic context is considerate at thread start.*/
+    tfm_memset(p_ctxa, 0, sizeof(*p_ctxa));
+    p_ctxa->r0 = r0;
+    p_ctxa->ra = ra;
+    p_ctxa->ra_lr = (uint32_t)exit_zone;
+    p_ctxa->xpsr = XPSR_T32;
+
+    tfm_memset(ctx, 0, sizeof(*ctx));
+    ctx->ctxb.sp = (uint32_t)p_ctxa;
+    ctx->ctxb.sp_limit = sp_limit;
+    ctx->ctxb.lr = LR_UNPRIVILEGED;
+}
+
+/*
+ * Stack status at PendSV entry:
+ *
+ *                                            [ R0 - R3  ]<- PSP
+ *                                            [ R12      ]
+ *                                            [ LR_of_RA ]
+ *                       MSP->[ ........ ]    [ RA       ]
+ *                            [ ........ ]    [ XPSR     ]
+ *                                            [ ........ ]
+ *                                            [ ........ ]
+ *
+ * Stack status before calling pendsv_do_schedule():
+ *
+ *                       MSP->[ R4 - R11 ]
+ *                            [ PSP      ]--->[ R0 - R3  ]
+ *                            [ PSP Limit]    [ R12      ]
+ *                            [ R2(dummy)]    [ LR_of_RA ]
+ *                            [ LR       ]    [ RA       ]
+ *                            [ ........ ]    [ XPSR     ]
+ *                            [ ........ ]    [ ........ ]
+ *                                            [ ........ ]
+ *
+ * pendsv_do_schedule() updates stacked context into current thread and
+ * replace stacked context with context of next thread.
+ *
+ * Scheduler does not support handler mode thread so take PSP/PSP_LIMIT as
+ * thread SP/SP_LIMIT. R2 holds dummy data due to stack operation is 8 bytes
+ * aligned.
+ */
+__attribute__((naked)) void PendSV_Handler(void)
+{
+    __ASM(
+        "mrs     r0, psp                    \n"
+        "mrs     r1, psplim                 \n"
+        "push    {r0, r1, r2, lr}           \n"
+        "push    {r4-r11}                   \n"
+        "mov     r0, sp                     \n"
+        "bl      tfm_pendsv_do_schedule     \n"
+        "pop     {r4-r11}                   \n"
+        "pop     {r0, r1, r2, lr}           \n"
+        "msr     psp, r0                    \n"
+        "msr     psplim, r1                 \n"
+        "bx      lr                         \n"
+    );
+}
+
+/* Reserved for future usage */
+__attribute__((naked)) void MemManage_Handler(void)
+{
+    __ASM("b    .");
+}
+
+__attribute__((naked)) void BusFault_Handler(void)
+{
+    __ASM("b    .");
+}
+__attribute__((naked)) void UsageFault_Handler(void)
+{
+    __ASM("b    .");
+}
diff --git a/secure_fw/core/ipc/tfm_thread.c b/secure_fw/core/ipc/tfm_thread.c
new file mode 100644
index 0000000..39f42ee
--- /dev/null
+++ b/secure_fw/core/ipc/tfm_thread.c
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#include <inttypes.h>
+#include <stdio.h>
+#include "tfm_arch_v8m.h"
+#include "tfm_thread.h"
+#include "tfm_utils.h"
+#include "secure_utilities.h"
+
+/* Force ZERO in case ZI(bss) clear is missing */
+static struct tfm_thrd_ctx *p_thrd_head = NULL;
+static struct tfm_thrd_ctx *p_runn_head = NULL;
+static struct tfm_thrd_ctx *p_curr_thrd = NULL;
+
+/* Define Macro to fetch global to support future expansion (PERCPU e.g.) */
+#define LIST_HEAD   p_thrd_head
+#define RUNN_HEAD   p_runn_head
+#define CURR_THRD   p_curr_thrd
+
+/* To get next running thread for scheduler */
+struct tfm_thrd_ctx *tfm_thrd_next_thread(void)
+{
+    struct tfm_thrd_ctx *pth = RUNN_HEAD;
+
+    /*
+     * First RUNNING thread has highest priority since threads are sorted with
+     * priority.
+     */
+    while (pth && pth->status != THRD_STAT_RUNNING) {
+        pth = pth->next;
+    }
+
+    return pth;
+}
+
+/* To get current thread for caller */
+struct tfm_thrd_ctx *tfm_thrd_curr_thread()
+{
+    return CURR_THRD;
+}
+
+/* Insert a new thread into list by descending priority (Highest at head) */
+static void insert_by_prior(struct tfm_thrd_ctx **head,
+                            struct tfm_thrd_ctx *node)
+{
+    if (*head == NULL || (node->prior <= (*head)->prior)) {
+        node->next = *head;
+        *head = node;
+    } else {
+        struct tfm_thrd_ctx *iter = *head;
+
+        while (iter->next && (node->prior > iter->next->prior)) {
+            iter = iter->next;
+        }
+        node->next = iter->next;
+        iter->next = node;
+    }
+}
+
+/*
+ * Set first running thread as head to reduce enumerate
+ * depth while searching for a first running thread.
+ */
+static void update_running_head(struct tfm_thrd_ctx **runn,
+                                struct tfm_thrd_ctx *node)
+{
+    if ((node->status == THRD_STAT_RUNNING) &&
+        (*runn == NULL || (node->prior <= (*runn)->prior))) {
+        *runn = node;
+    } else {
+        *runn = tfm_thrd_next_thread();
+    }
+}
+
+/* Set context members only. No validation here */
+void tfm_thrd_init(struct tfm_thrd_ctx *pth,
+                   tfm_thrd_func_t pfn, void *param,
+                   uint8_t *sp_base, uint8_t *sp_top)
+{
+    pth->prior = THRD_PRIOR_MEDIUM;
+    pth->status = THRD_STAT_CREATING;
+    pth->pfn = pfn;
+    pth->param = param;
+    pth->sp_base = sp_base;
+    pth->sp_top = sp_top;
+}
+
+uint32_t tfm_thrd_start(struct tfm_thrd_ctx *pth)
+{
+    /* Validate parameters before really start */
+    if ((pth->status != THRD_STAT_CREATING) ||
+        (pth->pfn == NULL)                  ||
+        (pth->sp_base == NULL)              ||
+        (pth->sp_top == NULL)) {
+        return THRD_ERR_INVALID_PARAM;
+    }
+
+    /* Thread management runs in handler mode; set context for thread mode. */
+    tfm_initialize_context(&pth->state_ctx,
+                           (uint32_t)pth->param, (uint32_t)pth->pfn,
+                           (uint32_t)pth->sp_base, (uint32_t)pth->sp_top);
+
+    /* Insert a new thread with priority */
+    insert_by_prior(&LIST_HEAD, pth);
+
+    /* Mark it as RUNNING after insertion */
+    tfm_thrd_set_status(pth, THRD_STAT_RUNNING);
+
+    return THRD_SUCCESS;
+}
+
+void tfm_thrd_set_status(struct tfm_thrd_ctx *pth, uint32_t new_status)
+{
+    TFM_ASSERT(pth != NULL && new_status < THRD_STAT_INVALID);
+
+    pth->status = new_status;
+    update_running_head(&RUNN_HEAD, pth);
+}
+
+/*
+ * TEMP WORKAROUND: The caller function who called thread module init needs to
+ * be returned. The caller is not a thread. Create a dummy IDLE thread to
+ * collect caller context; and schedule back to the caller with this context
+ * after all other real threads blocked.
+ *
+ * This WORKAROUND needs to be removed after IPC NSPM takes place.
+ */
+#define DUMMY_IDLE_TAG       0xDEEDDEED
+static uint8_t idle_stack[32] __attribute__((aligned(8)));
+static struct tfm_thrd_ctx idle_thread;
+static struct tfm_thrd_ctx *init_idle_thread(struct tfm_thrd_ctx *pth)
+{
+    /*
+     * IDLE thread is a thread with the lowest priority.
+     * It gets scheduled after all other higher priority threads get blocked.
+     * The entry of IDLE thread is a dummy and has no mean.
+     */
+    tfm_thrd_init(pth, (tfm_thrd_func_t)DUMMY_IDLE_TAG, NULL,
+                  (uint8_t *)&idle_stack[32], (uint8_t *)idle_stack);
+    tfm_thrd_priority(pth, THRD_PRIOR_LOWEST);
+    tfm_thrd_start(pth);
+    return pth;
+}
+
+/* Scheduling won't happen immediately but after the exception returns */
+void tfm_thrd_activate_schedule(void)
+{
+    /*
+     * The current thread can be NULL only when initializing. Create the IDLE
+     * thread and set it as the current thread to collect caller context.
+     */
+    if (CURR_THRD == NULL) {
+        CURR_THRD = init_idle_thread(&idle_thread);
+    }
+
+    tfm_trigger_pendsv();
+}
+
+/* Remove current thread out of the schedulable list */
+void tfm_thrd_do_exit(void)
+{
+    CURR_THRD->status = THRD_STAT_DETACH;
+    tfm_trigger_pendsv();
+}
+
+void tfm_thrd_context_switch(struct tfm_state_context_ext *ctxb,
+                             struct tfm_thrd_ctx *prev,
+                             struct tfm_thrd_ctx *next)
+{
+    /* Update latest context into the current thread context */
+    tfm_memcpy(&prev->state_ctx.ctxb, ctxb, sizeof(*ctxb));
+    /* Update background context with next thread's context */
+    tfm_memcpy(ctxb, &next->state_ctx.ctxb, sizeof(next->state_ctx.ctxb));
+    /* Set current thread indicator with next thread */
+    CURR_THRD = next;
+}
+
+/*
+ * This function is a reference implementation for PendSV handler in
+ * isolation level 1. More jobs (sandboxing e.g.) need to be done while
+ * scheduling in other isolation levels.
+ */
+void tfm_pendsv_do_schedule(struct tfm_state_context_ext *ctxb)
+{
+    struct tfm_thrd_ctx *pth = tfm_thrd_next_thread();
+
+    /* Swith context if another thread ready to run */
+    if (pth && pth != CURR_THRD) {
+        tfm_thrd_context_switch(ctxb, CURR_THRD, pth);
+    }
+}
diff --git a/secure_fw/core/ipc/tfm_utils.c b/secure_fw/core/ipc/tfm_utils.c
new file mode 100644
index 0000000..089f5b3
--- /dev/null
+++ b/secure_fw/core/ipc/tfm_utils.c
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#include <inttypes.h>
+#include <stdio.h>
+#include "tfm_utils.h"
+
+void tfm_panic(void)
+{
+    while (1)
+        ;
+}
diff --git a/secure_fw/core/ipc/tfm_wait.c b/secure_fw/core/ipc/tfm_wait.c
new file mode 100644
index 0000000..814ec89
--- /dev/null
+++ b/secure_fw/core/ipc/tfm_wait.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#include <inttypes.h>
+#include <stdio.h>
+#include "tfm_arch_v8m.h"
+#include "tfm_thread.h"
+#include "tfm_utils.h"
+#include "tfm_wait.h"
+
+void tfm_event_wait(struct tfm_event_ctx *pevt)
+{
+    struct tfm_thrd_ctx *curr_thrd = tfm_thrd_curr_thread();
+
+    TFM_ASSERT(pevt && pevt->magic == EVENT_MAGIC);
+
+    if (pevt->status == EVENT_STAT_WAITED) {
+        pevt->owner = curr_thrd;
+        pevt->retval = TFM_STATE_1ST_ARG(&pevt->owner->state_ctx);
+        tfm_thrd_set_status(pevt->owner, THRD_STAT_BLOCK);
+        tfm_thrd_activate_schedule();
+    }
+
+    pevt->status = EVENT_STAT_WAITED;
+}
+
+/* Peek the status to see if caller would block. */
+uint32_t tfm_event_peek(struct tfm_event_ctx *pevt)
+{
+    TFM_ASSERT(pevt && pevt->magic == EVENT_MAGIC);
+
+    return pevt->status;
+}
+
+void tfm_event_signal(struct tfm_event_ctx *pevt)
+{
+    TFM_ASSERT(pevt && pevt->magic == EVENT_MAGIC);
+
+    pevt->status = EVENT_STAT_SIGNALED;
+
+    /*
+     * Wake the blocked owner up and keep the status as EVENT_STAT_WAITED
+     * if there is an owner. Or the second event wait caller will return
+     * without block since status is EVENT_STAT_SIGNALED.
+     */
+    if (pevt->owner && pevt->owner->status == THRD_STAT_BLOCK) {
+        tfm_thrd_set_status(pevt->owner, THRD_STAT_RUNNING);
+        tfm_thrd_set_retval(pevt->owner, pevt->retval);
+        pevt->status = EVENT_STAT_WAITED;
+        tfm_thrd_activate_schedule();
+    }
+}
+
+void tfm_event_owner_retval(struct tfm_event_ctx *pmtx, uint32_t retval)
+{
+    TFM_ASSERT(pmtx && pmtx->magic == EVENT_MAGIC);
+
+    pmtx->retval = retval;
+}