Core: Move IPC model SPM code to 'spm' folder
- APIs in 'tfm_svcalls.c' are actually part of SPM, so move them
to 'secure_fw/spm/spm_ipc.c', and rename SPM APIs with 'tfm_spm'
prefix.
- APIs in 'tfm_psa_client_call.c' are also a part of SPM, move
the whole file and header file to SPM, and add 'tfm_spm' prefix
to API names.
- Move API declaration into 'spm_api.h' header.
- Add static declaration to internal-used functions.
- Remove inclusion of some unused header files.
Change-Id: I90ff634987ce9e01ddb3a294b7f1f5f222792f8c
Signed-off-by: Mingyang Sun <mingyang.sun@arm.com>
diff --git a/secure_fw/spm/spm_ipc.c b/secure_fw/spm/spm_ipc.c
index b88d4a8..90e688f 100644
--- a/secure_fw/spm/spm_ipc.c
+++ b/secure_fw/spm/spm_ipc.c
@@ -5,32 +5,33 @@
*
*/
-/* All the APIs defined in this file are used for IPC model. */
-
#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_api.h"
-#include "tfm_core_mem_check.h"
-#include "tfm_internal_defines.h"
+#include "psa/lifecycle.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_hal.h"
+#include "tfm_irq_list.h"
+#include "tfm_api.h"
+#include "tfm_secure_api.h"
+#include "tfm_memory_utils.h"
+#include "spm_api.h"
+#include "tfm_peripherals_def.h"
+#include "spm_db.h"
+#include "tfm_core_utils.h"
+#include "spm_psa_client_call.h"
+#include "tfm_rpc.h"
+#include "tfm_internal.h"
+#include "tfm_core_trustzone.h"
+#include "tfm_core_mem_check.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"
-#include "tfm_core_utils.h"
-#include "tfm_rpc.h"
-#include "tfm_irq_list.h"
#include "secure_fw/services/tfm_service_list.inc"
@@ -45,7 +46,10 @@
TFM_POOL_DECLARE(conn_handle_pool, sizeof(struct tfm_conn_handle_t),
TFM_CONN_HANDLE_MAX_NUM);
-/********************** SPM functions for handler mode ***********************/
+void tfm_irq_handler(uint32_t partition_id, psa_signal_t signal,
+ int32_t irq_line);
+
+#include "tfm_secure_irq_handlers_ipc.inc"
/* Service handle management functions */
psa_handle_t tfm_spm_create_conn_handle(struct tfm_spm_service_t *service,
@@ -97,8 +101,19 @@
return (struct tfm_conn_handle_t *)conn_handle;
}
-int32_t tfm_spm_free_conn_handle(struct tfm_spm_service_t *service,
- psa_handle_t conn_handle)
+/**
+ * \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
+ */
+static int32_t tfm_spm_free_conn_handle(struct tfm_spm_service_t *service,
+ psa_handle_t conn_handle)
{
struct tfm_conn_handle_t *p_handle;
@@ -121,9 +136,21 @@
return IPC_SUCCESS;
}
-int32_t tfm_spm_set_rhandle(struct tfm_spm_service_t *service,
- psa_handle_t conn_handle,
- void *rhandle)
+/**
+ * \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
+ */
+static int32_t tfm_spm_set_rhandle(struct tfm_spm_service_t *service,
+ psa_handle_t conn_handle,
+ void *rhandle)
{
struct tfm_conn_handle_t *p_handle;
@@ -141,8 +168,21 @@
return IPC_SUCCESS;
}
-void *tfm_spm_get_rhandle(struct tfm_spm_service_t *service,
- psa_handle_t conn_handle)
+/**
+ * \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
+ */
+static void *tfm_spm_get_rhandle(struct tfm_spm_service_t *service,
+ psa_handle_t conn_handle)
{
struct tfm_conn_handle_t *p_handle;
@@ -160,7 +200,20 @@
}
/* Partition management functions */
-struct tfm_spm_service_t *
+
+/**
+ * \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
+ */
+static struct tfm_spm_service_t *
tfm_spm_get_service_by_signal(struct spm_partition_desc_t *partition,
psa_signal_t signal)
{
@@ -219,7 +272,17 @@
return ((struct tfm_conn_handle_t *)conn_handle)->service;
}
-struct spm_partition_desc_t *tfm_spm_get_partition_by_id(int32_t partition_id)
+/**
+ * \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
+ */
+static struct spm_partition_desc_t *
+ tfm_spm_get_partition_by_id(int32_t partition_id)
{
uint32_t idx = get_partition_idx(partition_id);
@@ -293,7 +356,18 @@
}
/* Message functions */
-struct tfm_msg_body_t *tfm_spm_get_msg_from_handle(psa_handle_t msg_handle)
+
+/**
+ * \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
+ */
+static struct tfm_msg_body_t *
+ tfm_spm_get_msg_from_handle(psa_handle_t msg_handle)
{
/*
* The message handler passed by the caller is considered invalid in the
@@ -438,13 +512,31 @@
return IPC_SUCCESS;
}
-uint32_t tfm_spm_partition_get_stack_bottom(uint32_t partition_idx)
+/**
+ * \brief Get bottom of stack region for a partition
+ *
+ * \param[in] partition_idx Partition index
+ *
+ * \return Stack region bottom value
+ *
+ * \note This function doesn't check if partition_idx is valid.
+ */
+static uint32_t tfm_spm_partition_get_stack_bottom(uint32_t partition_idx)
{
return g_spm_partition_db.partitions[partition_idx].
memory_data->stack_bottom;
}
-uint32_t tfm_spm_partition_get_stack_top(uint32_t partition_idx)
+/**
+ * \brief Get top of stack region for a partition
+ *
+ * \param[in] partition_idx Partition index
+ *
+ * \return Stack region top value
+ *
+ * \note This function doesn't check if partition_idx is valid.
+ */
+static uint32_t tfm_spm_partition_get_stack_top(uint32_t partition_idx)
{
return g_spm_partition_db.partitions[partition_idx].memory_data->stack_top;
}
@@ -514,8 +606,6 @@
return IPC_ERROR_MEMORY_CHECK;
}
-/********************** SPM functions for thread mode ************************/
-
uint32_t tfm_spm_init(void)
{
uint32_t i, j, num;
@@ -666,3 +756,837 @@
*/
tfm_rpc_client_call_handler();
}
+
+/*********************** SPM functions for PSA Client APIs *******************/
+
+uint32_t tfm_spm_psa_framework_version(void)
+{
+ return tfm_spm_client_psa_framework_version();
+}
+
+uint32_t tfm_spm_psa_version(uint32_t *args, bool ns_caller)
+{
+ uint32_t sid;
+
+ TFM_CORE_ASSERT(args != NULL);
+ sid = (uint32_t)args[0];
+
+ return tfm_spm_client_psa_version(sid, ns_caller);
+}
+
+psa_status_t tfm_spm_psa_connect(uint32_t *args, bool ns_caller)
+{
+ uint32_t sid;
+ uint32_t version;
+
+ TFM_CORE_ASSERT(args != NULL);
+ sid = (uint32_t)args[0];
+ version = (uint32_t)args[1];
+
+ return tfm_spm_client_psa_connect(sid, version, ns_caller);
+}
+
+psa_status_t tfm_spm_psa_call(uint32_t *args, bool ns_caller, uint32_t lr)
+{
+ psa_handle_t handle;
+ psa_invec *inptr;
+ psa_outvec *outptr;
+ size_t in_num, out_num;
+ struct spm_partition_desc_t *partition = NULL;
+ uint32_t privileged;
+ int32_t type;
+ struct tfm_control_parameter_t ctrl_param;
+
+ TFM_CORE_ASSERT(args != NULL);
+ handle = (psa_handle_t)args[0];
+
+ partition = tfm_spm_get_running_partition();
+ if (!partition) {
+ tfm_core_panic();
+ }
+ privileged = tfm_spm_partition_get_privileged_mode(
+ partition->static_data->partition_flags);
+
+ /*
+ * 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((const void *)args[1],
+ sizeof(struct tfm_control_parameter_t), ns_caller,
+ TFM_MEMORY_ACCESS_RW, privileged) != IPC_SUCCESS) {
+ tfm_core_panic();
+ }
+
+ tfm_core_util_memcpy(&ctrl_param,
+ (const void *)args[1],
+ sizeof(ctrl_param));
+
+ type = ctrl_param.type;
+ in_num = ctrl_param.in_len;
+ out_num = ctrl_param.out_len;
+ inptr = (psa_invec *)args[2];
+ outptr = (psa_outvec *)args[3];
+
+ /* The request type must be zero or positive. */
+ if (type < 0) {
+ tfm_core_panic();
+ }
+
+ return tfm_spm_client_psa_call(handle, type, inptr, in_num, outptr, out_num,
+ ns_caller, privileged);
+}
+
+void tfm_spm_psa_close(uint32_t *args, bool ns_caller)
+{
+ psa_handle_t handle;
+
+ TFM_CORE_ASSERT(args != NULL);
+ handle = args[0];
+
+ tfm_spm_client_psa_close(handle, ns_caller);
+}
+
+uint32_t tfm_spm_get_lifecycle_state(void)
+{
+ /*
+ * FixMe: return PSA_LIFECYCLE_UNKNOWN to the caller directly. It will be
+ * implemented in the future.
+ */
+ return PSA_LIFECYCLE_UNKNOWN;
+}
+
+/********************* SPM functions for PSA Service APIs ********************/
+
+psa_signal_t tfm_spm_psa_wait(uint32_t *args)
+{
+ psa_signal_t signal_mask;
+ uint32_t timeout;
+ struct spm_partition_desc_t *partition = NULL;
+
+ TFM_CORE_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_core_panic();
+ }
+
+ /*
+ * It is a PROGRAMMER ERROR if the signal_mask does not include any assigned
+ * signals.
+ */
+ if ((partition->runtime_data.assigned_signals & signal_mask) == 0) {
+ tfm_core_panic();
+ }
+
+ /*
+ * Expected signals are included in signal wait mask, ignored signals
+ * should not be set and affect caller thread state. Save this mask for
+ * further checking while signals are ready to be set.
+ */
+ partition->runtime_data.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->runtime_data.signals & signal_mask) == 0) {
+ tfm_event_wait(&partition->runtime_data.signal_evnt);
+ }
+
+ return partition->runtime_data.signals & signal_mask;
+}
+
+psa_status_t tfm_spm_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 spm_partition_desc_t *partition = NULL;
+ uint32_t privileged;
+
+ TFM_CORE_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_core_panic();
+ }
+
+ partition = tfm_spm_get_running_partition();
+ if (!partition) {
+ tfm_core_panic();
+ }
+ privileged = tfm_spm_partition_get_privileged_mode(
+ partition->static_data->partition_flags);
+
+ /*
+ * 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(msg, sizeof(psa_msg_t), false, TFM_MEMORY_ACCESS_RW,
+ privileged) != IPC_SUCCESS) {
+ tfm_core_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 an RoT Service signal
+ * is returned by psa_wait().
+ */
+ if (partition->runtime_data.signals == 0) {
+ tfm_core_panic();
+ }
+
+ /*
+ * It is a fatal error if the RoT Service signal is not currently asserted.
+ */
+ if ((partition->runtime_data.signals & signal) == 0) {
+ tfm_core_panic();
+ }
+
+ /*
+ * Get RoT service by signal from partition. It is a fatal error if getting
+ * failed, which means the input signal is not correspond to an RoT service.
+ */
+ service = tfm_spm_get_service_by_signal(partition, signal);
+ if (!service) {
+ tfm_core_panic();
+ }
+
+ tmp_msg = tfm_msg_dequeue(&service->msg_queue);
+ if (!tmp_msg) {
+ return PSA_ERROR_DOES_NOT_EXIST;
+ }
+
+ ((struct tfm_conn_handle_t *)(tmp_msg->handle))->status =
+ TFM_HANDLE_STATUS_ACTIVE;
+
+ tfm_core_util_memcpy(msg, &tmp_msg->msg, sizeof(psa_msg_t));
+
+ /*
+ * There may be multiple 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->runtime_data.signals &= ~signal;
+ }
+
+ return PSA_SUCCESS;
+}
+
+void tfm_spm_psa_set_rhandle(uint32_t *args)
+{
+ psa_handle_t msg_handle;
+ void *rhandle = NULL;
+ struct tfm_msg_body_t *msg = NULL;
+
+ TFM_CORE_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_core_panic();
+ }
+
+ msg->msg.rhandle = rhandle;
+
+ /* Store reverse handle for following client calls. */
+ tfm_spm_set_rhandle(msg->service, msg->handle, rhandle);
+}
+
+size_t tfm_spm_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 spm_partition_desc_t *partition = NULL;
+
+ TFM_CORE_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_core_panic();
+ }
+
+ partition = msg->service->partition;
+ privileged = tfm_spm_partition_get_privileged_mode(
+ partition->static_data->partition_flags);
+
+ /*
+ * It is a fatal error if message handle does not refer to a request
+ * message
+ */
+ if (msg->msg.type < PSA_IPC_CALL) {
+ tfm_core_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_core_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_core_panic();
+ }
+
+ bytes = num_bytes > msg->msg.in_size[invec_idx] ?
+ msg->msg.in_size[invec_idx] : num_bytes;
+
+ tfm_core_util_memcpy(buffer, msg->invec[invec_idx].base, bytes);
+
+ /* There maybe some remaining data */
+ msg->invec[invec_idx].base = (char *)msg->invec[invec_idx].base + bytes;
+ msg->msg.in_size[invec_idx] -= bytes;
+
+ return bytes;
+}
+
+size_t tfm_spm_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_CORE_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_core_panic();
+ }
+
+ /*
+ * It is a fatal error if message handle does not refer to a request
+ * message
+ */
+ if (msg->msg.type < PSA_IPC_CALL) {
+ tfm_core_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_core_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 = (char *)msg->invec[invec_idx].base +
+ num_bytes;
+ msg->msg.in_size[invec_idx] -= num_bytes;
+
+ return num_bytes;
+}
+
+void tfm_spm_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 spm_partition_desc_t *partition = NULL;
+
+ TFM_CORE_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_core_panic();
+ }
+
+ partition = msg->service->partition;
+ privileged = tfm_spm_partition_get_privileged_mode(
+ partition->static_data->partition_flags);
+
+ /*
+ * It is a fatal error if message handle does not refer to a request
+ * message
+ */
+ if (msg->msg.type < PSA_IPC_CALL) {
+ tfm_core_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_core_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_core_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_core_panic();
+ }
+
+ tfm_core_util_memcpy((char *)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)
+{
+ uint32_t i;
+
+ /*
+ * FixeMe: abstract these part into dedicated functions to avoid
+ * accessing thread context in psa layer
+ */
+ /* If it is a NS request via RPC, the owner of this message is not set */
+ if (!is_tfm_rpc_msg(msg)) {
+ TFM_CORE_ASSERT(msg->ack_evnt.owner->state == THRD_STATE_BLOCK);
+ }
+
+ for (i = 0; i < PSA_MAX_IOVEC; i++) {
+ if (msg->msg.out_size[i] == 0) {
+ continue;
+ }
+
+ TFM_CORE_ASSERT(msg->caller_outvec[i].base == msg->outvec[i].base);
+
+ msg->caller_outvec[i].len = msg->outvec[i].len;
+ }
+}
+
+void tfm_spm_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;
+ int32_t ret = PSA_SUCCESS;
+
+ TFM_CORE_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_core_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_core_panic();
+ }
+
+ /*
+ * Three type of message are passed in this function: CONNECTION, REQUEST,
+ * DISCONNECTION. 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 returned if the
+ * input status is PSA_SUCCESS. Others return values are based on the
+ * input status.
+ */
+ if (status == PSA_SUCCESS) {
+ ret = msg->handle;
+ } else if (status == PSA_ERROR_CONNECTION_REFUSED) {
+ /* Refuse the client connection, indicating a permanent error. */
+ tfm_spm_free_conn_handle(service, msg->handle);
+ ret = PSA_ERROR_CONNECTION_REFUSED;
+ } else if (status == PSA_ERROR_CONNECTION_BUSY) {
+ /* Fail the client connection, indicating a transient error. */
+ ret = PSA_ERROR_CONNECTION_BUSY;
+ } else {
+ tfm_core_panic();
+ }
+ 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:
+ if (msg->msg.type >= PSA_IPC_CALL) {
+ /* Reply to a request message. Return values are based on status */
+ ret = status;
+ /*
+ * 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);
+ } else {
+ tfm_core_panic();
+ }
+ }
+
+ if (ret == PSA_ERROR_PROGRAMMER_ERROR) {
+ /*
+ * If the source of the programmer error is a Secure Partition, the SPM
+ * must panic the Secure Partition in response to a PROGRAMMER ERROR.
+ */
+ if (TFM_CLIENT_ID_IS_NS(msg->msg.client_id)) {
+ ((struct tfm_conn_handle_t *)(msg->handle))->status =
+ TFM_HANDLE_STATUS_CONNECT_ERROR;
+ } else {
+ tfm_core_panic();
+ }
+ } else {
+ ((struct tfm_conn_handle_t *)(msg->handle))->status =
+ TFM_HANDLE_STATUS_IDLE;
+ }
+
+ if (is_tfm_rpc_msg(msg)) {
+ tfm_rpc_client_call_reply(msg, ret);
+ } else {
+ tfm_event_wake(&msg->ack_evnt, ret);
+ }
+}
+
+/**
+ * \brief notify the partition with the signal.
+ *
+ * \param[in] partition_id The ID of the partition to be notified.
+ * \param[in] signal The signal that the partition is to be notified
+ * with.
+ *
+ * \retval void Success.
+ * \retval "Does not return" If partition_id is invalid.
+ */
+static void notify_with_signal(int32_t partition_id, psa_signal_t signal)
+{
+ struct spm_partition_desc_t *partition = NULL;
+
+ /*
+ * 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_core_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_core_panic();
+ }
+
+ partition->runtime_data.signals |= signal;
+
+ /*
+ * 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->runtime_data.signal_evnt,
+ partition->runtime_data.signals &
+ partition->runtime_data.signal_mask);
+}
+
+void tfm_spm_psa_notify(uint32_t *args)
+{
+ int32_t partition_id;
+
+ TFM_CORE_ASSERT(args != NULL);
+ partition_id = (int32_t)args[0];
+
+ notify_with_signal(partition_id, PSA_DOORBELL);
+}
+
+/**
+ * \brief assert signal for a given IRQ line.
+ *
+ * \param[in] partition_id The ID of the partition which handles this IRQ
+ * \param[in] signal The signal associated with this IRQ
+ * \param[in] irq_line The number of the IRQ line
+ *
+ * \retval void Success.
+ * \retval "Does not return" Partition ID is invalid
+ */
+void tfm_irq_handler(uint32_t partition_id, psa_signal_t signal,
+ int32_t irq_line)
+{
+ tfm_spm_hal_disable_irq(irq_line);
+ notify_with_signal(partition_id, signal);
+}
+
+void tfm_spm_psa_clear(void)
+{
+ struct spm_partition_desc_t *partition = NULL;
+
+ partition = tfm_spm_get_running_partition();
+ if (!partition) {
+ tfm_core_panic();
+ }
+
+ /*
+ * It is a fatal error if the Secure Partition's doorbell signal is not
+ * currently asserted.
+ */
+ if ((partition->runtime_data.signals & PSA_DOORBELL) == 0) {
+ tfm_core_panic();
+ }
+ partition->runtime_data.signals &= ~PSA_DOORBELL;
+}
+
+void tfm_spm_psa_panic(void)
+{
+ /*
+ * PSA FF recommends that the SPM causes the system to restart when a secure
+ * partition panics.
+ */
+ tfm_spm_hal_system_reset();
+}
+
+/**
+ * \brief Return the IRQ line number associated with a signal
+ *
+ * \param[in] partition_id The ID of the partition in which we look for
+ * the signal.
+ * \param[in] signal The signal we do the query for.
+ * \param[out] irq_line The irq line associated with signal
+ *
+ * \retval IPC_SUCCESS Execution successful, irq_line contains a valid
+ * value.
+ * \retval IPC_ERROR_GENERIC There was an error finding the IRQ line for the
+ * signal. irq_line is unchanged.
+ */
+static int32_t get_irq_line_for_signal(int32_t partition_id,
+ psa_signal_t signal,
+ int32_t *irq_line)
+{
+ size_t i;
+
+ for (i = 0; i < tfm_core_irq_signals_count; ++i) {
+ if (tfm_core_irq_signals[i].partition_id == partition_id &&
+ tfm_core_irq_signals[i].signal_value == signal) {
+ *irq_line = tfm_core_irq_signals[i].irq_line;
+ return IPC_SUCCESS;
+ }
+ }
+ return IPC_ERROR_GENERIC;
+}
+
+void tfm_spm_psa_eoi(uint32_t *args)
+{
+ psa_signal_t irq_signal;
+ int32_t irq_line = 0;
+ int32_t ret;
+ struct spm_partition_desc_t *partition = NULL;
+
+ TFM_CORE_ASSERT(args != NULL);
+ irq_signal = (psa_signal_t)args[0];
+
+ /* It is a fatal error if passed signal indicates more than one signals. */
+ if (!tfm_is_one_bit_set(irq_signal)) {
+ tfm_core_panic();
+ }
+
+ partition = tfm_spm_get_running_partition();
+ if (!partition) {
+ tfm_core_panic();
+ }
+
+ ret = get_irq_line_for_signal(partition->static_data->partition_id,
+ irq_signal, &irq_line);
+ /* It is a fatal error if passed signal is not an interrupt signal. */
+ if (ret != IPC_SUCCESS) {
+ tfm_core_panic();
+ }
+
+ /* It is a fatal error if passed signal is not currently asserted */
+ if ((partition->runtime_data.signals & irq_signal) == 0) {
+ tfm_core_panic();
+ }
+
+ partition->runtime_data.signals &= ~irq_signal;
+
+ tfm_spm_hal_clear_pending_irq(irq_line);
+ tfm_spm_hal_enable_irq(irq_line);
+}
+
+void tfm_spm_enable_irq(uint32_t *args)
+{
+ struct tfm_state_context_t *svc_ctx = (struct tfm_state_context_t *)args;
+ psa_signal_t irq_signal = svc_ctx->r0;
+ int32_t irq_line = 0;
+ int32_t ret;
+ struct spm_partition_desc_t *partition = NULL;
+
+ /* It is a fatal error if passed signal indicates more than one signals. */
+ if (!tfm_is_one_bit_set(irq_signal)) {
+ tfm_core_panic();
+ }
+
+ partition = tfm_spm_get_running_partition();
+ if (!partition) {
+ tfm_core_panic();
+ }
+
+ ret = get_irq_line_for_signal(partition->static_data->partition_id,
+ irq_signal, &irq_line);
+ /* It is a fatal error if passed signal is not an interrupt signal. */
+ if (ret != IPC_SUCCESS) {
+ tfm_core_panic();
+ }
+
+ tfm_spm_hal_enable_irq(irq_line);
+}
+
+void tfm_spm_disable_irq(uint32_t *args)
+{
+ struct tfm_state_context_t *svc_ctx = (struct tfm_state_context_t *)args;
+ psa_signal_t irq_signal = svc_ctx->r0;
+ int32_t irq_line = 0;
+ int32_t ret;
+ struct spm_partition_desc_t *partition = NULL;
+
+ /* It is a fatal error if passed signal indicates more than one signals. */
+ if (!tfm_is_one_bit_set(irq_signal)) {
+ tfm_core_panic();
+ }
+
+ partition = tfm_spm_get_running_partition();
+ if (!partition) {
+ tfm_core_panic();
+ }
+
+ ret = get_irq_line_for_signal(partition->static_data->partition_id,
+ irq_signal, &irq_line);
+ /* It is a fatal error if passed signal is not an interrupt signal. */
+ if (ret != IPC_SUCCESS) {
+ tfm_core_panic();
+ }
+
+ tfm_spm_hal_disable_irq(irq_line);
+}
+
+void tfm_spm_validate_caller(struct spm_partition_desc_t *p_cur_sp,
+ uint32_t *p_ctx, uint32_t exc_return,
+ bool ns_caller)
+{
+ uintptr_t stacked_ctx_pos;
+
+ if (ns_caller) {
+ /*
+ * The background IRQ can't be supported, since if SP is executing,
+ * the preempted context of SP can be different with the one who
+ * preempts veneer.
+ */
+ if (p_cur_sp->static_data->partition_id != TFM_SP_NON_SECURE_ID) {
+ tfm_core_panic();
+ }
+
+ /*
+ * It is non-secure caller, check if veneer stack contains
+ * multiple contexts.
+ */
+ stacked_ctx_pos = (uintptr_t)p_ctx +
+ sizeof(struct tfm_state_context_t) +
+ TFM_VENEER_STACK_GUARD_SIZE;
+
+ if (is_stack_alloc_fp_space(exc_return)) {
+#if defined (__FPU_USED) && (__FPU_USED == 1U)
+ if (FPU->FPCCR & FPU_FPCCR_TS_Msk) {
+ stacked_ctx_pos += TFM_ADDTIONAL_FP_CONTEXT_WORDS *
+ sizeof(uint32_t);
+ }
+#endif
+ stacked_ctx_pos += TFM_BASIC_FP_CONTEXT_WORDS * sizeof(uint32_t);
+ }
+
+ if (stacked_ctx_pos != p_cur_sp->runtime_data.sp_thrd.stk_top) {
+ tfm_core_panic();
+ }
+ } else if (p_cur_sp->static_data->partition_id <= 0) {
+ tfm_core_panic();
+ }
+}