blob: 98872a1562c1dcfbaa8ff6f9bb89ed6cdaf61fa0 [file] [log] [blame]
/*
* Copyright (c) 2025, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch_helpers.h>
#include <arm_arch_svc.h>
#include <debug.h>
#include <plat_topology.h>
#include <power_management.h>
#include <psci.h>
#include <smccc.h>
#include <string.h>
#include <tftf_lib.h>
#ifdef __aarch64__
#define CORTEX_X2_MIDR 0x410FD480
#define CORTEX_X3_MIDR 0x410FD4E0
#define CORTEX_X4_MIDR 0x410FD821
#define CORTEX_X925_MIDR 0x410FD850
#define NEOVERSE_V2_MIDR 0x410FD4F0
#define NEOVERSE_V3_MIDR 0x410FD840
static int cortex_x2_test(void);
static int cortex_x3_test(void);
static int cortex_x4_test(void);
static int cortex_x925_test(void);
static int neoverse_v2_test(void);
static int neoverse_v3_test(void);
struct ent {
unsigned int midr;
int (*smc_ret)(void);
};
struct ent negative_entries[] = {
{ .midr = CORTEX_X2_MIDR, .smc_ret = cortex_x2_test }
};
struct ent positive_entries[] = {
{ .midr = CORTEX_X3_MIDR, .smc_ret = cortex_x3_test },
{ .midr = CORTEX_X4_MIDR, .smc_ret = cortex_x4_test },
{ .midr = CORTEX_X925_MIDR, .smc_ret = cortex_x925_test },
{ .midr = NEOVERSE_V2_MIDR, .smc_ret = neoverse_v2_test},
{ .midr = NEOVERSE_V3_MIDR, .smc_ret = neoverse_v3_test},
};
static int cortex_x2_test(void)
{
return -1;
}
static int cortex_x3_test(void)
{
return 0;
}
static int cortex_x4_test(void)
{
return 0;
}
static int cortex_x925_test(void)
{
return 0;
}
static int neoverse_v2_test(void)
{
return 0;
}
static int neoverse_v3_test(void)
{
return 0;
}
static test_result_t test_smccc_entrypoint(void)
{
smc_args args;
smc_ret_values ret;
int32_t expected_ver;
unsigned int my_midr, midr_mask;
size_t i;
/* Check if SMCCC version is at least v1.1 */
expected_ver = MAKE_SMCCC_VERSION(1, 1);
memset(&args, 0, sizeof(args));
args.fid = SMCCC_VERSION;
ret = tftf_smc(&args);
if ((int32_t)ret.ret0 < expected_ver) {
tftf_testcase_printf("Unexpected SMCCC version: 0x%x\n",
(int)ret.ret0);
return TEST_RESULT_SKIPPED;
}
/* Check if SMCCC_ARCH_WORKAROUND_4 is required or not */
memset(&args, 0, sizeof(args));
args.fid = SMCCC_ARCH_FEATURES;
args.arg1 = SMCCC_ARCH_WORKAROUND_4;
ret = tftf_smc(&args);
/*
* Negetive test. Checks if select non-affected cores
* return -1 (Not supported)
*/
my_midr = (unsigned int)read_midr_el1();
midr_mask = (MIDR_IMPL_MASK << MIDR_IMPL_SHIFT) | (MIDR_PN_MASK << MIDR_PN_SHIFT);
for (i = 0; i < ARRAY_SIZE(negative_entries); i++) {
struct ent *entp = &negative_entries[i];
if ((my_midr & midr_mask) == (entp->midr & midr_mask)) {
if (entp->smc_ret() != -1) {
return TEST_RESULT_FAIL;
}
break;
}
}
if ((int)ret.ret0 == -1) {
tftf_testcase_printf("SMCCC_ARCH_WORKAROUND_4 is not implemented\n");
return TEST_RESULT_SKIPPED;
}
/* Check if the SMC return value matches our expectations */
for (i = 0; i < ARRAY_SIZE(positive_entries); i++) {
struct ent *entp = &positive_entries[i];
if ((my_midr & midr_mask) == (entp->midr & midr_mask)) {
if (entp->smc_ret() != 0) {
return TEST_RESULT_FAIL;
}
break;
}
}
if ((i == ARRAY_SIZE(positive_entries)) && ((int)ret.ret0) == 0) {
tftf_testcase_printf("TFTF workaround table out of sync with TF-A\n");
return TEST_RESULT_FAIL;
}
/* Invoke the workaround to make sure nothing nasty happens */
memset(&args, 0, sizeof(args));
args.fid = SMCCC_ARCH_WORKAROUND_4;
tftf_smc(&args);
return TEST_RESULT_SUCCESS;
}
test_result_t test_smccc_arch_workaround_4(void)
{
u_register_t lead_mpid, target_mpid;
int cpu_node, ret;
lead_mpid = read_mpidr_el1() & MPID_MASK;
/* Power on all the non-lead cores. */
for_each_cpu(cpu_node) {
target_mpid = tftf_get_mpidr_from_node(cpu_node);
if (lead_mpid == target_mpid) {
continue;
}
ret = tftf_cpu_on(target_mpid,
(uintptr_t)test_smccc_entrypoint, 0);
if (ret != PSCI_E_SUCCESS) {
ERROR("CPU ON failed for 0x%llx\n",
(unsigned long long)target_mpid);
return TEST_RESULT_FAIL;
}
/*
* Wait for test_smccc_entrypoint to return
* and the CPU to power down
*/
while (tftf_psci_affinity_info(target_mpid, MPIDR_AFFLVL0) !=
PSCI_STATE_OFF) {
continue;
}
}
return test_smccc_entrypoint();
}
#else
test_result_t test_smccc_arch_workaround_4(void)
{
INFO("%s skipped on AArch32\n", __func__);
return TEST_RESULT_SKIPPED;
}
#endif