Test: Add IPC test service

This is the first version for IPC secure function test service:
- Only psa_wait_any(), psa_get(), psa_read(), psa_write() and
  psa_end() are used now.
- Only support "block" mode for psa_wait_any()
Note:
- Remaining PSA functions TBD
- "Poll" mode for psa_wait_any() TBD

Change-Id: I882716075b9c92839d41372df355c364d24d7574
Signed-off-by: Edison Ai <edison.ai@arm.com>
diff --git a/secure_fw/core/tfm_thread.c b/secure_fw/core/tfm_thread.c
index 6ab0483..58bca8e 100644
--- a/secure_fw/core/tfm_thread.c
+++ b/secure_fw/core/tfm_thread.c
@@ -123,18 +123,3 @@
 
     return 0;
 }
-
-/* Test thread -- MOVE TO SP ! */
-static void *test_thread(void *param)
-{
-    int local_a = 0;
-
-    while (1) {
-        printf("%d times running !\n", local_a++);
-        tfm_thread_schedule();
-    }
-
-    return NULL;
-}
-
-REGISTER_TFM_THREAD(test_thread, (void *)0x1, 1024);
diff --git a/test/test_services/CMakeLists.inc b/test/test_services/CMakeLists.inc
index 42c309c..85eda32 100644
--- a/test/test_services/CMakeLists.inc
+++ b/test/test_services/CMakeLists.inc
@@ -62,6 +62,13 @@
 	list(APPEND ALL_SRC_C_NS "${CORE_TEST_DIR}/tfm_secure_client_service/tfm_secure_client_service_api.c")
 endif()
 
+if (NOT DEFINED CORE_TEST_IPC)
+	message(FATAL_ERROR "Incomplete build configuration: CORE_TEST_IPC is undefined. ")
+elseif (CORE_TEST_IPC)
+	list(APPEND ALL_SRC_C_S "${CORE_TEST_DIR}/tfm_ipc_test/ipc_test_service.c"
+		)
+endif()
+
 embedded_include_directories(PATH ${TFM_ROOT_DIR} ABSOLUTE)
 embedded_include_directories(PATH ${TFM_ROOT_DIR}/interface/include ABSOLUTE)
 
diff --git a/test/test_services/tfm_ipc_test/ipc_test_service.c b/test/test_services/tfm_ipc_test/ipc_test_service.c
new file mode 100644
index 0000000..ab1a1f1
--- /dev/null
+++ b/test/test_services/tfm_ipc_test/ipc_test_service.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include "psa_client.h"
+#include "psa_service.h"
+#include "secure_fw/core/tfm_thread.h"
+#include "secure_fw/core/secure_utilities.h"
+#include "secure_fw/core/tfm_secure_api.h"
+#include "tfm_api.h"
+
+#define IPC_SERVICE_BUFFER_LEN 256
+#define PSA_IPC_SIGNAL 1
+
+static int inuse = 0;
+
+static int ipc_service_connect(psa_msg_t *msg)
+{
+    uint32_t minor_version;
+
+    if (msg->in_size[0] == 0) {
+        return PSA_CONNECTION_REFUSED;
+    }
+
+    psa_read(msg->handle, 0, &minor_version, sizeof(minor_version));
+    printf("Requested minor version for service1 connect: %d.\r\n",
+           minor_version);
+
+    /* notify client if accepted or refused */
+    if (minor_version == 0) {
+        return PSA_CONNECTION_REFUSED;
+    }
+    return PSA_CONNECTION_ACCEPTED;
+}
+
+static psa_error_t ipc_service_call(psa_msg_t *msg)
+{
+    int i;
+    uint8_t rec_buf[IPC_SERVICE_BUFFER_LEN];
+    uint8_t send_buf[IPC_SERVICE_BUFFER_LEN] = "It is just for IPC call test.";
+
+    for (i = 0; i < PSA_MAX_IOVEC; i++) {
+        if (msg->in_size[i] != 0) {
+            psa_read(msg->handle, i, rec_buf, IPC_SERVICE_BUFFER_LEN);
+            printf("receive buffer index is %d, data is %s.\r\n", i,
+                                            (char *)rec_buf);
+        }
+        if (msg->out_size[i] != 0) {
+            psa_write(msg->handle, i, send_buf, IPC_SERVICE_BUFFER_LEN);
+        }
+    }
+    return PSA_SUCCESS;
+}
+
+/* Test thread */
+static void *ipc_test_partition_main(void *param)
+{
+    uint32_t signals = 0;
+    psa_msg_t msg;
+    int r;
+
+    while (1) {
+        signals = psa_wait_any(PSA_BLOCK);
+
+        printf("ipc get signals 0x%x\r\n", signals);
+
+        if (signals & PSA_IPC_SIGNAL) {
+            psa_get(PSA_IPC_SIGNAL, &msg);
+            switch (msg.type) {
+            case PSA_IPC_CONNECT:
+                if (inuse) {
+                    r = PSA_CONNECTION_REFUSED;
+                } else {
+                    inuse = 1;
+                    r = ipc_service_connect(&msg);
+                }
+                psa_end(msg.handle, r);
+                break;
+            case PSA_IPC_CALL:
+                psa_end(msg.handle, ipc_service_call(&msg));
+                break;
+            case PSA_IPC_DISCONNECT:
+                assert (inuse == 1);
+                inuse = 0;
+                psa_end(msg.handle, PSA_SUCCESS);
+                break;
+            default:
+                /* cannot get here? [broken SPM]. TODO*/
+                break;
+            }
+        }
+    }
+
+    return NULL;
+}
+
+REGISTER_TFM_THREAD(ipc_test_partition_main, (void *)0x1, 1024);