Core: Move spm code of IPC model to spm_api_ipc.c

- Moves API functions from tfm_spm.c to spm_api_ipc.c.
- Moves content from tfm_spm.h to spm_api.h.
- Removes tfm_spm.c and tfm_spm.h.

Change-Id: Idb333ec2e97e02206d6431dcbf13051aa4b108d0
Signed-off-by: Mingyang Sun <mingyang.sun@arm.com>
diff --git a/secure_fw/spm/CMakeLists.inc b/secure_fw/spm/CMakeLists.inc
index ef9094b..696feb4 100644
--- a/secure_fw/spm/CMakeLists.inc
+++ b/secure_fw/spm/CMakeLists.inc
@@ -26,6 +26,9 @@
 
 set (SS_SPM_C_SRC "${SS_SPM_DIR}/spm_api.c")
 
+if(TFM_PSA_API)
+	list(APPEND SS_SPM_C_SRC "${SS_SPM_DIR}/spm_api_ipc.c")
+endif()
 
 #Append all our source files to global lists.
 list(APPEND ALL_SRC_C ${SS_SPM_C_SRC})
diff --git a/secure_fw/spm/spm_api.c b/secure_fw/spm/spm_api.c
index 3a3b2f8..36f4afd 100644
--- a/secure_fw/spm/spm_api.c
+++ b/secure_fw/spm/spm_api.c
@@ -10,7 +10,7 @@
 #include <stdio.h>
 #include <string.h>
 #include "spm_api.h"
-#include "platform/include/tfm_spm_hal.h"
+#include "tfm_spm_hal.h"
 #include "tfm_memory_utils.h"
 #include "spm_db.h"
 #include "tfm_internal.h"
@@ -222,7 +222,6 @@
 #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)
@@ -273,9 +272,8 @@
     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);
+                            partition_idx, stack_frame->caller_partition_idx);
     stack_frame->caller_partition_idx = 0;
-
 }
 
 #endif /* !defined(TFM_PSA_API) */
@@ -305,7 +303,6 @@
 
     return tfm_spm_hal_partition_sandbox_config(&(part->memory_data),
                                                 part->platform_data);
-
 }
 
 enum spm_err_t tfm_spm_partition_sandbox_deconfig(uint32_t partition_idx)
diff --git a/secure_fw/spm/spm_api.h b/secure_fw/spm/spm_api.h
index adc57f2..6b8164b 100644
--- a/secure_fw/spm/spm_api.h
+++ b/secure_fw/spm/spm_api.h
@@ -12,9 +12,12 @@
 #include "tfm_api.h"
 #include "spm_partition_defs.h"
 #include "secure_fw/core/tfm_secure_api.h"
+#include <stdbool.h>
 #ifdef TFM_PSA_API
 #include "tfm_list.h"
 #include "tfm_wait.h"
+#include "tfm_message_queue.h"
+#include "tfm_secure_api.h"
 #endif
 
 #define SPM_INVALID_PARTITION_IDX     (~0U)
@@ -422,4 +425,301 @@
  */
 void tfm_spm_partition_change_privilege(uint32_t privileged);
 
