arm-trusted-firmware/services/std_svc/errata_abi/errata_abi_main.c
Arvind Ram Prakash bbff267b6f fix(errata-abi): add support for handling split workarounds
Certain erratum workarounds like Neoverse N1 1542419, need a part
of their mitigation done in EL3 and the rest in lower EL. But currently
such workarounds return HIGHER_EL_MITIGATION which indicates that the
erratum has already been mitigated by a higher EL(EL3 in this case)
which causes the lower EL to not apply it's part of the mitigation.

This patch fixes this issue by adding support for split workarounds
so that on certain errata we return AFFECTED even though EL3 has
applied it's workaround. This is done by reusing the chosen field of
erratum_entry structure into a bitfield that has two bitfields -
Bit 0 indicates that the erratum has been enabled in build,
Bit 1 indicates that the erratum is a split workaround and should
return AFFECTED instead of HIGHER_EL_MITIGATION.

SDEN documentation:
https://developer.arm.com/documentation/SDEN885747/latest

Signed-off-by: Arvind Ram Prakash <arvind.ramprakash@arm.com>
Change-Id: Iec94d665b5f55609507a219a7d1771eb75e7f4a7
2025-03-07 17:02:25 +01:00

284 lines
6.3 KiB
C

/*
* Copyright (c) 2023-2024, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include "cpu_errata_info.h"
#include <lib/cpus/cpu_ops.h>
#include <lib/cpus/errata.h>
#include <lib/smccc.h>
#include <lib/utils_def.h>
#include <services/errata_abi_svc.h>
#include <smccc_helpers.h>
/*
* Global pointer that points to the specific
* structure based on the MIDR part number
*/
struct em_cpu_list *cpu_ptr;
/* Structure array that holds CPU specific errata information */
struct em_cpu_list cpu_list[] = {
#if CORTEX_A78_H_INC
{
.cpu_midr = CORTEX_A78_MIDR,
.cpu_errata_list = {
[0] = {2712571, 0x00, 0x12},
[1 ... ERRATA_LIST_END] = UNDEF_ERRATA,
}
},
#endif /* CORTEX_A78_H_INC */
#if CORTEX_A78_AE_H_INC
{
.cpu_midr = CORTEX_A78_AE_MIDR,
.cpu_errata_list = {
[0] = {2712574, 0x00, 0x02},
[1 ... ERRATA_LIST_END] = UNDEF_ERRATA,
}
},
#endif /* CORTEX_A78_AE_H_INC */
#if CORTEX_A78C_H_INC
{
.cpu_midr = CORTEX_A78C_MIDR,
.cpu_errata_list = {
[0] = {2712575, 0x01, 0x02},
[1 ... ERRATA_LIST_END] = UNDEF_ERRATA,
}
},
#endif /* CORTEX_A78C_H_INC */
#if NEOVERSE_V1_H_INC
{
.cpu_midr = NEOVERSE_V1_MIDR,
.cpu_errata_list = {
[0] = {2701953, 0x00, 0x11},
[1 ... ERRATA_LIST_END] = UNDEF_ERRATA,
}
},
#endif /* NEOVERSE_V1_H_INC */
#if CORTEX_A710_H_INC
{
.cpu_midr = CORTEX_A710_MIDR,
.cpu_errata_list = {
[0] = {2701952, 0x00, 0x21},
[1 ... ERRATA_LIST_END] = UNDEF_ERRATA,
}
},
#endif /* CORTEX_A710_H_INC */
#if NEOVERSE_N2_H_INC
{
.cpu_midr = NEOVERSE_N2_MIDR,
.cpu_errata_list = {
[0] = {2728475, 0x00, 0x02},
[1 ... ERRATA_LIST_END] = UNDEF_ERRATA,
}
},
#endif /* NEOVERSE_N2_H_INC */
#if CORTEX_X2_H_INC
{
.cpu_midr = CORTEX_X2_MIDR,
.cpu_errata_list = {
[0] = {2701952, 0x00, 0x21},
[1 ... ERRATA_LIST_END] = UNDEF_ERRATA,
}
},
#endif /* CORTEX_X2_H_INC */
#if NEOVERSE_V2_H_INC
{
.cpu_midr = NEOVERSE_V2_MIDR,
.cpu_errata_list = {
[0] = {2719103, 0x00, 0x01},
[1 ... ERRATA_LIST_END] = UNDEF_ERRATA,
}
},
#endif /* NEOVERSE_V2_H_INC */
#if CORTEX_X3_H_INC
{
.cpu_midr = CORTEX_X3_MIDR,
.cpu_errata_list = {
[0] = {2701951, 0x00, 0x11},
[1 ... ERRATA_LIST_END] = UNDEF_ERRATA,
}
},
#endif /* CORTEX_X3_H_INC */
#if CORTEX_X4_H_INC
{
.cpu_midr = CORTEX_X4_MIDR,
.cpu_errata_list = {
[0] = {2701112, 0x00, 0x00},
[1 ... ERRATA_LIST_END] = UNDEF_ERRATA,
}
},
#endif /* CORTEX_X4_H_INC */
};
#if ERRATA_NON_ARM_INTERCONNECT
/* Check if the errata is enabled for non-arm interconnect */
static int32_t non_arm_interconnect_errata(uint32_t errata_id, long rev_var)
{
int32_t ret_val = EM_UNKNOWN_ERRATUM;
/* Determine the number of cpu listed in the cpu list */
uint8_t size_cpulist = ARRAY_SIZE(cpu_list);
/* Read the midr reg to extract cpu, revision and variant info */
uint32_t midr_val = read_midr();
for (uint8_t i = 0U; i < size_cpulist; i++) {
cpu_ptr = &cpu_list[i];
/*
* If the cpu partnumber in the cpu list, matches the midr
* part number, check to see if the errata ID matches
*/
if (EXTRACT_PARTNUM(midr_val) == EXTRACT_PARTNUM(cpu_ptr->cpu_midr)) {
struct em_cpu *ptr = NULL;
for (int j = 0; j < MAX_PLAT_CPU_ERRATA_ENTRIES; j++) {
ptr = &cpu_ptr->cpu_errata_list[j];
assert(ptr != NULL);
if (errata_id == ptr->em_errata_id) {
if (RXPX_RANGE(rev_var, ptr->em_rxpx_lo, ptr->em_rxpx_hi)) {
ret_val = EM_AFFECTED;
break;
}
ret_val = EM_NOT_AFFECTED;
break;
}
}
break;
}
}
return ret_val;
}
#endif
/* Function to check if the errata exists for the specific CPU and rxpx */
int32_t verify_errata_implemented(uint32_t errata_id, uint32_t forward_flag)
{
int32_t ret_val;
struct cpu_ops *cpu_ops;
struct erratum_entry *entry, *end;
long rev_var;
ret_val = EM_UNKNOWN_ERRATUM;
rev_var = cpu_get_rev_var();
#if ERRATA_NON_ARM_INTERCONNECT
ret_val = non_arm_interconnect_errata(errata_id, rev_var);
if (ret_val != EM_UNKNOWN_ERRATUM) {
return ret_val;
}
#endif
cpu_ops = get_cpu_ops_ptr();
assert(cpu_ops != NULL);
entry = cpu_ops->errata_list_start;
assert(entry != NULL);
end = cpu_ops->errata_list_end;
assert(end != NULL);
end--; /* point to the last erratum entry of the queried cpu */
while ((entry <= end) && (ret_val == EM_UNKNOWN_ERRATUM)) {
if (entry->id == errata_id) {
if (entry->check_func(rev_var)) {
if (entry->chosen & WA_ENABLED_MASK)
if (entry->chosen & SPLIT_WA_MASK)
return EM_AFFECTED;
else
return EM_HIGHER_EL_MITIGATION;
else
return EM_AFFECTED;
}
return EM_NOT_AFFECTED;
}
entry += 1;
}
return ret_val;
}
/* Predicate indicating that a function id is part of EM_ABI */
bool is_errata_fid(uint32_t smc_fid)
{
return ((smc_fid == ARM_EM_VERSION) ||
(smc_fid == ARM_EM_FEATURES) ||
(smc_fid == ARM_EM_CPU_ERRATUM_FEATURES));
}
bool validate_spsr_mode(void)
{
/* In AArch64, if the caller is EL1, return true */
#if __aarch64__
if (GET_EL(read_spsr_el3()) == MODE_EL1) {
return true;
}
return false;
#else
/* In AArch32, if in system/svc mode, return true */
uint8_t read_el_state = GET_M32(read_spsr());
if ((read_el_state == (MODE32_svc)) || (read_el_state == MODE32_sys)) {
return true;
}
return false;
#endif /* __aarch64__ */
}
uintptr_t errata_abi_smc_handler(uint32_t smc_fid, u_register_t x1,
u_register_t x2, u_register_t x3, u_register_t x4,
void *cookie, void *handle, u_register_t flags)
{
int32_t ret_id = EM_UNKNOWN_ERRATUM;
switch (smc_fid) {
case ARM_EM_VERSION:
SMC_RET1(handle, MAKE_SMCCC_VERSION(
EM_VERSION_MAJOR, EM_VERSION_MINOR
));
break; /* unreachable */
case ARM_EM_FEATURES:
if (is_errata_fid((uint32_t)x1)) {
SMC_RET1(handle, EM_SUCCESS);
}
SMC_RET1(handle, EM_NOT_SUPPORTED);
break; /* unreachable */
case ARM_EM_CPU_ERRATUM_FEATURES:
/*
* If the forward flag is greater than zero and the calling EL
* is EL1 in AArch64 or in system mode or svc mode in case of AArch32,
* return Invalid Parameters.
*/
if (((uint32_t)x2 != 0) && (validate_spsr_mode())) {
SMC_RET1(handle, EM_INVALID_PARAMETERS);
}
ret_id = verify_errata_implemented((uint32_t)x1, (uint32_t)x2);
SMC_RET1(handle, ret_id);
break; /* unreachable */
default:
{
WARN("Unimplemented Errata ABI Service Call: 0x%x\n", smc_fid);
SMC_RET1(handle, EM_UNKNOWN_ERRATUM);
break; /* unreachable */
}
}
}