blob: 58690ea8c56a312e1a5fe476c5d07005b048905c [file] [log] [blame]
/*
* Copyright (c) 2021-2023, 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>
#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.*/
SKIP_TEST_IF_AARCH32();
#ifdef __aarch64__
u_register_t reg;
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. */
SKIP_TEST_IF_SME_NOT_SUPPORTED();
/* Enable SME for use at NS EL2. */
sme_enable();
/* 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;
}
/*
* Iterate through values for LEN to detect supported vector lengths.
*/
/* 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);
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) {
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);
}
}
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 (is_feat_sme_fa64_supported()) {
VERBOSE("FA64 supported, trying illegal instruction.\n");
sme_try_illegal_instruction();
}
return TEST_RESULT_SUCCESS;
#endif /* __aarch64__ */
}