Core: Add unpriv API to get caller client ID

Add tfm_core_get_caller_client_id(void) function to the unprivileged
TF-M core API. This function can only be called from secure partition.
Also add test case to test this function in the positive core test
suite.

Change-Id: I8dcd07b62f7bf8e43258695283cbb719a9357e48
Signed-off-by: Mate Toth-Pal <mate.toth-pal@arm.com>
diff --git a/secure_fw/core/tfm_handler.c b/secure_fw/core/tfm_handler.c
index 2e8b805..3085d7b 100644
--- a/secure_fw/core/tfm_handler.c
+++ b/secure_fw/core/tfm_handler.c
@@ -27,6 +27,9 @@
         tfm_core_validate_secure_caller_handler(const uint32_t svc_args[]);
 
 extern void
+        tfm_core_get_caller_client_id_handler(const uint32_t svc_args[]);
+
+extern void
         tfm_core_memory_permission_check_handler(const uint32_t svc_args[]);
 
 /* This SVC handler is called when a secure partition requests access to a
@@ -169,6 +172,9 @@
     case TFM_SVC_VALIDATE_SECURE_CALLER:
         tfm_core_validate_secure_caller_handler(svc_args);
         break;
+    case TFM_SVC_GET_CALLER_CLIENT_ID:
+        tfm_core_get_caller_client_id_handler(svc_args);
+        break;
     case TFM_SVC_MEMORY_CHECK:
         tfm_core_memory_permission_check_handler(svc_args);
         break;
diff --git a/secure_fw/core/tfm_secure_api.c b/secure_fw/core/tfm_secure_api.c
index 34af9a8..5491386 100644
--- a/secure_fw/core/tfm_secure_api.c
+++ b/secure_fw/core/tfm_secure_api.c
@@ -10,6 +10,7 @@
 #include <stdbool.h>
 #include "cmsis.h"
 #include "tfm_secure_api.h"
+#include "tfm_nspm.h"
 #include "secure_utilities.h"
 #include "uart_stdout.h"
 #include "secure_fw/spm/spm_api.h"
@@ -89,6 +90,8 @@
     uint32_t partition_state;
     uint32_t partition_flags;
     struct tfm_exc_stack_t *svc_ctx = (struct tfm_exc_stack_t *)psp;
+    uint32_t caller_partition_id;
+    int32_t client_id;
 
     /* Check partition idx validity */
     if (caller_partition_idx == SPM_INVALID_PARTITION_IDX) {
@@ -119,10 +122,11 @@
     curr_part_data = tfm_spm_partition_get_runtime_data(partition_idx);
     partition_state = curr_part_data->partition_state;
     partition_flags = tfm_spm_partition_get_flags(partition_idx);
+    caller_partition_id = tfm_spm_partition_get_partition_id(
+                                                          caller_partition_idx);
 
     if ((tfm_secure_api_initializing) &&
-        (tfm_spm_partition_get_partition_id(caller_partition_idx)
-            == TFM_SP_CORE_ID) &&
+        (caller_partition_id == TFM_SP_CORE_ID) &&
         (partition_state == SPM_PARTITION_STATE_UNINIT)) {
 #if TFM_LVL != 1
         /* Make thread mode unprivileged while untrusted partition init is
@@ -163,6 +167,18 @@
                                                caller_partition_idx);
     tfm_spm_partition_store_context(caller_partition_idx, psp, excReturn);
 
+    if ((caller_flags&SPM_PART_FLAG_SECURE)) {
+        tfm_spm_partition_set_caller_client_id(partition_idx,
+                                               caller_partition_id);
+    } else {
+        client_id = tfm_nspm_get_current_client_id();
+        if (client_id >= 0)
+        {
+            return TFM_SECURE_LOCK_FAILED;
+        }
+        tfm_spm_partition_set_caller_client_id(partition_idx, client_id);
+    }
+
 #if (TFM_LVL != 1) && (TFM_LVL != 2)
     /* Dynamic partitioning is only done is TFM level 3 */
     tfm_spm_partition_sandbox_deconfig(caller_partition_idx);
@@ -461,6 +477,115 @@
     }
     svc_args[0] = res;
 }