+#ifdef TFM_PSA_API
+
+#define TFM_SPM_MAX_ROT_SERV_NUM        48
+#define TFM_VERSION_POLICY_RELAXED      0
+#define TFM_VERSION_POLICY_STRICT       1
+
+#define TFM_CONN_HANDLE_MAX_NUM         32
+
+/* RoT connection handle list */
+struct tfm_conn_handle_t {
+    psa_handle_t handle;            /* Handle value                         */
+    void *rhandle;                  /* Reverse handle value                 */
+    struct tfm_list_node_t list;    /* list node                            */
+};
+
+/* Service database defined by manifest */
+struct tfm_spm_service_db_t {
+    char *name;                     /* Service name                          */
+    uint32_t partition_id;          /* Partition ID which service belong to  */
+    psa_signal_t signal;            /* Service signal                        */
+    uint32_t sid;                   /* Service identifier                    */
+    bool non_secure_client;         /* If can be called by non secure client */
+    uint32_t minor_version;         /* Minor version                         */
+    uint32_t minor_policy;          /* Minor version policy                  */
+};
+
+/* RoT Service data */
+struct tfm_spm_service_t {
+    struct tfm_spm_service_db_t *service_db; /* Service database pointer     */
+    struct spm_partition_desc_t *partition;  /*
+                                              * Point to secure partition
+                                              * data
+                                              */
+    struct tfm_list_node_t handle_list;      /* Service handle list          */
+    struct tfm_msg_queue_t msg_queue;        /* Message queue                */
+    struct tfm_list_node_t list;             /* For list operation           */
+};
+
+/*************************** Extended SPM functions **************************/
+
+/**
+ * \brief   Get the running partition ID.
+ *
+ * \return  Returns the partition ID
+ */
+uint32_t tfm_spm_partition_get_running_partition_id(void);
+
+/**
+ * \brief                   Get the current partition mode.
+ *
+ * \param[in] partition_idx                 Index of current partition
+ *
+ * \retval TFM_PARTITION_PRIVILEGED_MODE    Privileged mode
+ * \retval TFM_PARTITION_UNPRIVILEGED_MODE  Unprivileged mode
+ */
+uint32_t tfm_spm_partition_get_privileged_mode(uint32_t partition_idx);
+
+/******************** Service handle management functions ********************/
+
+/**
+ * \brief                   Create connection handle for client connect
+ *
+ * \param[in] service       Target service context pointer
+ *
+ * \retval PSA_NULL_HANDLE  Create failed \ref PSA_NULL_HANDLE
+ * \retval >0               Service handle created, \ref psa_handle_t
+ */
+psa_handle_t tfm_spm_create_conn_handle(struct tfm_spm_service_t *service);
+
+/**
+ * \brief                   Free connection handle which not used anymore.
+ *
+ * \param[in] service       Target service context pointer
+ * \param[in] conn_handle   Connection handle created by
+ *                          tfm_spm_create_conn_handle(), \ref psa_handle_t
+ *
+ * \retval IPC_SUCCESS      Success
+ * \retval IPC_ERROR_BAD_PARAMETERS  Bad parameters input
+ * \retval "Does not return"  Panic for not find service by handle
+ */
+int32_t tfm_spm_free_conn_handle(struct tfm_spm_service_t *service,
+                                 psa_handle_t conn_handle);
+
+/**
+ * \brief                   Set reverse handle value for connection.
+ *
+ * \param[in] service       Target service context pointer
+ * \param[in] conn_handle   Connection handle created by
+ *                          tfm_spm_create_conn_handle(), \ref psa_handle_t
+ * \param[in] rhandle       rhandle need to save
+ *
+ * \retval IPC_SUCCESS      Success
+ * \retval IPC_ERROR_BAD_PARAMETERS  Bad parameters input
+ * \retval "Does not return"  Panic for not find handle node
+ */
+int32_t tfm_spm_set_rhandle(struct tfm_spm_service_t *service,
+                            psa_handle_t conn_handle,
+                            void *rhandle);
+
+/**
+ * \brief                   Get reverse handle value from connection hanlde.
+ *
+ * \param[in] service       Target service context pointer
+ * \param[in] conn_handle   Connection handle created by
+ *                          tfm_spm_create_conn_handle(), \ref psa_handle_t
+ *
+ * \retval void *           Success
+ * \retval "Does not return"  Panic for those:
+ *                              service pointer are NULL
+ *                              hanlde is \ref PSA_NULL_HANDLE
+ *                              handle node does not be found
+ */
+void *tfm_spm_get_rhandle(struct tfm_spm_service_t *service,
+                          psa_handle_t conn_handle);
+
+/******************** Partition management functions *************************/
+
+/**
+ * \brief                   Get current running partition context.
+ *
+ * \retval NULL             Failed
+ * \retval "Not NULL"       Return the parttion context pointer
+ *                          \ref spm_partition_desc_t structures
+ */
+struct spm_partition_desc_t *tfm_spm_get_running_partition(void);
+
+/**
+ * \brief                   Get the service context by signal.
+ *
+ * \param[in] partition     Partition context pointer
+ *                          \ref spm_partition_desc_t structures
+ * \param[in] signal        Signal associated with inputs to the Secure
+ *                          Partition, \ref psa_signal_t
+ *
+ * \retval NULL             Failed
+ * \retval "Not NULL"       Target service context pointer,
+ *                          \ref tfm_spm_service_t structures
+ */
+struct tfm_spm_service_t *
+    tfm_spm_get_service_by_signal(struct spm_partition_desc_t *partition,
+                                  psa_signal_t signal);
+
+/**
+ * \brief                   Get the service context by service ID.
+ *
+ * \param[in] sid           RoT Service identity
+ *
+ * \retval NULL             Failed
+ * \retval "Not NULL"       Target service context pointer,
+ *                          \ref tfm_spm_service_t structures
+ */
+struct tfm_spm_service_t *tfm_spm_get_service_by_sid(uint32_t sid);
+
+/**
+ * \brief                   Get the service context by connection handle.
+ *
+ * \param[in] conn_handle   Connection handle created by
+ *                          tfm_spm_create_conn_handle()
+ *
+ * \retval NULL             Failed
+ * \retval "Not NULL"       Target service context pointer,
+ *                          \ref tfm_spm_service_t structures
+ */
+struct tfm_spm_service_t *
+    tfm_spm_get_service_by_handle(psa_handle_t conn_handle);
+
+/**
+ * \brief                   Get the partition context by partition ID.
+ *
+ * \param[in] partition_id  Partition identity
+ *
+ * \retval NULL             Failed
+ * \retval "Not NULL"       Target partition context pointer,
+ *                          \ref spm_partition_desc_t structures
+ */
+struct spm_partition_desc_t *
+    tfm_spm_get_partition_by_id(int32_t partition_id);
+
+/************************ Message functions **********************************/
+
+/**
+ * \brief                   Get message context by message handle.
+ *
+ * \param[in] msg_handle    Message handle which is a reference generated
+ *                          by the SPM to a specific message.
+ *
+ * \return                  The message body context pointer
+ *                          \ref tfm_msg_body_t structures
+ */
+struct tfm_msg_body_t *tfm_spm_get_msg_from_handle(psa_handle_t msg_handle);
+
+/**
+ * \brief                   Create a message for PSA client call.
+ *
+ * \param[in] service       Target service context pointer, which can be
+ *                          obtained by partition management functions
+ * \prarm[in] handle        Connect handle return by psa_connect().
+ * \param[in] type          Message type, PSA_IPC_CONNECT, PSA_IPC_CALL or
+ *                          PSA_IPC_DISCONNECT
+ * \param[in] ns_caller     Whether from NS caller
+ * \param[in] invec         Array of input \ref psa_invec structures
+ * \param[in] in_len        Number of input \ref psa_invec structures
+ * \param[in] outvec        Array of output \ref psa_outvec structures
+ * \param[in] out_len       Number of output \ref psa_outvec structures
+ * \param[in] caller_outvec Array of caller output \ref psa_outvec structures
+ *
+ * \retval NULL             Failed
+ * \retval "Not NULL"       New message body pointer \ref tfm_msg_body_t
+ *                          structures
+ */
+struct tfm_msg_body_t *tfm_spm_create_msg(struct tfm_spm_service_t *service,
+                                          psa_handle_t handle,
+                                          int32_t type, int32_t ns_caller,
+                                          psa_invec *invec, size_t in_len,
+                                          psa_outvec *outvec, size_t out_len,
+                                          psa_outvec *caller_outvec);
+
+/**
+ * \brief                   Free message which unused anymore
+ *
+ * \param[in] msg           Message pointer which want to free
+ *                          \ref tfm_msg_body_t structures
+ *
+ * \retval void             Success
+ * \retval "Does not return" Failed
+ */
+void tfm_spm_free_msg(struct tfm_msg_body_t *msg);
+
+/**
+ * \brief                   Send message and wake up the SP who is waiting on
+ *                          message queue, block the current thread and
+ *                          scheduler triggered
+ *
+ * \param[in] service       Target service context pointer, which can be
+ *                          obtained by partition management functions
+ * \param[in] msg           message created by tfm_spm_create_msg()
+ *                          \ref tfm_msg_body_t structures
+ *
+ * \retval IPC_SUCCESS      Success
+ * \retval IPC_ERROR_BAD_PARAMETERS Bad parameters input
+ * \retval IPC_ERROR_GENERIC Failed to enqueue message to service message queue
+ */
+int32_t tfm_spm_send_event(struct tfm_spm_service_t *service,
+                           struct tfm_msg_body_t *msg);
+
+/**
+ * \brief                   Check the client minor version according to
+ *                          version policy
+ *
+ * \param[in] service       Target service context pointer, which can be get
+ *                          by partition management functions
+ * \param[in] minor_version Client support minor version
+ *
+ * \retval IPC_SUCCESS      Success
+ * \retval IPC_ERROR_BAD_PARAMETERS Bad parameters input
+ * \retval IPC_ERROR_VERSION Check failed
+ */
+int32_t tfm_spm_check_client_version(struct tfm_spm_service_t *service,
+                                     uint32_t minor_version);
+
+/**
+ * \brief                      Check the memory reference is valid.
+ *
+ * \param[in] buffer           Pointer of memory reference
+ * \param[in] len              Length of memory reference in bytes
+ * \param[in] ns_caller        From non-secure caller
+ * \param[in] access           Type of access specified by the
+ *                             \ref tfm_memory_access_e
+ * \param[in] privileged       Privileged mode or unprivileged mode:
+ *                             \ref TFM_PARTITION_UNPRIVILEGED_MODE
+ *                             \ref TFM_PARTITION_PRIVILEGED_MODE
+ *
+ * \retval IPC_SUCCESS               Success
+ * \retval IPC_ERROR_BAD_PARAMETERS  Bad parameters input
+ * \retval IPC_ERROR_MEMORY_CHECK    Check failed
+ */
+int32_t tfm_memory_check(void *buffer, size_t len, int32_t ns_caller,
+                         enum tfm_memory_access_e access,
+                         uint32_t privileged);
+
+/* This function should be called before schedule function */
+void tfm_spm_init(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 /* ifdef(TFM_PSA_API) */
+
 #endif /*__SPM_API_H__ */
diff --git a/secure_fw/spm/spm_api_ipc.c b/secure_fw/spm/spm_api_ipc.c
new file mode 100644
index 0000000..595a1a2
--- /dev/null
+++ b/secure_fw/spm/spm_api_ipc.c
@@ -0,0 +1,606 @@
+/*
+ * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#include <inttypes.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include "psa/client.h"
+#include "psa/service.h"
+#include "tfm_utils.h"
+#include "tfm_spm_hal.h"
+#include "spm_api.h"
+#include "spm_db.h"
+#include "tfm_internal_defines.h"
+#include "tfm_wait.h"
+#include "tfm_message_queue.h"
+#include "tfm_list.h"
+#include "tfm_pools.h"
+#include "tfm_thread.h"
+#include "region_defs.h"
+#include "tfm_nspm.h"
+#include "tfm_memory_utils.h"
+
+/* Extern SPM variable */
+extern struct spm_partition_db_t g_spm_partition_db;
+
+/* Extern secure lock variable */
+extern int32_t tfm_secure_lock;
+
+/* Pools */
+TFM_POOL_DECLARE(conn_handle_pool, sizeof(struct tfm_conn_handle_t),
+                 TFM_CONN_HANDLE_MAX_NUM);
+TFM_POOL_DECLARE(spm_service_pool, sizeof(struct tfm_spm_service_t),
+                 TFM_SPM_MAX_ROT_SERV_NUM);
+TFM_POOL_DECLARE(msg_db_pool, sizeof(struct tfm_msg_body_t),
+                 TFM_MSG_QUEUE_MAX_MSG_NUM);
+
+static struct tfm_spm_service_db_t g_spm_service_db[] = {
+    #include "secure_fw/services/tfm_service_list.inc"
+};
+
+/********************** SPM functions for handler mode ***********************/
+
+/* Service handle management functions */
+psa_handle_t tfm_spm_create_conn_handle(struct tfm_spm_service_t *service)
+{
+    struct tfm_conn_handle_t *node;
+
+    TFM_ASSERT(service);
+
+    /* Get buffer for handle list structure from handle pool */
+    node = (struct tfm_conn_handle_t *)tfm_pool_alloc(conn_handle_pool);
+    if (!node) {
+        return PSA_NULL_HANDLE;
+    }
+
+    /* Global unique handle, use handle buffer address directly */
+    node->handle = (psa_handle_t)node;
+
+    /* Add handle node to list for next psa functions */
+    tfm_list_add_tail(&service->handle_list, &node->list);
+
+    return node->handle;
+}
+
+static struct tfm_conn_handle_t *
+    tfm_spm_find_conn_handle_node(struct tfm_spm_service_t *service,
+                                  psa_handle_t conn_handle)
+{
+    struct tfm_conn_handle_t *handle_node;
+    struct tfm_list_node_t *node, *head;
+
+    TFM_ASSERT(service);
+
+    head = &service->handle_list;
+    TFM_LIST_FOR_EACH(node, head) {
+        handle_node = TFM_GET_CONTAINER_PTR(node, struct tfm_conn_handle_t,
+                                            list);
+        if (handle_node->handle == conn_handle) {
+            return handle_node;
+        }
+    }
+    return NULL;
+}
+
+int32_t tfm_spm_free_conn_handle(struct tfm_spm_service_t *service,
+                                 psa_handle_t conn_handle)
+{
+    struct tfm_conn_handle_t *node;
+
+    TFM_ASSERT(service);
+
+    /* There are many handles for each RoT Service */
+    node = tfm_spm_find_conn_handle_node(service, conn_handle);
+    if (!node) {
+        tfm_panic();
+    }
+
+    /* Remove node from handle list */
+    tfm_list_del_node(&node->list);
+
+    /* Back handle buffer to pool */
+    tfm_pool_free(node);
+    return IPC_SUCCESS;
+}
+
+int32_t tfm_spm_set_rhandle(struct tfm_spm_service_t *service,
+                            psa_handle_t conn_handle,
+                            void *rhandle)
+{
+    struct tfm_conn_handle_t *node;
+
+    TFM_ASSERT(service);
+    /* Set reverse handle value only be allowed for a connected handle */
+    TFM_ASSERT(conn_handle != PSA_NULL_HANDLE);
+
+    /* There are many handles for each RoT Service */
+    node = tfm_spm_find_conn_handle_node(service, conn_handle);
+    if (!node) {
+        tfm_panic();
+    }
+
+    node->rhandle = rhandle;
+    return IPC_SUCCESS;
+}
+
+void *tfm_spm_get_rhandle(struct tfm_spm_service_t *service,
+                          psa_handle_t conn_handle)
+{
+    struct tfm_conn_handle_t *node;
+
+    TFM_ASSERT(service);
+    /* Get reverse handle value only be allowed for a connected handle */
+    TFM_ASSERT(conn_handle != PSA_NULL_HANDLE);
+
+    /* There are many handles for each RoT Service */
+    node = tfm_spm_find_conn_handle_node(service, conn_handle);
+    if (!node) {
+        tfm_panic();
+    }
+
+    return node->rhandle;
+}
+
+/* Partition management functions */
+struct tfm_spm_service_t *
+    tfm_spm_get_service_by_signal(struct spm_partition_desc_t *partition,
+                                  psa_signal_t signal)
+{
+    struct tfm_list_node_t *node, *head;
+    struct tfm_spm_service_t *service;
+
+    TFM_ASSERT(partition);
+
+    if (tfm_list_is_empty(&partition->runtime_data.service_list)) {
+        tfm_panic();
+    }
+
+    head = &partition->runtime_data.service_list;
+    TFM_LIST_FOR_EACH(node, head) {
+        service = TFM_GET_CONTAINER_PTR(node, struct tfm_spm_service_t, list);
+        if (service->service_db->signal == signal) {
+            return service;
+        }
+    }
+    return NULL;
+}
+
+struct tfm_spm_service_t *tfm_spm_get_service_by_sid(uint32_t sid)
+{
+    uint32_t i;
+    struct tfm_list_node_t *node, *head;
+    struct tfm_spm_service_t *service;
+    struct spm_partition_desc_t *partition;
+
+    for (i = 0; i < g_spm_partition_db.partition_count; i++) {
+        partition = &g_spm_partition_db.partitions[i];
+        /* Skip partition without IPC flag */
+        if ((tfm_spm_partition_get_flags(i) & SPM_PART_FLAG_IPC) == 0) {
+            continue;
+        }
+
+        if (tfm_list_is_empty(&partition->runtime_data.service_list)) {
+            continue;
+        }
+
+        head = &partition->runtime_data.service_list;
+        TFM_LIST_FOR_EACH(node, head) {
+            service = TFM_GET_CONTAINER_PTR(node, struct tfm_spm_service_t,
+                                            list);
+            if (service->service_db->sid == sid) {
+                return service;
+            }
+        }
+    }
+    return NULL;
+}
+
+struct tfm_spm_service_t *
+    tfm_spm_get_service_by_handle(psa_handle_t conn_handle)
+{
+    uint32_t i;
+    struct tfm_conn_handle_t *handle;
+    struct tfm_list_node_t *service_node, *service_head;
+    struct tfm_list_node_t *handle_node, *handle_head;
+    struct tfm_spm_service_t *service;
+    struct spm_partition_desc_t *partition;
+
+    for (i = 0; i < g_spm_partition_db.partition_count; i++) {
+        partition = &g_spm_partition_db.partitions[i];
+        /* Skip partition without IPC flag */
+        if ((tfm_spm_partition_get_flags(i) & SPM_PART_FLAG_IPC) == 0) {
+            continue;
+        }
+
+        if (tfm_list_is_empty(&partition->runtime_data.service_list)) {
+            continue;
+        }
+
+        service_head = &partition->runtime_data.service_list;
+        TFM_LIST_FOR_EACH(service_node, service_head) {
+            service = TFM_GET_CONTAINER_PTR(service_node,
+                                            struct tfm_spm_service_t, list);
+            handle_head = &service->handle_list;
+            TFM_LIST_FOR_EACH(handle_node, handle_head) {
+                handle = TFM_GET_CONTAINER_PTR(handle_node,
+                                               struct tfm_conn_handle_t, list);
+                if (handle->handle == conn_handle) {
+                    return service;
+                }
+            }
+        }
+    }
+    return NULL;
+}
+
+struct spm_partition_desc_t *tfm_spm_get_partition_by_id(int32_t partition_id)
+{
+    uint32_t idx = get_partition_idx(partition_id);
+
+    if (idx != SPM_INVALID_PARTITION_IDX) {
+        return &(g_spm_partition_db.partitions[idx]);
+    }
+    return NULL;
+}
+
+struct spm_partition_desc_t *tfm_spm_get_running_partition(void)
+{
+    uint32_t spid;
+
+    spid = tfm_spm_partition_get_running_partition_id();
+
+    return tfm_spm_get_partition_by_id(spid);
+}
+
+int32_t tfm_spm_check_client_version(struct tfm_spm_service_t *service,
+                                     uint32_t minor_version)
+{
+    TFM_ASSERT(service);
+
+    switch (service->service_db->minor_policy) {
+    case TFM_VERSION_POLICY_RELAXED:
+        if (minor_version > service->service_db->minor_version) {
+            return IPC_ERROR_VERSION;
+        }
+        break;
+    case TFM_VERSION_POLICY_STRICT:
+        if (minor_version != service->service_db->minor_version) {
+            return IPC_ERROR_VERSION;
+        }
+        break;
+    default:
+        return IPC_ERROR_VERSION;
+    }
+    return IPC_SUCCESS;
+}
+
+/* Message functions */
+struct tfm_msg_body_t *tfm_spm_get_msg_from_handle(psa_handle_t msg_handle)
+{
+    /*
+     * There may be one error handle passed by the caller in two conditions:
+     *   1. Not a valid message handle.
+     *   2. Handle between different Partitions. Partition A passes one handle
+     *   belong to other Partitions and tries to access other's data.
+     * So, need do necessary checking to prevent those conditions.
+     */
+    struct tfm_msg_body_t *msg;
+    uint32_t partition_id;
+
+    msg = (struct tfm_msg_body_t *)msg_handle;
+    if (!msg) {
+        return NULL;
+    }
+
+    /*
+     * FixMe: For condition 1: using a magic number to define it's a message.
+     * It needs to be an enhancement to check the handle belong to service.
+     */
+    if (msg->magic != TFM_MSG_MAGIC) {
+        return NULL;
+    }
+
+    /* For condition 2: check if the partition ID is same */
+    partition_id = tfm_spm_partition_get_running_partition_id();
+    if (partition_id != msg->service->partition->static_data.partition_id) {
+        return NULL;
+    }
+
+    return msg;
+}
+
+struct tfm_msg_body_t *tfm_spm_create_msg(struct tfm_spm_service_t *service,
+                                          psa_handle_t handle,
+                                          int32_t type, int32_t ns_caller,
+                                          psa_invec *invec, size_t in_len,
+                                          psa_outvec *outvec, size_t out_len,
+                                          psa_outvec *caller_outvec)
+{
+    struct tfm_msg_body_t *msg = NULL;
+    uint32_t i;
+
+    TFM_ASSERT(service);
+    TFM_ASSERT(!(invec == NULL && in_len != 0));
+    TFM_ASSERT(!(outvec == NULL && out_len != 0));
+    TFM_ASSERT(in_len <= PSA_MAX_IOVEC);
+    TFM_ASSERT(out_len <= PSA_MAX_IOVEC);
+    TFM_ASSERT(in_len + out_len <= PSA_MAX_IOVEC);
+
+    /* Get message buffer from message pool */
+    msg = (struct tfm_msg_body_t *)tfm_pool_alloc(msg_db_pool);
+    if (!msg) {
+        return NULL;
+    }
+
+    /* Clear message buffer before using it */
+    tfm_memset(msg, 0, sizeof(struct tfm_msg_body_t));
+
+    tfm_event_init(&msg->ack_evnt);
+    msg->magic = TFM_MSG_MAGIC;
+    msg->service = service;
+    msg->handle = handle;
+    msg->caller_outvec = caller_outvec;
+    /* Get current partition id */
+    if (ns_caller) {
+        msg->msg.client_id = tfm_nspm_get_current_client_id();
+    } else {
+        msg->msg.client_id = tfm_spm_partition_get_running_partition_id();
+    }
+
+    /* Copy contents */
+    msg->msg.type = type;
+
+    for (i = 0; i < in_len; i++) {
+        msg->msg.in_size[i] = invec[i].len;
+        msg->invec[i].base = invec[i].base;
+    }
+
+    for (i = 0; i < out_len; i++) {
+        msg->msg.out_size[i] = outvec[i].len;
+        msg->outvec[i].base = outvec[i].base;
+        /* Out len is used to record the writed number, set 0 here again */
+        msg->outvec[i].len = 0;
+    }
+
+    /* Use message address as handle */
+    msg->msg.handle = (psa_handle_t)msg;
+
+    /* For connected handle, set rhandle to every message */
+    if (handle != PSA_NULL_HANDLE) {
+        msg->msg.rhandle = tfm_spm_get_rhandle(service, handle);
+    }
+
+    return msg;
+}
+
+void tfm_spm_free_msg(struct tfm_msg_body_t *msg)
+{
+    tfm_pool_free(msg);
+}
+
+int32_t tfm_spm_send_event(struct tfm_spm_service_t *service,
+                           struct tfm_msg_body_t *msg)
+{
+    struct spm_partition_runtime_data_t *p_runtime_data =
+                                            &service->partition->runtime_data;
+
+    TFM_ASSERT(service);
+    TFM_ASSERT(msg);
+
+    /* Enqueue message to service message queue */
+    if (tfm_msg_enqueue(&service->msg_queue, msg) != IPC_SUCCESS) {
+        return IPC_ERROR_GENERIC;
+    }
+
+    /* Messages put. Update signals */
+    p_runtime_data->signals |= service->service_db->signal;
+
+    tfm_event_wake(&p_runtime_data->signal_evnt, (p_runtime_data->signals &
+                                                  p_runtime_data->signal_mask));
+
+    tfm_event_wait(&msg->ack_evnt);
+
+    return IPC_SUCCESS;
+}
+
+/* SPM extend functions */
+uint32_t tfm_spm_partition_get_running_partition_id(void)
+{
+    struct tfm_thrd_ctx *pth = tfm_thrd_curr_thread();
+    struct spm_partition_desc_t *partition;
+
+    partition = TFM_GET_CONTAINER_PTR(pth, struct spm_partition_desc_t,
+                                      sp_thrd);
+    return partition->static_data.partition_id;
+}
+
+static struct tfm_thrd_ctx *
+    tfm_spm_partition_get_thread_info(uint32_t partition_idx)
+{
+    return &g_spm_partition_db.partitions[partition_idx].sp_thrd;
+}
+
+static tfm_thrd_func_t
+    tfm_spm_partition_get_init_func(uint32_t partition_idx)
+{
+    return (tfm_thrd_func_t)(g_spm_partition_db.partitions[partition_idx].
+                             static_data.partition_init);
+}
+
+static uint32_t tfm_spm_partition_get_priority(uint32_t partition_idx)
+{
+    return g_spm_partition_db.partitions[partition_idx].static_data.
+                    partition_priority;
+}
+
+int32_t tfm_memory_check(void *buffer, size_t len, int32_t ns_caller,
+                         enum tfm_memory_access_e access,
+                         uint32_t privileged)
+{
+    enum tfm_status_e err;
+
+    /* If len is zero, this indicates an empty buffer and base is ignored */
+    if (len == 0) {
+        return IPC_SUCCESS;
+    }
+
+    if (!buffer) {
+        return IPC_ERROR_BAD_PARAMETERS;
+    }
+
+    if ((uintptr_t)buffer > (UINTPTR_MAX - len)) {
+        return IPC_ERROR_MEMORY_CHECK;
+    }
+
+    if (access == TFM_MEMORY_ACCESS_RW) {
+        err = tfm_core_has_write_access_to_region(buffer, len, ns_caller,
+                                                  privileged);
+    } else {
+        err = tfm_core_has_read_access_to_region(buffer, len, ns_caller,
+                                                 privileged);
+    }
+    if (err == TFM_SUCCESS) {
+        return IPC_SUCCESS;
+    }
+
+    return IPC_ERROR_MEMORY_CHECK;
+}
+
+uint32_t tfm_spm_partition_get_privileged_mode(uint32_t partition_idx)
+{
+    if (tfm_spm_partition_get_flags(partition_idx) & SPM_PART_FLAG_PSA_ROT) {
+        return TFM_PARTITION_PRIVILEGED_MODE;
+    } else {
+        return TFM_PARTITION_UNPRIVILEGED_MODE;
+    }
+}
+
+/********************** SPM functions for thread mode ************************/
+
+void tfm_spm_init(void)
+{
+    uint32_t i, num;
+    struct spm_partition_desc_t *partition;
+    struct tfm_spm_service_t *service;
+    struct tfm_thrd_ctx *pth, this_thrd;
+
+    tfm_pool_init(conn_handle_pool,
+                  POOL_BUFFER_SIZE(conn_handle_pool),
+                  sizeof(struct tfm_conn_handle_t),
+                  TFM_CONN_HANDLE_MAX_NUM);
+    tfm_pool_init(spm_service_pool, POOL_BUFFER_SIZE(spm_service_pool),
+                  sizeof(struct tfm_spm_service_t),
+                  TFM_SPM_MAX_ROT_SERV_NUM);
+    tfm_pool_init(msg_db_pool, POOL_BUFFER_SIZE(msg_db_pool),
+                  sizeof(struct tfm_msg_body_t),
+                  TFM_MSG_QUEUE_MAX_MSG_NUM);
+
+    /* Init partition first for it will be used when init service */
+    for (i = 0; i < g_spm_partition_db.partition_count; i++) {
+        partition = &g_spm_partition_db.partitions[i];
+        tfm_spm_hal_configure_default_isolation(partition->platform_data);
+        partition->static_data.index = i;
+        if ((tfm_spm_partition_get_flags(i) & SPM_PART_FLAG_IPC) == 0) {
+            continue;
+        }
+
+        tfm_event_init(&partition->runtime_data.signal_evnt);
+        tfm_list_init(&partition->runtime_data.service_list);
+
+        pth = tfm_spm_partition_get_thread_info(i);
+        if (!pth) {
+            tfm_panic();
+        }
+
+        tfm_thrd_init(pth,
+                      tfm_spm_partition_get_init_func(i),
+                      NULL,
+                      (uint8_t *)tfm_spm_partition_get_stack_top(i),
+                      (uint8_t *)tfm_spm_partition_get_stack_bottom(i));
+
+        pth->prior = tfm_spm_partition_get_priority(i);
+
+        /* Kick off */
+        if (tfm_thrd_start(pth) != THRD_SUCCESS) {
+            tfm_panic();
+        }
+    }
+
+    /* Init Service */
+    num = sizeof(g_spm_service_db) / sizeof(struct tfm_spm_service_db_t);
+    for (i = 0; i < num; i++) {
+        partition =
+            tfm_spm_get_partition_by_id(g_spm_service_db[i].partition_id);
+        if (!partition) {
+            tfm_panic();
+        }
+        service = (struct tfm_spm_service_t *)tfm_pool_alloc(spm_service_pool);
+        if (!service) {
+            tfm_panic();
+        }
+        service->service_db = &g_spm_service_db[i];
+        service->partition = partition;
+        tfm_list_init(&service->handle_list);
+        tfm_list_add_tail(&partition->runtime_data.service_list,
+                          &service->list);
+    }
+
+    /*
+     * All threads initialized, start the scheduler.
+     *
+     * NOTE:
+     * Here is the booting privileged thread mode, and will never
+     * return to this place after scheduler is started. The start
+     * function has to save current runtime context to act as a
+     * 'current thread' to avoid repeating NULL 'current thread'
+     * checking while context switching. This saved context is worthy
+     * of being saved somewhere if there are potential usage purpose.
+     * Let's save this context in a local variable 'this_thrd' at
+     * current since there is no usage for it.
+     * Also set tfm_nspm_thread_entry as pfn for this thread to
+     * use in detecting NS/S thread scheduling changes.
+     */
+    this_thrd.pfn = (tfm_thrd_func_t)tfm_nspm_thread_entry;
+    tfm_thrd_start_scheduler(&this_thrd);
+}
+
+void tfm_pendsv_do_schedule(struct tfm_state_context_ext *ctxb)
+{
+#if TFM_LVL == 2
+    struct spm_partition_desc_t *p_next_partition;
+    uint32_t is_privileged;
+#endif
+    struct tfm_thrd_ctx *pth_next = tfm_thrd_next_thread();
+    struct tfm_thrd_ctx *pth_curr = tfm_thrd_curr_thread();
+
+    if (pth_next != NULL && pth_curr != pth_next) {
+#if TFM_LVL == 2
+        p_next_partition = TFM_GET_CONTAINER_PTR(pth_next,
+                                                 struct spm_partition_desc_t,
+                                                 sp_thrd);
+
+        if (p_next_partition->static_data.partition_flags &
+            SPM_PART_FLAG_PSA_ROT) {
+            is_privileged = TFM_PARTITION_PRIVILEGED_MODE;
+        } else {
+            is_privileged = TFM_PARTITION_UNPRIVILEGED_MODE;
+        }
+
+        tfm_spm_partition_change_privilege(is_privileged);
+#endif
+        /* Increase the secure lock, if we enter secure from non-secure */
+        if ((void *)pth_curr->pfn == (void *)tfm_nspm_thread_entry) {
+            ++tfm_secure_lock;
+        }
+        /* Decrease the secure lock, if we return from secure to non-secure */
+        if ((void *)pth_next->pfn == (void *)tfm_nspm_thread_entry) {
+            --tfm_secure_lock;
+        }
+
+        tfm_thrd_context_switch(ctxb, pth_curr, pth_next);
+    }
+}