blob: d038f862bb0127a588916c511a60c5a51dd1c5a4 [file] [log] [blame]
/*
* Copyright (c) 2018-2025, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch_helpers.h>
#include <assert.h>
#include <debug.h>
#include <drivers/arm/arm_gic.h>
#include <irq.h>
#include <mmio.h>
#include <platform.h>
#include <platform_def.h>
#include <smccc.h>
#include <std_svc.h>
#include <stdlib.h>
#include <string.h>
#include <test_helpers.h>
#include <tftf.h>
#define STRESS_COUNT 100
/*
* The shared data between the handler and the
* preempt_tsp_via_SGI routine.
*/
typedef struct {
smc_ret_values tsp_result;
int wait_for_fiq;
} irq_handler_shared_data;
static irq_handler_shared_data shared_data;
/*
* Handler for the SGI #0.
*/
static int sgi_handler(void *data)
{
/* Ensure this is the SGI we expect */
assert(*(unsigned int *)data == IRQ_NS_SGI_0);
if (shared_data.wait_for_fiq)
wfi(); /* We will get woken by the FIQ firing */
return 0;
}
/*
* This routine issues a SGI with interrupts disabled to make sure that the
* pending SGI will preempt a STD SMC.
*/
static test_result_t preempt_tsp_via_SGI(const smc_args *tsp_svc_params,
int hold_irq_handler_for_fiq)
{
int rc;
unsigned int core_mpid = read_mpidr_el1() & MPID_MASK;
unsigned int core_pos = platform_get_core_pos(core_mpid);
test_result_t result = TEST_RESULT_SUCCESS;
memset(&shared_data, 0, sizeof(shared_data));
if (hold_irq_handler_for_fiq)
shared_data.wait_for_fiq = 1;
/* Register Handler for the interrupt. SGIs #0 - #6 are available. */
rc = tftf_irq_register_handler_sgi(IRQ_NS_SGI_0, sgi_handler);
if (rc != 0) {
tftf_testcase_printf("Failed to register SGI handler. "
"Error code = %d\n", rc);
return TEST_RESULT_SKIPPED;
}
/* Enable SGI #0 */
tftf_irq_enable_sgi(IRQ_NS_SGI_0, GIC_HIGHEST_NS_PRIORITY);
/* Set PSTATE.I to 0. */
disable_irq();
/*
* Send SGI to the current CPU. It can't be handled because the
* interrupts are disabled.
*/
tftf_send_sgi(IRQ_NS_SGI_0, core_pos);
/*
* Invoke an STD SMC. Should be pre-empted because of the SGI that is
* waiting.
*/
shared_data.tsp_result = tftf_smc(tsp_svc_params);
if (shared_data.tsp_result.ret0 != TSP_SMC_PREEMPTED) {
tftf_testcase_printf("SMC returned 0x%llX instead of "
"TSP_SMC_PREEMPTED.\n",
(unsigned long long)shared_data.tsp_result.ret0);
result = TEST_RESULT_FAIL;
}
/* Set PSTATE.I to 1. The SGI will be handled after this. */
enable_irq();
/* Disable SGI #0 */
tftf_irq_disable_sgi(IRQ_NS_SGI_0);
/* Unregister handler */
rc = tftf_irq_unregister_handler_sgi(IRQ_NS_SGI_0);
if (rc != 0) {
tftf_testcase_printf("Failed to unregister IRQ handler. "
"Error code = %d\n", rc);
result = TEST_RESULT_FAIL;
}
return result;
}
/*
* @Test_Aim@ Test the secure world preemption by non secure interrupt.
*
* Steps: 1. Issue Standard SMC and preempt it via SGI
* 2. Resume the preempted SMC
* Returns SUCCESS if above 2 steps are performed correctly else failure.
*/
test_result_t tsp_int_and_resume(void)
{
smc_args tsp_svc_params;
smc_ret_values tsp_result = {0};
test_result_t res;
SKIP_TEST_IF_TSP_NOT_PRESENT();
/* Standard SMC */
tsp_svc_params.fid = TSP_STD_FID(TSP_ADD);
tsp_svc_params.arg1 = 4;
tsp_svc_params.arg2 = 6;
res = preempt_tsp_via_SGI(&tsp_svc_params, 0);
if (res == TEST_RESULT_FAIL)
return res;
/* Now that we have ensured preemption, issue RESUME */
tsp_svc_params.fid = TSP_FID_RESUME;
tsp_result = tftf_smc(&tsp_svc_params);
/* Check the result of the addition */
if (tsp_result.ret0 != 0 || tsp_result.ret1 != 8 ||
tsp_result.ret2 != 12) {
tftf_testcase_printf("SMC resume returned wrong result:"
"got %d %d %d expected: 0 8 12\n",
(unsigned int)tsp_result.ret0,
(unsigned int)tsp_result.ret1,
(unsigned int)tsp_result.ret2);
tftf_testcase_printf("SMC resume returned wrong result\n");
return TEST_RESULT_FAIL;
}
/* Standard SMC */
tsp_svc_params.fid = TSP_STD_FID(TSP_SUB);
tsp_svc_params.arg1 = 4;
tsp_svc_params.arg2 = 6;
res = preempt_tsp_via_SGI(&tsp_svc_params, 0);
if (res == TEST_RESULT_FAIL)
return res;
/* Now that we have ensured preemption, issue RESUME */
tsp_svc_params.fid = TSP_FID_RESUME;
tsp_result = tftf_smc(&tsp_svc_params);
/* Check the result of the substraction */
if (tsp_result.ret0 != 0 || tsp_result.ret1 != 0 ||
tsp_result.ret2 != 0) {
tftf_testcase_printf("SMC resume returned wrong result:"
"got %d %d %d expected: 0 0 0\n",
(unsigned int)tsp_result.ret0,
(unsigned int)tsp_result.ret1,
(unsigned int)tsp_result.ret2);
return TEST_RESULT_FAIL;
}
/* Standard SMC */
tsp_svc_params.fid = TSP_STD_FID(TSP_MUL);
tsp_svc_params.arg1 = 4;
tsp_svc_params.arg2 = 6;
res = preempt_tsp_via_SGI(&tsp_svc_params, 0);
if (res == TEST_RESULT_FAIL)
return res;
/* Now that we have ensured preemption, issue RESUME */
tsp_svc_params.fid = TSP_FID_RESUME;
tsp_result = tftf_smc(&tsp_svc_params);
/* Check the result of the multiplication */
if (tsp_result.ret0 != 0 || tsp_result.ret1 != 16 ||
tsp_result.ret2 != 36) {
tftf_testcase_printf("SMC resume returned wrong result:"
"got %d %d %d expected: 0 16 36\n",
(unsigned int)tsp_result.ret0,
(unsigned int)tsp_result.ret1,
(unsigned int)tsp_result.ret2);
return TEST_RESULT_FAIL;
}
/* Standard SMC */
tsp_svc_params.fid = TSP_STD_FID(TSP_DIV);
tsp_svc_params.arg1 = 4;
tsp_svc_params.arg2 = 6;
res = preempt_tsp_via_SGI(&tsp_svc_params, 0);
if (res == TEST_RESULT_FAIL)
return res;
/* Now that we have ensured preemption, issue RESUME */
tsp_svc_params.fid = TSP_FID_RESUME;
tsp_result = tftf_smc(&tsp_svc_params);
/* Check the result of the division */
if (tsp_result.ret0 != 0 || tsp_result.ret1 != 1 ||
tsp_result.ret2 != 1) {
tftf_testcase_printf("SMC resume returned wrong result:"
"got %d %d %d expected: 0 1 1\n",
(unsigned int)tsp_result.ret0,
(unsigned int)tsp_result.ret1,
(unsigned int)tsp_result.ret2);
return TEST_RESULT_FAIL;
}
return TEST_RESULT_SUCCESS;
}
/*
* @Test_Aim@ Verify Fast SMC request on an interrupted tsp returns error.
*
* Steps: 1. Issue Standard SMC and preempt it via SGI
* 2. Issue Fast SMC, this is not expected and TSP should return error.
* 3. Resume the preempted SMC and verify the result.
* Returns SUCCESS if above 3 steps are performed correctly else failure.
*/
test_result_t test_fast_smc_when_tsp_preempted(void)
{
smc_args tsp_svc_params;
smc_ret_values tsp_result = {0};
test_result_t res = TEST_RESULT_SUCCESS;
SKIP_TEST_IF_TSP_NOT_PRESENT();
/* Standard SMC */
tsp_svc_params.fid = TSP_STD_FID(TSP_ADD);
tsp_svc_params.arg1 = 4;
tsp_svc_params.arg2 = 6;
res = preempt_tsp_via_SGI(&tsp_svc_params, 0);
if (res == TEST_RESULT_FAIL)
return res;
/* Now that we have ensured preemption, issue Fast SMC */
tsp_svc_params.fid = TSP_FAST_FID(TSP_ADD);
tsp_svc_params.arg1 = 4;
tsp_svc_params.arg2 = 6;
tsp_result = tftf_smc(&tsp_svc_params);
if (tsp_result.ret0 != SMC_UNKNOWN) {
tftf_testcase_printf("Fast SMC should not execute"
"while SMC is preempted\n");
res = TEST_RESULT_FAIL;
}
/* Issue RESUME */
tsp_svc_params.fid = TSP_FID_RESUME;
tsp_result = tftf_smc(&tsp_svc_params);
/* Check the result of the addition */
if (tsp_result.ret0 != 0 || tsp_result.ret1 != 8 ||
tsp_result.ret2 != 12) {
tftf_testcase_printf("SMC resume returned wrong result:"
"got %d %d %d expected: 0 8 12\n",
(unsigned int)tsp_result.ret0,
(unsigned int)tsp_result.ret1,
(unsigned int)tsp_result.ret2);
res = TEST_RESULT_FAIL;
}
return res;
}
/*
* @Test_Aim@ Test the Standard SMC when tsp is pre-empted by interrupt.
*
* Steps:
* 1. Issue Standard SMC and preempt it via SGI
* 2. Issue another Standard SMC. this is not expected and TSP should return
* error.
* 3. Resume the preempted SMC or abort if the parameter `abort_smc` is set to
* 1.
* 4. Check the result if the SMC was resumed, or just carry on if it was
* aborted.
* Returns SUCCESS if above 4 steps are performed correctly else failure.
*/
static test_result_t test_std_smc_when_tsp_preempted(int abort_smc)
{
smc_args tsp_svc_params;
smc_ret_values tsp_result = {0};
test_result_t res = TEST_RESULT_SUCCESS;
SKIP_TEST_IF_TSP_NOT_PRESENT();
/* Standard SMC */
tsp_svc_params.fid = TSP_STD_FID(TSP_ADD);
tsp_svc_params.arg1 = 4;
tsp_svc_params.arg2 = 6;
res = preempt_tsp_via_SGI(&tsp_svc_params, 0);
if (res == TEST_RESULT_FAIL)
return res;
/* Now that we have ensured preemption, issue Standard SMC */
tsp_svc_params.fid = TSP_STD_FID(TSP_ADD);
tsp_svc_params.arg1 = 4;
tsp_svc_params.arg2 = 6;
tsp_result = tftf_smc(&tsp_svc_params);
if (tsp_result.ret0 != SMC_UNKNOWN) {
tftf_testcase_printf("Standard SMC should not execute while SMC is preempted\n");
res = TEST_RESULT_FAIL;
}
/* Issue ABORT or RESUME */
tsp_svc_params.fid = abort_smc ? TSP_FID_ABORT : TSP_FID_RESUME;
tsp_result = tftf_smc(&tsp_svc_params);
/*
* There is no way to check that the ABORT succeeded or failed because
* it will return SMC_UNKNOWN in both cases.
*/
if (!abort_smc) {
/*
* Check the result of the addition if we issued RESUME.
*/
if (tsp_result.ret0 != 0 || tsp_result.ret1 != 8 ||
tsp_result.ret2 != 12) {
tftf_testcase_printf("SMC resume returned wrong result: got %d %d %d expected: 0 8 12\n",
(unsigned int)tsp_result.ret0,
(unsigned int)tsp_result.ret1,
(unsigned int)tsp_result.ret2);
res = TEST_RESULT_FAIL;
}
}
return res;
}
test_result_t test_std_smc_when_tsp_preempted_resume(void)
{
return test_std_smc_when_tsp_preempted(0);
}
test_result_t test_std_smc_when_tsp_preempted_abort(void)
{
return test_std_smc_when_tsp_preempted(1);
}
/*
* @Test_Aim@ Test RESUME SMC call when TSP is not preempted. RESUME should fail.
*
* Issues resume SMC. This is not expected by TSP and returns error.
* This is a negative test, Return SUCCESS is RESUME returns SMC_UNKNOWN
*/
test_result_t test_resume_smc_without_preemption(void)
{
smc_args tsp_svc_params;
smc_ret_values tsp_result = {0};
SKIP_TEST_IF_TSP_NOT_PRESENT();
/* Issue RESUME */
tsp_svc_params.fid = TSP_FID_RESUME;
tsp_result = tftf_smc(&tsp_svc_params);
if (tsp_result.ret0 != SMC_UNKNOWN) {
tftf_testcase_printf("SMC Resume should return UNKNOWN, got:%d\n", \
(unsigned int)tsp_result.ret0);
return TEST_RESULT_FAIL;
}
return TEST_RESULT_SUCCESS;
}
/*
* @Test_Aim@ Stress Test the secure world preemption by non secure interrupt
*
* Steps: 1. Issue Standard SMC and preempt it via SGI
* 2. Resume the preempted SMC and repeat from Step 1 for STRESS_COUNT times.
* Returns SUCCESS if above 2 steps are performed correctly else failure.
*/
test_result_t tsp_int_and_resume_stress(void)
{
smc_args tsp_svc_params;
smc_ret_values tsp_result = {0};
test_result_t res = TEST_RESULT_SUCCESS;
SKIP_TEST_IF_TSP_NOT_PRESENT();
int count = 0;
NOTICE("This stress test will repeat %d times\n", STRESS_COUNT);
while ((count < STRESS_COUNT) &&
(res == TEST_RESULT_SUCCESS)) {
/* Standard SMC */
tsp_svc_params.fid = TSP_STD_FID(TSP_ADD);
tsp_svc_params.arg1 = 4;
tsp_svc_params.arg2 = 6;
/* Try to preempt TSP via IRQ */
res = preempt_tsp_via_SGI(&tsp_svc_params, 0);
if (res == TEST_RESULT_FAIL)
return res;
/* Issue RESUME */
tsp_svc_params.fid = TSP_FID_RESUME;
tsp_result = tftf_smc(&tsp_svc_params);
/* Check the result of the addition */
if (tsp_result.ret0 != 0 || tsp_result.ret1 != 8 ||
tsp_result.ret2 != 12) {
tftf_testcase_printf("SMC resume returned wrong result:"
"got %d %d %d expected: 0 8 12\n",
(unsigned int)tsp_result.ret0,
(unsigned int)tsp_result.ret1,
(unsigned int)tsp_result.ret2);
res = TEST_RESULT_FAIL;
}
count++;
}
return res;
}
/*
* @Test_Aim@ Test Secure FIQ when pre-empted by non secure interrupt.
*
* We really cannot verify whether FIQ fired and preempted the SGI handler
* or not. The TSP prints the address at which the execution was interrupted
* for the FIQ. By looking at the address printed from the TSP logs, we can
* verify that the SGI handler was interrupted by FIQ. For now, We are assuming
* CPU is woken by Secure Timer Interrupt.
*
* Steps: 1. Issue Standard SMC and preempt it via SGI
* 2. Wait in the SGI handler for FIQ which is firing every 500 ms.
* 3. Resume the preempted SMC
* Returns SUCCESS if above 3 steps are performed correctly else failure.
*/
test_result_t tsp_fiq_while_int(void)
{
smc_args tsp_svc_params;
smc_ret_values tsp_result = {0};
test_result_t res;
SKIP_TEST_IF_TSP_NOT_PRESENT();
/* Standard SMC */
tsp_svc_params.fid = TSP_STD_FID(TSP_ADD);
tsp_svc_params.arg1 = 4;
tsp_svc_params.arg2 = 6;
res = preempt_tsp_via_SGI(&tsp_svc_params, 1);
if (res == TEST_RESULT_FAIL)
return res;
/* Now that we have ensured preemption, issue RESUME */
tsp_svc_params.fid = TSP_FID_RESUME;
tsp_result = tftf_smc(&tsp_svc_params);
/* Check the result of the addition */
if (tsp_result.ret0 != 0 || tsp_result.ret1 != 8 ||
tsp_result.ret2 != 12) {
tftf_testcase_printf("SMC resume returned wrong result:"
"got %d %d %d expected: 0 8 12\n",
(unsigned int)tsp_result.ret0,
(unsigned int)tsp_result.ret1,
(unsigned int)tsp_result.ret2);
return TEST_RESULT_FAIL;
}
return TEST_RESULT_SUCCESS;
}