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/platform/ext/target/mps2/an519/partition/region_defs.h b/platform/ext/target/mps2/an519/partition/region_defs.h
index 0dfd62a..dbf55aa 100644
--- a/platform/ext/target/mps2/an519/partition/region_defs.h
+++ b/platform/ext/target/mps2/an519/partition/region_defs.h
@@ -67,7 +67,7 @@
 #define IMAGE_CODE_SIZE \
             (FLASH_PARTITION_SIZE - BL2_HEADER_SIZE - BL2_TRAILER_SIZE)
 
-#define CMSE_VENEER_REGION_SIZE     (0x000000E0)
+#define CMSE_VENEER_REGION_SIZE     (0x00000100)
 
 /* Use SRAM1 memory to store Code data */
 #define S_ROM_ALIAS_BASE  (0x10000000)
diff --git a/platform/ext/target/mps2/an521/partition/region_defs.h b/platform/ext/target/mps2/an521/partition/region_defs.h
index 82d96b6..3487d2f 100644
--- a/platform/ext/target/mps2/an521/partition/region_defs.h
+++ b/platform/ext/target/mps2/an521/partition/region_defs.h
@@ -67,7 +67,7 @@
 #define IMAGE_CODE_SIZE \
             (FLASH_PARTITION_SIZE - BL2_HEADER_SIZE - BL2_TRAILER_SIZE)
 
-#define CMSE_VENEER_REGION_SIZE     (0x000000E0)
+#define CMSE_VENEER_REGION_SIZE     (0x00000100)
 
 /* Use SRAM1 memory to store Code data */
 #define S_ROM_ALIAS_BASE  (0x10000000)
diff --git a/platform/ext/target/musca_a/partition/region_defs.h b/platform/ext/target/musca_a/partition/region_defs.h
index 203ef74..013a76d 100755
--- a/platform/ext/target/musca_a/partition/region_defs.h
+++ b/platform/ext/target/musca_a/partition/region_defs.h
@@ -51,7 +51,7 @@
 #define IMAGE_CODE_SIZE \
             (FLASH_PARTITION_SIZE - BL2_HEADER_SIZE - BL2_TRAILER_SIZE)
 
-#define CMSE_VENEER_REGION_SIZE     (0x000000E0)
+#define CMSE_VENEER_REGION_SIZE     (0x00000100)
 
 /*
  * Since we enable/disable flash during s/ns code copy to code sram we cannot
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
diff --git a/test/suites/core/non_secure/core_ns_positive_testsuite.c b/test/suites/core/non_secure/core_ns_positive_testsuite.c
index ac5711b..9accf53 100644
--- a/test/suites/core/non_secure/core_ns_positive_testsuite.c
+++ b/test/suites/core/non_secure/core_ns_positive_testsuite.c
@@ -33,6 +33,7 @@
 static void tfm_core_test_share_change(struct test_result_t *ret);
 static void tfm_core_test_ss_to_ss_buffer(struct test_result_t *ret);
 static void tfm_core_test_peripheral_access(struct test_result_t *ret);
+static void tfm_core_test_get_caller_client_id(struct test_result_t *ret);
 
 static struct test_t core_tests[] = {
 CORE_TEST_DESCRIPTION(CORE_TEST_ID_NS_THREAD, tfm_core_test_ns_thread,
@@ -59,6 +60,9 @@
 CORE_TEST_DESCRIPTION(CORE_TEST_ID_PERIPHERAL_ACCESS,
     tfm_core_test_peripheral_access,
     "Test service peripheral access"),
+CORE_TEST_DESCRIPTION(CORE_TEST_ID_GET_CALLER_CLIENT_ID,
+    tfm_core_test_get_caller_client_id,
+    "Test get caller client ID function"),
 };
 
 void register_testsuite_ns_core_positive(struct test_suite_t *p_test_suite)
@@ -330,3 +334,23 @@
         return;
     }
 }
+
+static void tfm_core_test_get_caller_client_id(struct test_result_t *ret)
+{
+    int32_t err;
+
+    args[0] = CORE_TEST_ID_GET_CALLER_CLIENT_ID;
+    err = tfm_core_test_call(tfm_core_test_sfn, args);
+
+    if (err != TFM_SUCCESS && err < TFM_PARTITION_SPECIFIC_ERROR_MIN) {
+        TEST_FAIL("Call to secure service should be successful.");
+        return;
+    }
+
+    if (err != CORE_TEST_ERRNO_SUCCESS) {
+        TEST_FAIL("The internal service call failed.");
+        return;
+    }
+
+    ret->val = TEST_PASSED;
+}
diff --git a/test/test_services/tfm_core_test/core_test_defs.h b/test/test_services/tfm_core_test/core_test_defs.h
index 4b36a44..9a44972 100644
--- a/test/test_services/tfm_core_test/core_test_defs.h
+++ b/test/test_services/tfm_core_test/core_test_defs.h
@@ -17,17 +17,18 @@
 #include "tfm_api.h"
 
 /* These definitions are used in symbols, only digits are permitted */
