feat(sme): update sme/mortlach tests
FEAT_SME is an optional architectural extension from v9.2.
Previously due to the lack of support in toolchain, testing
SME instructions were overlooked and minimal tests were added.
This patch addresses them, with additional tests to test
the SME instructions. In order to avoid toolchain requirements
we manually encode the instructions for accessing ZA array.
Signed-off-by: Jayanth Dodderi Chidanand <jayanthdodderi.chidanand@arm.com>
Change-Id: Ia9edd2711d548757b96495498bf9d47b9db68a09
diff --git a/include/common/test_helpers.h b/include/common/test_helpers.h
index 66a5f27..04b8625 100644
--- a/include/common/test_helpers.h
+++ b/include/common/test_helpers.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2022, Arm Limited. All rights reserved.
+ * Copyright (c) 2018-2023, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -13,6 +13,7 @@
#include <events.h>
#include <plat_topology.h>
#include <psci.h>
+#include <sme.h>
#include <spm_common.h>
#include <tftf_lib.h>
#include <trusted_os.h>
@@ -341,6 +342,14 @@
} \
} while (false)
+#define SKIP_TEST_IF_SME_NOT_SUPPORTED() \
+ do { \
+ if(!is_feat_sme_supported()) { \
+ tftf_testcase_printf("FEAT_SME 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 f43bc8a..2f4b0c9 100644
--- a/include/lib/aarch64/arch.h
+++ b/include/lib/aarch64/arch.h
@@ -328,8 +328,10 @@
#define MTE_IMPLEMENTED_EL0 ULL(1) /* MTE is only implemented at EL0 */
#define MTE_IMPLEMENTED_ELX ULL(2) /* MTE is implemented at all ELs */
-#define ID_AA64PFR1_EL1_SME_SHIFT U(24)
-#define ID_AA64PFR1_EL1_SME_MASK ULL(0xf)
+#define ID_AA64PFR1_EL1_SME_SHIFT U(24)
+#define ID_AA64PFR1_EL1_SME_MASK ULL(0xf)
+#define ID_AA64PFR1_EL1_SME_NOT_SUPPORTED ULL(0x0)
+#define ID_AA64PFR1_EL1_SME_SUPPORTED ULL(0x1)
/* ID_PFR1_EL1 definitions */
#define ID_PFR1_VIRTEXT_SHIFT U(12)
diff --git a/include/lib/aarch64/arch_features.h b/include/lib/aarch64/arch_features.h
index a6ce5ae..bb1d156 100644
--- a/include/lib/aarch64/arch_features.h
+++ b/include/lib/aarch64/arch_features.h
@@ -215,4 +215,21 @@
return (((read_id_aa64dfr0_el1() >> ID_AA64DFR0_HPMN0_SHIFT) &
ID_AA64DFR0_HPMN0_MASK) == ID_AA64DFR0_HPMN0_SUPPORTED);
}
+
+static inline bool is_feat_sme_supported(void)
+{
+ uint64_t features;
+
+ features = read_id_aa64pfr1_el1() >> ID_AA64PFR1_EL1_SME_SHIFT;
+ return (features & ID_AA64PFR1_EL1_SME_MASK) >= ID_AA64PFR1_EL1_SME_SUPPORTED;
+}
+
+static inline bool is_feat_sme_fa64_supported(void)
+{
+ uint64_t features;
+
+ features = read_id_aa64smfr0_el1();
+ return (features & ID_AA64SMFR0_EL1_FA64_BIT) != 0U;
+}
+
#endif /* ARCH_FEATURES_H */
diff --git a/include/lib/extensions/sme.h b/include/lib/extensions/sme.h
index d1a17c5..f443cea 100644
--- a/include/lib/extensions/sme.h
+++ b/include/lib/extensions/sme.h
@@ -7,16 +7,31 @@
#ifndef SME_H
#define SME_H
-#define SME_SMCR_LEN_MAX U(0x1FF)
+#define MAX_VL (512)
+#define MAX_VL_B (MAX_VL / 8)
+#define SME_SMCR_LEN_MAX U(0x1FF)
-bool feat_sme_supported(void);
-bool feat_sme_fa64_supported(void);
-int sme_enable(void);
-void sme_smstart(bool enable_za);
-void sme_smstop(bool disable_za);
+typedef enum {
+ SMSTART, /* enters streaming sve mode and enables SME ZA array */
+ SMSTART_SM, /* enters streaming sve mode only */
+ SMSTART_ZA, /* enables SME ZA array storage only */
+} smestart_instruction_type_t;
-/* Assembly function prototypes */
+typedef enum {
+ SMSTOP, /* exits streaming sve mode, & disables SME ZA array */
+ SMSTOP_SM, /* exits streaming sve mode only */
+ SMSTOP_ZA, /* disables SME ZA array storage only */
+} smestop_instruction_type_t;
+
+/* SME feature related prototypes. */
+void sme_enable(void);
+void sme_smstart(smestart_instruction_type_t smstart_type);
+void sme_smstop(smestop_instruction_type_t smstop_type);
+
+/* Assembly function prototypes. */
uint64_t sme_rdvl_1(void);
void sme_try_illegal_instruction(void);
+void sme_vector_to_ZA(const uint64_t *input_vector);
+void sme_ZA_to_vector(const uint64_t *output_vector);
#endif /* SME_H */
diff --git a/lib/extensions/sme/aarch64/sme.c b/lib/extensions/sme/aarch64/sme.c
index 28ddcd6..632b2a7 100644
--- a/lib/extensions/sme/aarch64/sme.c
+++ b/lib/extensions/sme/aarch64/sme.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2021-2023, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -8,57 +8,22 @@
#include <stdio.h>
#include <arch.h>
+#include <arch_features.h>
#include <arch_helpers.h>
+#include <debug.h>
#include <lib/extensions/sme.h>
-#ifdef __aarch64__
-
/*
- * feat_sme_supported
- * Check if SME is supported on this platform.
- * Return
- * true if SME supported, false if not.
+ * Function : sme_enable
+ * Enable SME for nonsecure use at EL2 for TFTF cases.
*/
-bool feat_sme_supported(void)
-{
- uint64_t features;
-
- features = read_id_aa64pfr1_el1() >> ID_AA64PFR1_EL1_SME_SHIFT;
- return (features & ID_AA64PFR1_EL1_SME_MASK) != 0U;
-}
-
-/*
- * feat_sme_fa64_supported
- * Check if FEAT_SME_FA64 is supported.
- * Return
- * True if supported, false if not.
- */
-bool feat_sme_fa64_supported(void)
-{
- uint64_t features;
-
- features = read_id_aa64smfr0_el1();
- return (features & ID_AA64SMFR0_EL1_FA64_BIT) != 0U;
-}
-
-/*
- * sme_enable
- * Enable SME for nonsecure use at EL2 for TFTF cases.
- * Return
- * 0 if successful.
- */
-int sme_enable(void)
+void sme_enable(void)
{
u_register_t reg;
- /* Make sure SME is supported. */
- if (!feat_sme_supported()) {
- return -1;
- }
-
/*
- * Make sure SME accesses don't cause traps by setting appropriate fields
- * in CPTR_EL2.
+ * Make sure SME accesses don't cause traps by setting appropriate
+ * fields in CPTR_EL2.
*/
reg = read_cptr_el2();
if ((read_hcr_el2() & HCR_E2H_BIT) == 0U) {
@@ -70,48 +35,78 @@
}
write_cptr_el2(reg);
- return 0;
+ isb();
+
}
/*
- * sme_smstart
- * This function enables streaming mode and optinally enables ZA array access
- * at the same time.
+ * Function: sme_smstart
+ * This function enables streaming mode and ZA array storage access
+ * independently or together based on the type of instruction variant.
+ *
* Parameters
- * enable_za: If set, ZA access is enabled. If cleared, ZA bit is untouched.
+ * smstart_type: If SMSTART, streaming mode and ZA access is enabled.
+ * If SMSTART_SM, streaming mode enabled.
+ * If SMSTART_ZA enables SME ZA storage and, ZT0 storage access.
*/
-void sme_smstart(bool enable_za)
+void sme_smstart(smestart_instruction_type_t smstart_type)
{
- u_register_t svcr = SVCR_SM_BIT;
+ u_register_t svcr = 0ULL;
- if (enable_za) {
- svcr |= SVCR_ZA_BIT;
+ switch (smstart_type) {
+ case SMSTART:
+ svcr = (SVCR_SM_BIT | SVCR_ZA_BIT);
+ break;
+
+ case SMSTART_SM:
+ svcr = SVCR_SM_BIT;
+ break;
+
+ case SMSTART_ZA:
+ svcr = SVCR_ZA_BIT;
+ break;
+
+ default:
+ ERROR("Illegal SMSTART Instruction Variant\n");
+ break;
}
-
write_svcr(read_svcr() | svcr);
+
+ isb();
}
/*
* sme_smstop
- * This function disables streaming mode OR disables ZA array access but not
- * both. It might seem strange but this is the functionality of the SMSTOP
- * assembly instruction.
+ * This function exits streaming mode and disables ZA array storage access
+ * independently or together based on the type of instruction variant.
+ *
* Parameters
- * disable_za: If set, ZA access is disabled but streaming mode is not
- * affected. If clear, streaming mode is exited and ZA bit is
- * left alone.
+ * smstop_type: If SMSTOP, exits streaming mode and ZA access is disabled
+ * If SMSTOP_SM, exits streaming mode.
+ * If SMSTOP_ZA disables SME ZA storage and, ZT0 storage access.
*/
-void sme_smstop(bool disable_za)
+void sme_smstop(smestop_instruction_type_t smstop_type)
{
- u_register_t svcr;
+ u_register_t svcr = 0ULL;
- if (disable_za) {
- svcr = ~SVCR_ZA_BIT;
- } else {
+ switch (smstop_type) {
+ case SMSTOP:
+ svcr = (~SVCR_SM_BIT) & (~SVCR_ZA_BIT);
+ break;
+
+ case SMSTOP_SM:
svcr = ~SVCR_SM_BIT;
+ break;
+
+ case SMSTOP_ZA:
+ svcr = ~SVCR_ZA_BIT;
+ break;
+
+ default:
+ ERROR("Illegal SMSTOP Instruction Variant\n");
+ break;
}
-
write_svcr(read_svcr() & svcr);
-}
-#endif /* __aarch64__ */
+ isb();
+}
diff --git a/lib/extensions/sme/aarch64/sme_helpers.S b/lib/extensions/sme/aarch64/sme_helpers.S
index 6261c90..c440f09 100644
--- a/lib/extensions/sme/aarch64/sme_helpers.S
+++ b/lib/extensions/sme/aarch64/sme_helpers.S
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2021-2023, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -8,11 +8,52 @@
#include <asm_macros.S>
#include <assert_macros.S>
-#ifdef __aarch64__
-
.arch armv8-a+sve
.globl sme_rdvl_1
.globl sme_try_illegal_instruction
+ .globl sme_vector_to_ZA
+ .globl sme_ZA_to_vector
+
+
+/*
+ * TODO: Due to the limitation with toolchain, SME intrinsics, still not being
+ * supported, instructions are manually encoded using the opcodes.
+ * Further, when the toolchain supports the requirements, these macros could
+ * be refactored.
+ */
+
+
+/*
+ * LDR (Loads a vector (an array of elements ) to ZA array ):
+ * LDR ZA[\nw, #\offset], [X\nxbase, #\offset, MUL VL]
+ *
+ * Arguments/Opcode bit field:
+ * nw : the vector select register W12-W15
+ * nxbase : 64-bit name of the general-purpose base register.
+ * offset : vector select and optional memory offset. Default to 0.
+ */
+.macro _ldr_za nw, nxbase, offset=0
+ .inst 0xe1000000 \
+ | (((\nw) & 3) << 13) \
+ | ((\nxbase) << 5) \
+ | ((\offset) & 0xf)
+.endm
+
+/*
+ * STR ( It stores an array of elements from ZA array to a vector ).
+ * STR ZA[\nw, #\offset], [X\nxbase, #\offset, MUL VL]
+ *
+ * Arguments/Opcode bit field:
+ * nw : the vector select register W12-W15
+ * nxbase : 64-bit name of the general-purpose base register.
+ * offset : vector select and optional memory offset. Default to 0.
+ */
+.macro _str_za nw, nxbase, offset=0
+ .inst 0xe1200000 \
+ | (((\nw) & 3) << 13) \
+ | ((\nxbase) << 5) \
+ | ((\offset) & 0xf)
+.endm
/*
* uint64_t sme_rdvl_1(void);
@@ -36,4 +77,25 @@
ret
endfunc sme_try_illegal_instruction
-#endif /* __aarch64__ */
+
+/**
+ * void sme_vector_to_ZA(uint64_t *input_vec)
+ *
+ * This function loads an vector of elements to an ZA Array storage
+ */
+func sme_vector_to_ZA
+ mov w12, wzr
+ _ldr_za 12, 0 // ZA.H[W12] loaded from [X0 / input_vector]
+ ret
+endfunc sme_vector_to_ZA
+
+/**
+ * void sme_ZA_to_vector(uint64_t *out_vec)
+ *
+ * This function stores elements from ZA Array storage to an ZA vector
+ */
+func sme_ZA_to_vector
+ mov w12, wzr
+ _str_za 12, 0 // ZA.H[W12] stored to [X0 / out_vector]
+ ret
+endfunc sme_ZA_to_vector
diff --git a/tftf/tests/extensions/sme/test_sme.c b/tftf/tests/extensions/sme/test_sme.c
index 9652b45..58690ea 100644
--- a/tftf/tests/extensions/sme/test_sme.c
+++ b/tftf/tests/extensions/sme/test_sme.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, Arm Limited. All rights reserved.
+ * Copyright (c) 2021-2023, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -14,6 +14,53 @@
#include <test_helpers.h>
#include <tftf_lib.h>
+#ifdef __aarch64__
+
+/* Global buffers*/
+static uint64_t ZA_In_vector[8] = {0xaaff, 0xbbff, 0xccff, 0xddff, 0xeeff,
+ 0xffff, 0xff00, 0xff00};
+static uint64_t ZA_Out_vector[8] = {0};
+
+
+/**
+ * sme_zero_ZA
+ * ZER0 : Zero a list of upto eight 64bit element ZA tiles.
+ * ZERO {<mask>} , where mask=ff, to clear all the 8, 64 bit elements.
+ */
+static void sme_zero_ZA(void)
+{
+ /**
+ * Due to the lack of support from the toolchain, instruction
+ * opcodes are used here.
+ * Manual Encoding Instruction, to Zero all the tiles of ZA array.
+ *
+ * TODO: Further, once the toolchain adds support for SME features
+ * this could be replaced with the actual instruction ZERO { <mask>}.
+ */
+ asm volatile(".inst 0xc008000f" : : : );
+}
+
+/**
+ * This function compares two buffers/vector elements
+ * Inputs: uint64_t *ZA_In_vector, ZA_Out_vector
+ * @return true : If both are equal
+ * @return false : If both are not equal
+ */
+static bool sme_cmp_vector(const uint64_t *ZA_In_vector, const uint64_t *ZA_Out_vector)
+{
+ bool ret = true;
+
+ for (int i = 0; i < (MAX_VL_B/8); i++) {
+ if (ZA_In_vector[i] != ZA_Out_vector[i]) {
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+#endif /* __aarch64__ */
+
test_result_t test_sme_support(void)
{
/* SME is an AArch64-only feature.*/
@@ -24,19 +71,13 @@
unsigned int current_vector_len;
unsigned int requested_vector_len;
unsigned int len_max;
+ unsigned int __unused svl_max = 0U;
/* Skip the test if SME is not supported. */
- if (!feat_sme_supported()) {
- INFO("SME not supported, skipping.\n");
- return TEST_RESULT_SKIPPED;
- }
+ SKIP_TEST_IF_SME_NOT_SUPPORTED();
/* Enable SME for use at NS EL2. */
- if (sme_enable() != 0) {
- ERROR("Could not enable SME.\n");
- return TEST_RESULT_FAIL;
- }
- isb();
+ sme_enable();
/* Make sure TPIDR2_EL0 is accessible. */
write_tpidr2_el0(0);
@@ -50,21 +91,12 @@
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);
+
+ /* Entering Streaming SVE mode */
+ sme_smstart(SMSTART_SM);
/* Write SMCR_EL2 with the LEN max to find implemented width. */
write_smcr_el2(SME_SMCR_LEN_MAX);
@@ -87,15 +119,48 @@
* match, we've found the largest supported value for SMLEN.
*/
if (current_vector_len == requested_vector_len) {
+ svl_max = current_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);
+ VERBOSE("NOT SUPPORTED: %u bits (LEN=%u)\n", requested_vector_len, i);
}
}
- sme_smstop(false);
+
+ INFO("Largest Supported Streaming Vector Length(SVL): %u bits \n", svl_max);
+
+ /* Exiting Streaming SVE mode */
+ sme_smstop(SMSTOP_SM);
+
+ /**
+ * Perform/Execute SME Instructions.
+ * SME Data processing instructions LDR, STR, and ZERO instructions that
+ * access the SME ZA storage are legal only if ZA is enabled.
+ */
+
+ /* Enable SME ZA Array Storage */
+ sme_smstart(SMSTART_ZA);
+
+ /* LDR : Load vector to ZA Array */
+ sme_vector_to_ZA(ZA_In_vector);
+
+ /* STR : Store Vector from ZA Array. */
+ sme_ZA_to_vector(ZA_Out_vector);
+
+ /* Compare both vectors to ensure load and store instructions have
+ * executed precisely.
+ */
+ if (!sme_cmp_vector(ZA_In_vector, ZA_Out_vector)) {
+ return TEST_RESULT_FAIL;
+ }
+
+ /* Zero or clear the entire ZA Array Storage/Tile */
+ sme_zero_ZA();
+
+ /* Disable the SME ZA array storage. */
+ sme_smstop(SMSTOP_ZA);
/* If FEAT_SME_FA64 then attempt to execute an illegal instruction. */
- if (feat_sme_fa64_supported()) {
+ if (is_feat_sme_fa64_supported()) {
VERBOSE("FA64 supported, trying illegal instruction.\n");
sme_try_illegal_instruction();
}