Trusted Firmware-A Tests, version 2.0

This is the first public version of the tests for the Trusted
Firmware-A project. Please see the documentation provided in the
source tree for more details.

Change-Id: I6f3452046a1351ac94a71b3525c30a4ca8db7867
Signed-off-by: Sandrine Bailleux <sandrine.bailleux@arm.com>
Co-authored-by: amobal01 <amol.balasokamble@arm.com>
Co-authored-by: Antonio Nino Diaz <antonio.ninodiaz@arm.com>
Co-authored-by: Asha R <asha.r@arm.com>
Co-authored-by: Chandni Cherukuri <chandni.cherukuri@arm.com>
Co-authored-by: David Cunado <david.cunado@arm.com>
Co-authored-by: Dimitris Papastamos <dimitris.papastamos@arm.com>
Co-authored-by: Douglas Raillard <douglas.raillard@arm.com>
Co-authored-by: dp-arm <dimitris.papastamos@arm.com>
Co-authored-by: Jeenu Viswambharan <jeenu.viswambharan@arm.com>
Co-authored-by: Jonathan Wright <jonathan.wright@arm.com>
Co-authored-by: Kévin Petit <kevin.petit@arm.com>
Co-authored-by: Roberto Vargas <roberto.vargas@arm.com>
Co-authored-by: Sathees Balya <sathees.balya@arm.com>
Co-authored-by: Shawon Roy <Shawon.Roy@arm.com>
Co-authored-by: Soby Mathew <soby.mathew@arm.com>
Co-authored-by: Thomas Abraham <thomas.abraham@arm.com>
Co-authored-by: Vikram Kanigiri <vikram.kanigiri@arm.com>
Co-authored-by: Yatharth Kochar <yatharth.kochar@arm.com>
diff --git a/tftf/framework/nvm_results_helpers.c b/tftf/framework/nvm_results_helpers.c
new file mode 100644
index 0000000..34ef19f
--- /dev/null
+++ b/tftf/framework/nvm_results_helpers.c
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2018, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <assert.h>
+#include <debug.h>
+#include <nvm.h>
+#include <platform.h>
+#include <spinlock.h>
+#include <stdarg.h>
+#include <string.h>
+
+/*
+ * Temporary buffer to store 1 test output.
+ * This will eventually be saved into NVM at the end of the execution
+ * of this test.
+ */
+static char testcase_output[TESTCASE_OUTPUT_MAX_SIZE];
+/*
+ * A test output can be written in several pieces by calling
+ * tftf_testcase_printf() multiple times. testcase_output_idx keeps the position
+ * of the last character written in testcase_output buffer and allows to easily
+ * append a new string at next call to tftf_testcase_printf().
+ */
+static unsigned int testcase_output_idx;
+
+/* Lock to avoid concurrent accesses to the testcase output buffer */
+static spinlock_t testcase_output_lock;
+
+static tftf_state_t tftf_init_state = {
+	.build_message		= "",
+	.test_to_run		= {
+		.testsuite_idx	= 0,
+		.testcase_idx	= 0,
+	},
+	.test_progress		= TEST_READY,
+	.testcase_buffer	= { 0 },
+	.testcase_results	= {
+		{
+			.result		= TEST_RESULT_NA,
+			.duration	= 0,
+			.output_offset	= 0,
+			.output_size	= 0,
+		}
+	},
+	.result_buffer_size	= 0,
+	.result_buffer		= NULL,
+};
+
+unsigned int new_test_session(void)
+{
+/* NEW_TEST_SESSION == 1 => we always want to start a new session */
+#if NEW_TEST_SESSION
+	INFO("Always starting a new test session (NEW_TEST_SESSION == 1)\n");
+	return 1;
+#endif
+	char saved_build_msg[BUILD_MESSAGE_SIZE];
+
+	/*
+	 * Check the validity of the build message stored in NVM.
+	 * It is invalid when it doesn't match with the TFTF binary currently
+	 * executing.
+	 */
+	tftf_nvm_read(TFTF_STATE_OFFSET(build_message), saved_build_msg,
+		BUILD_MESSAGE_SIZE);
+	return !!strncmp(build_message, saved_build_msg, BUILD_MESSAGE_SIZE);
+}
+
+STATUS tftf_init_nvm(void)
+{
+	INFO("Initialising NVM\n");
+
+	/* Copy the build message to identify the TFTF */
+	strncpy(tftf_init_state.build_message, build_message, BUILD_MESSAGE_SIZE);
+	return tftf_nvm_write(0, &tftf_init_state, sizeof(tftf_init_state));
+}
+
+STATUS tftf_clean_nvm(void)
+{
+	unsigned char corrupt_build_message = '\0';
+
+	/*
+	 * This will cause TFTF to re-initialise its data structures next time
+	 * it runs.
+	 */
+	return tftf_nvm_write(TFTF_STATE_OFFSET(build_message),
+			&corrupt_build_message,
+			sizeof(corrupt_build_message));
+}
+
+STATUS tftf_set_test_to_run(const test_ref_t test_to_run)
+{
+	return tftf_nvm_write(TFTF_STATE_OFFSET(test_to_run), &test_to_run,
+			sizeof(test_to_run));
+}
+
+STATUS tftf_get_test_to_run(test_ref_t *test_to_run)
+{
+	assert(test_to_run != NULL);
+	return tftf_nvm_read(TFTF_STATE_OFFSET(test_to_run), test_to_run,
+			sizeof(*test_to_run));
+}
+
+STATUS tftf_set_test_progress(test_progress_t test_progress)
+{
+	return tftf_nvm_write(TFTF_STATE_OFFSET(test_progress), &test_progress,
+			sizeof(test_progress));
+}
+
+STATUS tftf_get_test_progress(test_progress_t *test_progress)
+{
+	assert(test_progress != NULL);
+	return tftf_nvm_read(TFTF_STATE_OFFSET(test_progress), test_progress,
+			sizeof(*test_progress));
+}
+
+STATUS tftf_testcase_set_result(const test_case_t *testcase,
+				test_result_t result,
+				unsigned long long duration)
+{
+	STATUS status;
+	unsigned result_buffer_size = 0;
+	TESTCASE_RESULT test_result;
+
+	assert(testcase != NULL);
+
+	/* Initialize Test case result */
+	test_result.result = result;
+	test_result.duration = duration;
+	test_result.output_offset = 0;
+	test_result.output_size = strlen(testcase_output);
+
+	/* Does the test have an output? */
+	if (test_result.output_size != 0) {
+		/* Get the size of the buffer containing all tests outputs */
+		status = tftf_nvm_read(TFTF_STATE_OFFSET(result_buffer_size),
+				&result_buffer_size, sizeof(unsigned));
+		if (status != STATUS_SUCCESS)
+			goto reset_test_output;
+
+		/*
+		 * Write the output buffer at the end of the string buffer in
+		 * NVM
+		 */
+		test_result.output_offset = result_buffer_size;
+		status = tftf_nvm_write(
+			TFTF_STATE_OFFSET(result_buffer) + result_buffer_size,
+			testcase_output, test_result.output_size + 1);
+		if (status != STATUS_SUCCESS)
+			goto reset_test_output;
+
+		/* And update the buffer size into NVM */
+		result_buffer_size += test_result.output_size + 1;
+		status = tftf_nvm_write(TFTF_STATE_OFFSET(result_buffer_size),
+					&result_buffer_size, sizeof(unsigned));
+		if (status != STATUS_SUCCESS)
+			goto reset_test_output;
+	}
+
+	/* Write the test result into NVM */
+	status = tftf_nvm_write(TFTF_STATE_OFFSET(testcase_results) +
+				(testcase->index * sizeof(TESTCASE_RESULT)),
+				&test_result, sizeof(TESTCASE_RESULT));
+
+reset_test_output:
+	/* Reset test output buffer for the next test */
+	testcase_output_idx = 0;
+	testcase_output[0] = 0;
+
+	return status;
+}
+
+STATUS tftf_testcase_get_result(const test_case_t *testcase,
+				TESTCASE_RESULT *result,
+				char *test_output)
+{
+	STATUS status;
+	unsigned output_size;
+
+	assert(testcase != NULL);
+	assert(result != NULL);
+	assert(test_output != NULL);
+
+	status = tftf_nvm_read(TFTF_STATE_OFFSET(testcase_results)
+			+ (testcase->index * sizeof(TESTCASE_RESULT)),
+			result, sizeof(TESTCASE_RESULT));
+	if (status != STATUS_SUCCESS) {
+		return status;
+	}
+
+	output_size = result->output_size;
+
+	if (output_size != 0) {
+		status = tftf_nvm_read(TFTF_STATE_OFFSET(result_buffer)
+				+ result->output_offset,
+				test_output, output_size);
+		if (status != STATUS_SUCCESS)
+			return status;
+	}
+
+	test_output[output_size] = 0;
+
+	return STATUS_SUCCESS;
+}
+
+int tftf_testcase_printf(const char *format, ...)
+{
+	va_list ap;
+	int available;
+	int written = -1;
+
+	spin_lock(&testcase_output_lock);
+
+	assert(sizeof(testcase_output) >= testcase_output_idx);
+	available = sizeof(testcase_output) - testcase_output_idx;
+	if (available == 0) {
+		ERROR("%s: Output buffer is full ; the string won't be printed.\n",
+			__func__);
+		ERROR("%s: Consider increasing TESTCASE_OUTPUT_MAX_SIZE value.\n",
+			__func__);
+		goto release_lock;
+	}
+
+	va_start(ap, format);
+	written = vsnprintf(&testcase_output[testcase_output_idx], available,
+			format, ap);
+	va_end(ap);
+
+	if (written < 0) {
+		ERROR("%s: Output error (%d)", __func__, written);
+		goto release_lock;
+	}
+	/*
+	 * If vsnprintf() truncated the string due to the size limit passed as
+	 * an argument then its return value is the number of characters (not
+	 * including the trailing '\0') which would have been written to the
+	 * final string if enough space had been available. Thus, a return value
+	 * of size or more means that the output was truncated.
+	 *
+	 * Adjust the value of 'written' to reflect what has been actually
+	 * written.
+	 */
+	if (written >= available) {
+		ERROR("%s: String has been truncated (%u/%u bytes written).\n",
+			__func__, available - 1, written);
+		ERROR("%s: Consider increasing TESTCASE_OUTPUT_MAX_SIZE value.\n",
+			__func__);
+		written = available - 1;
+	}
+
+	/*
+	 * Update testcase_output_idx to point to the '\0' of the buffer.
+	 * The next call of tftf_testcase_printf() will overwrite '\0' to
+	 * append its new string to the buffer.
+	 */
+	testcase_output_idx += written;
+
+release_lock:
+	spin_unlock(&testcase_output_lock);
+	return written;
+}
+
+void tftf_notify_reboot(void)
+{
+#if DEBUG
+	/* This function must be called by tests, not by the framework */
+	test_progress_t test_progress;
+	tftf_get_test_progress(&test_progress);
+	assert(test_progress == TEST_IN_PROGRESS);
+#endif /* DEBUG */
+
+	VERBOSE("Test intends to reset\n");
+	tftf_set_test_progress(TEST_REBOOTING);
+}