feat(WFxT): add a test for WFxT instructions

This patch adds the test to verify the WFET and WFIT instructions
introduced by FEAT_WFxT.

WFET and WFIT instructions assist in generating local-timeout event
and thereby act as wakeup event for the PE, when the virtual count
in CNTVCT_EL0 (counter-timer virtual count) register equals or exceed
the timeout value passed with these instructions.

Accordingly, this testcase verifies whether the time lapsed matches the
value supplied with WFET and WFIT instructions.

Signed-off-by: Manish V Badarkhe <Manish.Badarkhe@arm.com>
Signed-off-by: Jayanth Dodderi Chidanand <jayanthdodderi.chidanand@arm.com>
Change-Id: I9aea5da869db8520e305e49989cb71f166a582eb
diff --git a/include/common/test_helpers.h b/include/common/test_helpers.h
index e838270..0d31fce 100644
--- a/include/common/test_helpers.h
+++ b/include/common/test_helpers.h
@@ -285,7 +285,7 @@
 #define SKIP_TEST_IF_AFP_NOT_SUPPORTED()					\
 	do {									\
 		if (!get_feat_afp_present()) {					\
-			tftf_testcase_printf("ARMv8.7-afp not supported");	\
+			tftf_testcase_printf("ARMv8.7-afp not supported\n");	\
 			return TEST_RESULT_SKIPPED;				\
 		}								\
 	} while (false)
@@ -317,6 +317,14 @@
 		}								\
 	} while (false)
 