-#define CORE_TEST_ID_NS_THREAD          1001
-#define CORE_TEST_ID_CHECK_INIT         1003
-#define CORE_TEST_ID_RECURSION          1004
-#define CORE_TEST_ID_MEMORY_PERMISSIONS 1005
-#define CORE_TEST_ID_MPU_ACCESS         1006
-#define CORE_TEST_ID_BUFFER_CHECK       1007
-#define CORE_TEST_ID_SS_TO_SS           1008
-#define CORE_TEST_ID_SHARE_REDIRECTION  1009
-#define CORE_TEST_ID_SS_TO_SS_BUFFER    1010
-#define CORE_TEST_ID_PERIPHERAL_ACCESS  1012
-#define CORE_TEST_ID_BLOCK              2001
+#define CORE_TEST_ID_NS_THREAD            1001
+#define CORE_TEST_ID_CHECK_INIT           1003
+#define CORE_TEST_ID_RECURSION            1004
+#define CORE_TEST_ID_MEMORY_PERMISSIONS   1005
+#define CORE_TEST_ID_MPU_ACCESS           1006
+#define CORE_TEST_ID_BUFFER_CHECK         1007
+#define CORE_TEST_ID_SS_TO_SS             1008
+#define CORE_TEST_ID_SHARE_REDIRECTION    1009
+#define CORE_TEST_ID_SS_TO_SS_BUFFER      1010
+#define CORE_TEST_ID_PERIPHERAL_ACCESS    1012
+#define CORE_TEST_ID_GET_CALLER_CLIENT_ID 1013
+#define CORE_TEST_ID_BLOCK                2001
 
 /* Use lower 16 bits in return value for error code, upper 16 for line number
  * in test service
diff --git a/test/test_services/tfm_core_test/tfm_ss_core_test.c b/test/test_services/tfm_core_test/tfm_ss_core_test.c
index f10fd8c..27e1ea9 100644
--- a/test/test_services/tfm_core_test/tfm_ss_core_test.c
+++ b/test/test_services/tfm_core_test/tfm_ss_core_test.c
@@ -13,11 +13,27 @@
 #include "test/test_services/tfm_core_test_2/tfm_ss_core_test_2_veneers.h"
 #include "secure_fw/core/secure_utilities.h"
 #include "tfm_secure_api.h"
+#include "spm_partition_defs.h"
 
 #include "smm_mps2.h"
 
 static int32_t partition_init_done;
 
+#define INVALID_NS_CLIENT_ID  0x49abcdef
+#define EXPECTED_NS_CLIENT_ID (-1)
+
+/* Don't initialise caller_partition_id_zi and expect it to be linked in the
+ * zero-initialised data area
+ */
+static int32_t caller_client_id_zi;
+
+/* Initialise caller_partition_id_rw and expect it to be linked in the
+ * read-write data area
+ */
+static int32_t caller_client_id_rw = INVALID_NS_CLIENT_ID;
+
+static int32_t* invalid_addresses [] = {(int32_t*)0x0, (int32_t*)0xFFF12000};
+
 int32_t core_test_init(void)
 {
     partition_init_done = 1;
@@ -252,6 +268,49 @@
     }
 }
 