+/**
+ * \brief Check whether a buffer is ok for writing to by the privileged API
+ *        function.
+ *
+ * This function checks whether the caller partition owns the buffer, can write
+ * to it, and the buffer has proper alignment.
+ *
+ * \param[in] partition_idx     Partition index
+ * \param[in] start_addr        The start address of the buffer
+ * \param[in] len               The length of the buffer
+ * \param[in] alignment         The expected alignment (in bits)
+ *
+ * \return 1 if the check passes, 0 otherwise.
+ *
+ * \note For a 0 long buffer the check fails.
+ */
+static int32_t check_buffer_access(uint32_t partition_idx,
+                                  void* start_addr, size_t len,
+                                  uint32_t alignment)
+{
+    uintptr_t start_addr_value = (uintptr_t)start_addr;
+    uintptr_t end_addr_value = (uintptr_t)start_addr + len;
+    uintptr_t alignment_mask;
+
+    alignment_mask = (((uintptr_t)1) << alignment) - 1;
+
+    /* Check that the pointer is aligned properly */
+    if (start_addr_value & alignment_mask) {
+        /* not aligned, return error */
+        return 0;
+    }
+
+    /* Protect against overflow (and zero len) */
+    if (end_addr_value <= start_addr_value)
+    {
+        return 0;
+    }
+
+#if TFM_LVL == 1
+    /* For privileged partition execution, all secure data memory and stack
+     * is accessible
+     */
+    if (start_addr_value >= S_DATA_START &&
+        end_addr_value <= (S_DATA_START + S_DATA_SIZE)) {
+        return 1;
+    }
+#else
+    /* For non-privileged execution the partition's data and stack is
+     * accessible
+     */
+    if (start_addr_value >=
+            tfm_spm_partition_get_stack_bottom(partition_idx) &&
+        end_addr_value <=
+            tfm_spm_partition_get_stack_top(partition_idx)) {
+        return 1;
+    }
+    if (start_addr_value >=
+           tfm_spm_partition_get_rw_start(partition_idx) &&
+        end_addr_value <=
+           tfm_spm_partition_get_rw_limit(partition_idx)) {
+        return 1;
+    }
+    if (start_addr_value >=
+           tfm_spm_partition_get_zi_start(partition_idx) &&
+        end_addr_value <=
+           tfm_spm_partition_get_zi_limit(partition_idx)) {
+        return 1;
+    }
+#endif
+    return 0;
+}
+
+void tfm_core_get_caller_client_id_handler(uint32_t *svc_args)
+{
+    uintptr_t result_ptr_value = svc_args[0];
+    uint32_t running_partition_idx =
+            tfm_spm_partition_get_running_partition_idx();
+    const uint32_t running_partition_flags =
+            tfm_spm_partition_get_flags(running_partition_idx);
+    const struct spm_partition_runtime_data_t *curr_part_data =
+            tfm_spm_partition_get_runtime_data(running_partition_idx);
+    int res = 0;
+
+    if (!(running_partition_flags&SPM_PART_FLAG_SECURE))  {
+        /* This handler shouldn't be called from outside partition context.
+         * Partitions are only allowed to run while S domain is locked.
+         */
+        svc_args[0] = TFM_ERROR_INVALID_PARAMETER;
+        return;
+    }
+
+    /* Make sure that the output pointer points to a memory area that is owned
+     * by the partition
+     */
+    res = check_buffer_access(running_partition_idx,
+                              (void*)result_ptr_value,
+                              sizeof(curr_part_data->caller_client_id),
+                              2);
+    if (!res) {
+        /* Not in accessible range, return error */
+        svc_args[0] = TFM_ERROR_INVALID_PARAMETER;
+        return;
+    }
+
+    *((int32_t *)result_ptr_value) = curr_part_data->caller_client_id;
+
+    /* Store return value in r0 */
+    svc_args[0] = TFM_SUCCESS;
+}
 
 void tfm_core_memory_permission_check_handler(uint32_t *svc_args)
 {
diff --git a/secure_fw/core/tfm_secure_api.h b/secure_fw/core/tfm_secure_api.h
index 3d4a9ae..25e76f8 100644
--- a/secure_fw/core/tfm_secure_api.h
+++ b/secure_fw/core/tfm_secure_api.h
@@ -73,6 +73,8 @@
 
 extern int32_t tfm_core_validate_secure_caller(void);
 
+extern int32_t tfm_core_get_caller_client_id(int32_t *caller_client_id);
+
 extern int32_t tfm_core_memory_permission_check(
         void *ptr, uint32_t size, int32_t access);
 
diff --git a/secure_fw/core/tfm_svc.h b/secure_fw/core/tfm_svc.h
index 419ee66..9c4d1a5 100644
--- a/secure_fw/core/tfm_svc.h
+++ b/secure_fw/core/tfm_svc.h
@@ -14,6 +14,7 @@
     TFM_SVC_SFN_REQUEST = 0,
     TFM_SVC_SFN_RETURN,
     TFM_SVC_VALIDATE_SECURE_CALLER,
+    TFM_SVC_GET_CALLER_CLIENT_ID,
     TFM_SVC_MEMORY_CHECK,
     TFM_SVC_SET_SHARE_AREA,
     TFM_SVC_PRINT,
diff --git a/secure_fw/core/tfm_unpriv_api.c b/secure_fw/core/tfm_unpriv_api.c
index ceac806..6817c36 100644
--- a/secure_fw/core/tfm_unpriv_api.c
+++ b/secure_fw/core/tfm_unpriv_api.c
@@ -109,6 +109,15 @@
 }
 
 __attribute__((naked))