+#define SKIP_TEST_IF_WFXT_NOT_SUPPORTED()					\
+	do {									\
+		if (!get_feat_wfxt_present()) {					\
+			tftf_testcase_printf("ARMv8.7-WFxT not supported\n");	\
+			return TEST_RESULT_SKIPPED;				\
+		}								\
+	} while (false)
+
 /* Helper macro to verify if system suspend API is supported */
 #define is_psci_sys_susp_supported()	\
 		(tftf_get_psci_feature_info(SMC_PSCI_SYSTEM_SUSPEND)		\
diff --git a/include/lib/aarch64/arch.h b/include/lib/aarch64/arch.h
index dd0f899..c5c94fb 100644
--- a/include/lib/aarch64/arch.h
+++ b/include/lib/aarch64/arch.h
@@ -210,6 +210,12 @@
 #define ID_AA64ISAR1_APA_WIDTH	U(4)
 #define ID_AA64ISAR1_APA_MASK	ULL(0xf)
 
+/* ID_AA64ISAR2_EL1 definitions */
+#define ID_AA64ISAR2_EL1		S3_0_C0_C6_2
+#define ID_AA64ISAR2_WFXT_MASK		ULL(0xf)
+#define ID_AA64ISAR2_WFXT_SHIFT		U(0x0)
+#define ID_AA64ISAR2_WFXT_SUPPORTED	ULL(0x2)
+
 /* ID_AA64MMFR0_EL1 definitions */
 #define ID_AA64MMFR0_EL1_PARANGE_SHIFT	U(0)
 #define ID_AA64MMFR0_EL1_PARANGE_MASK	ULL(0xf)
diff --git a/include/lib/aarch64/arch_features.h b/include/lib/aarch64/arch_features.h
index 77b9f9d..46d12c9 100644
--- a/include/lib/aarch64/arch_features.h
+++ b/include/lib/aarch64/arch_features.h
@@ -8,7 +8,6 @@
 #define ARCH_FEATURES_H
 
 #include <stdbool.h>
-
 #include <arch_helpers.h>
 
 static inline bool is_armv7_gentimer_present(void)
@@ -112,21 +111,21 @@
 
 static inline bool get_armv9_0_trbe_support(void)
 {
-        return ((read_id_aa64dfr0_el1() >> ID_AA64DFR0_TRACEBUFFER_SHIFT) &
+	return ((read_id_aa64dfr0_el1() >> ID_AA64DFR0_TRACEBUFFER_SHIFT) &
 		ID_AA64DFR0_TRACEBUFFER_MASK) ==
 		ID_AA64DFR0_TRACEBUFFER_SUPPORTED;
 }
 
 static inline bool get_armv8_4_trf_support(void)
 {
-        return ((read_id_aa64dfr0_el1() >> ID_AA64DFR0_TRACEFILT_SHIFT) &
+	return ((read_id_aa64dfr0_el1() >> ID_AA64DFR0_TRACEFILT_SHIFT) &
 		ID_AA64DFR0_TRACEFILT_MASK) ==
 		ID_AA64DFR0_TRACEFILT_SUPPORTED;
 }
 
 static inline bool get_armv8_0_sys_reg_trace_support(void)
 {
-        return ((read_id_aa64dfr0_el1() >> ID_AA64DFR0_TRACEVER_SHIFT) &
+	return ((read_id_aa64dfr0_el1() >> ID_AA64DFR0_TRACEVER_SHIFT) &
 		ID_AA64DFR0_TRACEVER_MASK) ==
 		ID_AA64DFR0_TRACEVER_SUPPORTED;
 }
@@ -161,4 +160,10 @@
 		ID_AA64DFR0_BRBE_SUPPORTED;
 }
 
+static inline bool get_feat_wfxt_present(void)
+{
+	return (((read_id_aa64isar2_el1() >> ID_AA64ISAR2_WFXT_SHIFT) &
+		ID_AA64ISAR2_WFXT_MASK) == ID_AA64ISAR2_WFXT_SUPPORTED);
+}
+
 #endif /* ARCH_FEATURES_H */
diff --git a/include/lib/aarch64/arch_helpers.h b/include/lib/aarch64/arch_helpers.h
index b6d924b..b366cdd 100644
--- a/include/lib/aarch64/arch_helpers.h
+++ b/include/lib/aarch64/arch_helpers.h
@@ -75,6 +75,13 @@
 	__asm__ (#_op " " #_type);			\
 }
 
+/* Define function for system instruction with register with variable parameter */
+#define DEFINE_SYSOP_PARAM_FUNC(_op)                   \
+static inline void _op(uint64_t v)                     \
+{                                                      \
+	__asm__ (#_op " " "%0" : : "r" (v));           \
+}
+
 /* Define function for system instruction with register parameter */
 #define DEFINE_SYSOP_TYPE_PARAM_FUNC(_op, _type)	\
 static inline void _op ## _type(uint64_t v)		\
@@ -199,6 +206,7 @@
 DEFINE_SYSOP_FUNC(wfi)
 DEFINE_SYSOP_FUNC(wfe)
 DEFINE_SYSOP_FUNC(sev)
+DEFINE_SYSOP_FUNC(sevl)
 DEFINE_SYSOP_TYPE_FUNC(dsb, sy)
 DEFINE_SYSOP_TYPE_FUNC(dmb, sy)
 DEFINE_SYSOP_TYPE_FUNC(dmb, st)
@@ -217,6 +225,9 @@
 DEFINE_SYSOP_TYPE_FUNC(dmb, ish)
 DEFINE_SYSOP_FUNC(isb)
 
+DEFINE_SYSOP_PARAM_FUNC(wfit)
+DEFINE_SYSOP_PARAM_FUNC(wfet)
+
 static inline void enable_irq(void)
 {
 	/*
@@ -557,6 +568,9 @@
 /* Control floating point behaviour */
 DEFINE_RENAME_SYSREG_RW_FUNCS(fpcr, FPCR)
 
+/* ID_AA64ISAR2_EL1 */
+DEFINE_RENAME_SYSREG_READ_FUNC(id_aa64isar2_el1, ID_AA64ISAR2_EL1)
+
 #define IS_IN_EL(x) \
 	(GET_EL(read_CurrentEl()) == MODE_EL##x)
 
diff --git a/tftf/tests/extensions/wfxt/test_wfxt.c b/tftf/tests/extensions/wfxt/test_wfxt.c
new file mode 100644
index 0000000..bb3e486
--- /dev/null
+++ b/tftf/tests/extensions/wfxt/test_wfxt.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stdlib.h>
+#include <arch.h>
+#include <arch_helpers.h>
+#include <debug.h>
+#include <test_helpers.h>
+#include <tftf_lib.h>
+
+typedef enum {
+	EXEC_WFIT = 0,
+	EXEC_WFET
+} exec_wfxt;
+
+#ifdef __aarch64__
+static test_result_t test_wfxt_inst(exec_wfxt val, uint64_t ms)
+{
+	__asm__ volatile(".arch armv8.7-a");
+	uint64_t timer_cnt1, timer_cnt2, feed_cnt;
+	uint64_t timer_freq = read_cntfrq_el0();
+	uint64_t ms_to_counts = ((ms * timer_freq) / 1000U);
+
+	timer_cnt1 = virtualcounter_read();
+	feed_cnt = timer_cnt1 + ms_to_counts;
+
+	if (val == EXEC_WFIT) {
+		wfit(feed_cnt);
+	} else {
+		wfet(feed_cnt);
+	}
+
+	timer_cnt2 = virtualcounter_read();
+
+	/* Lapsed time should be at least equal to sleep time */
+	if ((timer_cnt2 - timer_cnt1) >= ms_to_counts) {
+		return TEST_RESULT_SUCCESS;
+	} else {
+		/* unlikely ends up here */
+		uint64_t lapsed_ms = ((timer_cnt2 - timer_cnt1) * 1000) / timer_freq;
+
+		ERROR("Time elapsed: actual(%llu)ms vs requested(%llu)ms \n",
+		      lapsed_ms, ms);
+		return TEST_RESULT_FAIL;
+	}
+}
+#endif /* __aarch64__ */
+
+test_result_t test_wfet_instruction(void)
+{
+	SKIP_TEST_IF_AARCH32();
+
+#ifdef __aarch64__
+	SKIP_TEST_IF_WFXT_NOT_SUPPORTED();
+
+	/*
+	 * first invocation of wfe returns immediately clearing the event
+	 * register
+	 */
+	sevl();
+	wfe();
+
+	return test_wfxt_inst(EXEC_WFET, 10);
+#endif /* __aarch64__ */
+}
+
+test_result_t test_wfit_instruction(void)
+{
+	test_result_t ret;
+
+	SKIP_TEST_IF_AARCH32();
+
+#ifdef __aarch64__
+	SKIP_TEST_IF_WFXT_NOT_SUPPORTED();
+
+	/* disable irqs to run wfi till timeout */
+	disable_irq();
+
+	ret = test_wfxt_inst(EXEC_WFIT, 10);
+
+	/* enable irq back */
+	enable_irq();
+#endif /* __aarch64__ */
+
+	return ret;
+}
diff --git a/tftf/tests/tests-cpu-extensions.mk b/tftf/tests/tests-cpu-extensions.mk
index 0719486..061ab7e 100644
--- a/tftf/tests/tests-cpu-extensions.mk
+++ b/tftf/tests/tests-cpu-extensions.mk
@@ -19,6 +19,7 @@
 	extensions/sys_reg_trace/test_sys_reg_trace.c			\
 	extensions/trbe/test_trbe.c					\
 	extensions/trf/test_trf.c					\
+	extensions/wfxt/test_wfxt.c					\
 	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		\
diff --git a/tftf/tests/tests-cpu-extensions.xml b/tftf/tests/tests-cpu-extensions.xml
index ba85866..bc84659 100644
--- a/tftf/tests/tests-cpu-extensions.xml
+++ b/tftf/tests/tests-cpu-extensions.xml
@@ -27,6 +27,8 @@
     <testcase name="SME support" function="test_sme_support" />
     <testcase name="SPE support" function="test_spe_support" />
     <testcase name="AFP support" function="test_afp_support" />
+    <testcase name="Test wfit instruction" function="test_wfit_instruction" />
+    <testcase name="Test wfet instruction" function="test_wfet_instruction" />
   </testsuite>
 
   <testsuite name="ARM_ARCH_SVC" description="Arm Architecture Service tests">