feat(sme): add basic SME tests

This test enters streaming mode and iterates through supported SME
vector lengths to ensure that EL3 has properly enabled SME for use at
lower non-secure ELs. If FA64 is present, it attempts to execute an
illegal instruction.

Signed-off-by: John Powell <john.powell@arm.com>
Change-Id: Ic80a1e5652a88261524778329d3bc99901a799d8
diff --git a/tftf/framework/framework.mk b/tftf/framework/framework.mk
index 8ec18ea..2965926 100644
--- a/tftf/framework/framework.mk
+++ b/tftf/framework/framework.mk
@@ -80,7 +80,9 @@
 # ARMv8.3 Pointer Authentication support files
 FRAMEWORK_SOURCES	+=						\
 	lib/extensions/pauth/aarch64/pauth.c				\
-	lib/extensions/pauth/aarch64/pauth_helpers.S
+	lib/extensions/pauth/aarch64/pauth_helpers.S			\
+	lib/extensions/sme/aarch64/sme.c				\
+	lib/extensions/sme/aarch64/sme_helpers.S
 endif
 
 TFTF_LINKERFILE		:=	tftf/framework/tftf.ld.S
diff --git a/tftf/tests/extensions/sme/test_sme.c b/tftf/tests/extensions/sme/test_sme.c
new file mode 100644
index 0000000..4bf6e59
--- /dev/null
+++ b/tftf/tests/extensions/sme/test_sme.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <arch_features.h>
+#include <arch_helpers.h>
+#include <lib/extensions/sme.h>
+#include <test_helpers.h>
+#include <tftf_lib.h>
+
+test_result_t test_sme_support(void)
+{
+	/* SME is an AArch64-only feature.*/
+	SKIP_TEST_IF_AARCH32();
+
+#ifdef __aarch64__
+	u_register_t reg;
+	unsigned int current_vector_len;
+	unsigned int requested_vector_len;
+	unsigned int len_max;
+
+	/* Skip the test if SME is not supported. */
+	if (!feat_sme_supported()) {
+		INFO("SME not supported, skipping.\n");
+		return TEST_RESULT_SKIPPED;
+	}
+
+	/* Enable SME for use at NS EL2. */
+	if (sme_enable() != 0) {
+		ERROR("Could not enable SME.\n");
+		return TEST_RESULT_FAIL;
+	}
+
+	/* Make sure TPIDR2_EL0 is accessible. */
+	write_tpidr2_el0(0);
+	if (read_tpidr2_el0() != 0) {
+		ERROR("Could not read TPIDR2_EL0.\n");
+		return TEST_RESULT_FAIL;
+	}
+	write_tpidr2_el0(0xb0bafe77);
+	if (read_tpidr2_el0() != 0xb0bafe77) {
+		ERROR("Could not write TPIDR2_EL0.\n");
+		return TEST_RESULT_FAIL;
+	}
+
+	/* Make sure we can start and stop streaming mode. */
+	VERBOSE("Entering Streaming SVE mode.\n");
+	sme_smstart(false);
+	read_smcr_el2();
+	sme_smstop(false);
+	sme_smstart(true);
+	read_smcr_el2();
+	sme_smstop(true);
+
+	/*
+	 * Iterate through values for LEN to detect supported vector lengths.
+	 * SME instructions aren't supported by GCC yet so for now this is all
+	 * we'll do.
+	 */
+	sme_smstart(false);
+
+	/* Write SMCR_EL2 with the LEN max to find implemented width. */
+	write_smcr_el2(SME_SMCR_LEN_MAX);
+	len_max = (unsigned int)read_smcr_el2();
+	VERBOSE("Maximum SMCR_EL2.LEN value: 0x%x\n", len_max);
+	VERBOSE("Enumerating supported vector lengths...\n");
+	for (unsigned int i = 0; i <= len_max; i++) {
+		/* Load new value into SMCR_EL2.LEN */
+		reg = read_smcr_el2();
+		reg &= ~(SMCR_ELX_LEN_MASK << SMCR_ELX_LEN_SHIFT);
+		reg |= (i << SMCR_ELX_LEN_SHIFT);
+		write_smcr_el2(reg);
+
+		/* Compute current and requested vector lengths in bits. */
+		current_vector_len = ((unsigned int)sme_rdvl_1() * 8U);
+		requested_vector_len = (i+1U)*128U;
+
+		/*
+		 * We count down from the maximum SMLEN value, so if the values
+		 * match, we've found the largest supported value for SMLEN.
+		 */
+		if (current_vector_len == requested_vector_len) {
+			VERBOSE("SUPPORTED:     %u bits (LEN=%u)\n", requested_vector_len, i);
+		} else {
+			VERBOSE("NOT SUPPORTED: %u bits (LEN=%u)\n", requested_vector_len, i);
+		}
+	}
+	sme_smstop(false);
+
+	/* If FEAT_SME_FA64 then attempt to execute an illegal instruction. */
+	if (feat_sme_fa64_supported()) {
+		VERBOSE("FA64 supported, trying illegal instruction.\n");
+		sme_try_illegal_instruction();
+	}
+
+	return TEST_RESULT_SUCCESS;
+#endif /* __aarch64__ */
+}
diff --git a/tftf/tests/tests-cpu-extensions.mk b/tftf/tests/tests-cpu-extensions.mk
index 5ceb340..6563b2a 100644
--- a/tftf/tests/tests-cpu-extensions.mk
+++ b/tftf/tests/tests-cpu-extensions.mk
@@ -18,4 +18,5 @@
 	runtime_services/arm_arch_svc/smccc_arch_soc_id.c		\
 	runtime_services/arm_arch_svc/smccc_arch_workaround_1.c		\
 	runtime_services/arm_arch_svc/smccc_arch_workaround_2.c		\
+	extensions/sme/test_sme.c					\
 )
diff --git a/tftf/tests/tests-cpu-extensions.xml b/tftf/tests/tests-cpu-extensions.xml
index a1e3f8f..990ab9f 100644
--- a/tftf/tests/tests-cpu-extensions.xml
+++ b/tftf/tests/tests-cpu-extensions.xml
@@ -23,6 +23,7 @@
     <testcase name="Use trace buffer control Registers" function="test_trbe_enabled" />
     <testcase name="Use trace filter control Registers" function="test_trf_enabled" />
     <testcase name="Use trace system Registers" function="test_sys_reg_trace_enabled" />
+    <testcase name="SME support" function="test_sme_support" />
   </testsuite>
 
   <testsuite name="ARM_ARCH_SVC" description="Arm Architecture Service tests">