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/CMakeLists.inc b/secure_fw/spm/CMakeLists.inc
index 23d8e83..cb458d5 100644
--- a/secure_fw/spm/CMakeLists.inc
+++ b/secure_fw/spm/CMakeLists.inc
@@ -27,7 +27,8 @@
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_ipc.c")
+ list(APPEND SS_SPM_C_SRC "${SS_SPM_DIR}/spm_ipc.c"
+ "${SS_SPM_DIR}/spm_psa_client_call.c")
else()
list(APPEND SS_SPM_C_SRC "${SS_SPM_DIR}/spm_func.c")
endif()
@@ -41,7 +42,6 @@
embedded_include_directories(PATH ${TFM_ROOT_DIR}/interface/include ABSOLUTE)
embedded_include_directories(PATH ${TFM_ROOT_DIR}/secure_fw/spm ABSOLUTE)
embedded_include_directories(PATH ${TFM_ROOT_DIR}/secure_fw/core/include ABSOLUTE)
-embedded_include_directories(PATH ${TFM_ROOT_DIR} ABSOLUTE)
set(BUILD_CMSIS_CORE Off)
set(BUILD_RETARGET Off)
diff --git a/secure_fw/spm/spm_api.c b/secure_fw/spm/spm_api.c
index f71db78..9dae74b 100644
--- a/secure_fw/spm/spm_api.c
+++ b/secure_fw/spm/spm_api.c
@@ -19,7 +19,6 @@
#include "tfm_core.h"
#include "tfm_peripherals_def.h"
#include "spm_partition_defs.h"
-#include "psa/lifecycle.h"
#include "region.h"
#define NON_SECURE_INTERNAL_PARTITION_DB_IDX 0
@@ -108,15 +107,6 @@
TFM_PARTITION_PRIVILEGED_MODE;
}
-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;
-}
-
__attribute__((section("SFN")))
void tfm_spm_partition_change_privilege(uint32_t privileged)
{
diff --git a/secure_fw/spm/spm_api.h b/secure_fw/spm/spm_api.h
index b28cac7..0ed8893 100644
--- a/secure_fw/spm/spm_api.h
+++ b/secure_fw/spm/spm_api.h
@@ -387,28 +387,6 @@
/*************************** IPC definitions **************************/
/**
- * \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.
- */
-uint32_t tfm_spm_partition_get_stack_bottom(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.
- */
-uint32_t tfm_spm_partition_get_stack_top(uint32_t partition_idx);
-
-/**
* \brief Get the running partition ID.
*
* \return Returns the partition ID
@@ -441,52 +419,6 @@
int32_t tfm_spm_validate_conn_handle(psa_handle_t conn_handle,
int32_t client_id);
-/**
- * \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 *************************/
/**
@@ -499,22 +431,6 @@
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
@@ -538,32 +454,9 @@
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 Get message context by connect handle.
*
* \param[in] conn_handle Service connect handle.
@@ -687,7 +580,6 @@
*/
uint32_t tfm_spm_init(void);
-
/*
* \brief This function get the current PSA RoT lifecycle state.
*
@@ -699,6 +591,293 @@
*/
uint32_t tfm_spm_get_lifecycle_state(void);
-#endif /* ifdef(TFM_PSA_API) */
+/* Svcall for PSA Client APIs */
+
+/**
+ * \brief SVC handler for \ref psa_framework_version.
+ *
+ * \return version The version of the PSA Framework implementation
+ * that is providing the runtime services to the
+ * caller.
+ */
+uint32_t tfm_spm_psa_framework_version(void);
+
+/**
+ * \brief SVC handler for \ref psa_version.
+ *
+ * \param[in] args Include all input arguments: sid.
+ * \param[in] ns_caller If 'true', call from non-secure client.
+ * Or from secure client.
+ *
+ * \retval PSA_VERSION_NONE The RoT Service is not implemented, or the
+ * caller is not permitted to access the service.
+ * \retval > 0 The version of the implemented RoT Service.
+ */
+uint32_t tfm_spm_psa_version(uint32_t *args, bool ns_caller);
+
+/**
+ * \brief SVC handler for \ref psa_connect.
+ *
+ * \param[in] args Include all input arguments:
+ * sid, version.
+ * \param[in] ns_caller If 'true', call from non-secure client.
+ * Or from secure client.
+ *
+ * \retval PSA_SUCCESS Success.
+ * \retval PSA_ERROR_CONNECTION_REFUSED The SPM or RoT Service has refused the
+ * connection.
+ * \retval PSA_ERROR_CONNECTION_BUSY The SPM or RoT Service cannot make the
+ * connection at the moment.
+ * \retval "Does not return" The RoT Service ID and version are not
+ * supported, or the caller is not permitted to
+ * access the service.
+ */
+psa_status_t tfm_spm_psa_connect(uint32_t *args, bool ns_caller);
+
+/**
+ * \brief SVC handler for \ref psa_call.
+ *
+ * \param[in] args Include all input arguments:
+ * handle, in_vec, in_len, out_vec, out_len.
+ * \param[in] ns_caller If 'true', call from non-secure client.
+ * Or from secure client.
+ * \param[in] lr EXC_RETURN value of the SVC.
+ *
+ * \retval >=0 RoT Service-specific status value.
+ * \retval <0 RoT Service-specific error code.
+ * \retval PSA_ERROR_PROGRAMMER_ERROR The connection has been terminated by the
+ * RoT Service. The call is a PROGRAMMER ERROR if
+ * one or more of the following are true:
+ * \arg An invalid handle was passed.
+ * \arg The connection is already handling a request.
+ * \arg type < 0.
+ * \arg An invalid memory reference was provided.
+ * \arg in_len + out_len > PSA_MAX_IOVEC.
+ * \arg The message is unrecognized by the RoT
+ * Service or incorrectly formatted.
+ */
+psa_status_t tfm_spm_psa_call(uint32_t *args, bool ns_caller, uint32_t lr);
+
+/**
+ * \brief SVC handler for \ref psa_close.
+ *
+ * \param[in] args Include all input arguments: handle.
+ * \param[in] ns_caller If 'true', call from non-secure client.
+ * Or from secure client.
+ *
+ * \retval void Success.
+ * \retval "Does not return" The call is invalid, one or more of the
+ * following are true:
+ * \arg An invalid handle was provided that is not
+ * the null handle.
+ * \arg The connection is handling a request.
+ */
+void tfm_spm_psa_close(uint32_t *args, bool ns_caller);
+
+/* Svcall 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.
+ */
+psa_signal_t tfm_spm_psa_wait(uint32_t *args);
+
+/**
+ * \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_ERROR_DOES_NOT_EXIST 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 an RoT Service.
+ * \arg The RoT Service signal is not currently
+ * asserted.
+ * \arg The msg pointer provided is not a valid memory
+ * reference.
+ */
+psa_status_t tfm_spm_psa_get(uint32_t *args);
+
+/**
+ * \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.
+ */
+void tfm_spm_psa_set_rhandle(uint32_t *args);
+
+/**
+ * \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 request
+ * 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.
+ */
+size_t tfm_spm_psa_read(uint32_t *args);
+
+/**
+ * \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 request
+ * message.
+ * \arg invec_idx is equal to or greater than
+ * \ref PSA_MAX_IOVEC.
+ */
+size_t tfm_spm_psa_skip(uint32_t *args);
+
+/**
+ * \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 request
+ * 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.
+ */
+void tfm_spm_psa_write(uint32_t *args);
+
+/**
+ * \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.
+ */
+void tfm_spm_psa_reply(uint32_t *args);
+
+/**
+ * \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.
+ */
+void tfm_spm_psa_notify(uint32_t *args);
+
+/**
+ * \brief SVC handler for \ref psa_clear.
+ *
+ * \retval void Success.
+ * \retval "Does not return" The Secure Partition's doorbell signal is not
+ * currently asserted.
+ */
+void tfm_spm_psa_clear(void);
+
+/**
+ * \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.
+ */
+void tfm_spm_psa_eoi(uint32_t *args);
+
+/**
+ * \brief SVC hander of enabling irq_line of the specified irq_signal.
+ *
+ * \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.
+ */
+void tfm_spm_enable_irq(uint32_t *args);
+
+/**
+ * \brief SVC hander of disabling irq_line of the specified irq_signal.
+ *
+ * \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.
+ */
+void tfm_spm_disable_irq(uint32_t *args);
+
+/**
+ * \brief Validate the whether NS caller re-enter.
+ *
+ * \param[in] p_cur_sp Pointer to current partition.
+ * \param[in] p_ctx Pointer to current stack context.
+ * \param[in] exc_return EXC_RETURN value.
+ * \param[in] ns_caller If 'true', call from non-secure client.
+ * Or from secure client.
+ *
+ * \retval void Success.
+ */
+void tfm_spm_validate_caller(struct spm_partition_desc_t *p_cur_sp,
+ uint32_t *p_ctx, uint32_t exc_return,
+ bool ns_caller);
+
+/**
+ * \brief Terminate execution within the calling Secure Partition and will not
+ * return.
+ *
+ * \retval "Does not return"
+ */
+void tfm_spm_psa_panic(void);
+
+#endif /* defined(TFM_PSA_API) */
#endif /*__SPM_API_H__ */
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();
+ }
+}
diff --git a/secure_fw/spm/spm_psa_client_call.c b/secure_fw/spm/spm_psa_client_call.c
new file mode 100644
index 0000000..ab29cb7
--- /dev/null
+++ b/secure_fw/spm/spm_psa_client_call.c
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "psa/service.h"
+#include "spm_api.h"
+#include "tfm_core_utils.h"
+#include "tfm_internal_defines.h"
+#include "tfm_memory_utils.h"
+#include "tfm_message_queue.h"
+#include "spm_psa_client_call.h"
+#include "tfm_utils.h"
+#include "tfm_wait.h"
+#include "tfm_nspm.h"
+
+uint32_t tfm_spm_client_psa_framework_version(void)
+{
+ return PSA_FRAMEWORK_VERSION;
+}
+
+uint32_t tfm_spm_client_psa_version(uint32_t sid, bool ns_caller)
+{
+ struct tfm_spm_service_t *service;
+
+ /*
+ * 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 (tfm_spm_check_authorization(sid, service, ns_caller) != IPC_SUCCESS) {
+ return PSA_VERSION_NONE;
+ }
+
+ return service->service_db->version;
+}
+
+psa_status_t tfm_spm_client_psa_connect(uint32_t sid, uint32_t version,
+ bool ns_caller)
+{
+ struct tfm_spm_service_t *service;
+ struct tfm_msg_body_t *msg;
+ psa_handle_t connect_handle;
+ int32_t client_id;
+
+ /* 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_core_panic();
+ }
+
+ if (ns_caller) {
+ client_id = tfm_nspm_get_current_client_id();
+ } else {
+ client_id = tfm_spm_partition_get_running_partition_id();
+ }
+
+ /*
+ * It is a fatal error if the caller is not authorized to access the RoT
+ * Service.
+ */
+ if (tfm_spm_check_authorization(sid, service, ns_caller) != IPC_SUCCESS) {
+ tfm_core_panic();
+ }
+
+ /*
+ * Create connection handle here since it is possible to return the error
+ * code to client when creation fails.
+ */
+ connect_handle = tfm_spm_create_conn_handle(service, client_id);
+ if (connect_handle == PSA_NULL_HANDLE) {
+ return PSA_ERROR_CONNECTION_BUSY;
+ }
+
+ /*
+ * 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, version) != IPC_SUCCESS) {
+ tfm_core_panic();
+ }
+
+ msg = tfm_spm_get_msg_buffer_from_conn_handle(connect_handle);
+ if (!msg) {
+ /* Have no enough resource to create message */
+ return PSA_ERROR_CONNECTION_BUSY;
+ }
+
+ /* No input or output needed for connect message */
+ tfm_spm_fill_msg(msg, service, connect_handle, PSA_IPC_CONNECT,
+ client_id, NULL, 0, NULL, 0, NULL);
+
+ /*
+ * Send message and wake up the SP who is waiting on message queue,
+ * and scheduler triggered
+ */
+ tfm_spm_send_event(service, msg);
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t tfm_spm_client_psa_call(psa_handle_t handle, int32_t type,
+ const psa_invec *inptr, size_t in_num,
+ psa_outvec *outptr, size_t out_num,
+ bool ns_caller, uint32_t privileged)
+{
+ psa_invec invecs[PSA_MAX_IOVEC];
+ psa_outvec outvecs[PSA_MAX_IOVEC];
+ struct tfm_spm_service_t *service;
+ struct tfm_msg_body_t *msg;
+ int i, j;
+ int32_t client_id;
+
+ /* It is a fatal error if in_len + out_len > PSA_MAX_IOVEC. */
+ if ((in_num > PSA_MAX_IOVEC) ||
+ (out_num > PSA_MAX_IOVEC) ||
+ (in_num + out_num > PSA_MAX_IOVEC)) {
+ tfm_core_panic();
+ }
+
+ if (ns_caller) {
+ client_id = tfm_nspm_get_current_client_id();
+ } else {
+ client_id = tfm_spm_partition_get_running_partition_id();
+ }
+
+ /* It is a fatal error if an invalid handle was passed. */
+ if (tfm_spm_validate_conn_handle(handle, client_id) != IPC_SUCCESS) {
+ tfm_core_panic();
+ }
+ service = tfm_spm_get_service_by_handle(handle);
+ if (!service) {
+ /* FixMe: Need to implement one mechanism to resolve this failure. */
+ tfm_core_panic();
+ }
+
+ /* It is a fatal error if the connection is currently handling a request. */
+ if (((struct tfm_conn_handle_t *)handle)->status ==
+ TFM_HANDLE_STATUS_ACTIVE) {
+ tfm_core_panic();
+ }
+
+ /*
+ * Return PSA_ERROR_PROGRAMMER_ERROR immediately for the connection
+ * has been terminated by the RoT Service.
+ */
+ if (((struct tfm_conn_handle_t *)handle)->status ==
+ TFM_HANDLE_STATUS_CONNECT_ERROR) {
+ return PSA_ERROR_PROGRAMMER_ERROR;
+ }
+
+ /*
+ * 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(inptr, in_num * sizeof(psa_invec), ns_caller,
+ TFM_MEMORY_ACCESS_RO, privileged) != IPC_SUCCESS) {
+ tfm_core_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(outptr, out_num * sizeof(psa_outvec), ns_caller,
+ TFM_MEMORY_ACCESS_RW, privileged) != IPC_SUCCESS) {
+ tfm_core_panic();
+ }
+
+ tfm_core_util_memset(invecs, 0, sizeof(invecs));
+ tfm_core_util_memset(outvecs, 0, sizeof(outvecs));
+
+ /* Copy the address out to avoid TOCTOU attacks. */
+ tfm_core_util_memcpy(invecs, inptr, in_num * sizeof(psa_invec));
+ tfm_core_util_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(invecs[i].base, invecs[i].len, ns_caller,
+ TFM_MEMORY_ACCESS_RO, privileged) != IPC_SUCCESS) {
+ tfm_core_panic();
+ }
+ }
+
+ /*
+ * Clients must never overlap input parameters because of the risk of a
+ * double-fetch inconsistency.
+ * Overflow is checked in tfm_memory_check functions.
+ */
+ for (i = 0; i + 1 < in_num; i++) {
+ for (j = i+1; j < in_num; j++) {
+ if (!((char *) invecs[j].base + invecs[j].len <=
+ (char *) invecs[i].base ||
+ (char *) invecs[j].base >=
+ (char *) invecs[i].base + invecs[i].len)) {
+ tfm_core_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_core_panic();
+ }
+ }
+
+ /*
+ * FixMe: Need to check if the message is unrecognized by the RoT
+ * Service or incorrectly formatted.
+ */
+ msg = tfm_spm_get_msg_buffer_from_conn_handle(handle);
+ if (!msg) {
+ /* FixMe: Need to implement one mechanism to resolve this failure. */
+ tfm_core_panic();
+ }
+
+ tfm_spm_fill_msg(msg, service, handle, type, client_id, invecs,
+ in_num, outvecs, out_num, outptr);
+
+ /*
+ * 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_core_panic();
+ }
+ return PSA_SUCCESS;
+}
+
+void tfm_spm_client_psa_close(psa_handle_t handle, bool ns_caller)
+{
+ struct tfm_spm_service_t *service;
+ struct tfm_msg_body_t *msg;
+ int32_t client_id;
+
+ /* It will have no effect if called with the NULL handle */
+ if (handle == PSA_NULL_HANDLE) {
+ return;
+ }
+
+ if (ns_caller) {
+ client_id = tfm_nspm_get_current_client_id();
+ } else {
+ client_id = tfm_spm_partition_get_running_partition_id();
+ }
+
+ /*
+ * It is a fatal error if an invalid handle was provided that is not the
+ * null handle.
+ */
+ if (tfm_spm_validate_conn_handle(handle, client_id) != IPC_SUCCESS) {
+ tfm_core_panic();
+ }
+ service = tfm_spm_get_service_by_handle(handle);
+ if (!service) {
+ /* FixMe: Need to implement one mechanism to resolve this failure. */
+ tfm_core_panic();
+ }
+
+ msg = tfm_spm_get_msg_buffer_from_conn_handle(handle);
+ if (!msg) {
+ /* FixMe: Need to implement one mechanism to resolve this failure. */
+ tfm_core_panic();
+ }
+
+ /* It is a fatal error if the connection is currently handling a request. */
+ if (((struct tfm_conn_handle_t *)handle)->status ==
+ TFM_HANDLE_STATUS_ACTIVE) {
+ tfm_core_panic();
+ }
+
+ /* No input or output needed for close message */
+ tfm_spm_fill_msg(msg, service, handle, PSA_IPC_DISCONNECT, client_id,
+ NULL, 0, NULL, 0, NULL);
+
+ /*
+ * Send message and wake up the SP who is waiting on message queue,
+ * and scheduler triggered
+ */
+ tfm_spm_send_event(service, msg);
+}
diff --git a/secure_fw/spm/spm_psa_client_call.h b/secure_fw/spm/spm_psa_client_call.h
new file mode 100644
index 0000000..5146e76
--- /dev/null
+++ b/secure_fw/spm/spm_psa_client_call.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __TFM_PSA_CLIENT_CALL_H__
+#define __TFM_PSA_CLIENT_CALL_H__
+
+#include <stdint.h>
+#include "psa/client.h"
+
+/* Common handlers for PSA client calls */
+
+/**
+ * \brief handler for \ref psa_framework_version.
+ *
+ * \return version The version of the PSA Framework implementation
+ * that is providing the runtime services.
+ */
+uint32_t tfm_spm_client_psa_framework_version(void);
+
+/**
+ * \brief handler for \ref psa_version.
+ *
+ * \param[in] sid RoT Service identity.
+ * \param[in] ns_caller If 'true', call from non-secure client.
+ * Otherwise from secure client.
+ *
+ * \retval PSA_VERSION_NONE The RoT Service is not implemented, or the
+ * caller is not permitted to access the service.
+ * \retval > 0 The version of the implemented RoT Service.
+ */
+uint32_t tfm_spm_client_psa_version(uint32_t sid, bool ns_caller);
+
+/**
+ * \brief handler for \ref psa_connect.
+ *
+ * \param[in] sid RoT Service identity.
+ * \param[in] version The version of the RoT Service.
+ * \param[in] ns_caller If 'true', call from non-secure client.
+ * Otherwise from secure client.
+ *
+ * \retval PSA_SUCCESS Success.
+ * \retval PSA_ERROR_CONNECTION_REFUSED The SPM or RoT Service has refused the
+ * connection.
+ * \retval PSA_ERROR_CONNECTION_BUSY The SPM or RoT Service cannot make the
+ * connection at the moment.
+ * \retval "Does not return" The RoT Service ID and version are not
+ * supported, or the caller is not permitted to
+ * access the service.
+ */
+psa_status_t tfm_spm_client_psa_connect(uint32_t sid, uint32_t version,
+ bool ns_caller);
+
+/**
+ * \brief handler for \ref psa_call.
+ *
+ * \param[in] handle Service handle to the established connection,
+ * \ref psa_handle_t
+ * \param[in] type The request type.
+ * Must be zero( \ref PSA_IPC_CALL) or positive.
+ * \param[in] inptr Array of input psa_invec structures.
+ * \ref psa_invec
+ * \param[in] in_num Number of input psa_invec structures.
+ * \ref psa_invec
+ * \param[in] outptr Array of output psa_outvec structures.
+ * \ref psa_outvec
+ * \param[in] out_num Number of outut psa_outvec structures.
+ * \ref psa_outvec
+ * \param[in] ns_caller If 'true', call from non-secure client.
+ * Otherwise from secure client.
+ * \param[in] privileged Privileged mode or unprivileged mode:
+ * \ref TFM_PARTITION_UNPRIVILEGED_MODE
+ * \ref TFM_PARTITION_PRIVILEGED_MODE
+ *
+ * \retval PSA_SUCCESS Success.
+ * \retval "Does not return" The call is invalid, one or more of the
+ * following are true:
+ * \arg An invalid handle was passed.
+ * \arg The connection is already handling a request.
+ * \arg An invalid memory reference was provided.
+ * \arg in_num + out_num > PSA_MAX_IOVEC.
+ * \arg The message is unrecognized by the RoT
+ * Service or incorrectly formatted.
+ */
+psa_status_t tfm_spm_client_psa_call(psa_handle_t handle, int32_t type,
+ const psa_invec *inptr, size_t in_num,
+ psa_outvec *outptr, size_t out_num,
+ bool ns_caller, uint32_t privileged);
+
+/**
+ * \brief handler for \ref psa_close.
+ *
+ * \param[in] handle Service handle to the connection to be closed,
+ * \ref psa_handle_t
+ * \param[in] ns_caller If 'true', call from non-secure client.
+ * Otherwise from secure client.
+ *
+ * \retval void Success.
+ * \retval "Does not return" The call is invalid, one or more of the
+ * following are true:
+ * \arg An invalid handle was provided that is not
+ * the null handle.
+ * \arg The connection is handling a request.
+ */
+void tfm_spm_client_psa_close(psa_handle_t handle, bool ns_caller);
+
+#endif