blob: 939223145def95acf12c0d1c05586a1b647420b4 [file] [log] [blame]
/*
* Copyright (c) 2023-2025, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch_helpers.h>
#include <arm_arch_svc.h>
#include <drivers/arm/arm_gic.h>
#include <irq.h>
#include <platform.h>
#include <psci.h>
#include <serror.h>
#include <smccc.h>
#include <tftf_lib.h>
#ifdef __aarch64__
static volatile uint64_t serror_triggered;
static volatile uint64_t irq_triggered;
static u_register_t expected_ver;
extern void inject_unrecoverable_ras_error(void);
/*
* Tests to verify reflection of lower EL SErrors in RAS KFH mode.
*
* These tests exercises the path of EL3 reflection of SError back to lower
* EL, which gets triggered as part of error synchronization during EL3
* entry. This test works in conjunction with "ras_kfh_reflection.patch"
* present in CI repository.
*
* One test each to verify reflection in sync and async exception.
*
*/
static bool serror_handler(bool *incr_elr_elx)
{
serror_triggered = 1;
*incr_elr_elx = false;
tftf_testcase_printf("SError event received.\n");
return true;
}
static int irq_handler(void *data)
{
irq_triggered = 1;
tftf_testcase_printf("IRQ received.\n");
return true;
}
/*
* Test Steps:
* 1. Register a custom SError handler for tftf
* 2. Make an SMC call to get the SMCCC version which will be used for
* comparing later on, along with that it also changes SCR_EL3.I = 1
* to route IRQ to EL3.
* 3. Disable SError (PSTATE.A = 1)
* 4. Inject RAS error and give time for it to trigger.
* 5. Register an SGI handler and inject SGI.
* 6. Becaue the IRQ is targeted to EL3 it will trap in EL3 irq_vector_entry
* 7. On entering EL3 it will find that SError is pending, So it will call
* "reflect_pending_serror_to_lower_el" and eret.
* 8. TF-A will eret back from EL3(without handling IRQ) and during ERET
* change SCR_EL3.I back to 0 along with unmasking SError for TFTF.
* SPSR.PSTATE.A = 0.
* 9. At tftf entry it will see both IRQ and SError pending, so it can take
* either of exception first (based on priority of SError/IRQ). The fvp model
* on which it was tested, IRQ is taken first.
* 10.First IRQ handler will be called and then SError handler will called.
*
*/
test_result_t test_ras_kfh_reflect_irq(void)
{
smc_args args;
unsigned int mpid = read_mpidr_el1();
unsigned int core_pos = platform_get_core_pos(mpid);
const unsigned int sgi_id = IRQ_NS_SGI_0;
smc_ret_values smc_ret;
int ret;
/* Get the SMCCC version to compare against */
memset(&args, 0, sizeof(args));
args.fid = SMCCC_VERSION;
smc_ret = tftf_smc(&args);
expected_ver = smc_ret.ret0;
register_custom_serror_handler(serror_handler);
disable_serror();
inject_unrecoverable_ras_error();
waitms(50);
ret = tftf_irq_register_handler_sgi(sgi_id, irq_handler);
if (ret != 0) {
tftf_testcase_printf("Failed to register initial IRQ handler\n");
return TEST_RESULT_FAIL;
}
tftf_irq_enable_sgi(sgi_id, GIC_HIGHEST_NS_PRIORITY);
tftf_send_sgi(sgi_id, core_pos);
if ((serror_triggered == false) || (irq_triggered == false)) {
tftf_testcase_printf("SError or IRQ is not triggered\n");
return TEST_RESULT_FAIL;
}
ret = tftf_irq_unregister_handler_sgi(sgi_id);
if (ret != 0) {
tftf_testcase_printf("Failed to unregister IRQ handler\n");
return TEST_RESULT_FAIL;
}
unregister_custom_serror_handler();
return TEST_RESULT_SUCCESS;
}
/*
* Test Steps:
* 1. Register a custom SError handler for tftf
* 3. Disable SError (PSTATE.A = 1)
* 4. Inject RAS error and give time for it to trigger.
* 5. Ensure SError is not triggered before making SMC call.
* 7. On entering EL3 it will find that SError is pending, So it will call
* "reflect_pending_serror_to_lower_el" and eret.
* 8. TF-A will eret back from EL3(without handling SMC) and during ERET
* unmask SError for TFTF (SPSR.PSTATE.A = 0).
* 9 .At TFTF entry it will see an SError pending which will cause registered
* SError handler to be called.
* 10.After retruning back from EL3 the original SMC request will be handled.
*/
test_result_t test_ras_kfh_reflect_sync(void)
{
smc_args args;
smc_ret_values ret;
serror_triggered = 0;
register_custom_serror_handler(serror_handler);
disable_serror();
inject_unrecoverable_ras_error();
waitms(50);
/* Ensure that we are testing reflection path, SMC before SError */
if (serror_triggered == true) {
tftf_testcase_printf("SError was triggered before SMC\n");
return TEST_RESULT_FAIL;
}
memset(&args, 0, sizeof(args));
args.fid = SMCCC_VERSION;
ret = tftf_smc(&args);
tftf_testcase_printf("SMCCC Version = %d.%d\n",
(int)((ret.ret0 >> SMCCC_VERSION_MAJOR_SHIFT) & SMCCC_VERSION_MAJOR_MASK),
(int)((ret.ret0 >> SMCCC_VERSION_MINOR_SHIFT) & SMCCC_VERSION_MINOR_MASK));
if ((int32_t)ret.ret0 != expected_ver) {
tftf_testcase_printf("Unexpected SMCCC version: 0x%x\n", (int)ret.ret0);
return TEST_RESULT_FAIL;
}
unregister_custom_serror_handler();
if (serror_triggered == false) {
tftf_testcase_printf("SError is not triggered\n");
return TEST_RESULT_FAIL;
}
return TEST_RESULT_SUCCESS;
}
#else
test_result_t test_ras_kfh_reflect_irq(void)
{
tftf_testcase_printf("Not supported on AArch32.\n");
return TEST_RESULT_SKIPPED;
}
test_result_t test_ras_kfh_reflect_sync(void)
{
tftf_testcase_printf("Not supported on AArch32.\n");
return TEST_RESULT_SKIPPED;
}
#endif