debugfs: add debug filesystem tests

Change-Id: Idc36627dabe5af0076fe300ee0c071dce1662a0d
Signed-off-by: Ambroise Vincent <ambroise.vincent@arm.com>
Signed-off-by: Olivier Deprez <olivier.deprez@arm.com>
diff --git a/tftf/tests/runtime_services/sip_service/debugfs.h b/tftf/tests/runtime_services/sip_service/debugfs.h
new file mode 100644
index 0000000..cf09e0b
--- /dev/null
+++ b/tftf/tests/runtime_services/sip_service/debugfs.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef DEBUGFS_H
+#define DEBUGFS_H
+
+#define NAMELEN   13 /* Maximum length of a file name */
+#define PATHLEN   41 /* Maximum length of a path */
+#define STATLEN   41 /* Size of static part of dir format */
+#define ROOTLEN   (2 + 4) /* Size needed to encode root string */
+#define FILNAMLEN (2 + NAMELEN) /* Size needed to encode filename */
+#define DIRLEN    (STATLEN + FILNAMLEN + 3*ROOTLEN) /* Size of dir entry */
+
+#define KSEEK_SET 0
+#define KSEEK_CUR 1
+#define KSEEK_END 2
+
+#define NELEM(tab) (sizeof(tab) / sizeof((tab)[0]))
+
+typedef unsigned short qid_t;
+
+/*******************************************************************************
+ * This structure contains the necessary information to represent a 9p
+ * directory.
+ ******************************************************************************/
+typedef struct {
+	char name[NAMELEN];
+	long length;
+	unsigned char mode;
+	unsigned char type;
+	unsigned char dev;
+	qid_t qid;
+} dir_t;
+
+enum devflags {
+	O_READ   = 1 << 0,
+	O_WRITE  = 1 << 1,
+	O_RDWR   = 1 << 2,
+	O_BIND   = 1 << 3,
+	O_DIR    = 1 << 4,
+	O_STAT   = 1 << 5
+};
+
+#define MOUNT		0
+#define CREATE		1
+#define OPEN		2
+#define CLOSE		3
+#define READ		4
+#define WRITE		5
+#define SEEK		6
+#define BIND		7
+#define STAT		8
+#define INIT		10
+#define VERSION		11
+
+#endif /* DEBUGFS_H */
diff --git a/tftf/tests/runtime_services/sip_service/test_debugfs.c b/tftf/tests/runtime_services/sip_service/test_debugfs.c
new file mode 100644
index 0000000..6c626e9
--- /dev/null
+++ b/tftf/tests/runtime_services/sip_service/test_debugfs.c
@@ -0,0 +1,356 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <platform_def.h>
+#include <tftf_lib.h>
+#include <xlat_tables_v2.h>
+
+#include "debugfs.h"
+
+#define SMC_OK			(0)
+
+#define DEBUGFS_VERSION		(0x00000001)
+#define DEBUGFS_SMC_64		(0xC2000030)
+#define MAX_PATH_LEN		(256)
+
+/* DebugFS shared buffer area */
+#ifndef PLAT_ARM_DEBUGFS_BASE
+#define PLAT_ARM_DEBUGFS_BASE		(0x81000000)
+#define PLAT_ARM_DEBUGFS_SIZE		(0x1000)
+#endif /* PLAT_ARM_DEBUGFS_BASE */
+
+union debugfs_parms {
+	struct {
+		char fname[MAX_PATH_LEN];
+	} open;
+
+	struct mount {
+		char srv[MAX_PATH_LEN];
+		char where[MAX_PATH_LEN];
+		char spec[MAX_PATH_LEN];
+	} mount;
+
+	struct {
+		char path[MAX_PATH_LEN];
+		dir_t dir;
+	} stat;
+
+	struct {
+		char oldpath[MAX_PATH_LEN];
+		char newpath[MAX_PATH_LEN];
+	} bind;
+};
+
+typedef struct {
+	char *name;
+	qid_t qid;
+} dir_expected_t;
+
+static const dir_expected_t root_dir_expected[] = {
+	{ "dev",   0x8001 },
+	{ "blobs", 0x8003 },
+	{ "fip",   0x8002 }
+};
+
+static unsigned int read_buffer[4096 / sizeof(unsigned int)];
+
+static void *const payload = (void *) PLAT_ARM_DEBUGFS_BASE;
+
+static int init(unsigned long long phys_addr)
+{
+	smc_ret_values ret;
+	smc_args args;
+
+	args.fid  = DEBUGFS_SMC_64;
+	args.arg1 = INIT;
+	args.arg2 = phys_addr;
+	ret = tftf_smc(&args);
+
+	return (ret.ret0 == SMC_OK) ? 0 : -1;
+}
+
+static int version(void)
+{
+	smc_ret_values ret;
+	smc_args args;
+
+	args.fid  = DEBUGFS_SMC_64;
+	args.arg1 = VERSION;
+	ret = tftf_smc(&args);
+
+	return (ret.ret0 == SMC_OK) ? ret.ret1 : -1;
+}
+
+static int open(const char *name, int flags)
+{
+	union debugfs_parms *parms = payload;
+	smc_ret_values ret;
+	smc_args args;
+
+	strlcpy(parms->open.fname, name, MAX_PATH_LEN);
+
+	args.fid  = DEBUGFS_SMC_64;
+	args.arg1 = OPEN;
+	args.arg2 = (u_register_t) flags;
+	ret = tftf_smc(&args);
+
+	return (ret.ret0 == SMC_OK) ? ret.ret1 : -1;
+}
+
+static int read(int fd, void *buf, size_t size)
+{
+	smc_ret_values ret;
+	smc_args args;
+
+	args.fid  = DEBUGFS_SMC_64;
+	args.arg1 = READ;
+	args.arg2 = (u_register_t) fd;
+	args.arg3 = (u_register_t) size;
+
+	ret = tftf_smc(&args);
+
+	if (ret.ret0 == SMC_OK) {
+		memcpy(buf, payload, size);
+		return ret.ret1;
+	}
+
+	return -1;
+}
+
+static int close(int fd)
+{
+	smc_ret_values ret;
+	smc_args args;
+
+	args.fid  = DEBUGFS_SMC_64;
+	args.arg1 = CLOSE;
+	args.arg2 = (u_register_t) fd;
+
+	ret = tftf_smc(&args);
+
+	return (ret.ret0 == SMC_OK) ? 0 : -1;
+}
+
+static int mount(char *srv, char *where, char *spec)
+{
+	union debugfs_parms *parms = payload;
+	smc_ret_values ret;
+	smc_args args;
+
+	strlcpy(parms->mount.srv, srv, MAX_PATH_LEN);
+	strlcpy(parms->mount.where, where, MAX_PATH_LEN);
+	strlcpy(parms->mount.spec, spec, MAX_PATH_LEN);
+
+	args.fid  = DEBUGFS_SMC_64;
+	args.arg1 = MOUNT;
+
+	ret = tftf_smc(&args);
+
+	return (ret.ret0 == SMC_OK) ? 0 : -1;
+}
+
+static int stat(const char *name, dir_t *dir)
+{
+	union debugfs_parms *parms = payload;
+	smc_ret_values ret;
+	smc_args args;
+
+	strlcpy(parms->stat.path, name, MAX_PATH_LEN);
+
+	args.fid  = DEBUGFS_SMC_64;
+	args.arg1 = STAT;
+
+	ret = tftf_smc(&args);
+
+	if (ret.ret0 == SMC_OK) {
+		memcpy(dir, &parms->stat.dir, sizeof(dir_t));
+		return 0;
+	}
+
+	return -1;
+}
+
+static int seek(int fd, long offset, int whence)
+{
+	smc_ret_values ret;
+	smc_args args;
+
+	args.fid  = DEBUGFS_SMC_64;
+	args.arg1 = SEEK;
+	args.arg2 = (u_register_t) fd;
+	args.arg3 = (u_register_t) offset;
+	args.arg4 = (u_register_t) whence;
+
+	ret = tftf_smc(&args);
+
+	return (ret.ret0 == SMC_OK) ? 0 : -1;
+}
+
+static bool compare_dir(const dir_expected_t *dir_expected,
+			unsigned int iteration, dir_t *dir)
+{
+	return ((memcmp(dir->name, dir_expected[iteration].name,
+		       strlen(dir_expected[iteration].name)) == 0) &&
+		(dir->qid == dir_expected[iteration].qid));
+}
+
+static void dir_print(dir_t *dir)
+{
+	tftf_testcase_printf("name: %s, length: %ld, mode: %d, type: %d, "
+			     "dev: %d, qid: 0x%x\n",
+			     dir->name,
+			     dir->length,
+			     dir->mode,
+			     dir->type,
+			     dir->dev,
+			     dir->qid);
+}
+
+/*
+ * @Test_Aim@ Issue SMCs to TF-A calling debugfs functions in order to test
+ * the exposure of the filesystem.
+ * The result is displayed on the console, something that should look like:
+ * > ls /
+ * dev
+ * fip
+ * blobs
+ */
+test_result_t test_debugfs(void)
+{
+	int fd, ret, iteration;
+	dir_t dir;
+
+	/* Get debugfs interface version (if implemented)*/
+	ret = version();
+	if (ret != DEBUGFS_VERSION) {
+		/* Likely debugfs feature is not implemented */
+		return TEST_RESULT_SKIPPED;
+	}
+
+	/* Initialize debugfs feature, this maps the NS shared buffer in SWd */
+	ret = init(PLAT_ARM_DEBUGFS_BASE);
+	if (ret != 0) {
+		return TEST_RESULT_FAIL;
+	}
+
+	/* Calling init a second time should fail */
+	ret = init(PLAT_ARM_DEBUGFS_BASE);
+	if (ret == 0) {
+		tftf_testcase_printf("init succeeded ret=%d\n", ret);
+		return TEST_RESULT_FAIL;
+	}
+
+	/* open non-existing directory */
+	fd = open("/dummy", O_READ);
+	if (fd >= 0) {
+		tftf_testcase_printf("open succeeded fd=%d\n", fd);
+		return TEST_RESULT_FAIL;
+	}
+
+	/* stat non-existent file from root */
+	ret = stat("/unknown", &dir);
+	if (ret == 0) {
+		tftf_testcase_printf("stat succeeded ret=%d\n", ret);
+		return TEST_RESULT_FAIL;
+	}
+
+	/***************** Root directory listing **************/
+	/* open root directory */
+	fd = open("/", O_READ);
+	if (fd < 0) {
+		tftf_testcase_printf("open failed fd=%d\n", fd);
+		return TEST_RESULT_FAIL;
+	}
+
+	/* read directory entries */
+	iteration = 0;
+	ret = read(fd, &dir, sizeof(dir));
+	while (ret > 0) {
+		if (compare_dir(root_dir_expected, iteration++,
+				&dir) == false) {
+			dir_print(&dir);
+			return TEST_RESULT_FAIL;
+		}
+
+		ret = read(fd, &dir, sizeof(dir));
+	}
+
+	/* close root directory handle */
+	ret = close(fd);
+	if (ret < 0) {
+		tftf_testcase_printf("close failed ret=%d\n", ret);
+		return TEST_RESULT_FAIL;
+	}
+
+	/***************** FIP operations **************/
+	/* mount fip */
+	ret = mount("#F", "/fip", "/blobs/fip.bin");
+	if (ret < 0) {
+		tftf_testcase_printf("mount failed ret=%d\n", ret);
+		return TEST_RESULT_FAIL;
+	}
+
+	/* stat a non-existent file from fip */
+	ret = stat("/fip/unknown", &dir);
+	if (ret == 0) {
+		tftf_testcase_printf("stat succeeded ret=%d\n", ret);
+		return TEST_RESULT_FAIL;
+	}
+
+	/* detect bl2 image presence */
+	ret = stat("/fip/bl2.bin", &dir);
+	if (ret != 0) {
+		tftf_testcase_printf("stat failed ret=%d\n", ret);
+		return TEST_RESULT_FAIL;
+	}
+
+	/* open bl2 */
+	fd = open("/fip/bl2.bin", O_READ);
+	if (fd < 0) {
+		tftf_testcase_printf("open failed fd=%d\n", fd);
+		return TEST_RESULT_FAIL;
+	}
+
+	/* read first 128 bytes */
+	ret = read(fd, read_buffer, 128);
+	if (ret != 128) {
+		tftf_testcase_printf("read failed(%d) ret=%d\n", __LINE__, ret);
+		return TEST_RESULT_FAIL;
+	}
+
+	/* Compare first word of bl2 binary */
+	if (read_buffer[0] != 0xaa0003f4) {
+		tftf_testcase_printf("read ret %d, buf[0]: 0x%x\n",
+				     ret, read_buffer[0]);
+		return TEST_RESULT_FAIL;
+	}
+
+	/* rewind to file start */
+	ret = seek(fd, 0, KSEEK_SET);
+	if (ret != 0) {
+		tftf_testcase_printf("seek failed ret=%d\n", ret);
+		return TEST_RESULT_FAIL;
+	}
+
+	size_t read_size = 0;
+	do {
+		ret = read(fd, read_buffer, sizeof(read_buffer));
+		if (ret < 0) {
+			tftf_testcase_printf("read failed(%d) ret=%d\n",
+					     __LINE__, ret);
+			return TEST_RESULT_FAIL;
+		}
+		read_size += ret;
+	} while (ret);
+
+	if (read_size != dir.length) {
+		tftf_testcase_printf("read size mismatch read_size=%zu "
+				"dir.length=%ld\n", read_size, dir.length);
+		return TEST_RESULT_FAIL;
+	}
+
+	return TEST_RESULT_SUCCESS;
+}
diff --git a/tftf/tests/tests-debugfs.mk b/tftf/tests/tests-debugfs.mk
new file mode 100644
index 0000000..77fafd7
--- /dev/null
+++ b/tftf/tests/tests-debugfs.mk
@@ -0,0 +1,10 @@
+#
+# Copyright (c) 2019, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+TESTS_SOURCES	+=							\
+	$(addprefix tftf/tests/runtime_services/sip_service/,		\
+		test_debugfs.c					\
+	)
diff --git a/tftf/tests/tests-debugfs.xml b/tftf/tests/tests-debugfs.xml
new file mode 100644
index 0000000..4da3152
--- /dev/null
+++ b/tftf/tests/tests-debugfs.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  Copyright (c) 2019, Arm Limited. All rights reserved.
+
+  SPDX-License-Identifier: BSD-3-Clause
+-->
+
+<testsuites>
+  <!--
+      Test suite exercising execution state switch SiP service.
+  -->
+  <testsuite name="DebugFS" description="Test ARM SiP DebugFS service">
+    <testcase name="Expose filesystem" function="test_debugfs" />
+  </testsuite>
+
+</testsuites>
diff --git a/tftf/tests/tests-standard.mk b/tftf/tests/tests-standard.mk
index 9ef75bb..97d530f 100644
--- a/tftf/tests/tests-standard.mk
+++ b/tftf/tests/tests-standard.mk
@@ -21,6 +21,7 @@
 	tests-tftf-validation.mk		\
 	tests-tsp.mk				\
 	tests-uncontainable.mk			\
+	tests-debugfs.mk                        \
 )
 
 include ${TESTS_MAKEFILE}
diff --git a/tftf/tests/tests-standard.xml b/tftf/tests/tests-standard.xml
index fa57621..a865981 100644
--- a/tftf/tests/tests-standard.xml
+++ b/tftf/tests/tests-standard.xml
@@ -21,6 +21,7 @@
   <!ENTITY tests-performance SYSTEM "tests-performance.xml">
   <!ENTITY tests-smc SYSTEM "tests-smc.xml">
   <!ENTITY tests-pmu-leakage SYSTEM "tests-pmu-leakage.xml">
+  <!ENTITY tests-debugfs SYSTEM "tests-debugfs.xml">
 ]>
 
 <testsuites>
@@ -37,5 +38,6 @@
   &tests-performance;
   &tests-smc;
   &tests-pmu-leakage;
+  &tests-debugfs;
 
 </testsuites>