Merge changes from topic "mp/sec_intr_management"
* changes:
test: add test to exercise secure interrupt handling while ..
test: add test to exercise secure interrupt handling while SP is blocked
test: add test to exercise secure interrupt handling while SP is waiting
test: add test to exercise secure interrupt handling while SP is running
feat: add support for secure interrupt handling and completion
feat: add spm_deactivate_interrupt helper function
feat: add a test command to send request to a VM/SP ..
feat: add a test command to request a Cactus SP to start wdog timer
feat: add helper functions for SP sleep
feat: add support for SP805 Trusted watchdog module helper APIs
refactor: changes to support both instances of SP805
diff --git a/drivers/arm/sp805/sp805.c b/drivers/arm/sp805/sp805.c
index 2318c40..85da43a 100644
--- a/drivers/arm/sp805/sp805.c
+++ b/drivers/arm/sp805/sp805.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Arm Limited. All rights reserved.
+ * Copyright (c) 2018-2021, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -109,45 +109,75 @@
return mmio_read_32(base + SP805_WDOG_PCELL_ID_OFF + (id << 2));
}
-void sp805_wdog_start(uint32_t wdog_cycles)
+static void sp805_wdog_start_(unsigned long base, uint32_t wdog_cycles)
{
/* Unlock to access the watchdog registers */
- sp805_write_wdog_lock(SP805_WDOG_BASE, SP805_WDOG_UNLOCK_ACCESS);
+ sp805_write_wdog_lock(base, SP805_WDOG_UNLOCK_ACCESS);
/* Write the number of cycles needed */
- sp805_write_wdog_load(SP805_WDOG_BASE, wdog_cycles);
+ sp805_write_wdog_load(base, wdog_cycles);
/* Enable reset interrupt and watchdog interrupt on expiry */
- sp805_write_wdog_ctrl(SP805_WDOG_BASE,
+ sp805_write_wdog_ctrl(base,
SP805_WDOG_CTRL_RESEN | SP805_WDOG_CTRL_INTEN);
/* Lock registers so that they can't be accidently overwritten */
- sp805_write_wdog_lock(SP805_WDOG_BASE, 0x0);
+ sp805_write_wdog_lock(base, 0x0);
}
-void sp805_wdog_stop(void)
+static void sp805_wdog_stop_(unsigned long base)
{
/* Unlock to access the watchdog registers */
- sp805_write_wdog_lock(SP805_WDOG_BASE, SP805_WDOG_UNLOCK_ACCESS);
+ sp805_write_wdog_lock(base, SP805_WDOG_UNLOCK_ACCESS);
/* Clearing INTEN bit stops the counter */
- sp805_write_wdog_ctrl(SP805_WDOG_BASE, 0x00);
+ sp805_write_wdog_ctrl(base, 0x00);
/* Lock registers so that they can't be accidently overwritten */
- sp805_write_wdog_lock(SP805_WDOG_BASE, 0x0);
+ sp805_write_wdog_lock(base, 0x0);
}
-void sp805_wdog_refresh(void)
+static void sp805_wdog_refresh_(unsigned long base)
{
/* Unlock to access the watchdog registers */
- sp805_write_wdog_lock(SP805_WDOG_BASE, SP805_WDOG_UNLOCK_ACCESS);
+ sp805_write_wdog_lock(base, SP805_WDOG_UNLOCK_ACCESS);
/*
* Write of any value to WdogIntClr clears interrupt and reloads
* the counter from the value in WdogLoad Register.
*/
- sp805_write_wdog_int_clr(SP805_WDOG_BASE, 1);
+ sp805_write_wdog_int_clr(base, 1);
/* Lock registers so that they can't be accidently overwritten */
- sp805_write_wdog_lock(SP805_WDOG_BASE, 0x0);
+ sp805_write_wdog_lock(base, 0x0);
+}
+
+void sp805_wdog_start(uint32_t wdog_cycles)
+{
+ sp805_wdog_start_(SP805_WDOG_BASE, wdog_cycles);
+}
+
+void sp805_wdog_stop(void)
+{
+ sp805_wdog_stop_(SP805_WDOG_BASE);
+}
+
+void sp805_wdog_refresh(void)
+{
+ sp805_wdog_refresh_(SP805_WDOG_BASE);
+}
+
+void sp805_twdog_start(uint32_t wdog_cycles)
+{
+ sp805_wdog_start_(SP805_TWDOG_BASE, wdog_cycles);
+}
+
+void sp805_twdog_stop(void)
+{
+ sp805_wdog_stop_(SP805_TWDOG_BASE);
+}
+
+void sp805_twdog_refresh(void)
+{
+ sp805_wdog_refresh_(SP805_TWDOG_BASE);
}
diff --git a/include/drivers/arm/sp805.h b/include/drivers/arm/sp805.h
index c033ccf..75bcc12 100644
--- a/include/drivers/arm/sp805.h
+++ b/include/drivers/arm/sp805.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Arm Limited. All rights reserved.
+ * Copyright (c) 2018-2021, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -47,9 +47,17 @@
#define SP805_WDOG_PCELL_ID_SHIFT 0
#define SP805_WDOG_PCELL_ID_MASK 0xff
+#define ARM_SP805_TWDG_CLK_HZ 32768
+
+/* Public APIs for non-trusted watchdog module. */
void sp805_wdog_start(unsigned int wdog_cycles);
void sp805_wdog_stop(void);
void sp805_wdog_refresh(void);
+/* Public APIs for trusted watchdog module. */
+void sp805_twdog_start(unsigned int wdog_cycles);
+void sp805_twdog_stop(void);
+void sp805_twdog_refresh(void);
+
#endif /* __SP805_H__ */
diff --git a/include/lib/aarch64/arch_helpers.h b/include/lib/aarch64/arch_helpers.h
index 696cd01..8287422 100644
--- a/include/lib/aarch64/arch_helpers.h
+++ b/include/lib/aarch64/arch_helpers.h
@@ -562,4 +562,16 @@
return read_cntpct_el0();
}
+/* Read the value of the Counter-timer virtual count. */
+static inline uint64_t virtualcounter_read(void)
+{
+ /*
+ * The instruction barrier is needed to guarantee that we read an
+ * accurate value. Otherwise, the CPU might speculatively read it and
+ * return a stale value.
+ */
+ isb();
+ return read_cntvct_el0();
+}
+
#endif /* ARCH_HELPERS_H */
diff --git a/include/runtime_services/cactus_test_cmds.h b/include/runtime_services/cactus_test_cmds.h
index b588934..d4cf407 100644
--- a/include/runtime_services/cactus_test_cmds.h
+++ b/include/runtime_services/cactus_test_cmds.h
@@ -63,7 +63,7 @@
static inline smc_ret_values cactus_response(
ffa_id_t source, ffa_id_t dest, uint32_t response)
{
- return ffa_msg_send_direct_resp64(source, dest, response, 0, 0, 0, 0);
+ return cactus_send_response(source, dest, response, 0, 0, 0, 0);
}
static inline uint32_t cactus_get_response(smc_ret_values ret)
@@ -264,11 +264,32 @@
0);
}
+/**
+ * Command to request cactus to forward sleep command for the given time in ms
+ *
+ * The sender of this command expects to receive CACTUS_SUCCESS if the requested
+ * echo interaction happened successfully, or CACTUS_ERROR otherwise.
+ */
+#define CACTUS_FWD_SLEEP_CMD (CACTUS_SLEEP_CMD + 1)
+
+static inline smc_ret_values cactus_fwd_sleep_cmd(
+ ffa_id_t source, ffa_id_t dest, ffa_id_t fwd_dest,
+ uint32_t sleep_time)
+{
+ return cactus_send_cmd(source, dest, CACTUS_FWD_SLEEP_CMD, sleep_time,
+ fwd_dest, 0, 0);
+}
+
static inline uint32_t cactus_get_sleep_time(smc_ret_values ret)
{
return (uint32_t)ret.ret4;
}
+static inline ffa_id_t cactus_get_fwd_sleep_dest(smc_ret_values ret)
+{
+ return (ffa_id_t)ret.ret5;
+}
+
/**
* Command to request cactus to enable/disable an interrupt
*
@@ -431,4 +452,23 @@
receiver, sender, notifications, flags);
}
+/**
+ * Request to start trusted watchdog timer.
+ *
+ * The command id is the hex representaton of the string "WDOG"
+ */
+#define CACTUS_TWDOG_START_CMD U(0x57444f47)
+
+static inline smc_ret_values cactus_send_twdog_cmd(
+ ffa_id_t source, ffa_id_t dest, uint64_t time)
+{
+ return cactus_send_cmd(source, dest, CACTUS_TWDOG_START_CMD, time, 0, 0,
+ 0);
+}
+
+static inline uint32_t cactus_get_wdog_duration(smc_ret_values ret)
+{
+ return (uint32_t)ret.ret4;
+}
+
#endif
diff --git a/plat/arm/fvp/include/platform_def.h b/plat/arm/fvp/include/platform_def.h
index 3afc9b8..7fc147d 100644
--- a/plat/arm/fvp/include/platform_def.h
+++ b/plat/arm/fvp/include/platform_def.h
@@ -37,6 +37,10 @@
/* Base address of non-trusted watchdog (SP805) */
#define SP805_WDOG_BASE 0x1C0F0000
+/* Base address of trusted watchdog (SP805) */
+#define SP805_TWDOG_BASE 0x2A490000
+#define IRQ_TWDOG_INTID 56
+
/*******************************************************************************
* Base address and size of external NVM flash
******************************************************************************/
diff --git a/plat/arm/juno/include/platform_def.h b/plat/arm/juno/include/platform_def.h
index 0f9bb77..2de11fd 100644
--- a/plat/arm/juno/include/platform_def.h
+++ b/plat/arm/juno/include/platform_def.h
@@ -42,6 +42,10 @@
/* Base address of non-trusted watchdog (SP805) */
#define SP805_WDOG_BASE 0x1C0F0000
+/* Base address of trusted watchdog (SP805) */
+#define SP805_TWDOG_BASE 0x2A4A0000
+#define IRQ_TWDOG_INTID 86
+
/* Memory mapped Generic timer interfaces */
#define SYS_CNT_BASE1 0x2a830000
diff --git a/spm/cactus/aarch64/cactus_exceptions.S b/spm/cactus/aarch64/cactus_exceptions.S
index 31cdbf9..6aec16d 100644
--- a/spm/cactus/aarch64/cactus_exceptions.S
+++ b/spm/cactus/aarch64/cactus_exceptions.S
@@ -101,7 +101,7 @@
func irq_vector_entry
sub sp, sp, #0x100
save_gp_regs
- bl cactus_irq_handler
+ bl cactus_interrupt_handler
restore_gp_regs
add sp, sp, #0x100
eret
@@ -110,7 +110,7 @@
func fiq_vector_entry
sub sp, sp, #0x100
save_gp_regs
- bl cactus_fiq_handler
+ bl cactus_interrupt_handler
restore_gp_regs
add sp, sp, #0x100
eret
diff --git a/spm/cactus/cactus.mk b/spm/cactus/cactus.mk
index 6252d2d..4109579 100644
--- a/spm/cactus/cactus.mk
+++ b/spm/cactus/cactus.mk
@@ -62,6 +62,7 @@
tftf/framework/${ARCH}/exception_report.c
CACTUS_SOURCES += drivers/arm/pl011/${ARCH}/pl011_console.S \
+ drivers/arm/sp805/sp805.c \
lib/${ARCH}/cache_helpers.S \
lib/${ARCH}/misc_helpers.S \
lib/smc/${ARCH}/asm_smc.S \
diff --git a/spm/cactus/cactus_interrupt.c b/spm/cactus/cactus_interrupt.c
index f61df94..31c6a69 100644
--- a/spm/cactus/cactus_interrupt.c
+++ b/spm/cactus/cactus_interrupt.c
@@ -6,46 +6,50 @@
#include <debug.h>
+#include "cactus_message_loop.h"
+#include "cactus_test_cmds.h"
+#include <drivers/arm/sp805.h>
#include <ffa_helpers.h>
#include <sp_helpers.h>
+#include "spm_common.h"
#include <spm_helpers.h>
-#include "cactus_test_cmds.h"
-#include "spm_common.h"
+#include <platform_def.h>
extern ffa_id_t g_ffa_id;
-static void managed_exit_handler(void)
+void cactus_interrupt_handler(void)
{
- /*
- * Real SP will save its context here.
- * Send interrupt ID for acknowledgement
- */
- cactus_response(g_ffa_id, HYP_ID, MANAGED_EXIT_INTERRUPT_ID);
-}
+ uint32_t intid = spm_interrupt_get();
-int cactus_irq_handler(void)
-{
- uint32_t irq_num;
+ switch (intid) {
+ case MANAGED_EXIT_INTERRUPT_ID:
+ /*
+ * A secure partition performs its housekeeping and sends a
+ * direct response to signal interrupt completion.
+ * This is a pure virtual interrupt, no need for deactivation.
+ */
+ cactus_response(g_ffa_id, HYP_ID, MANAGED_EXIT_INTERRUPT_ID);
+ break;
+ case IRQ_TWDOG_INTID:
+ /*
+ * Interrupt triggered due to Trusted watchdog timer expiry.
+ * Clear the interrupt and stop the timer.
+ */
+ NOTICE("Trusted WatchDog timer stopped\n");
+ sp805_twdog_stop();
- irq_num = spm_interrupt_get();
+ /* Perform secure interrupt de-activation. */
+ spm_interrupt_deactivate(intid);
- ERROR("%s: Interrupt ID %u not handled!\n", __func__, irq_num);
-
- return 0;
-}
-
-int cactus_fiq_handler(void)
-{
- uint32_t fiq_num;
-
- fiq_num = spm_interrupt_get();
-
- if (fiq_num == MANAGED_EXIT_INTERRUPT_ID) {
- managed_exit_handler();
- } else {
- ERROR("%s: Interrupt ID %u not handled!\n", __func__, fiq_num);
+ break;
+ default:
+ /*
+ * Currently the only source of secure interrupt is Trusted
+ * Watchdog timer.
+ */
+ ERROR("%s: Interrupt ID %x not handled!\n", __func__,
+ intid);
+ panic();
}
-
- return 0;
}
diff --git a/spm/cactus/cactus_main.c b/spm/cactus/cactus_main.c
index 78bb8fc..c80abd9 100644
--- a/spm/cactus/cactus_main.c
+++ b/spm/cactus/cactus_main.c
@@ -69,14 +69,26 @@
}
if (ffa_func_id(ffa_ret) != FFA_MSG_SEND_DIRECT_REQ_SMC32 &&
- ffa_func_id(ffa_ret) != FFA_MSG_SEND_DIRECT_REQ_SMC64) {
+ ffa_func_id(ffa_ret) != FFA_MSG_SEND_DIRECT_REQ_SMC64 &&
+ ffa_func_id(ffa_ret) != FFA_INTERRUPT) {
ERROR("%s(%u) unknown func id 0x%x\n",
__func__, vm_id, ffa_func_id(ffa_ret));
break;
}
- destination = ffa_dir_msg_dest(ffa_ret);
+ if (ffa_func_id(ffa_ret) == FFA_INTERRUPT) {
+ /*
+ * Received FFA_INTERRUPT in waiting state.
+ * The interrupt id is passed although this is just
+ * informational as we're running with virtual
+ * interrupts unmasked and the interrupt is processed
+ * by the interrupt handler.
+ */
+ ffa_ret = ffa_msg_wait();
+ continue;
+ }
+ destination = ffa_dir_msg_dest(ffa_ret);
if (destination != vm_id) {
ERROR("%s(%u) invalid vm id 0x%x\n",
__func__, vm_id, destination);
diff --git a/spm/cactus/cactus_tests/cactus_message_loop.c b/spm/cactus/cactus_tests/cactus_message_loop.c
index fde7074..c24f6fc 100644
--- a/spm/cactus/cactus_tests/cactus_message_loop.c
+++ b/spm/cactus/cactus_tests/cactus_message_loop.c
@@ -32,7 +32,7 @@
uint64_t in_cmd;
if (cmd_args == NULL || ret == NULL) {
- ERROR("Invalid argumentos passed to %s!\n", __func__);
+ ERROR("Invalid arguments passed to %s!\n", __func__);
return false;
}
diff --git a/spm/cactus/cactus_tests/cactus_test_interrupts.c b/spm/cactus/cactus_tests/cactus_test_interrupts.c
index b675dfc..ced5dca 100644
--- a/spm/cactus/cactus_tests/cactus_test_interrupts.c
+++ b/spm/cactus/cactus_tests/cactus_test_interrupts.c
@@ -5,32 +5,70 @@
*/
#include <common/debug.h>
+#include <drivers/arm/sp805.h>
#include <sp_helpers.h>
#include <spm_helpers.h>
#include "cactus_message_loop.h"
#include "cactus_test_cmds.h"
+#include <platform.h>
+
CACTUS_CMD_HANDLER(sleep_cmd, CACTUS_SLEEP_CMD)
{
- uint64_t timer_freq = read_cntfrq_el0();
- uint64_t time1, time2, time_lapsed;
+ uint64_t time_lapsed;
uint32_t sleep_time = cactus_get_sleep_time(*args);
- VERBOSE("Request to sleep %x for %ums.\n", ffa_dir_msg_dest(*args), sleep_time);
+ VERBOSE("Request to sleep %x for %ums.\n", ffa_dir_msg_dest(*args),
+ sleep_time);
- time1 = read_cntvct_el0();
- sp_sleep(sleep_time);
- time2 = read_cntvct_el0();
+ time_lapsed = sp_sleep_elapsed_time(sleep_time);
- /* Lapsed time should be at least equal to sleep time */
- time_lapsed = ((time2 - time1) * 1000) / timer_freq;
+ /* Lapsed time should be at least equal to sleep time. */
+ VERBOSE("Sleep complete: %llu\n", time_lapsed);
return cactus_response(ffa_dir_msg_dest(*args),
ffa_dir_msg_source(*args),
time_lapsed);
}
+CACTUS_CMD_HANDLER(sleep_fwd_cmd, CACTUS_FWD_SLEEP_CMD)
+{
+ smc_ret_values ffa_ret;
+ ffa_id_t vm_id = ffa_dir_msg_dest(*args);
+ ffa_id_t fwd_dest = cactus_get_fwd_sleep_dest(*args);
+ uint32_t sleep_ms = cactus_get_sleep_time(*args);
+
+ VERBOSE("VM%x requested %x to sleep for value %u\n",
+ ffa_dir_msg_source(*args), fwd_dest, sleep_ms);
+
+ ffa_ret = cactus_sleep_cmd(vm_id, fwd_dest, sleep_ms);
+
+ while (ffa_ret.ret0 == FFA_INTERRUPT) {
+ /* Received FFA_INTERRUPT in blocked state. */
+ VERBOSE("Processing FFA_INTERRUPT while blocked on direct response\n");
+ unsigned int my_core_pos = platform_get_core_pos(read_mpidr_el1());
+
+ ffa_ret = ffa_run(fwd_dest, my_core_pos);
+ }
+
+ if (!is_ffa_direct_response(ffa_ret)) {
+ ERROR("Encountered error in CACTUS_FWD_SLEEP_CMD response\n");
+ return cactus_error_resp(vm_id, ffa_dir_msg_source(*args),
+ CACTUS_ERROR_FFA_CALL);
+ }
+
+ if (cactus_get_response(ffa_ret) != sleep_ms) {
+ ERROR("Request returned: %u ms!\n",
+ cactus_get_response(ffa_ret));
+ return cactus_error_resp(vm_id, ffa_dir_msg_source(*args),
+ CACTUS_ERROR_TEST);
+
+ }
+
+ return cactus_success_resp(vm_id, ffa_dir_msg_source(*args), 0);
+}
+
CACTUS_CMD_HANDLER(interrupt_cmd, CACTUS_INTERRUPT_CMD)
{
uint32_t int_id = cactus_get_interrupt_id(*args);
@@ -49,3 +87,17 @@
ffa_dir_msg_source(*args),
CACTUS_SUCCESS);
}
+
+CACTUS_CMD_HANDLER(twdog_cmd, CACTUS_TWDOG_START_CMD)
+{
+ ffa_id_t vm_id = ffa_dir_msg_dest(*args);
+ ffa_id_t source = ffa_dir_msg_source(*args);
+
+ uint64_t time_ms = cactus_get_wdog_duration(*args);
+
+ VERBOSE("Starting TWDOG: %llums\n", time_ms);
+ sp805_twdog_refresh();
+ sp805_twdog_start((time_ms * ARM_SP805_TWDG_CLK_HZ) / 1000);
+
+ return cactus_success_resp(vm_id, source, time_ms);
+}
diff --git a/spm/cactus/plat/arm/fvp/fdts/cactus.dts b/spm/cactus/plat/arm/fvp/fdts/cactus.dts
index 298ca19..89c0d04 100644
--- a/spm/cactus/plat/arm/fvp/fdts/cactus.dts
+++ b/spm/cactus/plat/arm/fvp/fdts/cactus.dts
@@ -113,5 +113,13 @@
smmu-id = <0>;
stream-ids = <0x0 0x1>;
};
+
+ sec_wdog {
+ /* SP805 Trusted Watchdog Module */
+ base-address = <0x00000000 0x2a490000>;
+ pages-count = <32>; /* Two 64KB pages */
+ attributes = <0x3>; /* read-write */
+ interrupts = <56 0x900>;
+ };
};
};
diff --git a/spm/common/sp_helpers.c b/spm/common/sp_helpers.c
index a6b6bc5..77031f8 100644
--- a/spm/common/sp_helpers.c
+++ b/spm/common/sp_helpers.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Arm Limited. All rights reserved.
+ * Copyright (c) 2018-2021, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -58,16 +58,25 @@
INFO("Test \"%s\" end.\n", test_desc);
}
-void sp_sleep(uint32_t ms)
+uint64_t sp_sleep_elapsed_time(uint32_t ms)
{
uint64_t timer_freq = read_cntfrq_el0();
VERBOSE("%s: Timer frequency = %llu\n", __func__, timer_freq);
VERBOSE("%s: Sleeping for %u milliseconds...\n", __func__, ms);
- uint64_t time1 = read_cntvct_el0();
+
+ uint64_t time1 = virtualcounter_read();
volatile uint64_t time2 = time1;
+
while ((time2 - time1) < ((ms * timer_freq) / 1000U)) {
- time2 = read_cntvct_el0();
+ time2 = virtualcounter_read();
}
+
+ return ((time2 - time1) * 1000) / timer_freq;
+}
+
+void sp_sleep(uint32_t ms)
+{
+ (void)sp_sleep_elapsed_time(ms);
}
diff --git a/spm/common/sp_helpers.h b/spm/common/sp_helpers.h
index 9f5400c..6fe8ec0 100644
--- a/spm/common/sp_helpers.h
+++ b/spm/common/sp_helpers.h
@@ -56,6 +56,9 @@
void announce_test_start(const char *test_desc);
void announce_test_end(const char *test_desc);
+/* Sleep for at least 'ms' milliseconds and return the elapsed time(ms). */
+uint64_t sp_sleep_elapsed_time(uint32_t ms);
+
/* Sleep for at least 'ms' milliseconds. */
void sp_sleep(uint32_t ms);
diff --git a/spm/common/spm_helpers.c b/spm/common/spm_helpers.c
index 2ccf3f7..82fdae5 100644
--- a/spm/common/spm_helpers.c
+++ b/spm/common/spm_helpers.c
@@ -49,3 +49,20 @@
return (int64_t)ret.ret0;
}
+
+/**
+ * Hypervisor call to drop the priority and de-activate a secure interrupt.
+ * Returns 0 on success, or -1 if passing an invalid interrupt id.
+ */
+int64_t spm_interrupt_deactivate(uint32_t vint_id)
+{
+ hvc_args args = {
+ .fid = SPM_INTERRUPT_DEACTIVATE,
+ .arg1 = vint_id, /* pint_id */
+ .arg2 = vint_id
+ };
+
+ hvc_ret_values ret = tftf_hvc(&args);
+
+ return (int64_t)ret.ret0;
+}
diff --git a/spm/common/spm_helpers.h b/spm/common/spm_helpers.h
index 10f7316..25c6493 100644
--- a/spm/common/spm_helpers.h
+++ b/spm/common/spm_helpers.h
@@ -13,6 +13,7 @@
/* Should match with IDs defined in SPM/Hafnium */
#define SPM_INTERRUPT_ENABLE (0xFF03)
#define SPM_INTERRUPT_GET (0xFF04)
+#define SPM_INTERRUPT_DEACTIVATE (0xFF08)
#define SPM_DEBUG_LOG (0xBD000000)
/*
@@ -21,6 +22,7 @@
uint32_t spm_interrupt_get(void);
int64_t spm_interrupt_enable(uint32_t int_id, bool enable, enum interrupt_pin pin);
+int64_t spm_interrupt_deactivate(uint32_t vint_id);
void spm_debug_log(char c);
#endif /* SPMC_H */
diff --git a/tftf/tests/runtime_services/secure_service/test_ffa_secure_interrupts.c b/tftf/tests/runtime_services/secure_service/test_ffa_secure_interrupts.c
new file mode 100644
index 0000000..6ff30f6
--- /dev/null
+++ b/tftf/tests/runtime_services/secure_service/test_ffa_secure_interrupts.c
@@ -0,0 +1,457 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <cactus_test_cmds.h>
+#include <ffa_endpoints.h>
+#include <ffa_helpers.h>
+#include <test_helpers.h>
+#include <timer.h>
+
+#define SENDER HYP_ID
+#define RECEIVER SP_ID(1)
+#define RECEIVER_2 SP_ID(2)
+#define SP_SLEEP_TIME 1000U
+#define NS_TIME_SLEEP 1500U
+#define ECHO_VAL1 U(0xa0a0a0a0)
+
+static const struct ffa_uuid expected_sp_uuids[] = {
+ {PRIMARY_UUID}, {SECONDARY_UUID}
+ };
+
+static bool configure_trusted_wdog_interrupt(ffa_id_t source, ffa_id_t dest,
+ bool enable)
+{
+ smc_ret_values ret_values;
+
+ ret_values = cactus_interrupt_cmd(source, dest, IRQ_TWDOG_INTID,
+ enable, INTERRUPT_TYPE_IRQ);
+
+ if (!is_ffa_direct_response(ret_values)) {
+ ERROR("Expected a direct response message while configuring"
+ " TWDOG interrupt\n");
+ return false;
+ }
+
+ if (cactus_get_response(ret_values) != CACTUS_SUCCESS) {
+ ERROR("Failed to configure Trusted Watchdog interrupt\n");
+ return false;
+ }
+ return true;
+}
+
+static bool enable_trusted_wdog_interrupt(ffa_id_t source, ffa_id_t dest)
+{
+ return configure_trusted_wdog_interrupt(source, dest, true);
+}
+
+static bool disable_trusted_wdog_interrupt(ffa_id_t source, ffa_id_t dest)
+{
+ return configure_trusted_wdog_interrupt(source, dest, false);
+}
+
+/*
+ * @Test_Aim@ Test secure interrupt handling while first Secure Partition is
+ * in RUNNING state.
+ *
+ * 1. Send a direct message request command to first Cactus SP to start the
+ * trusted watchdog timer.
+ *
+ * 2. Send a command to SP to sleep by executing a busy loop.
+ *
+ * 3. While SP is running the busy loop, Secure interrupt should trigger during
+ * this time.
+ *
+ * 4. The interrupt will be trapped to SPM as IRQ. SPM will inject the virtual
+ * IRQ to the first SP through vIRQ conduit and perform eret to resume
+ * execution in SP.
+ *
+ * 5. Execution traps to irq handler of Cactus SP. It will handle the secure
+ * interrupt triggered by the trusted watchdog timer.
+ *
+ * 6. Cactus SP will perform End-Of-Interrupt and resume execution in the busy
+ * loop.
+ *
+ * 7. Cactus SP will send a direct response message with the elapsed time back
+ * to the normal world.
+ *
+ * 8. We make sure the time elapsed in the sleep routine by SP is not less than
+ * the requested value.
+ *
+ * 9. For robustness of state transition checks, TFTF sends echo command using
+ * a direct request message.
+ *
+ * 10. Further, TFTF expects SP to return with a success value through a direct
+ * response message.
+ *
+ * 11. Test finishes successfully once the TFTF disables the trusted watchdog
+ * interrupt through a direct message request command.
+ *
+ */
+test_result_t test_ffa_sec_interrupt_sp_running(void)
+{
+ smc_ret_values ret_values;
+
+ CHECK_SPMC_TESTING_SETUP(1, 1, expected_sp_uuids);
+
+ /* Enable trusted watchdog interrupt as IRQ in the secure side. */
+ if (!enable_trusted_wdog_interrupt(SENDER, RECEIVER)) {
+ return TEST_RESULT_FAIL;
+ }
+
+ ret_values = cactus_send_twdog_cmd(SENDER, RECEIVER, 50);
+
+ if (!is_ffa_direct_response(ret_values)) {
+ ERROR("Expected a direct response for starting TWDOG timer\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ /* Send request to first Cactus SP to sleep */
+ ret_values = cactus_sleep_cmd(SENDER, RECEIVER, SP_SLEEP_TIME);
+
+ /*
+ * Secure interrupt should trigger during this time, Cactus
+ * will handle the trusted watchdog timer.
+ */
+ if (!is_ffa_direct_response(ret_values)) {
+ ERROR("Expected a direct response for sleep command\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ VERBOSE("Secure interrupt has preempted execution: %u\n",
+ cactus_get_response(ret_values));
+
+ /* Make sure elapsed time not less than sleep time */
+ if (cactus_get_response(ret_values) < SP_SLEEP_TIME) {
+ ERROR("Lapsed time less than requested sleep time\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ ret_values = cactus_echo_send_cmd(SENDER, RECEIVER, ECHO_VAL1);
+
+ if (!is_ffa_direct_response(ret_values)) {
+ ERROR("Expected direct response for echo command\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ if (cactus_get_response(ret_values) != CACTUS_SUCCESS ||
+ cactus_echo_get_val(ret_values) != ECHO_VAL1) {
+ ERROR("Echo Failed!\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ /* Disable Trusted Watchdog interrupt. */
+ if (!disable_trusted_wdog_interrupt(SENDER, RECEIVER)) {
+ return TEST_RESULT_FAIL;
+ }
+
+ return TEST_RESULT_SUCCESS;
+}
+
+/*
+ * @Test_Aim@ Test secure interrupt handling while Secure Partition is waiting
+ * for a message.
+ *
+ * 1. Send a direct message request command to first Cactus SP to start the
+ * trusted watchdog timer.
+ *
+ * 2. Once the SP returns with a direct response message, it moves to WAITING
+ state.
+ *
+ * 3. Execute a busy loop to sleep for NS_TIME_SLEEP ms.
+ *
+ * 4. Trusted watchdog timer expires during this time which leads to secure
+ * interrupt being triggered while cpu is executing in normal world.
+ *
+ * 5. The interrupt is trapped to BL31/SPMD as FIQ and later synchronously
+ * delivered to SPM.
+ *
+ * 6. SPM injects a virtual IRQ to first Cactus Secure Partition.
+ *
+ * 7. Once the SP handles the interrupt, it returns execution back to normal
+ * world using FFA_MSG_WAIT call.
+ *
+ * 8. SPM, through the help of SPMD, resumes execution in normal world to
+ * continue the busy loop.
+ *
+ * 9. We make sure the time elapsed in the sleep routine is not less than
+ * the requested value.
+ *
+ * 10. For robustness of state transition checks, TFTF sends echo command using
+ * a direct request message.
+ *
+ * 11. Further, TFTF expects SP to return with a success value through a direct
+ * response message.
+ *
+ * 12. Test finishes successfully once the TFTF disables the trusted watchdog
+ * interrupt through a direct message request command.
+ *
+ */
+test_result_t test_ffa_sec_interrupt_sp_waiting(void)
+{
+ uint64_t time1;
+ volatile uint64_t time2, time_lapsed;
+ uint64_t timer_freq = read_cntfrq_el0();
+ smc_ret_values ret_values;
+
+ CHECK_SPMC_TESTING_SETUP(1, 1, expected_sp_uuids);
+
+ /* Enable trusted watchdog interrupt as IRQ in the secure side. */
+ if (!enable_trusted_wdog_interrupt(SENDER, RECEIVER)) {
+ return TEST_RESULT_FAIL;
+ }
+
+ /*
+ * Send a message to SP1 through direct messaging.
+ */
+ ret_values = cactus_send_twdog_cmd(SENDER, RECEIVER, 100);
+
+ if (!is_ffa_direct_response(ret_values)) {
+ ERROR("Expected a direct response for starting TWDOG timer\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ time1 = syscounter_read();
+
+ /*
+ * Sleep for NS_TIME_SLEEP ms. This ensures secure wdog timer triggers during this
+ * time. We explicitly do not use tftf_timer_sleep();
+ */
+ waitms(NS_TIME_SLEEP);
+ time2 = syscounter_read();
+
+ /* Lapsed time should be at least equal to sleep time */
+ time_lapsed = ((time2 - time1) * 1000) / timer_freq;
+
+ if (time_lapsed < NS_TIME_SLEEP) {
+ ERROR("Time elapsed less than expected value: %llu vs %u\n",
+ time_lapsed, NS_TIME_SLEEP);
+ return TEST_RESULT_FAIL;
+ }
+
+ ret_values = cactus_echo_send_cmd(SENDER, RECEIVER, ECHO_VAL1);
+
+ if (!is_ffa_direct_response(ret_values)) {
+ ERROR("Expected direct response for echo command\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ if (cactus_get_response(ret_values) != CACTUS_SUCCESS ||
+ cactus_echo_get_val(ret_values) != ECHO_VAL1) {
+ ERROR("Echo Failed!\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ /* Disable Trusted Watchdog interrupt. */
+ if (!disable_trusted_wdog_interrupt(SENDER, RECEIVER)) {
+ return TEST_RESULT_FAIL;
+ }
+
+ return TEST_RESULT_SUCCESS;
+}
+
+/*
+ * @Test_Aim@ Test secure interrupt handling while first Secure Partition is
+ * in BLOCKED state.
+ *
+ * 1. Send a direct message request command to first Cactus SP to start the
+ * trusted watchdog timer.
+ *
+ * 2. Send a direct request to first SP to forward sleep command to second SP.
+ *
+ * 3. While second SP is running the busy loop, Secure interrupt should trigger
+ * during this time.
+ *
+ * 4. The interrupt will be trapped to SPM as IRQ. SPM will inject the virtual
+ * IRQ to the first SP through vIRQ conduit and perform eret to resume
+ * execution in first SP.
+ *
+ * 5. Execution traps to irq handler of Cactus SP. It will handle the secure
+ * interrupt triggered by the trusted watchdog timer.
+ *
+ * 6. First SP performs EOI by calling interrupt deactivate ABI and invokes
+ * FFA_RUN to resume second SP in the busy loop.
+ *
+ * 7. Second SP will complete the busy sleep loop and send a direct response
+ * message with the elapsed time back to the first SP.
+ *
+ * 8. First SP checks for the elapsed time and sends a direct response with
+ * a SUCCESS value back to tftf.
+ *
+ * 9. For robustness of state transition checks, TFTF sends echo command using
+ * a direct request message to first SP.
+ *
+ * 10. Further, TFTF expects SP to return with a success value through a direct
+ * response message.
+ *
+ * 11. Test finishes successfully once the TFTF disables the trusted watchdog
+ * interrupt through a direct message request command.
+ */
+test_result_t test_ffa_sec_interrupt_sp_blocked(void)
+{
+ smc_ret_values ret_values;
+
+ CHECK_SPMC_TESTING_SETUP(1, 1, expected_sp_uuids);
+
+ /* Enable trusted watchdog interrupt as IRQ in the secure side. */
+ if (!enable_trusted_wdog_interrupt(SENDER, RECEIVER)) {
+ return TEST_RESULT_FAIL;
+ }
+
+ ret_values = cactus_send_twdog_cmd(SENDER, RECEIVER, 100);
+
+ if (!is_ffa_direct_response(ret_values)) {
+ ERROR("Expected a direct response for starting TWDOG timer\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ /*
+ * Send request to first Cactus SP to send request to Second Cactus
+ * SP to sleep
+ */
+ ret_values = cactus_fwd_sleep_cmd(SENDER, RECEIVER, RECEIVER_2,
+ SP_SLEEP_TIME);
+
+ /*
+ * Secure interrupt should trigger during this time, Cactus
+ * will handle the trusted watchdog timer.
+ */
+ if (!is_ffa_direct_response(ret_values)) {
+ ERROR("Expected a direct response\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ if (cactus_get_response(ret_values) != CACTUS_SUCCESS) {
+ return TEST_RESULT_FAIL;
+ }
+
+ ret_values = cactus_echo_send_cmd(SENDER, RECEIVER, ECHO_VAL1);
+
+ if (!is_ffa_direct_response(ret_values)) {
+ ERROR("Expected direct response for echo command\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ if (cactus_get_response(ret_values) != CACTUS_SUCCESS ||
+ cactus_echo_get_val(ret_values) != ECHO_VAL1) {
+ ERROR("Echo Failed!\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ /* Disable Trusted Watchdog interrupt. */
+ if (!disable_trusted_wdog_interrupt(SENDER, RECEIVER)) {
+ return TEST_RESULT_FAIL;
+ }
+
+ return TEST_RESULT_SUCCESS;
+}
+
+/*
+ * @Test_Aim@ Test secure interrupt handling while first Secure Partition is
+ * in WAITING state while the second Secure Partition is running.
+ *
+ * 1. Send a direct message request command to first Cactus SP to start the
+ * trusted watchdog timer.
+ *
+ * 2. Send a direct request to second SP to sleep by executing a busy loop.
+ *
+ * 3. While second SP is running the busy loop, Secure interrupt should trigger
+ * during this time.
+ *
+ * 4. The interrupt is trapped to the SPM as a physical IRQ. The SPM injects a
+ * virtual IRQ to the first SP and resumes it while it is in waiting state.
+ *
+ * 5. Execution traps to irq handler of the first Cactus SP. It will handle the
+ * secure interrupt triggered by the trusted watchdog timer.
+ *
+ * 6. Cactus SP will perform End-Of-Interrupt by calling the interrupt
+ * deactivate HVC and invoke FFA_MSG_WAIT ABI to perform interrupt signal
+ * completion.
+ *
+ * 7. SPM then resumes the second SP which was pre-empted by secure interrupt.
+ *
+ * 8. Second SP will complete the busy sleep loop and send a direct response
+ * message with the elapsed time back to the first SP.
+ *
+ * 9. We make sure the time elapsed in the sleep routine by SP is not less than
+ * the requested value.
+ *
+ * 10. For robustness of state transition checks, TFTF sends echo command using
+ * a direct request message to both SPs.
+ *
+ * 11. Further, TFTF expects SP to return with a success value through a direct
+ * response message.
+ *
+ * 12. Test finishes successfully once the TFTF disables the trusted watchdog
+ * interrupt through a direct message request command.
+ */
+test_result_t test_ffa_sec_interrupt_sp1_waiting_sp2_running(void)
+{
+ smc_ret_values ret_values;
+
+ CHECK_SPMC_TESTING_SETUP(1, 1, expected_sp_uuids);
+
+ /* Enable trusted watchdog interrupt as IRQ in the secure side. */
+ if (!enable_trusted_wdog_interrupt(SENDER, RECEIVER)) {
+ return TEST_RESULT_FAIL;
+ }
+
+ ret_values = cactus_send_twdog_cmd(SENDER, RECEIVER, 100);
+
+ if (!is_ffa_direct_response(ret_values)) {
+ ERROR("Expected a direct response for starting TWDOG timer\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ /* Send request to Second Cactus SP to sleep. */
+ ret_values = cactus_sleep_cmd(SENDER, RECEIVER_2, SP_SLEEP_TIME);
+
+ /*
+ * Secure interrupt should trigger during this time, Cactus
+ * will handle the trusted watchdog timer.
+ */
+ if (!is_ffa_direct_response(ret_values)) {
+ ERROR("Expected a direct response for sleep command\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ /* Make sure elapsed time not less than sleep time. */
+ if (cactus_get_response(ret_values) < SP_SLEEP_TIME) {
+ ERROR("Lapsed time less than requested sleep time\n");
+ }
+
+ ret_values = cactus_echo_send_cmd(SENDER, RECEIVER, ECHO_VAL1);
+
+ if (!is_ffa_direct_response(ret_values)) {
+ ERROR("Echo to SP1 Failed no response!\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ if (cactus_get_response(ret_values) != CACTUS_SUCCESS ||
+ cactus_echo_get_val(ret_values) != ECHO_VAL1) {
+ ERROR("Echo to SP1 Failed!\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ ret_values = cactus_echo_send_cmd(SENDER, RECEIVER_2, ECHO_VAL1);
+
+ if (!is_ffa_direct_response(ret_values)) {
+ ERROR("Echo to SP2 Failed no response!\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ if (cactus_get_response(ret_values) != CACTUS_SUCCESS ||
+ cactus_echo_get_val(ret_values) != ECHO_VAL1) {
+ ERROR("Echo to SP2 Failed!\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ /* Disable Trusted Watchdog interrupt. */
+ if (!disable_trusted_wdog_interrupt(SENDER, RECEIVER)) {
+ return TEST_RESULT_FAIL;
+ }
+
+ return TEST_RESULT_SUCCESS;
+}
diff --git a/tftf/tests/tests-spm.mk b/tftf/tests/tests-spm.mk
index b5a9d0c..79cfb61 100644
--- a/tftf/tests/tests-spm.mk
+++ b/tftf/tests/tests-spm.mk
@@ -10,6 +10,7 @@
spm_common.c \
test_ffa_direct_messaging.c \
test_ffa_interrupts.c \
+ test_ffa_secure_interrupts.c \
test_ffa_memory_sharing.c \
test_ffa_setup_and_discovery.c \
test_ffa_notifications.c \
diff --git a/tftf/tests/tests-spm.xml b/tftf/tests/tests-spm.xml
index 5752b8e..0954a78 100644
--- a/tftf/tests/tests-spm.xml
+++ b/tftf/tests/tests-spm.xml
@@ -92,6 +92,14 @@
description="Test non-secure Interrupts" >
<testcase name="Test NS interrupts"
function="test_ffa_ns_interrupt" />
+ <testcase name="Test Secure interrupt handling while SP running"
+ function="test_ffa_sec_interrupt_sp_running" />
+ <testcase name="Test Secure interrupt handling while SP waiting"
+ function="test_ffa_sec_interrupt_sp_waiting" />
+ <testcase name="Test Secure interrupt handling while SP blocked"
+ function="test_ffa_sec_interrupt_sp_blocked" />
+ <testcase name="Test Secure interrupt handling while SP1 waiting SP2 running"
+ function="test_ffa_sec_interrupt_sp1_waiting_sp2_running" />
</testsuite>
<testsuite name="SMMUv3 tests"