+static int32_t test_get_caller_client_id(void)
+{
+    /* Call to a special service that checks the caller service ID */
+    size_t i;
+    int32_t ret;
+    int32_t caller_client_id_stack = INVALID_NS_CLIENT_ID;
+
+    caller_client_id_zi = INVALID_NS_CLIENT_ID;
+
+    ret = tfm_core_test_2_check_caller_client_id();
+    if (ret != TFM_SUCCESS) {
+        return CORE_TEST_ERRNO_SLAVE_SP_CALL_FAILURE;
+    }
+
+    /* test with invalid output pointers */
+    for (i = 0; i < sizeof(invalid_addresses)/sizeof(invalid_addresses[0]); ++i)
+    {
+        ret = tfm_core_get_caller_client_id(invalid_addresses[i]);
+        if (ret != TFM_ERROR_INVALID_PARAMETER) {
+            return CORE_TEST_ERRNO_TEST_FAULT;
+        }
+    }
+
+    /* test with valid output pointers */
+    ret = tfm_core_get_caller_client_id(&caller_client_id_zi);
+    if (ret != TFM_SUCCESS || caller_client_id_zi != EXPECTED_NS_CLIENT_ID) {
+        return CORE_TEST_ERRNO_TEST_FAULT;
+    }
+
+    ret = tfm_core_get_caller_client_id(&caller_client_id_rw);
+    if (ret != TFM_SUCCESS || caller_client_id_rw != EXPECTED_NS_CLIENT_ID) {
+        return CORE_TEST_ERRNO_TEST_FAULT;
+    }
+
+    ret = tfm_core_get_caller_client_id(&caller_client_id_stack);
+    if (ret != TFM_SUCCESS ||
+            caller_client_id_stack != EXPECTED_NS_CLIENT_ID) {
+        return CORE_TEST_ERRNO_TEST_FAULT;
+    }
+
+    return TFM_SUCCESS;
+}
+
 #ifdef CORE_TEST_INTERACTIVE
 #define MPS2_USERPB0_BASE   (0x50302008)
 #define MPS2_USERPB0_MASK   (0x1)
@@ -315,6 +374,8 @@
         return test_ss_to_ss_buffer((uint32_t *)arg1, (uint32_t *)arg2, arg3);
     case CORE_TEST_ID_PERIPHERAL_ACCESS:
         return test_peripheral_access();
+    case CORE_TEST_ID_GET_CALLER_CLIENT_ID:
+        return test_get_caller_client_id();
     case CORE_TEST_ID_BLOCK:
         return test_block(arg1, arg2, arg3);
     case CORE_TEST_ID_NS_THREAD:
diff --git a/test/test_services/tfm_core_test_2/manifest.yaml b/test/test_services/tfm_core_test_2/manifest.yaml
index cb4230e..8cf94c1 100644
--- a/test/test_services/tfm_core_test_2/manifest.yaml
+++ b/test/test_services/tfm_core_test_2/manifest.yaml
@@ -31,6 +31,14 @@
       "non_secure_clients": true,
       "minor_version": 1,
       "minor_policy": "strict"
+    },
+    {
+      "sfid": "TFM_CORE_TEST_2_SFN_CHECK_CALLER_CLIENT_ID_SFID",
+      "signal": "TFM_CORE_TEST_2_SFN_CHECK_CALLER_CLIENT_ID",
+      "tfm_symbol": "spm_core_test_2_check_caller_client_id",
+      "non_secure_clients": true,
+      "minor_version": 1,
+      "minor_policy": "strict"
     }
   ],
   "source_files": [
diff --git a/test/test_services/tfm_core_test_2/tfm_ss_core_test_2.c b/test/test_services/tfm_core_test_2/tfm_ss_core_test_2.c
index 165fbfe..052c0b0 100644
--- a/test/test_services/tfm_core_test_2/tfm_ss_core_test_2.c
+++ b/test/test_services/tfm_core_test_2/tfm_ss_core_test_2.c
@@ -9,8 +9,23 @@
 #include "tfm_ss_core_test_2.h"
 #include "tfm_api.h"
 #include "tfm_secure_api.h"
+#include "spm_partition_defs.h"
 #include "test/test_services/tfm_core_test/core_test_defs.h"
 
+#define INVALID_NS_CLIENT_ID  0x49abcdef
+
+/* Don't initialise caller_partition_id_zi and expect it to be linked in the
+ * zero-initialised data area
+ */
+static int32_t caller_client_id_zi;
+
+/* Initialise caller_partition_id_rw and expect it to be linked in the
+ * read-write data area
+ */
+static int32_t caller_client_id_rw = INVALID_NS_CLIENT_ID;
+
+static int32_t* invalid_addresses [] = {(int32_t*)0x0, (int32_t*)0xFFF12000};
+
 /* FIXME: Add a testcase to test that a failed init makes the secure partition
  * closed, and none of its functions can be called.
  * A new test service for this purpose is to be added.
@@ -25,6 +40,43 @@
     return TFM_SUCCESS;
 }
 
+int32_t spm_core_test_2_check_caller_client_id(void)
+{
+    size_t i;
+    int32_t caller_client_id_stack = INVALID_NS_CLIENT_ID;
+    int32_t ret;
+
+    caller_client_id_zi = INVALID_NS_CLIENT_ID;
+
+    /* test with invalid output pointers */
+    for (i = 0; i < sizeof(invalid_addresses)/sizeof(invalid_addresses[0]); ++i)
+    {
+        ret = tfm_core_get_caller_client_id(invalid_addresses[i]);
+        if (ret != TFM_ERROR_INVALID_PARAMETER) {
+            return CORE_TEST_ERRNO_TEST_FAULT;
+        }
+    }
+
+    /* test with valid output pointers */
+    ret = tfm_core_get_caller_client_id(&caller_client_id_zi);
+    if (ret != TFM_SUCCESS || caller_client_id_zi != TFM_SP_CORE_TEST_ID) {
+        return CORE_TEST_ERRNO_TEST_FAULT;
+    }
+
+    ret = tfm_core_get_caller_client_id(&caller_client_id_rw);
+    if (ret != TFM_SUCCESS || caller_client_id_rw != TFM_SP_CORE_TEST_ID) {
+        return CORE_TEST_ERRNO_TEST_FAULT;
+    }
+
+    ret = tfm_core_get_caller_client_id(&caller_client_id_stack);
+    if (ret != TFM_SUCCESS ||
+            caller_client_id_stack != TFM_SP_CORE_TEST_ID) {
+        return CORE_TEST_ERRNO_TEST_FAULT;
+    }
+
+    return TFM_SUCCESS;
+}
+
 /* Invert function */
 #define SFN_INVERT_MAX_LEN 128
 
