test(SMCCC): test SMCCC_ARCH_FEATURE_AVAILABILITY

This test calls the function with each valid argument and checks that
every bit is set if its relevant feature is present in the system. It
also fails the test if any set bit in the return value has not been
checked. This should serve as a reminder to update this test for every
new feature that is implemented.

Only feature that tfa supports are tested with the expectation that new
ones will be added in the future.

Co-developed-by: Charlie Bareham <charlie.bareham@arm.com>
Signed-off-by: Boyan Karatotev <boyan.karatotev@arm.com>
Change-Id: I801326a49810bb76bfc3b9d06780d416dcc32a40
diff --git a/tftf/tests/runtime_services/arm_arch_svc/smccc_feature_availability.c b/tftf/tests/runtime_services/arm_arch_svc/smccc_feature_availability.c
new file mode 100644
index 0000000..1418ca1
--- /dev/null
+++ b/tftf/tests/runtime_services/arm_arch_svc/smccc_feature_availability.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2024, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <tftf.h>
+
+#include <arm_arch_svc.h>
+#include <smccc.h>
+#include <test_helpers.h>
+#include <tftf_lib.h>
+
+static inline u_register_t get_feature_for_reg(u_register_t reg)
+{
+	smc_args args = {0};
+	smc_ret_values ret;
+
+	args.fid = SMCCC_ARCH_FEATURE_AVAILABILITY | (SMC_64 << FUNCID_CC_SHIFT);
+	args.arg1 = reg;
+	ret = tftf_smc(&args);
+
+	/* APi is pretty simple, support was already checked. This is simpler */
+	assert((int)ret.ret0 == SMC_ARCH_CALL_SUCCESS);
+	return ret.ret1;
+}
+
+static inline bool always_present(void)
+{
+	return true;
+}
+
+/*
+ * Checks that the bit's matches the feature's presence
+ * feat_func should be is_feat_xyz_present(), but get_feat_xyz_support() will
+ * also work.
+ */
+#define CHECK_BIT_SET(feat_func, bit)						\
+	do {									\
+		if (!!feat_func() != !!(reg & bit)) {				\
+			tftf_testcase_printf(					\
+				#feat_func " says feature is %ssupported but "	\
+				#bit " was %sset!\n", feat_func() ? "" : "not ",\
+				(reg & bit) ? "" : "not ");			\
+			bad = true;						\
+		}								\
+		reg &= ~bit;							\
+	} while (0)
+
+/* when support for a new feature is added, we want this test to be
+ * updated. Fail the test so it's noticed. */
+#define CHECK_NO_BITS_SET(reg_name)						\
+	do {									\
+		if (reg != 0) {							\
+			tftf_testcase_printf(					\
+			#reg_name " still has values set: 0x%lx. "		\
+				"Test needs to be updated\n", reg);		\
+			bad = true;						\
+		}								\
+	} while (0)
+
+test_result_t test_smccc_arch_feature_availability(void)
+{
+	SKIP_TEST_IF_AARCH32();
+#ifdef __aarch64__
+
+	SKIP_TEST_IF_SMCCC_VERSION_LT(1, 1);
+	SKIP_TEST_IF_SMCCC_FUNC_NOT_SUPPORTED(SMCCC_ARCH_FEATURE_AVAILABILITY);
+
+	u_register_t reg;
+	bool bad = false;
+
+	reg = get_feature_for_reg(SCR_EL3_OPCODE);
+	CHECK_BIT_SET(is_armv8_9_fgt2_present,			SCR_FGTEN2_BIT);
+	CHECK_BIT_SET(is_feat_fpmr_present,			SCR_EnFPM_BIT);
+	CHECK_BIT_SET(is_feat_d128_supported,			SCR_D128En_BIT);
+	CHECK_BIT_SET(is_feat_s1pie_present,			SCR_PIEN_BIT);
+	CHECK_BIT_SET(is_feat_sctlr2_supported,			SCR_SCTLR2En_BIT);
+	CHECK_BIT_SET(is_feat_tcr2_supported,			SCR_TCR2EN_BIT);
+	CHECK_BIT_SET(is_feat_the_supported,			SCR_RCWMASKEn_BIT);
+	CHECK_BIT_SET(is_feat_sme_supported,			SCR_ENTP2_BIT);
+	CHECK_BIT_SET(is_feat_rng_trap_present,			SCR_TRNDR_BIT);
+	CHECK_BIT_SET(is_feat_gcs_present,			SCR_GCSEn_BIT);
+	CHECK_BIT_SET(get_feat_hcx_support,			SCR_HXEn_BIT);
+	CHECK_BIT_SET(is_feat_ls64_accdata_present,		SCR_ADEn_BIT);
+	CHECK_BIT_SET(is_feat_ls64_accdata_present,		SCR_EnAS0_BIT);
+	CHECK_BIT_SET(is_feat_amuv1p1_present,			SCR_AMVOFFEN_BIT);
+	CHECK_BIT_SET(get_armv8_6_ecv_support,			SCR_ECVEN_BIT);
+	CHECK_BIT_SET(is_armv8_6_fgt_present,			SCR_FGTEN_BIT);
+	CHECK_BIT_SET(is_feat_mte2_present,			SCR_ATA_BIT);
+	CHECK_BIT_SET(is_feat_csv2_2_present,			SCR_EnSCXT_BIT);
+	CHECK_BIT_SET(is_armv8_3_pauth_present,			SCR_APK_BIT);
+	CHECK_BIT_SET(is_feat_ras_present,			SCR_TERR_BIT);
+	CHECK_NO_BITS_SET(SCR_EL3);
+
+	reg = get_feature_for_reg(CPTR_EL3_OPCODE);
+	CHECK_BIT_SET(always_present,				CPTR_EL3_TCPAC_BIT);
+	CHECK_BIT_SET(is_feat_amuv1_present,			CPTR_EL3_TAM_BIT);
+	CHECK_BIT_SET(get_armv8_0_sys_reg_trace_support,	CPTR_EL3_TTA_BIT);
+	CHECK_BIT_SET(is_feat_sme_supported,			CPTR_EL3_ESM_BIT);
+	CHECK_BIT_SET(always_present,				CPTR_EL3_TFP_BIT);
+	CHECK_BIT_SET(is_armv8_2_sve_present,			CPTR_EL3_EZ_BIT);
+	CHECK_NO_BITS_SET(CPTR_EL3);
+
+	reg = get_feature_for_reg(MDCR_EL3_OPCODE);
+	CHECK_BIT_SET(get_feat_brbe_support,			MDCR_SBRBE(1));
+	CHECK_BIT_SET(is_armv8_6_fgt_present,			MDCR_TDCC_BIT);
+	CHECK_BIT_SET(is_feat_trbe_present,			MDCR_NSTB(1));
+	CHECK_BIT_SET(get_armv8_4_trf_support,			MDCR_TTRF_BIT);
+	CHECK_BIT_SET(is_feat_spe_supported,			MDCR_NSPB(1));
+	CHECK_BIT_SET(is_feat_doublelock_present,		MDCR_TDOSA_BIT);
+	CHECK_BIT_SET(always_present,				MDCR_TDA_BIT);
+	CHECK_BIT_SET(get_feat_pmuv3_supported,			MDCR_TPM_BIT);
+	CHECK_NO_BITS_SET(MDCR_EL3);
+
+	reg = get_feature_for_reg(MPAM3_EL3_OPCODE);
+	CHECK_BIT_SET(is_feat_mpam_supported,			MPAM3_EL3_TRAPLOWER_BIT);
+	CHECK_NO_BITS_SET(MPAM3_EL3);
+
+	if (bad)
+		return TEST_RESULT_FAIL;
+
+	return TEST_RESULT_SUCCESS;
+#endif /* __aarch64__ */
+}
diff --git a/tftf/tests/tests-cpu-extensions.mk b/tftf/tests/tests-cpu-extensions.mk
index 39783b1..7375633 100644
--- a/tftf/tests/tests-cpu-extensions.mk
+++ b/tftf/tests/tests-cpu-extensions.mk
@@ -33,4 +33,5 @@
 	runtime_services/arm_arch_svc/smccc_arch_workaround_1.c		\
 	runtime_services/arm_arch_svc/smccc_arch_workaround_2.c		\
 	runtime_services/arm_arch_svc/smccc_arch_workaround_3.c		\
+	runtime_services/arm_arch_svc/smccc_feature_availability.c	\
 )
diff --git a/tftf/tests/tests-cpu-extensions.xml b/tftf/tests/tests-cpu-extensions.xml
index bc09f37..51aa27c 100644
--- a/tftf/tests/tests-cpu-extensions.xml
+++ b/tftf/tests/tests-cpu-extensions.xml
@@ -50,6 +50,7 @@
      <testcase name="SMCCC_ARCH_WORKAROUND_2 test" function="test_smccc_arch_workaround_2" />
      <testcase name="SMCCC_ARCH_WORKAROUND_3 test" function="test_smccc_arch_workaround_3" />
      <testcase name="SMCCC_ARCH_SOC_ID test" function="test_smccc_arch_soc_id" />
+     <testcase name="SMCCC_ARCH_FEATURE_AVAILABILITY test" function="test_smccc_arch_feature_availability" />
   </testsuite>
 
 </testsuites>