blob: 16004f448302da9a992b3f8d597b078f2a840ed1 [file] [log] [blame]
/*
* Copyright (c) 2018-2025, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch_helpers.h>
#include <debug.h>
#include <drivers/arm/private_timer.h>
#include <events.h>
#include <plat_topology.h>
#include <platform.h>
#include <power_management.h>
#include <sdei.h>
#include <tftf_lib.h>
#include <timer.h>
#define EV_COOKIE 0xDEADBEEF
#define TIMER_TIMEO_MS 10
extern sdei_handler_t sdei_entrypoint;
extern sdei_handler_t sdei_entrypoint_resume;
/*
* the bound event number as returned from sdei_interrupt_bind(), passed
* to the per-cpu SDEI test function
*/
static int bound_ev;
/* true if the test is using a private interrupt source, false otherwise. */
static int private_interrupt;
static spinlock_t cpu_count_lock;
static volatile int cpu_count;
static volatile int participating_cpu_count;
/* Helper function to wait for CPUs participating in the test. */
static void wait_for_participating_cpus(void)
{
assert(participating_cpu_count <= PLATFORM_CORE_COUNT);
spin_lock(&cpu_count_lock);
cpu_count++;
spin_unlock(&cpu_count_lock);
assert(cpu_count <= PLATFORM_CORE_COUNT);
while (cpu_count != participating_cpu_count)
continue;
}
void sdei_trigger_event(void)
{
printf("%s: triggering SDEI event\n", __func__);
if (private_interrupt)
private_timer_start(TIMER_TIMEO_MS);
else
tftf_program_timer(TIMER_TIMEO_MS);
}
static test_result_t sdei_event(void)
{
long long ret;
wait_for_participating_cpus();
printf("%s: mpidr = 0x%llx\n", __func__,
(unsigned long long)read_mpidr_el1());
ret = sdei_event_register(bound_ev, sdei_entrypoint_resume, EV_COOKIE,
SDEI_REGF_RM_PE, read_mpidr_el1());
if (ret < 0) {
tftf_testcase_printf("SDEI event register failed: 0x%llx\n",
ret);
return TEST_RESULT_FAIL;
}
ret = sdei_event_enable(bound_ev);
if (ret < 0) {
tftf_testcase_printf("SDEI event enable failed: 0x%llx\n", ret);
goto err0;
}
ret = sdei_pe_unmask();
if (ret < 0) {
tftf_testcase_printf("SDEI pe unmask failed: 0x%llx\n", ret);
goto err1;
}
sdei_trigger_event();
sdei_handler_done();
sdei_pe_mask();
err1:
sdei_event_disable(bound_ev);
err0:
sdei_event_unregister(bound_ev);
if (ret < 0)
return TEST_RESULT_FAIL;
return TEST_RESULT_SUCCESS;
}
int sdei_event_handler(int ev, unsigned long long arg)
{
printf("%s: handler fired\n", __func__);
assert(arg == EV_COOKIE);
if (private_interrupt)
private_timer_stop();
else
tftf_cancel_timer();
return 0;
}
/* Handle an SDEI event on all cores in sequence. */
test_result_t test_sdei_event_serial(void)
{
struct sdei_intr_ctx intr_ctx;
u_register_t lead_mpid, target_mpid;
int cpu_node;
long long ret;
lead_mpid = read_mpidr_el1() & MPID_MASK;
participating_cpu_count = 1;
init_spinlock(&cpu_count_lock);
cpu_count = 0;
ret = sdei_version();
if (ret != MAKE_SDEI_VERSION(1, 0, 0)) {
tftf_testcase_printf("Unexpected SDEI version: 0x%llx\n", ret);
return TEST_RESULT_SKIPPED;
}
disable_irq();
bound_ev = sdei_interrupt_bind(tftf_get_timer_irq(), &intr_ctx);
if (bound_ev < 0) {
tftf_testcase_printf("SDEI interrupt bind failed: %x\n",
bound_ev);
return TEST_RESULT_FAIL;
}
/* use a shared interrupt source for this test-case */
private_interrupt = 0;
for_each_cpu(cpu_node) {
target_mpid = tftf_get_mpidr_from_node(cpu_node) & MPID_MASK;
if (lead_mpid == target_mpid)
continue;
ret = tftf_cpu_on(target_mpid,
(uintptr_t)sdei_event, 0);
if (ret != PSCI_E_SUCCESS) {
ERROR("CPU ON failed for 0x0x%llx\n",
(unsigned long long)target_mpid);
goto err0;
}
while (tftf_psci_affinity_info(target_mpid, MPIDR_AFFLVL0) !=
PSCI_STATE_OFF)
continue;
cpu_count--;
}
assert(cpu_count == 0);
if (sdei_event() != TEST_RESULT_SUCCESS)
goto err0;
cpu_count--;
assert(cpu_count == 0);
sdei_interrupt_release(bound_ev, &intr_ctx);
enable_irq();
return TEST_RESULT_SUCCESS;
err0:
sdei_private_reset();
sdei_shared_reset();
sdei_interrupt_release(bound_ev, &intr_ctx);
enable_irq();
return TEST_RESULT_FAIL;
}
/* Handle an SDEI event on all cores in parallel. */
test_result_t test_sdei_event_parallel(void)
{
struct sdei_intr_ctx intr_ctx;
u_register_t lead_mpid, target_mpid;
int cpu_node;
long long ret;
lead_mpid = read_mpidr_el1() & MPID_MASK;
participating_cpu_count = tftf_get_total_cpus_count();
init_spinlock(&cpu_count_lock);
cpu_count = 0;
ret = sdei_version();
if (ret != MAKE_SDEI_VERSION(1, 0, 0)) {
tftf_testcase_printf("Unexpected SDEI version: 0x%llx\n", ret);
return TEST_RESULT_SKIPPED;
}
disable_irq();
bound_ev = sdei_interrupt_bind(IRQ_PCPU_HP_TIMER, &intr_ctx);
if (bound_ev < 0) {
tftf_testcase_printf("SDEI interrupt bind failed: %x\n",
bound_ev);
return TEST_RESULT_FAIL;
}
/* use a private interrupt source for this test-case */
private_interrupt = 1;
for_each_cpu(cpu_node) {
target_mpid = tftf_get_mpidr_from_node(cpu_node) & MPID_MASK;
if (lead_mpid == target_mpid)
continue;
ret = tftf_cpu_on(target_mpid,
(uintptr_t)sdei_event, 0);
if (ret != PSCI_E_SUCCESS) {
ERROR("CPU ON failed for 0x0x%llx\n",
(unsigned long long)target_mpid);
goto err0;
}
}
if (sdei_event() != TEST_RESULT_SUCCESS)
goto err0;
for_each_cpu(cpu_node) {
target_mpid = tftf_get_mpidr_from_node(cpu_node) & MPID_MASK;
if (lead_mpid == target_mpid)
continue;
while (tftf_psci_affinity_info(target_mpid, MPIDR_AFFLVL0) !=
PSCI_STATE_OFF)
continue;
cpu_count--;
}
cpu_count--;
assert(cpu_count == 0);
sdei_interrupt_release(bound_ev, &intr_ctx);
enable_irq();
return TEST_RESULT_SUCCESS;
err0:
sdei_private_reset();
sdei_shared_reset();
sdei_interrupt_release(bound_ev, &intr_ctx);
enable_irq();
return TEST_RESULT_FAIL;
}
static test_result_t sdei_event_signal_self(bool enable, bool unmask)
{
long long ret;
ret = sdei_event_register(0, sdei_entrypoint_resume, EV_COOKIE,
SDEI_REGF_RM_PE, read_mpidr_el1());
if (ret < 0) {
tftf_testcase_printf("SDEI event register failed: 0x%llx\n",
ret);
return TEST_RESULT_FAIL;
}
if (enable) {
ret = sdei_event_enable(0);
if (ret < 0) {
tftf_testcase_printf("SDEI event enable failed: 0x%llx\n", ret);
goto err0;
}
}
if (unmask) {
ret = sdei_pe_unmask();
if (ret < 0) {
tftf_testcase_printf("SDEI pe unmask failed: 0x%llx\n", ret);
goto err1;
}
}
ret = sdei_event_signal(read_mpidr_el1());
if (ret < 0) {
tftf_testcase_printf("SDEI event signal failed: 0x%llx\n", ret);
goto err2;
}
sdei_handler_done();
err2:
sdei_pe_mask();
err1:
sdei_event_disable(0);
err0:
sdei_event_unregister(0);
if (ret < 0)
return TEST_RESULT_FAIL;
return TEST_RESULT_SUCCESS;
}
static test_result_t sdei_event_signal_self_all(void)
{
return sdei_event_signal_self(true, true);
}
/* Each core signals itself using SDEI event signalling. */
test_result_t test_sdei_event_signal_serial(void)
{
u_register_t lead_mpid, target_mpid;
int cpu_node;
long long ret;
lead_mpid = read_mpidr_el1() & MPID_MASK;
ret = sdei_version();
if (ret != MAKE_SDEI_VERSION(1, 0, 0)) {
tftf_testcase_printf("Unexpected SDEI version: 0x%llx\n", ret);
return TEST_RESULT_SKIPPED;
}
disable_irq();
for_each_cpu(cpu_node) {
target_mpid = tftf_get_mpidr_from_node(cpu_node) & MPID_MASK;
if (lead_mpid == target_mpid)
continue;
ret = tftf_cpu_on(target_mpid,
(uintptr_t)sdei_event_signal_self_all, 0);
if (ret != PSCI_E_SUCCESS) {
ERROR("CPU ON failed for 0x%llx\n",
(unsigned long long)target_mpid);
ret = -1;
goto err0;
}
while (tftf_psci_affinity_info(target_mpid, MPIDR_AFFLVL0) !=
PSCI_STATE_OFF)
continue;
}
if (sdei_event_signal_self_all() != TEST_RESULT_SUCCESS) {
ret = -1;
goto err0;
}
err0:
enable_irq();
if (ret < 0)
return TEST_RESULT_FAIL;
return TEST_RESULT_SUCCESS;
}
static event_t cpu_ready[PLATFORM_CORE_COUNT];
static test_result_t sdei_wait_for_event_signal(void)
{
int core_pos;
long long ret;
ret = sdei_event_register(0, sdei_entrypoint_resume, EV_COOKIE,
SDEI_REGF_RM_PE, read_mpidr_el1());
if (ret < 0) {
tftf_testcase_printf("SDEI event register failed: 0x%llx\n",
ret);
return TEST_RESULT_FAIL;
}
ret = sdei_event_enable(0);
if (ret < 0) {
tftf_testcase_printf("SDEI event enable failed: 0x%llx\n", ret);
goto err0;
}
ret = sdei_pe_unmask();
if (ret < 0) {
tftf_testcase_printf("SDEI pe unmask failed: 0x%llx\n", ret);
goto err1;
}
core_pos = platform_get_core_pos(read_mpidr_el1());
tftf_send_event(&cpu_ready[core_pos]);
sdei_handler_done();
sdei_pe_mask();
err1:
sdei_event_disable(0);
err0:
sdei_event_unregister(0);
if (ret < 0)
return TEST_RESULT_FAIL;
return TEST_RESULT_SUCCESS;
}
/*
* The primary core will signal all other cores
* use SDEI event signalling.
*/
test_result_t test_sdei_event_signal_all(void)
{
u_register_t lead_mpid, target_mpid;
int cpu_node, core_pos;
int i;
long long ret;
for (i = 0; i < PLATFORM_CORE_COUNT; i++)
tftf_init_event(&cpu_ready[i]);
lead_mpid = read_mpidr_el1() & MPID_MASK;
ret = sdei_version();
if (ret != MAKE_SDEI_VERSION(1, 0, 0)) {
tftf_testcase_printf("Unexpected SDEI version: 0x%llx\n", ret);
return TEST_RESULT_SKIPPED;
}
disable_irq();
for_each_cpu(cpu_node) {
target_mpid = tftf_get_mpidr_from_node(cpu_node) & MPID_MASK;
if (lead_mpid == target_mpid)
continue;
ret = tftf_cpu_on(target_mpid,
(uintptr_t)sdei_wait_for_event_signal, 0);
if (ret != PSCI_E_SUCCESS) {
ERROR("CPU ON failed for 0x0x%llx\n",
(unsigned long long)target_mpid);
ret = -1;
goto err0;
}
core_pos = platform_get_core_pos(target_mpid);
tftf_wait_for_event(&cpu_ready[core_pos]);
}
for_each_cpu(cpu_node) {
target_mpid = tftf_get_mpidr_from_node(cpu_node) & MPID_MASK;
if (lead_mpid == target_mpid)
continue;
ret = sdei_event_signal(target_mpid);
if (ret < 0) {
tftf_testcase_printf("SDEI event signal failed: 0x%llx\n",
ret);
ret = -1;
goto err0;
}
}
err0:
enable_irq();
if (ret < 0)
return TEST_RESULT_FAIL;
return TEST_RESULT_SUCCESS;
}
/* SDEI event signaling state should be: registered, enabled, unmasked */
test_result_t test_sdei_event_signal_state(void)
{
long long ret;
ret = sdei_version();
if (ret != MAKE_SDEI_VERSION(1, 0, 0)) {
tftf_testcase_printf("Unexpected SDEI version: 0x%llx\n", ret);
return TEST_RESULT_SKIPPED;
}
disable_irq();
if (sdei_event_signal_self(true, true) == TEST_RESULT_FAIL) {
tftf_testcase_printf("UNEXPECTED: SDEI event signaling failed when "
"enabled and unmasked\n");
ret = -1;
goto err0;
} else {
tftf_testcase_printf("EXPECTED: SDEI event signaling succeeded when "
"enabled and unmasked\n");
}
if (sdei_event_signal_self(false, true) == TEST_RESULT_SUCCESS) {
tftf_testcase_printf("UNEXPECTED: SDEI event signaling succeeded when "
"disabled and unmasked\n");
ret = -1;
goto err0;
} else {
tftf_testcase_printf("EXPECTED: SDEI event signaling failed when "
"disabled and unmasked\n");
}
if (sdei_event_signal_self(true, false) == TEST_RESULT_SUCCESS) {
tftf_testcase_printf("UNEXPECTED: SDEI event signaling succeeded when "
"enabled and masked\n");
ret = -1;
goto err0;
} else {
tftf_testcase_printf("EXPECTED: SDEI event signaling failed when "
"enabled and masked\n");
}
if (sdei_event_signal_self(false, false) == TEST_RESULT_SUCCESS) {
tftf_testcase_printf("UNEXPECTED: SDEI event signaling succeeded when "
"disabled and masked\n");
ret = -1;
goto err0;
} else {
tftf_testcase_printf("EXPECTED: SDEI event signaling failed when "
"disabled and masked\n");
}
err0:
enable_irq();
if (ret < 0)
return TEST_RESULT_FAIL;
return TEST_RESULT_SUCCESS;
}