diff --git a/test/test_services/tfm_core_test_2/tfm_ss_core_test_2.h b/test/test_services/tfm_core_test_2/tfm_ss_core_test_2.h
index 8981694..e1d7bc0 100644
--- a/test/test_services/tfm_core_test_2/tfm_ss_core_test_2.h
+++ b/test/test_services/tfm_core_test_2/tfm_ss_core_test_2.h
@@ -36,6 +36,16 @@
 int32_t spm_core_test_2_sfn_invert(
         int32_t *res_ptr, uint32_t *in_ptr, uint32_t *out_ptr, int32_t len);
 
+/**
+ * \brief A minimal test secure function to be called from another partition.
+ *
+ * Checks the functionality of querying the client ID of the caller service.
+ *
+ * \return Returns \ref TFM_SUCCESS on success, \ref CORE_TEST_ERRNO_TEST_FAULT
+ *         othervise.
+ */
+int32_t spm_core_test_2_check_caller_client_id(void);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/test/test_services/tfm_core_test_2/tfm_ss_core_test_2_veneers.c b/test/test_services/tfm_core_test_2/tfm_ss_core_test_2_veneers.c
index 6ac3585..3ec7e38 100644
--- a/test/test_services/tfm_core_test_2/tfm_ss_core_test_2_veneers.c
+++ b/test/test_services/tfm_core_test_2/tfm_ss_core_test_2_veneers.c
@@ -27,3 +27,11 @@
                              spm_core_test_2_sfn_invert,
                              res_ptr, in_ptr, out_ptr, len);
 }
+
+__tfm_secure_gateway_attributes__
+int32_t tfm_core_test_2_check_caller_client_id(void)
+{
+   TFM_CORE_SFN_REQUEST(TFM_SP_CORE_TEST_2_ID,
+                            spm_core_test_2_check_caller_client_id,
+                            0, 0, 0, 0);
+}
diff --git a/test/test_services/tfm_core_test_2/tfm_ss_core_test_2_veneers.h b/test/test_services/tfm_core_test_2/tfm_ss_core_test_2_veneers.h
index a483a98..b556bdd 100644
--- a/test/test_services/tfm_core_test_2/tfm_ss_core_test_2_veneers.h
+++ b/test/test_services/tfm_core_test_2/tfm_ss_core_test_2_veneers.h
@@ -36,6 +36,17 @@
                                    uint32_t *in_ptr,
                                    uint32_t *out_ptr,
                                    int32_t len);
+
+/**
+ * \brief A minimal test secure function to be called from another partition.
+ *
+ * Checks the functionality of querying the client ID of the caller service.
+ *
+ * \return Returns \ref TFM_SUCCESS on success, \ref CORE_TEST_ERRNO_TEST_FAULT
+ *         othervise.
+ */
+int32_t tfm_core_test_2_check_caller_client_id(void);
+
 #ifdef __cplusplus
 }
 #endif