+int32_t tfm_core_get_caller_client_id(int32_t *caller_client_id)
+{
+    __ASM(
+        "SVC %0\n"
+        "BX LR\n"
+        : : "I" (TFM_SVC_GET_CALLER_CLIENT_ID));
+}
+
+__attribute__((naked))
 int32_t tfm_core_validate_secure_caller(void)
 {
     __ASM(
diff --git a/secure_fw/spm/spm_api.c b/secure_fw/spm/spm_api.c
index ff68245..77bf5be 100644
--- a/secure_fw/spm/spm_api.c
+++ b/secure_fw/spm/spm_api.c
@@ -210,6 +210,30 @@
     return g_spm_partition_db.partitions[partition_idx].memory_data.stack_top;
 }
 
+uint32_t tfm_spm_partition_get_zi_start(uint32_t partition_idx)
+{
+    return g_spm_partition_db.partitions[partition_idx].
+            memory_data.zi_start;
+}
+
+uint32_t tfm_spm_partition_get_zi_limit(uint32_t partition_idx)
+{
+    return g_spm_partition_db.partitions[partition_idx].
+            memory_data.zi_limit;
+}
+
+uint32_t tfm_spm_partition_get_rw_start(uint32_t partition_idx)
+{
+    return g_spm_partition_db.partitions[partition_idx].
+            memory_data.rw_start;
+}
+
+uint32_t tfm_spm_partition_get_rw_limit(uint32_t partition_idx)
+{
+    return g_spm_partition_db.partitions[partition_idx].
+            memory_data.rw_limit;
+}
+
 void tfm_spm_partition_set_stack(uint32_t partition_idx, uint32_t stack_ptr)
 {
     g_spm_partition_db.partitions[partition_idx].
@@ -260,6 +284,13 @@
             caller_partition_idx = caller_partition_idx;
 }
 
+void tfm_spm_partition_set_caller_client_id(uint32_t partition_idx,
+                                            int32_t caller_client_id)
+{
+    g_spm_partition_db.partitions[partition_idx].runtime_data.
+            caller_client_id = caller_client_id;
+}
+
 enum spm_err_t tfm_spm_partition_set_share(uint32_t partition_idx,
                                            uint32_t share)
 {
diff --git a/secure_fw/spm/spm_api.h b/secure_fw/spm/spm_api.h
index 67cbfbf..f0034af 100644
--- a/secure_fw/spm/spm_api.h
+++ b/secure_fw/spm/spm_api.h
@@ -42,6 +42,7 @@
 struct spm_partition_runtime_data_t {
     uint32_t partition_state;
     uint32_t caller_partition_idx;
+    int32_t caller_client_id;
     uint32_t share;
     uint32_t stack_ptr;
     uint32_t lr;
@@ -125,6 +126,52 @@
 uint32_t tfm_spm_partition_get_flags(uint32_t partition_idx);
 
 /**
+ * \brief Get the start of the zero-initialised region for a partition
+ *
+ * \param[in] partition_idx     Partition idx
+ *
+ * \return Start of the zero-initialised region
+ *
+ * \note This function doesn't check if partition_idx is valid.
+ */
+uint32_t tfm_spm_partition_get_zi_start(uint32_t partition_idx);
+
+/**
+ * \brief Get the limit of the zero-initialised region for a partition
+ *
+ * \param[in] partition_idx     Partition idx
+ *
+ * \return Limit of the zero-initialised region
+ *
+ * \note This function doesn't check if partition_idx is valid.
+ * \note The address returned is not part of the region.
+ */
+uint32_t tfm_spm_partition_get_zi_limit(uint32_t partition_idx);
+
+/**
+ * \brief Get the start of the read-write region for a partition
+ *
+ * \param[in] partition_idx     Partition idx
+ *
+ * \return Start of the read-write region
+ *
+ * \note This function doesn't check if partition_idx is valid.
+ */
+uint32_t tfm_spm_partition_get_rw_start(uint32_t partition_idx);
+
+/**
+ * \brief Get the limit of the read-write region for a partition
+ *
+ * \param[in] partition_idx     Partition idx
+ *
+ * \return Limit of the read-write region
+ *
+ * \note This function doesn't check if partition_idx is valid.
+ * \note The address returned is not part of the region.
+ */
+uint32_t tfm_spm_partition_get_rw_limit(uint32_t partition_idx);
+
+/**
  * \brief Get the current runtime data of a partition
  *
  * \param[in] partition_idx     Partition index
@@ -189,6 +236,17 @@
                                                 uint32_t caller_partition_idx);
 
 /**
+* \brief Set the caller client ID for a given partition
+*
+* \param[in] partition_idx        Partition index
+* \param[in] caller_client_id     The ID of the calling client
+*
+* \note This function doesn't check if any of the partition_idxs are valid.
+*/
+void tfm_spm_partition_set_caller_client_id(uint32_t partition_idx,
+                                            int32_t caller_client_id);
+
+/**
  * \brief Set the buffer share region of the partition
  *
  * \param[in] partition_idx  Partition index