aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKen Liu <ken.liu@arm.com>2018-09-20 22:42:31 +0800
committerEdison Ai <edison.ai@arm.com>2019-01-22 10:39:24 +0800
commit91d44da94e906430c72d6907d77a86d10e1a77c0 (patch)
tree3d41679a730ca1ae35a483d5f066f53c8be01142
parentf09acd4ac3593bf7dbaba846e44ebc02b6525813 (diff)
downloadtrusted-firmware-m-91d44da94e906430c72d6907d77a86d10e1a77c0.tar.gz
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>
-rw-r--r--secure_fw/core/ipc/CMakeLists.inc4
-rw-r--r--secure_fw/core/ipc/include/tfm_arch_v8m.h66
-rw-r--r--secure_fw/core/ipc/include/tfm_thread.h223
-rw-r--r--secure_fw/core/ipc/include/tfm_utils.h23
-rw-r--r--secure_fw/core/ipc/include/tfm_wait.h86
-rw-r--r--secure_fw/core/ipc/tfm_arch_v8m.c124
-rw-r--r--secure_fw/core/ipc/tfm_thread.c195
-rw-r--r--secure_fw/core/ipc/tfm_utils.c15
-rw-r--r--secure_fw/core/ipc/tfm_wait.c62
9 files changed, 798 insertions, 0 deletions
diff --git a/secure_fw/core/ipc/CMakeLists.inc b/secure_fw/core/ipc/CMakeLists.inc
index 87437ae63c..ff8e633b58 100644
--- a/secure_fw/core/ipc/CMakeLists.inc
+++ b/secure_fw/core/ipc/CMakeLists.inc
@@ -36,6 +36,10 @@ elseif (TFM_PSA_API)
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 0000000000..96914c0746
--- /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 0000000000..8a6d86364d
--- /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 0000000000..70707b887d
--- /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 0000000000..2bed0aec3d
--- /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 0000000000..5c50636896
--- /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 0000000000..39f42ee234
--- /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 0000000000..089f5b3810
--- /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 0000000000..814ec89469
--- /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;
+}