test: arch timer in nwd is honored across world switch
This patch introduces a test to ensure that the functionality of arch
(EL1 physical) timer configured by NWd endpoint, such as an hypervisor,
is not corrupted by SPMC when an SP also configures the arch timer for
its own use.
Also, necessary helpers and utilities to create the test scenario have
been added.
Signed-off-by: Madhukar Pappireddy <madhukar.pappireddy@arm.com>
Signed-off-by: Olivier Deprez <olivier.deprez@arm.com>
Change-Id: I1cfd1e1117412b2b23a57af30064c41dc2e66e0b
diff --git a/include/runtime_services/cactus_test_cmds.h b/include/runtime_services/cactus_test_cmds.h
index 282cfbe..a5fb4b6 100644
--- a/include/runtime_services/cactus_test_cmds.h
+++ b/include/runtime_services/cactus_test_cmds.h
@@ -691,4 +691,23 @@
return (uint64_t)ret.arg4;
}
+#define CACTUS_SET_ARCH_TIMER_CMD U(0x54494d4552)
+
+static inline struct ffa_value cactus_send_arch_timer_cmd(
+ ffa_id_t source, ffa_id_t dest, uint64_t deadline, uint64_t wait)
+{
+ return cactus_send_cmd(source, dest, CACTUS_SET_ARCH_TIMER_CMD, deadline, wait,
+ 0, 0);
+}
+
+static inline uint64_t cactus_get_timer_deadline(struct ffa_value ret)
+{
+ return (uint64_t)ret.arg4;
+}
+
+static inline uint64_t cactus_get_timer_wait_time(struct ffa_value ret)
+{
+ return (uint64_t)ret.arg5;
+}
+
#endif
diff --git a/include/runtime_services/spm_common.h b/include/runtime_services/spm_common.h
index c794cb0..4dd5e5c 100644
--- a/include/runtime_services/spm_common.h
+++ b/include/runtime_services/spm_common.h
@@ -24,6 +24,8 @@
/* ID for the first Secure Partition. */
#define SPM_VM_ID_FIRST SP_ID(1)
+#define TIMER_VIRTUAL_INTID U(3)
+
/* INTID for the managed exit virtual interrupt. */
#define MANAGED_EXIT_INTERRUPT_ID U(4)
diff --git a/spm/cactus/cactus.mk b/spm/cactus/cactus.mk
index 0fa0ab5..b50975c 100644
--- a/spm/cactus/cactus.mk
+++ b/spm/cactus/cactus.mk
@@ -57,6 +57,7 @@
cactus_test_memory_sharing.c \
cactus_tests_smmuv3.c \
cactus_test_notifications.c \
+ cactus_test_timer.c \
)
# TODO: Remove dependency on TFTF files.
@@ -66,7 +67,8 @@
tftf/tests/runtime_services/secure_service/${ARCH}/ffa_arch_helpers.S \
tftf/tests/runtime_services/secure_service/ffa_helpers.c \
tftf/tests/runtime_services/secure_service/spm_common.c \
- tftf/framework/${ARCH}/exception_report.c
+ tftf/framework/${ARCH}/exception_report.c \
+ lib/delay/delay.c
CACTUS_SOURCES += drivers/arm/pl011/${ARCH}/pl011_console.S \
drivers/arm/sp805/sp805.c \
diff --git a/spm/cactus/cactus_interrupt.c b/spm/cactus/cactus_interrupt.c
index 0a9035f..d0c15a5 100644
--- a/spm/cactus/cactus_interrupt.c
+++ b/spm/cactus/cactus_interrupt.c
@@ -89,18 +89,29 @@
VERBOSE("Resuming the suspended command\n");
}
+static void timer_interrupt_handler(void)
+{
+ /* Disable the EL1 physical arch timer. */
+ write_cntp_ctl_el0(0);
+
+ spm_interrupt_deactivate(TIMER_VIRTUAL_INTID);
+ NOTICE("serviced el1 physical timer\n");
+}
+
void register_maintenance_interrupt_handlers(void)
{
sp_register_interrupt_handler(send_managed_exit_response,
managed_exit_interrupt_id);
sp_register_interrupt_handler(notification_pending_interrupt_handler,
NOTIFICATION_PENDING_INTERRUPT_INTID);
+ sp_register_interrupt_handler(timer_interrupt_handler,
+ TIMER_VIRTUAL_INTID);
}
void cactus_interrupt_handler_irq(void)
{
uint32_t intid = spm_interrupt_get();
- unsigned int core_pos = get_current_core_id();
+ unsigned int core_pos = spm_get_my_core_pos();
last_serviced_interrupt[core_pos] = intid;
@@ -118,7 +129,7 @@
void cactus_interrupt_handler_fiq(void)
{
uint32_t intid = spm_interrupt_get();
- unsigned int core_pos = get_current_core_id();
+ unsigned int core_pos = spm_get_my_core_pos();
last_serviced_interrupt[core_pos] = intid;
diff --git a/spm/cactus/cactus_main.c b/spm/cactus/cactus_main.c
index b608d7b..40f7618 100644
--- a/spm/cactus/cactus_main.c
+++ b/spm/cactus/cactus_main.c
@@ -288,6 +288,12 @@
enable_irq();
enable_fiq();
+ /* Disable the arch timer at boot. */
+ write_cntp_ctl_el0(0);
+
+ /* Enable the arch timer virtual interrupt. */
+ spm_interrupt_enable(TIMER_VIRTUAL_INTID, true, 0);
+
if (primary_cold_boot == false) {
goto msg_loop;
}
diff --git a/spm/cactus/cactus_tests/cactus_test_interrupts.c b/spm/cactus/cactus_tests/cactus_test_interrupts.c
index c589119..0ae5011 100644
--- a/spm/cactus/cactus_tests/cactus_test_interrupts.c
+++ b/spm/cactus/cactus_tests/cactus_test_interrupts.c
@@ -224,7 +224,7 @@
CACTUS_CMD_HANDLER(interrupt_serviced_cmd, CACTUS_LAST_INTERRUPT_SERVICED_CMD)
{
- unsigned int core_pos = get_current_core_id();
+ unsigned int core_pos = spm_get_my_core_pos();
return cactus_response(ffa_dir_msg_dest(*args),
ffa_dir_msg_source(*args),
diff --git a/spm/cactus/cactus_tests/cactus_test_timer.c b/spm/cactus/cactus_tests/cactus_test_timer.c
new file mode 100644
index 0000000..6c8af0c
--- /dev/null
+++ b/spm/cactus/cactus_tests/cactus_test_timer.c
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2024, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "cactus_message_loop.h"
+#include "cactus_test_cmds.h"
+#include "debug.h"
+
+uint32_t ms_to_ticks(uint64_t ms)
+{
+ return ms * read_cntfrq_el0() / 1000;
+}
+
+CACTUS_CMD_HANDLER(set_virtual_timer, CACTUS_SET_ARCH_TIMER_CMD)
+{
+ uint64_t deadline_ms = cactus_get_timer_deadline(*args);
+ uint64_t wait_time = cactus_get_timer_wait_time(*args);
+ uint32_t ticks = ms_to_ticks(deadline_ms);
+
+ write_cntp_ctl_el0(0);
+ write_cntp_tval_el0(ticks);
+ write_cntp_ctl_el0(1);
+
+ if (wait_time != 0U) {
+ waitms(wait_time);
+ }
+
+ return cactus_response(ffa_dir_msg_dest(*args),
+ ffa_dir_msg_source(*args),
+ CACTUS_SUCCESS);
+}
diff --git a/tftf/tests/aarch32_tests_to_skip.txt b/tftf/tests/aarch32_tests_to_skip.txt
index f05235d..b802e68 100644
--- a/tftf/tests/aarch32_tests_to_skip.txt
+++ b/tftf/tests/aarch32_tests_to_skip.txt
@@ -18,3 +18,4 @@
RMI and SPM tests
FF-A SMCCC compliance
FF-A Indirect Messaging
+EL1 Physical arch timer tests
diff --git a/tftf/tests/runtime_services/secure_service/test_ffa_arch_timer.c b/tftf/tests/runtime_services/secure_service/test_ffa_arch_timer.c
new file mode 100644
index 0000000..cff3aeb
--- /dev/null
+++ b/tftf/tests/runtime_services/secure_service/test_ffa_arch_timer.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2024, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <cactus_test_cmds.h>
+#include <drivers/arm/arm_gic.h>
+#include <drivers/arm/gic_v3.h>
+#include <ffa_endpoints.h>
+#include <ffa_helpers.h>
+#include <irq.h>
+#include <spm_test_helpers.h>
+#include <test_helpers.h>
+#include <timer.h>
+
+#define SLEEP_TIME 100U
+#define SENDER HYP_ID
+#define RECEIVER SP_ID(2)
+#define EL1_PHYS_TIMER_IRQ 30
+
+static const struct ffa_uuid expected_sp_uuids[] = {
+ {PRIMARY_UUID}, {SECONDARY_UUID}
+ };
+
+static volatile int arch_timer_received;
+
+static int arch_timer_handler(void *data)
+{
+ INFO("Nwd irq handler executed\n");
+
+ /* Stop the timer now. */
+ write_cntp_ctl_el0(0);
+
+ /* Disable the EL1 physical timer interrupt and unregister the handler. */
+ tftf_irq_disable(EL1_PHYS_TIMER_IRQ);
+ tftf_irq_unregister_handler(EL1_PHYS_TIMER_IRQ);
+
+ arch_timer_received = 1;
+
+ return 0;
+}
+
+static bool check_arch_timer_handled(void)
+{
+ return (arch_timer_received != 0);
+}
+
+static uint64_t ms_to_ticks(uint64_t ms)
+{
+ return (ms * read_cntfrq_el0()) / 1000;
+}
+
+/**
+ * The aim is this test is to ensure that the functionality of arch (EL1
+ * physical) timer configured by NWd endpoint, such as an hypervisor, is not
+ * corrupted by SPMC when an SP also configures the arch timer for its own use.
+ * Any endpoint that programs a deadline through arch timer shall receive the
+ * timer interrupt eventually.
+ */
+test_result_t test_ffa_physical_arch_timer_nwd_set_swd_preempt(void)
+{
+ struct ffa_value ret_values;
+ int ret;
+ unsigned int core_pos = get_current_core_id();
+
+ CHECK_SPMC_TESTING_SETUP(1, 2, expected_sp_uuids);
+
+ /* Register the interrupt handler for EL1 physical timer. */
+ ret = tftf_irq_register_handler(EL1_PHYS_TIMER_IRQ, arch_timer_handler);
+ if (ret != 0) {
+ ERROR("Failed to program EL1 physical timer (%d)\n", ret);
+ return TEST_RESULT_FAIL;
+ }
+
+ arch_timer_received = 0;
+ tftf_irq_enable(EL1_PHYS_TIMER_IRQ, GIC_HIGHEST_NS_PRIORITY);
+
+ /* Configure the EL1 physical timer to expire in 20ms. */
+ write_cntp_ctl_el0(0);
+ write_cntp_tval_el0(ms_to_ticks(20));
+ write_cntp_ctl_el0(1);
+
+ /*
+ * Send a command to SP requesting it to setup its arch timer. SP shall
+ * also wait for enough time to let the normal world interrupt trigger
+ * leading to preemption of the SP's execution context.
+ */
+ ret_values = cactus_send_arch_timer_cmd(SENDER, RECEIVER, 100, 30);
+
+ if (ffa_func_id(ret_values) != FFA_INTERRUPT) {
+ ERROR("Expected FFA_INTERRUPT as return.\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ if (!check_arch_timer_handled()) {
+ ERROR("Normal world timer interrupt hasn't actually been handled.\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ /*
+ * Wait for sufficient time to let the SPMC host timer tracking the
+ * SP's deadline, to trigger. SPMC will eventually queue the virtual
+ * secure timer interrupt for receiver SP since it is in PREEMPTED
+ * state.
+ */
+ waitms(100);
+
+ /*
+ * Resume the Cactus SP using FFA_RUN ABI for it to complete the
+ * sleep routine and send the direct response message.
+ */
+ ret_values = ffa_run(RECEIVER, core_pos);
+
+ if (!is_ffa_direct_response(ret_values)) {
+ return TEST_RESULT_FAIL;
+ }
+
+ if (cactus_get_response(ret_values) == CACTUS_ERROR) {
+ return TEST_RESULT_FAIL;
+ }
+
+ /* Check for the last serviced secure virtual interrupt. */
+ ret_values = cactus_get_last_interrupt_cmd(SENDER, RECEIVER);
+
+ if (!is_ffa_direct_response(ret_values)) {
+ ERROR("Expected a direct response for last serviced interrupt"
+ " command\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ /* Make sure arch timer interrupt was serviced. */
+ if (cactus_get_response(ret_values) != TIMER_VIRTUAL_INTID) {
+ ERROR("Trusted watchdog timer interrupt not serviced by SP\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ return TEST_RESULT_SUCCESS;
+}
diff --git a/tftf/tests/tests-spm.mk b/tftf/tests/tests-spm.mk
index 1ac5e84..dba4555 100644
--- a/tftf/tests/tests-spm.mk
+++ b/tftf/tests/tests-spm.mk
@@ -23,6 +23,7 @@
test_spm_smmu.c \
test_ffa_exceptions.c \
test_ffa_group0_interrupts.c \
+ test_ffa_arch_timer.c \
)
ifeq (${ARCH},aarch64)
diff --git a/tftf/tests/tests-spm.xml b/tftf/tests/tests-spm.xml
index 30449cd..60470fe 100644
--- a/tftf/tests/tests-spm.xml
+++ b/tftf/tests/tests-spm.xml
@@ -249,4 +249,10 @@
function="test_ffa_indirect_message_sp_to_vm" />
</testsuite>
+ <testsuite name="EL1 Physical arch timer tests"
+ description="Test the physical arch timer can be used from an SP" >
+ <testcase name="NWd physical timer deadline expires in secure world "
+ function="test_ffa_physical_arch_timer_nwd_set_swd_preempt" />
+ </testsuite>
+
</testsuites>