mbed-os5 only for TYBLE16

Dependents:   TYBLE16_simple_data_logger TYBLE16_MP3_Air

Revision:
0:5b88d5760320
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/TARGET_PSA/TARGET_TFM/COMPONENT_SPE/secure_fw/core/ipc/tfm_svcalls.c	Tue Dec 17 23:23:45 2019 +0000
@@ -0,0 +1,1026 @@
+/*
+ * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "psa_client.h"
+#include "psa_service.h"
+#include "tfm_svc.h"
+#include "tfm_svcalls.h"
+#include "tfm_thread.h"
+#include "tfm_wait.h"
+#include "tfm_utils.h"
+#include "tfm_internal_defines.h"
+#include "tfm_message_queue.h"
+#include "tfm_spm.h"
+#include "tfm_api.h"
+#include "tfm_secure_api.h"
+#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)
+{
+    return PSA_FRAMEWORK_VERSION;
+}
+
+uint32_t tfm_svcall_psa_version(uint32_t *args, int32_t ns_caller)
+{
+    uint32_t sid;
+    struct tfm_spm_service_t *service;
+
+    TFM_ASSERT(args != NULL);
+    sid = (uint32_t)args[0];
+    /*
+     * It should return PSA_VERSION_NONE if the RoT Service is not
+     * implemented.
+     */
+    service = tfm_spm_get_service_by_sid(sid);
+    if (!service) {
+        return PSA_VERSION_NONE;
+    }
+
+    /*
+     * It should return PSA_VERSION_NONE if the caller is not authorized
+     * to access the RoT Service.
+     */
+    if (ns_caller && !service->service_db->non_secure_client) {
+        return PSA_VERSION_NONE;
+    }
+
+    return service->service_db->minor_version;
+}
+
+psa_handle_t tfm_svcall_psa_connect(uint32_t *args, int32_t ns_caller)
+{
+    uint32_t sid;
+    uint32_t minor_version;
+    struct tfm_spm_service_t *service;
+    struct tfm_msg_body_t *msg;
+
+    TFM_ASSERT(args != NULL);
+    sid = (uint32_t)args[0];
+    minor_version = (uint32_t)args[1];
+
+    /* It is a fatal error if the RoT Service does not exist on the platform */
+    service = tfm_spm_get_service_by_sid(sid);
+    if (!service) {
+        tfm_panic();
+    }
+
+    /*
+     * It is a fatal error if the caller is not authorized to access the RoT
+     * Service.
+     */
+    if (ns_caller && !service->service_db->non_secure_client) {
+        tfm_panic();
+    }
+
+    /*
+     * It is a fatal error if the version of the RoT Service requested is not
+     * supported on the platform.
+     */
+    if (tfm_spm_check_client_version(service, minor_version) != IPC_SUCCESS) {
+        tfm_panic();
+    }
+
+    /* No input or output needed for connect message */
+    msg = tfm_spm_create_msg(service, PSA_NULL_HANDLE, PSA_IPC_CONNECT,
+                             ns_caller, NULL, 0, NULL, 0, NULL);
+    if (!msg) {
+        return PSA_NULL_HANDLE;
+    }
+
+    /*
+     * Send message and wake up the SP who is waiting on message queue,
+     * and scheduler triggered
+     */
+    tfm_spm_send_event(service, msg);
+
+    return PSA_NULL_HANDLE;
+}
+
+psa_status_t tfm_svcall_psa_call(uint32_t *args, int32_t ns_caller, uint32_t lr)
+{
+    psa_handle_t handle;
+    psa_invec *inptr, invecs[PSA_MAX_IOVEC];
+    psa_outvec *outptr, outvecs[PSA_MAX_IOVEC];
+    size_t in_num, out_num;
+    struct tfm_spm_service_t *service;
+    struct tfm_msg_body_t *msg;
+    int i;
+    struct tfm_spm_ipc_partition_t *partition = NULL;
+    uint32_t privileged;
+
+    TFM_ASSERT(args != NULL);
+    handle = (psa_handle_t)args[0];
+
+    partition = tfm_spm_get_running_partition();
+    if (!partition) {
+        tfm_panic();
+    }
+    privileged = tfm_spm_partition_get_privileged_mode(partition->index);
+
+    if (!ns_caller) {
+        inptr = (psa_invec *)args[1];
+        in_num = (size_t)args[2];
+        outptr = (psa_outvec *)args[3];
+        /*
+         * 5th parameter is pushed at stack top before SVC, then PE hardware
+         * stacks the execution context. The size of the context depends on
+         * various settings:
+         * - if FP is not used, 5th parameter is at 8th position counting
+         *   from SP;
+         * - if FP is used and FPCCR_S.TS is 0, 5th parameter is at 26th
+         *   position counting from SP;
+         * - if FP is used and FPCCR_S.TS is 1, 5th parameter is at 42th
+         *   position counting from SP.
+         */
+         if (lr & EXC_RETURN_FPU_FRAME_BASIC) {
+            out_num = (size_t)args[8];
+#if defined (__FPU_USED) && (__FPU_USED == 1U)
+         } else if (FPU->FPCCR & FPU_FPCCR_TS_Msk) {
+            out_num = (size_t)args[42];
+#endif
+         } else {
+            out_num = (size_t)args[26];
+         }
+    } else {
+        /*
+         * FixMe: From non-secure caller, vec and len are composed into a new
+         * struct parameter. Need to extract them.
+         */
+        /*
+         * Read parameters from the arguments. It is a fatal error if the
+         * memory reference for buffer is invalid or not readable.
+         */
+        if (tfm_memory_check((void *)args[1], sizeof(uint32_t),
+            ns_caller, TFM_MEMORY_ACCESS_RO, privileged) != IPC_SUCCESS) {
+            tfm_panic();
+        }
+        if (tfm_memory_check((void *)args[2], sizeof(uint32_t),
+            ns_caller, TFM_MEMORY_ACCESS_RO, privileged) != IPC_SUCCESS) {
+            tfm_panic();
+        }
+
+        inptr = (psa_invec *)((psa_invec *)args[1])->base;
+        in_num = ((psa_invec *)args[1])->len;
+        outptr = (psa_outvec *)((psa_invec *)args[2])->base;
+        out_num = ((psa_invec *)args[2])->len;
+    }
+
+    /* It is a fatal error if in_len + out_len > PSA_MAX_IOVEC. */
+    if (in_num + out_num > PSA_MAX_IOVEC) {
+        tfm_panic();
+    }
+
+    /* It is a fatal error if an invalid handle was passed. */
+    service = tfm_spm_get_service_by_handle(handle);
+    if (!service) {
+        /* FixMe: Need to implement one mechanism to resolve this failure. */
+        tfm_panic();
+    }
+
+    /*
+     * Read client invecs from the wrap input vector. It is a fatal error
+     * if the memory reference for the wrap input vector is invalid or not
+     * readable.
+     */
+    if (tfm_memory_check((void *)inptr, in_num * sizeof(psa_invec),
+        ns_caller, TFM_MEMORY_ACCESS_RO, privileged) != IPC_SUCCESS) {
+        tfm_panic();
+    }
+    /*
+     * Read client outvecs from the wrap output vector and will update the
+     * actual length later. It is a fatal error if the memory reference for
+     * the wrap output vector is invalid or not read-write.
+     */
+    if (tfm_memory_check((void *)outptr, out_num * sizeof(psa_outvec),
+        ns_caller, TFM_MEMORY_ACCESS_RW, privileged) != IPC_SUCCESS) {
+        tfm_panic();
+    }
+
+    tfm_memset(invecs, 0, sizeof(invecs));
+    tfm_memset(outvecs, 0, sizeof(outvecs));
+
+    /* Copy the address out to avoid TOCTOU attacks. */
+    tfm_memcpy(invecs, inptr, in_num * sizeof(psa_invec));
+    tfm_memcpy(outvecs, outptr, out_num * sizeof(psa_outvec));
+
+    /*
+     * For client input vector, it is a fatal error if the provided payload
+     * memory reference was invalid or not readable.
+     */
+    for (i = 0; i < in_num; i++) {
+        if (tfm_memory_check((void *)invecs[i].base, invecs[i].len,
+            ns_caller, TFM_MEMORY_ACCESS_RO, privileged) != IPC_SUCCESS) {
+            tfm_panic();
+        }
+    }
+    /*
+     * For client output vector, it is a fatal error if the provided payload
+     * memory reference was invalid or not read-write.
+     */
+    for (i = 0; i < out_num; i++) {
+        if (tfm_memory_check(outvecs[i].base, outvecs[i].len,
+            ns_caller, TFM_MEMORY_ACCESS_RW, privileged) != IPC_SUCCESS) {
+            tfm_panic();
+        }
+    }
+
+    /*
+     * FixMe: Need to check if the message is unrecognized by the RoT
+     * Service or incorrectly formatted.
+     */
+    msg = tfm_spm_create_msg(service, handle, PSA_IPC_CALL, ns_caller, invecs,
+                             in_num, outvecs, out_num, outptr);
+    if (!msg) {
+        /* FixMe: Need to implement one mechanism to resolve this failure. */
+        tfm_panic();
+    }
+
+    /*
+     * Send message and wake up the SP who is waiting on message queue,
+     * and scheduler triggered
+     */
+    if (tfm_spm_send_event(service, msg) != IPC_SUCCESS) {
+        /* FixMe: Need to refine failure process here. */
+        tfm_panic();
+    }
+    return PSA_SUCCESS;
+}
+
+void tfm_svcall_psa_close(uint32_t *args, int32_t ns_caller)
+{
+    psa_handle_t handle;
+    struct tfm_spm_service_t *service;
+    struct tfm_msg_body_t *msg;
+
+    TFM_ASSERT(args != NULL);
+    handle = args[0];
+    /* It will have no effect if called with the NULL handle */
+    if (handle == PSA_NULL_HANDLE) {
+        return;
+    }
+
+    /*
+     * It is a fatal error if an invalid handle was provided that is not the
+     * null handle..
+     */
+    service = tfm_spm_get_service_by_handle(handle);
+    if (!service) {
+        /* FixMe: Need to implement one mechanism to resolve this failure. */
+        tfm_panic();
+    }
+
+    /* No input or output needed for close message */
+    msg = tfm_spm_create_msg(service, handle, PSA_IPC_DISCONNECT, ns_caller,
+                             NULL, 0, NULL, 0, NULL);
+    if (!msg) {
+        /* FixMe: Need to implement one mechanism to resolve this failure. */
+        return;
+    }
+
+    /*
+     * Send message and wake up the SP who is waiting on message queue,
+     * and scheduler triggered
+     */
+    tfm_spm_send_event(service, msg);
+}
+
+/*********************** SVC handler for PSA Service APIs ********************/
+
+/**
+ * \brief SVC handler for \ref psa_wait.
+ *
+ * \param[in] args              Include all input arguments:
+ *                              signal_mask, timeout.
+ *
+ * \retval >0                   At least one signal is asserted.
+ * \retval 0                    No signals are asserted. This is only seen when
+ *                              a polling timeout is used.
+ */
+static psa_signal_t tfm_svcall_psa_wait(uint32_t *args)
+{
+    psa_signal_t signal_mask;
+    uint32_t timeout;
+    struct tfm_spm_ipc_partition_t *partition = NULL;
+
+    TFM_ASSERT(args != NULL);
+    signal_mask = (psa_signal_t)args[0];
+    timeout = args[1];
+
+    /*
+     * Timeout[30:0] are reserved for future use.
+     * SPM must ignore the value of RES.
+     */
+    timeout &= PSA_TIMEOUT_MASK;
+
+    partition = tfm_spm_get_running_partition();
+    if (!partition) {
+        tfm_panic();
+    }
+
+    /*
+     * Expected signals are included in signal wait mask, ignored signals
+     * should not be set and affect caller thread status. Save this mask for
+     * further checking while signals are ready to be set.
+     */
+    partition->signal_mask = signal_mask;
+
+    /*
+     * tfm_event_wait() blocks the caller thread if no signals are available.
+     * In this case, the return value of this function is temporary set into
+     * runtime context. After new signal(s) are available, the return value
+     * is updated with the available signal(s) and blocked thread gets to run.
+     */
+    if (timeout == PSA_BLOCK && (partition->signals & signal_mask) == 0) {
+        tfm_event_wait(&partition->signal_evnt);
+    }
+
+    return partition->signals & signal_mask;
+}
+
+/**
+ * \brief SVC handler for \ref psa_get.
+ *
+ * \param[in] args              Include all input arguments: signal, msg.
+ *
+ * \retval PSA_SUCCESS          Success, *msg will contain the delivered
+ *                              message.
+ * \retval PSA_ERR_NOMSG        Message could not be delivered.
+ * \retval "Does not return"    The call is invalid because one or more of the
+ *                              following are true:
+ * \arg                           signal has more than a single bit set.
+ * \arg                           signal does not correspond to a RoT Service.
+ * \arg                           The RoT Service signal is not currently
+ *                                asserted.
+ * \arg                           The msg pointer provided is not a valid memory
+ *                                reference.
+ */
+static psa_status_t tfm_svcall_psa_get(uint32_t *args)
+{
+    psa_signal_t signal;
+    psa_msg_t *msg = NULL;
+    struct tfm_spm_service_t *service = NULL;
+    struct tfm_msg_body_t *tmp_msg = NULL;
+    struct tfm_spm_ipc_partition_t *partition = NULL;
+    uint32_t privileged;
+
+    TFM_ASSERT(args != NULL);
+    signal = (psa_signal_t)args[0];
+    msg = (psa_msg_t *)args[1];
+
+    /*
+     * Only one message could be retrieved every time for psa_get(). It is a
+     * fatal error if the input signal has more than a signal bit set.
+     */
+    if (tfm_bitcount(signal) != 1) {
+        tfm_panic();
+    }
+
+    partition = tfm_spm_get_running_partition();
+    if (!partition) {
+        tfm_panic();
+    }
+    privileged = tfm_spm_partition_get_privileged_mode(partition->index);
+
+    /*
+     * Write the message to the service buffer. It is a fatal error if the
+     * input msg pointer is not a valid memory reference or not read-write.
+     */
+    if (tfm_memory_check((void *)msg, sizeof(psa_msg_t),
+        false, TFM_MEMORY_ACCESS_RW, privileged) != IPC_SUCCESS) {
+        tfm_panic();
+    }
+
+    /*
+     * It is a fatal error if the caller call psa_get() when no message has
+     * been set. The caller must call this function after a RoT Service signal
+     * is returned by psa_wait().
+     */
+    if (partition->signals == 0) {
+        tfm_panic();
+    }
+
+    /*
+     * It is a fatal error if the RoT Service signal is not currently asserted.
+     */
+    if ((partition->signals & signal) == 0) {
+        tfm_panic();
+    }
+
+    /*
+     * Get Rot service by signal from partition. It is a fatal error if geting
+     * failed which mean the input signal is not correspond to a RoT service.
+     */
+    service = tfm_spm_get_service_by_signal(partition, signal);
+    if (!service) {
+        tfm_panic();
+    }
+
+    tmp_msg = tfm_msg_dequeue(&service->msg_queue);
+    if (!tmp_msg) {
+        return PSA_ERR_NOMSG;
+    }
+
+    tfm_memcpy(msg, &tmp_msg->msg, sizeof(psa_msg_t));
+
+    /*
+     * There may be mutiple messages for this RoT Service signal, do not clear
+     * its mask until no remaining message.
+     */
+    if (tfm_msg_queue_is_empty(&service->msg_queue)) {
+        partition->signals &= ~signal;
+    }
+
+    return PSA_SUCCESS;
+}
+
+/**
+ * \brief SVC handler for \ref psa_set_rhandle.
+ *
+ * \param[in] args              Include all input arguments:
+ *                              msg_handle, rhandle.
+ *
+ * \retval void                 Success, rhandle will be provided with all
+ *                              subsequent messages delivered on this
+ *                              connection.
+ * \retval "Does not return"    msg_handle is invalid.
+ */
+static void tfm_svcall_psa_set_rhandle(uint32_t *args)
+{
+    psa_handle_t msg_handle;
+    void *rhandle = NULL;
+    struct tfm_msg_body_t *msg = NULL;
+
+    TFM_ASSERT(args != NULL);
+    msg_handle = (psa_handle_t)args[0];
+    rhandle = (void *)args[1];
+
+    /* It is a fatal error if message handle is invalid */
+    msg = tfm_spm_get_msg_from_handle(msg_handle);
+    if (!msg) {
+        tfm_panic();
+    }
+
+    /*
+     * Connection handle is not created while SP is processing PSA_IPC_CONNECT
+     * message. Store reverse handle temporarily and re-set it after the
+     * connection created.
+     */
+    if (msg->handle != PSA_NULL_HANDLE) {
+        tfm_spm_set_rhandle(msg->service, msg->handle, rhandle);
+    } else {
+        msg->msg.rhandle = rhandle;
+    }
+}
+
+/**
+ * \brief SVC handler for \ref psa_read.
+ *
+ * \param[in] args              Include all input arguments:
+ *                              msg_handle, invec_idx, buffer, num_bytes.
+ *
+ * \retval >0                   Number of bytes copied.
+ * \retval 0                    There was no remaining data in this input
+ *                              vector.
+ * \retval "Does not return"    The call is invalid, one or more of the
+ *                              following are true:
+ * \arg                           msg_handle is invalid.
+ * \arg                           msg_handle does not refer to a
+ *                                \ref PSA_IPC_CALL message.
+ * \arg                           invec_idx is equal to or greater than
+ *                                \ref PSA_MAX_IOVEC.
+ * \arg                           the memory reference for buffer is invalid or
+ *                                not writable.
+ */
+static size_t tfm_svcall_psa_read(uint32_t *args)
+{
+    psa_handle_t msg_handle;
+    uint32_t invec_idx;
+    void *buffer = NULL;
+    size_t num_bytes;
+    size_t bytes;
+    struct tfm_msg_body_t *msg = NULL;
+    uint32_t privileged;
+    struct tfm_spm_ipc_partition_t *partition = NULL;
+
+    TFM_ASSERT(args != NULL);
+    msg_handle = (psa_handle_t)args[0];
+    invec_idx = args[1];
+    buffer = (void *)args[2];
+    num_bytes = (size_t)args[3];
+
+    /* It is a fatal error if message handle is invalid */
+    msg = tfm_spm_get_msg_from_handle(msg_handle);
+    if (!msg) {
+        tfm_panic();
+    }
+
+    partition = msg->service->partition;
+    privileged = tfm_spm_partition_get_privileged_mode(partition->index);
+
+    /*
+     * It is a fatal error if message handle does not refer to a PSA_IPC_CALL
+     * message
+     */
+    if (msg->msg.type != PSA_IPC_CALL) {
+        tfm_panic();
+    }
+
+    /*
+     * It is a fatal error if invec_idx is equal to or greater than
+     * PSA_MAX_IOVEC
+     */
+    if (invec_idx >= PSA_MAX_IOVEC) {
+        tfm_panic();
+    }
+
+    /* There was no remaining data in this input vector */
+    if (msg->msg.in_size[invec_idx] == 0) {
+        return 0;
+    }
+
+    /*
+     * Copy the client data to the service buffer. It is a fatal error
+     * if the memory reference for buffer is invalid or not read-write.
+     */
+    if (tfm_memory_check(buffer, num_bytes, false,
+        TFM_MEMORY_ACCESS_RW, privileged) != IPC_SUCCESS) {
+        tfm_panic();
+    }
+
+    bytes = num_bytes > msg->msg.in_size[invec_idx] ?
+                        msg->msg.in_size[invec_idx] : num_bytes;
+
+    tfm_memcpy(buffer, msg->invec[invec_idx].base, bytes);
+
+    /* There maybe some remaining data */
+    msg->invec[invec_idx].base += bytes;
+    msg->msg.in_size[invec_idx] -= bytes;
+
+    return bytes;
+}
+
+/**
+ * \brief SVC handler for \ref psa_skip.
+ *
+ * \param[in] args              Include all input arguments:
+ *                              msg_handle, invec_idx, num_bytes.
+ *
+ * \retval >0                   Number of bytes skipped.
+ * \retval 0                    There was no remaining data in this input
+ *                              vector.
+ * \retval "Does not return"    The call is invalid, one or more of the
+ *                              following are true:
+ * \arg                           msg_handle is invalid.
+ * \arg                           msg_handle does not refer to a
+ *                                \ref PSA_IPC_CALL message.
+ * \arg                           invec_idx is equal to or greater than
+ *                                \ref PSA_MAX_IOVEC.
+ */
+static size_t tfm_svcall_psa_skip(uint32_t *args)
+{
+    psa_handle_t msg_handle;
+    uint32_t invec_idx;
+    size_t num_bytes;
+    struct tfm_msg_body_t *msg = NULL;
+
+    TFM_ASSERT(args != NULL);
+    msg_handle = (psa_handle_t)args[0];
+    invec_idx = args[1];
+    num_bytes = (size_t)args[2];
+
+    /* It is a fatal error if message handle is invalid */
+    msg = tfm_spm_get_msg_from_handle(msg_handle);
+    if (!msg) {
+        tfm_panic();
+    }
+
+    /*
+     * It is a fatal error if message handle does not refer to a PSA_IPC_CALL
+     * message
+     */
+    if (msg->msg.type != PSA_IPC_CALL) {
+        tfm_panic();
+    }
+
+    /*
+     * It is a fatal error if invec_idx is equal to or greater than
+     * PSA_MAX_IOVEC
+     */
+    if (invec_idx >= PSA_MAX_IOVEC) {
+        tfm_panic();
+    }
+
+    /* There was no remaining data in this input vector */
+    if (msg->msg.in_size[invec_idx] == 0) {
+        return 0;
+    }
+
+    /*
+     * If num_bytes is greater than the remaining size of the input vector then
+     * the remaining size of the input vector is used.
+     */
+    if (num_bytes > msg->msg.in_size[invec_idx]) {
+        num_bytes = msg->msg.in_size[invec_idx];
+    }
+
+    /* There maybe some remaining data */
+    msg->invec[invec_idx].base += num_bytes;
+    msg->msg.in_size[invec_idx] -= num_bytes;
+
+    return num_bytes;
+}
+
+/**
+ * \brief SVC handler for \ref psa_write.
+ *
+ * \param[in] args              Include all input arguments:
+ *                              msg_handle, outvec_idx, buffer, num_bytes.
+ *
+ * \retval void                 Success
+ * \retval "Does not return"    The call is invalid, one or more of the
+ *                              following are true:
+ * \arg                           msg_handle is invalid.
+ * \arg                           msg_handle does not refer to a
+ *                                \ref PSA_IPC_CALL message.
+ * \arg                           outvec_idx is equal to or greater than
+ *                                \ref PSA_MAX_IOVEC.
+ * \arg                           The memory reference for buffer is invalid.
+ * \arg                           The call attempts to write data past the end
+ *                                of the client output vector.
+ */
+static void tfm_svcall_psa_write(uint32_t *args)
+{
+    psa_handle_t msg_handle;
+    uint32_t outvec_idx;
+    void *buffer = NULL;
+    size_t num_bytes;
+    struct tfm_msg_body_t *msg = NULL;
+    uint32_t privileged;
+    struct tfm_spm_ipc_partition_t *partition = NULL;
+
+    TFM_ASSERT(args != NULL);
+    msg_handle = (psa_handle_t)args[0];
+    outvec_idx = args[1];
+    buffer = (void *)args[2];
+    num_bytes = (size_t)args[3];
+
+    /* It is a fatal error if message handle is invalid */
+    msg = tfm_spm_get_msg_from_handle(msg_handle);
+    if (!msg) {
+        tfm_panic();
+    }
+
+    partition = msg->service->partition;
+    privileged = tfm_spm_partition_get_privileged_mode(partition->index);
+
+    /*
+     * It is a fatal error if message handle does not refer to a PSA_IPC_CALL
+     * message
+     */
+    if (msg->msg.type != PSA_IPC_CALL) {
+        tfm_panic();
+    }
+
+    /*
+     * It is a fatal error if outvec_idx is equal to or greater than
+     * PSA_MAX_IOVEC
+     */
+    if (outvec_idx >= PSA_MAX_IOVEC) {
+        tfm_panic();
+    }
+
+    /*
+     * It is a fatal error if the call attempts to write data past the end of
+     * the client output vector
+     */
+    if (num_bytes > msg->msg.out_size[outvec_idx] -
+        msg->outvec[outvec_idx].len) {
+        tfm_panic();
+    }
+
+    /*
+     * Copy the service buffer to client outvecs. It is a fatal error
+     * if the memory reference for buffer is invalid or not readable.
+     */
+    if (tfm_memory_check(buffer, num_bytes, false,
+        TFM_MEMORY_ACCESS_RO, privileged) != IPC_SUCCESS) {
+        tfm_panic();
+    }
+
+    tfm_memcpy(msg->outvec[outvec_idx].base + msg->outvec[outvec_idx].len,
+               buffer, num_bytes);
+
+    /* Update the write number */
+    msg->outvec[outvec_idx].len += num_bytes;
+}
+
+static void update_caller_outvec_len(struct tfm_msg_body_t *msg)
+{
+    int32_t i = 0;
+
+    /*
+     * FixeMe: abstract these part into dedicated functions to avoid
+     * accessing thread context in psa layer
+     */
+    TFM_ASSERT(msg->ack_evnt.owner->status == THRD_STAT_BLOCK);
+
+    while (msg->msg.out_size[i] != 0) {
+        TFM_ASSERT(msg->caller_outvec[i].base == msg->outvec[i].base);
+        msg->caller_outvec[i].len = msg->outvec[i].len;
+        i++;
+    }
+}
+/**
+ * \brief SVC handler for \ref psa_reply.
+ *
+ * \param[in] args              Include all input arguments:
+ *                              msg_handle, status.
+ *
+ * \retval void                 Success.
+ * \retval "Does not return"    The call is invalid, one or more of the
+ *                              following are true:
+ * \arg                         msg_handle is invalid.
+ * \arg                         An invalid status code is specified for the
+ *                              type of message.
+ */
+static void tfm_svcall_psa_reply(uint32_t *args)
+{
+    psa_handle_t msg_handle;
+    psa_status_t status;
+    struct tfm_spm_service_t *service = NULL;
+    struct tfm_msg_body_t *msg = NULL;
+    psa_handle_t connect_handle;
+    int32_t ret = PSA_SUCCESS;
+
+    TFM_ASSERT(args != NULL);
+    msg_handle = (psa_handle_t)args[0];
+    status = (psa_status_t)args[1];
+
+    /* It is a fatal error if message handle is invalid */
+    msg = tfm_spm_get_msg_from_handle(msg_handle);
+    if (!msg) {
+        tfm_panic();
+    }
+
+    /*
+     * RoT Service information is needed in this function, stored it in message
+     * body structure. Only two parameters are passed in this function: handle
+     * and status, so it is useful and simply to do like this.
+     */
+    service = msg->service;
+    if (!service) {
+        tfm_panic();
+    }
+
+    /*
+     * Three type of message are passed in this function: CONNECT, CALL,
+     * DISCONNECT. It needs to process differently for each type.
+     */
+    switch (msg->msg.type) {
+    case PSA_IPC_CONNECT:
+        /*
+         * Reply to PSA_IPC_CONNECT message. Connect handle is created if the
+         * input status is PSA_SUCCESS. Others return values are based on the
+         * input status.
+         */
+        if (status == PSA_SUCCESS) {
+            connect_handle = tfm_spm_create_conn_handle(service);
+            if (connect_handle == PSA_NULL_HANDLE) {
+                tfm_panic();
+            }
+            ret = connect_handle;
+
+            /* Set reverse handle after connection created if needed. */
+            if (msg->msg.rhandle) {
+                tfm_spm_set_rhandle(service, connect_handle, msg->msg.rhandle);
+            }
+        } else if (status == PSA_CONNECTION_REFUSED) {
+            ret = PSA_CONNECTION_REFUSED;
+        } else if (status == PSA_CONNECTION_BUSY) {
+            ret = PSA_CONNECTION_BUSY;
+        } else {
+            tfm_panic();
+        }
+        break;
+    case PSA_IPC_CALL:
+        /* Reply to PSA_IPC_CALL message. Return values are based on status */
+        if (status == PSA_SUCCESS) {
+            ret = PSA_SUCCESS;
+        } else if (status == PSA_DROP_CONNECTION) {
+            ret = PSA_DROP_CONNECTION;
+        } else if ((status >= (INT32_MIN + 1)) &&
+                   (status <= (INT32_MIN + 127))) {
+            tfm_panic();
+        } else if ((status >= (INT32_MIN + 128)) && (status <= -1)) {
+            ret = status;
+        } else if ((status >= 1) && (status <= INT32_MAX)) {
+            ret = status;
+        } else {
+            tfm_panic();
+        }
+
+        /*
+         * The total number of bytes written to a single parameter must be
+         * reported to the client by updating the len member of the psa_outvec
+         * structure for the parameter before returning from psa_call().
+         */
+        update_caller_outvec_len(msg);
+        break;
+    case PSA_IPC_DISCONNECT:
+        /* Service handle is not used anymore */
+        tfm_spm_free_conn_handle(service, msg->handle);
+
+        /*
+         * If the message type is PSA_IPC_DISCONNECT, then the status code is
+         * ignored
+         */
+        break;
+    default:
+        tfm_panic();
+    }
+
+    tfm_event_wake(&msg->ack_evnt, ret);
+
+    /* Message should not be unsed anymore */
+    tfm_spm_free_msg(msg);
+}
+
+/**
+ * \brief SVC handler for \ref psa_notify.
+ *
+ * \param[in] args              Include all input arguments: partition_id.
+ *
+ * \retval void                 Success.
+ * \retval "Does not return"    partition_id does not correspond to a Secure
+ *                              Partition.
+ */
+static void tfm_svcall_psa_notify(uint32_t *args)
+{
+    int32_t partition_id;
+    struct tfm_spm_ipc_partition_t *partition = NULL;
+
+    TFM_ASSERT(args != NULL);
+    partition_id = (int32_t)args[0];
+
+    /*
+     * The value of partition_id must be greater than zero as the target of
+     * notification must be a Secure Partition, providing a Non-secure
+     * Partition ID is a fatal error.
+     */
+    if (!TFM_CLIENT_ID_IS_S(partition_id)) {
+        tfm_panic();
+    }
+
+    /*
+     * It is a fatal error if partition_id does not correspond to a Secure
+     * Partition.
+     */
+    partition = tfm_spm_get_partition_by_id(partition_id);
+    if (!partition) {
+        tfm_panic();
+    }
+
+    partition->signals |= PSA_DOORBELL;
+
+    /*
+     * The target partition may be blocked with waiting for signals after
+     * called psa_wait(). Set the return value with the available signals
+     * before wake it up with tfm_event_signal().
+     */
+    tfm_event_wake(&partition->signal_evnt,
+                   partition->signals & partition->signal_mask);
+}
+
+/**
+ * \brief SVC handler for \ref psa_clear.
+ *
+ * \retval void                 Success.
+ * \retval "Does not return"    The Secure Partition's doorbell signal is not
+ *                              currently asserted.
+ */
+static void tfm_svcall_psa_clear(uint32_t *args)
+{
+    struct tfm_spm_ipc_partition_t *partition = NULL;
+
+    partition = tfm_spm_get_running_partition();
+    if (!partition) {
+        tfm_panic();
+    }
+
+    /*
+     * It is a fatal error if the Secure Partition's doorbell signal is not
+     * currently asserted.
+     */
+    if ((partition->signals & PSA_DOORBELL) == 0) {
+        tfm_panic();
+    }
+    partition->signals &= ~PSA_DOORBELL;
+}
+
+/**
+ * \brief SVC handler for \ref psa_eoi.
+ *
+ * \param[in] args              Include all input arguments: irq_signal.
+ *
+ * \retval void                 Success.
+ * \retval "Does not return"    The call is invalid, one or more of the
+ *                              following are true:
+ * \arg                           irq_signal is not an interrupt signal.
+ * \arg                           irq_signal indicates more than one signal.
+ * \arg                           irq_signal is not currently asserted.
+ */
+static void tfm_svcall_psa_eoi(uint32_t *args)
+{
+    psa_signal_t irq_signal;
+    struct tfm_spm_ipc_partition_t *partition = NULL;
+
+    TFM_ASSERT(args != NULL);
+    irq_signal = (psa_signal_t)args[0];
+
+    partition = tfm_spm_get_running_partition();
+    if (!partition) {
+        tfm_panic();
+    }
+
+    /*
+     * FixMe: It is a fatal error if passed signal is not an interrupt signal.
+     */
+
+    /* It is a fatal error if passed signal indicates more than one signals. */
+    if (tfm_bitcount(partition->signals) != 1) {
+        tfm_panic();
+    }
+
+    /* It is a fatal error if passed signal is not currently asserted */
+    if ((partition->signals & irq_signal) == 0) {
+        tfm_panic();
+    }
+
+    partition->signals &= ~irq_signal;
+
+    /* FixMe: re-enable interrupt */
+}
+
+int32_t SVC_Handler_IPC(tfm_svc_number_t svc_num, uint32_t *ctx, uint32_t lr)
+{
+    switch (svc_num) {
+    case TFM_SVC_SCHEDULE:
+        tfm_thrd_activate_schedule();
+        break;
+    case TFM_SVC_EXIT_THRD:
+        tfm_svcall_thrd_exit();
+        break;
+    case TFM_SVC_PSA_FRAMEWORK_VERSION:
+        return tfm_svcall_psa_framework_version();
+    case TFM_SVC_PSA_VERSION:
+        return tfm_svcall_psa_version(ctx, 0);
+    case TFM_SVC_PSA_CONNECT:
+        return tfm_svcall_psa_connect(ctx, 0);
+    case TFM_SVC_PSA_CALL:
+        return tfm_svcall_psa_call(ctx, 0, lr);
+    case TFM_SVC_PSA_CLOSE:
+        tfm_svcall_psa_close(ctx, 0);
+        break;
+    case TFM_SVC_PSA_WAIT:
+        return tfm_svcall_psa_wait(ctx);
+    case TFM_SVC_PSA_GET:
+        return tfm_svcall_psa_get(ctx);
+    case TFM_SVC_PSA_SET_RHANDLE:
+        tfm_svcall_psa_set_rhandle(ctx);
+        break;
+    case TFM_SVC_PSA_READ:
+        return tfm_svcall_psa_read(ctx);
+    case TFM_SVC_PSA_SKIP:
+        return tfm_svcall_psa_skip(ctx);
+    case TFM_SVC_PSA_WRITE:
+        tfm_svcall_psa_write(ctx);
+        break;
+    case TFM_SVC_PSA_REPLY:
+        tfm_svcall_psa_reply(ctx);
+        break;
+    case TFM_SVC_PSA_NOTIFY:
+        tfm_svcall_psa_notify(ctx);
+        break;
+    case TFM_SVC_PSA_CLEAR:
+        tfm_svcall_psa_clear(ctx);
+        break;
+    case TFM_SVC_PSA_EOI:
+        tfm_svcall_psa_eoi(ctx);
+        break;
+    default:
+        break;
+    }
+    return PSA_SUCCESS;
+}