xtest: SPMC Add basic test

Add a first FF-A test. It will test the basic functionality of the FF-A
messages.
The tests are added to a new test suite(ffa_spmc) and can be run with the:
xtest -t ffa_spmc command

The corresponding SPs can be found in the trusted-services repo.

Currently the TS debugfs driver is needed to communicate with the SPs. It
can be loaded by running the
/mnt/host/out/linux-arm-ffa-user/load_module.sh script which is created
as part of the linux-arm-ffa-user target.

Signed-off-by: Jelle Sels <jelle.sels@arm.com>
Acked-by: Jerome Forissier <jerome.forissier@linaro.org>
diff --git a/host/xtest/CMakeLists.txt b/host/xtest/CMakeLists.txt
index 040043a..320d336 100644
--- a/host/xtest/CMakeLists.txt
+++ b/host/xtest/CMakeLists.txt
@@ -62,6 +62,9 @@
 	xtest_test.c
 	xtest_uuid_helpers.c
 )
+if(CFG_SECURE_PARTITION AND CFG_SPMC_TESTS)
+	list (APPEND SRC ffa_spmc_1000.c)
+endif()
 
 if (WITH_GP_TESTS)
 	add_compile_options(-DWITH_GP_TESTS=1)
diff --git a/host/xtest/Makefile b/host/xtest/Makefile
index a898c57..af45f5e 100644
--- a/host/xtest/Makefile
+++ b/host/xtest/Makefile
@@ -83,6 +83,10 @@
 	xtest_test.c \
 	xtest_uuid_helpers.c
 
+ifeq ($(CFG_SECURE_PARTITION)-$(CFG_SPMC_TESTS),y-y)
+srcs += ffa_spmc_1000.c
+endif
+
 ifeq ($(CFG_SECSTOR_TA_MGMT_PTA),y)
 srcs += install_ta.c
 endif
diff --git a/host/xtest/ffa_spmc_1000.c b/host/xtest/ffa_spmc_1000.c
new file mode 100644
index 0000000..acbd8ea
--- /dev/null
+++ b/host/xtest/ffa_spmc_1000.c
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Copyright (c) 2022, Arm Limited and Contributors. All rights reserved.
+ */
+#include <fcntl.h>
+#include <ffa.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include "include/uapi/linux/arm_ffa_user.h"
+#include "xtest_helpers.h"
+#include "xtest_test.h"
+
+#define FFA_DRIVER_FS_PATH	"/sys/kernel/debug/arm_ffa_user"
+#define SPMC_TEST_OK 0xaa
+
+enum sp_tests {
+	EP_TEST_SP,
+	EP_TEST_SP_COMMUNICATION,
+};
+
+static int ffa_fd = -1;
+
+static const char test_endpoint1_uuid[] =
+	"5c9edbc3-7b3a-4367-9f83-7c191ae86a37";
+static const char test_endpoint2_uuid[] =
+	"7817164c-c40c-4d1a-867a-9bb2278cf41a";
+static const char test_endpoint3_uuid[] =
+	"23eb0100-e32a-4497-9052-2f11e584afa6";
+
+static struct ffa_ioctl_ep_desc test_endpoint1 = {
+	.uuid_ptr = (uint64_t) test_endpoint1_uuid,
+};
+
+static struct ffa_ioctl_ep_desc test_endpoint2 = {
+	.uuid_ptr = (uint64_t) test_endpoint2_uuid,
+};
+
+static struct ffa_ioctl_ep_desc test_endpoint3 = {
+	.uuid_ptr = (uint64_t) test_endpoint3_uuid,
+};
+
+static void close_debugfs(void)
+{
+	int err = 0;
+
+	if (ffa_fd >= 0) {
+		err = close(ffa_fd);
+		if (err < 0)
+			Do_ADBG_Log("Error: Could not close the FF-A driver");
+	}
+	ffa_fd = -1;
+}
+
+static bool init_sp_xtest(ADBG_Case_t *c)
+{
+	if (ffa_fd < 0) {
+		ffa_fd = open(FFA_DRIVER_FS_PATH, O_RDWR);
+		if (ffa_fd < 0) {
+			Do_ADBG_Log("Error: Could not open the FF-A driver");
+			return false;
+		}
+	}
+	return true;
+}
+
+static int start_sp_test(uint16_t endpoint, enum sp_tests test,
+			 struct ffa_ioctl_msg_args *args)
+{
+	args->dst_id = endpoint;
+	args->args[0] = (uint32_t)test;
+	return ioctl(ffa_fd, FFA_IOC_MSG_SEND, args);
+}
+
+static uint16_t get_endpoint_id(uint64_t endp)
+{
+	struct ffa_ioctl_ep_desc sid = { .uuid_ptr = endp };
+
+	/* Get ID of destination SP based on UUID */
+	if(ioctl(ffa_fd, FFA_IOC_GET_PART_ID, &sid))
+		return 0xffff;
+
+	return sid.id;
+}
+
+static void xtest_ffa_spmc_test_1001(ADBG_Case_t *c)
+{
+	struct ffa_ioctl_msg_args args = { 0 };
+	uint16_t endpoint1_id = 0;
+	uint16_t endpoint2_id = 0;
+	int rc = 0;
+
+	Do_ADBG_BeginSubCase(c, "SP1 comms check");
+	if (!init_sp_xtest(c)) {
+		Do_ADBG_Log("Failed to initialise test, skipping SP test");
+		goto out;
+	}
+
+	endpoint1_id = get_endpoint_id(test_endpoint1.uuid_ptr);
+	if (endpoint1_id == 0xffff) {
+		Do_ADBG_Log("Could not contact xtest_1 sp, skipping SP test");
+		Do_ADBG_Log("Add xtest_1 sp to the image to enable tests");
+		goto out;
+	}
+
+	memset(&args, 0, sizeof(args));
+	rc = start_sp_test(endpoint1_id, EP_TEST_SP, &args);
+	if (!ADBG_EXPECT_COMPARE_SIGNED(c, rc, ==, 0))
+		goto out;
+
+	if (!ADBG_EXPECT_COMPARE_UNSIGNED(c, args.args[0], ==, SPMC_TEST_OK))
+		goto out;
+	Do_ADBG_EndSubCase(c, "SP1 comms check");
+
+	Do_ADBG_BeginSubCase(c, "Sp2 comms check");
+	endpoint2_id = get_endpoint_id(test_endpoint2.uuid_ptr);
+	if (endpoint2_id == 0xffff) {
+		Do_ADBG_Log("Could not contact xtest_2 sp, skipping SP test");
+		Do_ADBG_Log("Add xtest_2 sp to the image to enable tests");
+		goto out;
+	}
+
+	memset(&args, 0, sizeof(args));
+	rc = start_sp_test(endpoint2_id, EP_TEST_SP, &args);
+	if (!ADBG_EXPECT_COMPARE_SIGNED(c, rc, ==, 0))
+		goto out;
+
+	if (!ADBG_EXPECT_COMPARE_UNSIGNED(c, args.args[0], ==, SPMC_TEST_OK))
+		goto out;
+	Do_ADBG_EndSubCase(c, "Sp2 comms check");
+
+	/* Test SP to SP messaging. */
+	Do_ADBG_BeginSubCase(c, "SP to SP messaging check");
+	memset(&args, 0, sizeof(args));
+	args.args[1] = endpoint2_id;
+
+	rc = start_sp_test(endpoint1_id, EP_TEST_SP_COMMUNICATION, &args);
+	ADBG_EXPECT_COMPARE_SIGNED(c, rc, ==, 0);
+	ADBG_EXPECT_COMPARE_UNSIGNED(c, args.args[0], ==, SPMC_TEST_OK);
+
+	memset(&args, 0, sizeof(args));
+	args.args[1] = endpoint1_id;
+
+	rc = start_sp_test(endpoint2_id, EP_TEST_SP_COMMUNICATION, &args);
+	ADBG_EXPECT_COMPARE_SIGNED(c, rc, ==, 0);
+	ADBG_EXPECT_COMPARE_UNSIGNED(c, args.args[0], ==, SPMC_TEST_OK);
+
+
+out:
+	Do_ADBG_EndSubCase(c, NULL);
+	close_debugfs();
+}
+
+ADBG_CASE_DEFINE(ffa_spmc, 1001, xtest_ffa_spmc_test_1001,
+		 "Test FF-A communication");
diff --git a/host/xtest/include/uapi/linux/arm_ffa_user.h b/host/xtest/include/uapi/linux/arm_ffa_user.h
new file mode 100644
index 0000000..9ef0be3
--- /dev/null
+++ b/host/xtest/include/uapi/linux/arm_ffa_user.h
@@ -0,0 +1,79 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2020-2022, Arm Limited
+ */
+
+#ifndef __ARM_FFA_USER_H
+#define __ARM_FFA_USER_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+#define FFA_IOC_MAGIC	0xf0
+#define FFA_IOC_BASE	0
+
+/**
+ * struct ffa_ioctl_ep_desc - Query endpoint ID
+ * @uuid_ptr:	[in] Pointer to queried UUID. Format must be an RFC 4122 string,
+ * 		i.e. "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee".
+ * @id:		[out] 16-bit ID of endpoint.
+ */
+struct ffa_ioctl_ep_desc {
+	__u64 uuid_ptr;
+	__u16 id;
+};
+#define FFA_IOC_GET_PART_ID	_IOWR(FFA_IOC_MAGIC, FFA_IOC_BASE + 0, \
+				      struct ffa_ioctl_ep_desc)
+
+/**
+ * struct ffa_ioctl_msg_args - Send direct message request
+ * @args:	[in/out] Arguments of FFA_MSG_SEND_DIRECT_REQ (w3-w7). If the
+ * 		response is FFA_MSG_SEND_DIRECT_RESP, the received arguments are
+ * 		returned in this field.
+ * @dst_id:	[in] 16-bit ID of destination endpoint.
+ */
+struct ffa_ioctl_msg_args {
+	__u32 args[5];
+	__u16 dst_id;
+};
+#define FFA_IOC_MSG_SEND	_IOWR(FFA_IOC_MAGIC, FFA_IOC_BASE + 1, \
+				      struct ffa_ioctl_msg_args)
+
+/**
+ * struct ffa_ioctl_shm_desc - Share/reclaim memory region
+ * @handle:	[in/out] Handle assigned by the SPM. Output when used with
+ * 		FFA_IOC_SHM_INIT, input when used with FFA_IOC_SHM_DEINIT.
+ * @size:	[in/out] In: the required size of region in bytes. Out: the
+ * 		actual region size allocated by the kernel. Unused on reclaim.
+ * @dst_id:	[in] 16-bit ID of destination endpoint. Unused on reclaim.
+ */
+struct ffa_ioctl_shm_desc {
+	__u64 handle;
+	__u64 size;
+	__u16 dst_id;
+};
+#define FFA_IOC_SHM_INIT	_IOWR(FFA_IOC_MAGIC, FFA_IOC_BASE + 2, \
+				      struct ffa_ioctl_shm_desc)
+
+#define FFA_IOC_SHM_DEINIT	_IOW(FFA_IOC_MAGIC, FFA_IOC_BASE + 3, \
+				     struct ffa_ioctl_shm_desc)
+
+/**
+ * struct ffa_ioctl_buf_desc - Read/write shared memory region
+ * @handle:	[in] Handle of the memory region.
+ * @buf_ptr:	[in] Pointer to user space buffer. Data is copied from/to this
+ * 		buffer to/from the memory region shared with the given endpoint.
+ * @buf_len:	[in] Length of read/write in bytes.
+ */
+struct ffa_ioctl_buf_desc {
+	__u64 handle;
+	__u64 buf_ptr;
+	__u64 buf_len;
+};
+#define FFA_IOC_SHM_READ	_IOW(FFA_IOC_MAGIC, FFA_IOC_BASE + 4, \
+				     struct ffa_ioctl_buf_desc)
+
+#define FFA_IOC_SHM_WRITE	_IOW(FFA_IOC_MAGIC, FFA_IOC_BASE + 5, \
+				     struct ffa_ioctl_buf_desc)
+
+#endif /* __ARM_FFA_USER_H */
diff --git a/host/xtest/xtest_main.c b/host/xtest/xtest_main.c
index 00cf667..0815d5d 100644
--- a/host/xtest/xtest_main.c
+++ b/host/xtest/xtest_main.c
@@ -2,6 +2,7 @@
 /*
  * Copyright (c) 2016, Linaro Limited
  * Copyright (c) 2014, STMicroelectronics International N.V.
+ * Copyright (c) 2022, Arm Limited and Contributors. All rights reserved.
  */
 
 #include <err.h>
@@ -35,6 +36,9 @@
 #ifdef CFG_PKCS11_TA
 ADBG_SUITE_DEFINE(pkcs11);
 #endif
+#ifdef CFG_SPMC_TESTS
+ADBG_SUITE_DEFINE(ffa_spmc);
+#endif
 ADBG_SUITE_DEFINE(regression);
 
 char *xtest_tee_name = NULL;
@@ -53,7 +57,13 @@
 #define PKCS11_SUITE	""
 #endif
 
-static char gsuitename[] = "regression" GP_SUITE PKCS11_SUITE;
+#ifdef CFG_SPMC_TESTS
+#define FFA_SPMC_SUITE	"+ffa_spmc"
+#else
+#define FFA_SPMC_SUITE	""
+#endif
+
+static char gsuitename[] = "regression" GP_SUITE PKCS11_SUITE FFA_SPMC_SUITE;
 
 void usage(char *program);
 
@@ -72,6 +82,9 @@
 #ifdef CFG_PKCS11_TA
 	printf(" pkcs11");
 #endif
+#ifdef CFG_SPMC_TESTS
+	printf(" ffa_spmc");
+#endif
 	printf("\n");
 	printf("\t                   To run several suites, use multiple names\n");
 	printf("\t                   separated by a '+')\n");
diff --git a/host/xtest/xtest_test.h b/host/xtest/xtest_test.h
index bb083f7..215024b 100644
--- a/host/xtest/xtest_test.h
+++ b/host/xtest/xtest_test.h
@@ -20,6 +20,9 @@
 #ifdef CFG_PKCS11_TA
 ADBG_SUITE_DECLARE(pkcs11);
 #endif
+#ifdef CFG_SPMC_TESTS
+ADBG_SUITE_DECLARE(ffa_spmc);
+#endif
 ADBG_SUITE_DECLARE(regression);
 
 /* TEEC_Result */