aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/runtime_services/cactus_test_cmds.h50
-rw-r--r--include/runtime_services/ffa_helpers.h1
-rw-r--r--include/runtime_services/spm_common.h12
-rw-r--r--spm/cactus/aarch64/cactus_entrypoint.S4
-rw-r--r--spm/cactus/aarch64/cactus_exceptions.S131
-rw-r--r--spm/cactus/cactus.mk5
-rw-r--r--spm/cactus/cactus_debug.c2
-rw-r--r--spm/cactus/cactus_interrupt.c51
-rw-r--r--spm/cactus/cactus_main.c17
-rw-r--r--spm/cactus/cactus_tests/cactus_test_ffa.c1
-rw-r--r--spm/cactus/cactus_tests/cactus_test_interrupts.c51
-rw-r--r--spm/cactus/plat/arm/fvp/fdts/cactus.dts4
-rw-r--r--spm/common/sp_helpers.c25
-rw-r--r--spm/common/sp_helpers.h16
-rw-r--r--spm/common/spm_helpers.c51
-rw-r--r--spm/common/spm_helpers.h26
-rw-r--r--tftf/tests/runtime_services/secure_service/test_ffa_interrupts.c150
-rw-r--r--tftf/tests/runtime_services/secure_service/test_spci_blocking_interrupt.c121
-rw-r--r--tftf/tests/tests-spm.mk1
-rw-r--r--tftf/tests/tests-spm.xml6
20 files changed, 555 insertions, 170 deletions
diff --git a/include/runtime_services/cactus_test_cmds.h b/include/runtime_services/cactus_test_cmds.h
index 246f4f979..d03a97fcb 100644
--- a/include/runtime_services/cactus_test_cmds.h
+++ b/include/runtime_services/cactus_test_cmds.h
@@ -8,6 +8,7 @@
#define CACTUS_TEST_CMDS
#include <ffa_helpers.h>
+#include <spm_common.h>
/**
* Success and error return to be sent over a msg response.
@@ -249,4 +250,53 @@ static inline smc_ret_values cactus_req_simd_fill_send_cmd(
0);
}
+/**
+ * Command to request cactus to sleep for the given time in ms
+ *
+ * The command id is the hex representation of string "sleep"
+ */
+#define CACTUS_SLEEP_CMD U(0x736c656570)
+
+static inline smc_ret_values cactus_sleep_cmd(
+ ffa_vm_id_t source, ffa_vm_id_t dest, uint32_t sleep_time)
+{
+ return cactus_send_cmd(source, dest, CACTUS_SLEEP_CMD, sleep_time, 0, 0,
+ 0);
+}
+
+static inline uint32_t cactus_get_sleep_time(smc_ret_values ret)
+{
+ return (uint32_t)ret.ret4;
+}
+
+/**
+ * Command to request cactus to enable/disable an interrupt
+ *
+ * The command id is the hex representation of string "intr"
+ */
+#define CACTUS_INTERRUPT_CMD U(0x696e7472)
+
+static inline smc_ret_values cactus_interrupt_cmd(
+ ffa_vm_id_t source, ffa_vm_id_t dest, uint32_t interrupt_id,
+ bool enable, uint32_t pin)
+{
+ return cactus_send_cmd(source, dest, CACTUS_INTERRUPT_CMD, interrupt_id,
+ enable, pin, 0);
+}
+
+static inline uint32_t cactus_get_interrupt_id(smc_ret_values ret)
+{
+ return (uint32_t)ret.ret4;
+}
+
+static inline bool cactus_get_interrupt_enable(smc_ret_values ret)
+{
+ return (bool)ret.ret5;
+}
+
+static inline enum interrupt_pin cactus_get_interrupt_pin(smc_ret_values ret)
+{
+ return (enum interrupt_pin)ret.ret6;
+}
+
#endif
diff --git a/include/runtime_services/ffa_helpers.h b/include/runtime_services/ffa_helpers.h
index 4af051b87..592327af7 100644
--- a/include/runtime_services/ffa_helpers.h
+++ b/include/runtime_services/ffa_helpers.h
@@ -17,7 +17,6 @@
typedef unsigned short ffa_vm_id_t;
typedef unsigned short ffa_vm_count_t;
typedef unsigned short ffa_vcpu_count_t;
-typedef uint32_t ffa_int_id_t;
typedef uint64_t ffa_memory_handle_t;
/** Flags to indicate properties of receivers during memory region retrieval. */
typedef uint8_t ffa_memory_receiver_flags_t;
diff --git a/include/runtime_services/spm_common.h b/include/runtime_services/spm_common.h
index dbb113bac..50159ecb8 100644
--- a/include/runtime_services/spm_common.h
+++ b/include/runtime_services/spm_common.h
@@ -14,6 +14,18 @@
/* Hypervisor ID at physical FFA instance */
#define HYP_ID (0)
+/* ID for the first Secure Partition. */
+#define SPM_VM_ID_FIRST SP_ID(1)
+
+/* INTID for the managed exit virtual interrupt. */
+#define MANAGED_EXIT_INTERRUPT_ID U(4)
+
+/** IRQ/FIQ pin used for signaling a virtual interrupt. */
+enum interrupt_pin {
+ INTERRUPT_TYPE_IRQ,
+ INTERRUPT_TYPE_FIQ,
+};
+
/*
* The bit 15 of the FF-A ID indicates whether the partition is executing
* in the normal world, in case it is a Virtual Machine (VM); or in the
diff --git a/spm/cactus/aarch64/cactus_entrypoint.S b/spm/cactus/aarch64/cactus_entrypoint.S
index 7775b4697..154106337 100644
--- a/spm/cactus/aarch64/cactus_entrypoint.S
+++ b/spm/cactus/aarch64/cactus_entrypoint.S
@@ -59,8 +59,8 @@ secondary_cold_entry:
isb
/* Set up exceptions vector table */
- adrp x1, tftf_vector
- add x1, x1, :lo12:tftf_vector
+ adrp x1, cactus_vector
+ add x1, x1, :lo12:cactus_vector
msr vbar_el1, x1
isb
diff --git a/spm/cactus/aarch64/cactus_exceptions.S b/spm/cactus/aarch64/cactus_exceptions.S
new file mode 100644
index 000000000..31cdbf9a5
--- /dev/null
+++ b/spm/cactus/aarch64/cactus_exceptions.S
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <asm_macros.S>
+
+ .globl cactus_vector
+
+/*
+ * Exception vector code for unhandled exceptions.
+ * Print a crash dump on the UART and loops forever.
+ */
+.macro unhandled_exception name
+ vector_entry \name
+ b crash_dump
+ end_vector_entry \name
+.endm
+
+vector_base cactus_vector
+
+ /*
+ * Current EL with SP0 : 0x0 - 0x200.
+ */
+unhandled_exception sync_sp0
+unhandled_exception irq_sp0
+unhandled_exception fiq_sp0
+unhandled_exception serr_sp0
+
+ /*
+ * Current EL with SPx : 0x200 - 0x400.
+ */
+unhandled_exception sync_spx
+
+vector_entry irq_spx
+ b irq_vector_entry
+end_vector_entry irq_spx
+
+vector_entry fiq_spx
+ b fiq_vector_entry
+end_vector_entry fiq_spx
+
+unhandled_exception serr_spx
+
+ /*
+ * Lower EL using AArch64 : 0x400 - 0x600.
+ */
+unhandled_exception sync_a64
+unhandled_exception irq_a64
+unhandled_exception fiq_a64
+unhandled_exception serr_a64
+
+ /*
+ * Lower EL using AArch32 : 0x600 - 0x800.
+ */
+unhandled_exception sync_a32
+unhandled_exception irq_a32
+unhandled_exception fiq_a32
+unhandled_exception serr_a32
+
+.macro save_gp_regs
+ stp x0, x1, [sp, #0x0]
+ stp x2, x3, [sp, #0x10]
+ stp x4, x5, [sp, #0x20]
+ stp x6, x7, [sp, #0x30]
+ stp x8, x9, [sp, #0x40]
+ stp x10, x11, [sp, #0x50]
+ stp x12, x13, [sp, #0x60]
+ stp x14, x15, [sp, #0x70]
+ stp x16, x17, [sp, #0x80]
+ stp x18, x19, [sp, #0x90]
+ stp x20, x21, [sp, #0xa0]
+ stp x22, x23, [sp, #0xb0]
+ stp x24, x25, [sp, #0xc0]
+ stp x26, x27, [sp, #0xd0]
+ stp x28, x29, [sp, #0xe0]
+ /* We push xzr simply to keep the stack 16-byte aligned. */
+ stp x30, xzr, [sp, #0xf0]
+.endm
+
+.macro restore_gp_regs
+ ldp x30, xzr, [sp, #0xf0]
+ ldp x28, x29, [sp, #0xe0]
+ ldp x26, x27, [sp, #0xd0]
+ ldp x24, x25, [sp, #0xc0]
+ ldp x22, x23, [sp, #0xb0]
+ ldp x20, x21, [sp, #0xa0]
+ ldp x18, x19, [sp, #0x90]
+ ldp x16, x17, [sp, #0x80]
+ ldp x14, x15, [sp, #0x70]
+ ldp x12, x13, [sp, #0x60]
+ ldp x10, x11, [sp, #0x50]
+ ldp x8, x9, [sp, #0x40]
+ ldp x6, x7, [sp, #0x30]
+ ldp x4, x5, [sp, #0x20]
+ ldp x2, x3, [sp, #0x10]
+ ldp x0, x1, [sp, #0x0]
+.endm
+
+func irq_vector_entry
+ sub sp, sp, #0x100
+ save_gp_regs
+ bl cactus_irq_handler
+ restore_gp_regs
+ add sp, sp, #0x100
+ eret
+endfunc irq_vector_entry
+
+func fiq_vector_entry
+ sub sp, sp, #0x100
+ save_gp_regs
+ bl cactus_fiq_handler
+ restore_gp_regs
+ add sp, sp, #0x100
+ eret
+endfunc fiq_vector_entry
+
+func crash_dump
+ /* Save general-purpose registers on the stack. */
+ sub sp, sp, #0x100
+ save_gp_regs
+
+ /* Save original stack pointer value on the stack. */
+ add x1, sp, #0x100
+ str x1, [sp, #0xf8]
+
+ /* Print the saved CPU context on the UART. */
+ mov x0, sp
+ b print_exception
+endfunc crash_dump
diff --git a/spm/cactus/cactus.mk b/spm/cactus/cactus.mk
index 08b824c13..a52120f1f 100644
--- a/spm/cactus/cactus.mk
+++ b/spm/cactus/cactus.mk
@@ -32,18 +32,22 @@ CACTUS_INCLUDES := \
CACTUS_SOURCES := \
$(addprefix spm/cactus/, \
aarch64/cactus_entrypoint.S \
+ aarch64/cactus_exceptions.S \
cactus_debug.c \
+ cactus_interrupt.c \
cactus_main.c \
) \
$(addprefix spm/common/, \
aarch64/sp_arch_helpers.S \
sp_helpers.c \
+ spm_helpers.c \
) \
$(addprefix spm/cactus/cactus_tests/, \
cactus_message_loop.c \
cactus_test_cpu_features.c \
cactus_test_direct_messaging.c \
cactus_test_ffa.c \
+ cactus_test_interrupts.c \
cactus_test_memory_sharing.c \
)
@@ -53,7 +57,6 @@ CACTUS_SOURCES += \
tftf/framework/${ARCH}/asm_debug.S \
tftf/tests/runtime_services/secure_service/ffa_helpers.c \
tftf/tests/runtime_services/secure_service/spm_common.c \
- tftf/framework/${ARCH}/exceptions.S \
tftf/framework/${ARCH}/exception_report.c
CACTUS_SOURCES += drivers/arm/pl011/${ARCH}/pl011_console.S \
diff --git a/spm/cactus/cactus_debug.c b/spm/cactus/cactus_debug.c
index 43093a6bb..30a25278d 100644
--- a/spm/cactus/cactus_debug.c
+++ b/spm/cactus/cactus_debug.c
@@ -6,7 +6,7 @@
#include <drivers/arm/pl011.h>
#include <drivers/console.h>
-#include <sp_helpers.h>
+#include <spm_helpers.h>
#include "cactus.h"
diff --git a/spm/cactus/cactus_interrupt.c b/spm/cactus/cactus_interrupt.c
new file mode 100644
index 000000000..7de36cf0c
--- /dev/null
+++ b/spm/cactus/cactus_interrupt.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <debug.h>
+
+#include <ffa_helpers.h>
+#include <sp_helpers.h>
+#include <spm_helpers.h>
+
+#include "cactus_test_cmds.h"
+#include "spm_common.h"
+
+extern ffa_vm_id_t g_ffa_id;
+
+static void managed_exit_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);
+}
+
+int cactus_irq_handler(void)
+{
+ uint32_t irq_num;
+
+ irq_num = spm_interrupt_get();
+
+ 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);
+ }
+
+ return 0;
+}
diff --git a/spm/cactus/cactus_main.c b/spm/cactus/cactus_main.c
index e54f3b0da..45d2db039 100644
--- a/spm/cactus/cactus_main.c
+++ b/spm/cactus/cactus_main.c
@@ -20,6 +20,7 @@
#include <plat/common/platform.h>
#include <platform_def.h>
#include <sp_helpers.h>
+#include <spm_helpers.h>
#include <std_svc.h>
#include "cactus_def.h"
@@ -32,6 +33,9 @@ extern const char version_string[];
extern void secondary_cold_entry(void);
+/* Global ffa_id */
+ffa_vm_id_t g_ffa_id;
+
/*
*
* Message loop function
@@ -175,18 +179,17 @@ void __dead2 cactus_main(bool primary_cold_boot)
/* Get current FFA id */
smc_ret_values ffa_id_ret = ffa_id_get();
+ ffa_vm_id_t ffa_id = (ffa_vm_id_t)(ffa_id_ret.ret2 & 0xffff);
if (ffa_func_id(ffa_id_ret) != FFA_SUCCESS_SMC32) {
ERROR("FFA_ID_GET failed.\n");
panic();
}
- ffa_vm_id_t ffa_id = ffa_id_ret.ret2 & 0xffff;
if (primary_cold_boot == true) {
/* Clear BSS */
memset((void *)CACTUS_BSS_START,
0, CACTUS_BSS_END - CACTUS_BSS_START);
-
mb.send = (void *) get_sp_tx_start(ffa_id);
mb.recv = (void *) get_sp_rx_start(ffa_id);
@@ -194,8 +197,18 @@ void __dead2 cactus_main(bool primary_cold_boot)
cactus_plat_configure_mmu(ffa_id);
}
+ /*
+ * The local ffa_id value is held on the stack. The global g_ffa_id
+ * value is set after BSS is cleared.
+ */
+ g_ffa_id = ffa_id;
+
enable_mmu_el1(0);
+ /* Enable IRQ/FIQ */
+ enable_irq();
+ enable_fiq();
+
if (primary_cold_boot == false) {
goto msg_loop;
}
diff --git a/spm/cactus/cactus_tests/cactus_test_ffa.c b/spm/cactus/cactus_tests/cactus_test_ffa.c
index c1ba78350..2ade7bd98 100644
--- a/spm/cactus/cactus_tests/cactus_test_ffa.c
+++ b/spm/cactus/cactus_tests/cactus_test_ffa.c
@@ -11,6 +11,7 @@
#include <cactus_platform_def.h>
#include <ffa_endpoints.h>
#include <sp_helpers.h>
+#include <spm_helpers.h>
#include <spm_common.h>
#include <lib/libc/string.h>
diff --git a/spm/cactus/cactus_tests/cactus_test_interrupts.c b/spm/cactus/cactus_tests/cactus_test_interrupts.c
new file mode 100644
index 000000000..b675dfc1e
--- /dev/null
+++ b/spm/cactus/cactus_tests/cactus_test_interrupts.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <common/debug.h>
+#include <sp_helpers.h>
+#include <spm_helpers.h>
+
+#include "cactus_message_loop.h"
+#include "cactus_test_cmds.h"
+
+CACTUS_CMD_HANDLER(sleep_cmd, CACTUS_SLEEP_CMD)
+{
+ uint64_t timer_freq = read_cntfrq_el0();
+ uint64_t time1, time2, 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);
+
+ time1 = read_cntvct_el0();
+ sp_sleep(sleep_time);
+ time2 = read_cntvct_el0();
+
+ /* Lapsed time should be at least equal to sleep time */
+ time_lapsed = ((time2 - time1) * 1000) / timer_freq;
+
+ return cactus_response(ffa_dir_msg_dest(*args),
+ ffa_dir_msg_source(*args),
+ time_lapsed);
+}
+
+CACTUS_CMD_HANDLER(interrupt_cmd, CACTUS_INTERRUPT_CMD)
+{
+ uint32_t int_id = cactus_get_interrupt_id(*args);
+ bool enable = cactus_get_interrupt_enable(*args);
+ enum interrupt_pin pin = cactus_get_interrupt_pin(*args);
+ int64_t ret;
+
+ ret = spm_interrupt_enable(int_id, enable, pin);
+ if (ret != 0) {
+ return cactus_error_resp(ffa_dir_msg_dest(*args),
+ ffa_dir_msg_source(*args),
+ CACTUS_ERROR_TEST);
+ }
+
+ return cactus_response(ffa_dir_msg_dest(*args),
+ ffa_dir_msg_source(*args),
+ CACTUS_SUCCESS);
+}
diff --git a/spm/cactus/plat/arm/fvp/fdts/cactus.dts b/spm/cactus/plat/arm/fvp/fdts/cactus.dts
index 45292730a..eb569f7a1 100644
--- a/spm/cactus/plat/arm/fvp/fdts/cactus.dts
+++ b/spm/cactus/plat/arm/fvp/fdts/cactus.dts
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ * Copyright (c) 2019-2021, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
@@ -27,7 +27,7 @@
entrypoint-offset = <0x00001000>;
xlat-granule = <0>; /* 4KiB */
boot-order = <0>;
- messaging-method = <0>; /* Direct messaging only */
+ messaging-method = <3>; /* Direct messaging with managed exit */
run-time-model = <0>; /* Run to completion */
/* Boot protocol */
diff --git a/spm/common/sp_helpers.c b/spm/common/sp_helpers.c
index 5d76dc8e0..a6b6bc5d9 100644
--- a/spm/common/sp_helpers.c
+++ b/spm/common/sp_helpers.c
@@ -71,28 +71,3 @@ void sp_sleep(uint32_t ms)
time2 = read_cntvct_el0();
}
}
-
-/*******************************************************************************
- * Hypervisor Calls Wrappers
- ******************************************************************************/
-
-ffa_int_id_t spm_interrupt_get(void)
-{
- hvc_args args = {
- .fid = SPM_INTERRUPT_GET
- };
-
- hvc_ret_values ret = tftf_hvc(&args);
-
- return ret.ret0;
-}
-
-void spm_debug_log(char c)
-{
- hvc_args args = {
- .fid = SPM_DEBUG_LOG,
- .arg1 = c
- };
-
- (void)tftf_hvc(&args);
-}
diff --git a/spm/common/sp_helpers.h b/spm/common/sp_helpers.h
index ec9222789..399200a01 100644
--- a/spm/common/sp_helpers.h
+++ b/spm/common/sp_helpers.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ * Copyright (c) 2018-2021, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -11,12 +11,6 @@
#include <tftf_lib.h>
#include <ffa_helpers.h>
-#define SPM_VM_ID_FIRST SP_ID(1)
-
-/* Should match with IDs defined in SPM/Hafnium */
-#define SPM_INTERRUPT_GET (0xFF04)
-#define SPM_DEBUG_LOG (0xBD000000)
-
typedef struct {
u_register_t fid;
u_register_t arg1;
@@ -65,12 +59,4 @@ void announce_test_end(const char *test_desc);
/* Sleep for at least 'ms' milliseconds. */
void sp_sleep(uint32_t ms);
-/*
- * Hypervisor Calls Wrappers
- */
-
-ffa_int_id_t spm_interrupt_get(void);
-
-void spm_debug_log(char c);
-
#endif /* SP_HELPERS_H */
diff --git a/spm/common/spm_helpers.c b/spm/common/spm_helpers.c
new file mode 100644
index 000000000..2ccf3f7d5
--- /dev/null
+++ b/spm/common/spm_helpers.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <spm_helpers.h>
+
+/*******************************************************************************
+ * Hypervisor Calls Wrappers
+ ******************************************************************************/
+
+uint32_t spm_interrupt_get(void)
+{
+ hvc_args args = {
+ .fid = SPM_INTERRUPT_GET
+ };
+
+ hvc_ret_values ret = tftf_hvc(&args);
+
+ return ret.ret0;
+}
+
+void spm_debug_log(char c)
+{
+ hvc_args args = {
+ .fid = SPM_DEBUG_LOG,
+ .arg1 = c
+ };
+
+ (void)tftf_hvc(&args);
+}
+
+/**
+ * Hypervisor call to enable/disable SP delivery of a virtual interrupt of
+ * int_id value through the IRQ or FIQ vector (pin).
+ * Returns 0 on success, or -1 if passing an invalid interrupt id.
+ */
+int64_t spm_interrupt_enable(uint32_t int_id, bool enable, enum interrupt_pin pin)
+{
+ hvc_args args = {
+ .fid = SPM_INTERRUPT_ENABLE,
+ .arg1 = int_id,
+ .arg2 = enable,
+ .arg3 = pin
+ };
+
+ 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
new file mode 100644
index 000000000..10f73161c
--- /dev/null
+++ b/spm/common/spm_helpers.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SPMC_H
+#define SPMC_H
+
+#include <ffa_helpers.h>
+#include <spm_common.h>
+
+/* Should match with IDs defined in SPM/Hafnium */
+#define SPM_INTERRUPT_ENABLE (0xFF03)
+#define SPM_INTERRUPT_GET (0xFF04)
+#define SPM_DEBUG_LOG (0xBD000000)
+
+/*
+ * Hypervisor Calls Wrappers
+ */
+
+uint32_t spm_interrupt_get(void);
+int64_t spm_interrupt_enable(uint32_t int_id, bool enable, enum interrupt_pin pin);
+void spm_debug_log(char c);
+
+#endif /* SPMC_H */
diff --git a/tftf/tests/runtime_services/secure_service/test_ffa_interrupts.c b/tftf/tests/runtime_services/secure_service/test_ffa_interrupts.c
new file mode 100644
index 000000000..7c70de2c3
--- /dev/null
+++ b/tftf/tests/runtime_services/secure_service/test_ffa_interrupts.c
@@ -0,0 +1,150 @@
+/*
+ * 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>
+
+static volatile int timer_irq_received;
+
+#define SENDER HYP_ID
+#define RECEIVER SP_ID(1)
+#define SLEEP_TIME 200U
+
+static const struct ffa_uuid expected_sp_uuids[] = {
+ {PRIMARY_UUID}
+ };
+
+/*
+ * ISR for the timer interrupt. Update a global variable to check it has been
+ * called.
+ */
+static int timer_handler(void *data)
+{
+ assert(timer_irq_received == 0);
+ timer_irq_received = 1;
+ return 0;
+}
+
+/*
+ * @Test_Aim@ Test non-secure interrupts while executing Secure Partition.
+ *
+ * 1. Enable managed exit interrupt by sending interrupt_enable command to
+ * Cactus.
+ *
+ * 2. Register a handler for the non-secure timer interrupt. Program it to fire
+ * in a certain time.
+ *
+ * 3. Send a blocking request to Cactus to execute in busy loop.
+ *
+ * 4. While executing in busy loop, the non-secure timer should
+ * fire and trap into SPM running at S-EL2 as FIQ.
+ *
+ * 5. SPM injects a managed exit virtual FIQ into Cactus (as configured in the
+ * interrupt enable call), causing it to run its interrupt handler.
+ *
+ * 6. Cactus's managed exit handler acknowledges interrupt arrival by
+ * requesting the interrupt id to the SPMC, and check if it is the
+ * MANAGED_EXIT_INTERRUPT_ID.
+ *
+ * 7. Check whether the pending non-secure timer interrupt successfully got
+ * handled in TFTF.
+ *
+ * 8. Send a direct message request command to resume Cactus's execution.
+ * It resumes in the sleep loop and completes it. It then returns with
+ * a direct message response. Check if time lapsed is greater than
+ * sleeping time.
+ *
+ */
+test_result_t test_ffa_ns_interrupt(void)
+{
+ int ret;
+ smc_ret_values ret_values;
+
+ CHECK_SPMC_TESTING_SETUP(1, 0, expected_sp_uuids);
+
+ /* Enable managed exit interrupt as FIQ in the secure side. */
+ ret_values = cactus_interrupt_cmd(SENDER, RECEIVER, MANAGED_EXIT_INTERRUPT_ID,
+ true, INTERRUPT_TYPE_FIQ);
+
+ if (!is_ffa_direct_response(ret_values)) {
+ return TEST_RESULT_FAIL;
+ }
+
+ if (cactus_get_response(ret_values) != CACTUS_SUCCESS) {
+ ERROR("Failed to enable Managed exit interrupt\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ /* Program timer */
+ timer_irq_received = 0;
+ tftf_timer_register_handler(timer_handler);
+
+ ret = tftf_program_timer(100);
+ if (ret < 0) {
+ ERROR("Failed to program timer (%d)\n", ret);
+ return TEST_RESULT_FAIL;
+ }
+
+ /* Send request to primary Cactus to sleep for 200ms */
+ ret_values = cactus_sleep_cmd(SENDER, RECEIVER, SLEEP_TIME);
+
+ if (!is_ffa_direct_response(ret_values)) {
+ return TEST_RESULT_FAIL;
+ }
+
+ /*
+ * Managed exit interrupt occurs during this time, Cactus
+ * will respond with interrupt ID.
+ */
+ if (cactus_get_response(ret_values) != MANAGED_EXIT_INTERRUPT_ID) {
+ ERROR("Managed exit interrupt did not occur!\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ /* Check that the timer interrupt has been handled in NS-world (TFTF) */
+ tftf_cancel_timer();
+ tftf_timer_unregister_handler();
+
+ if (timer_irq_received == 0) {
+ ERROR("Timer interrupt hasn't actually been handled.\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ /*
+ * Send a dummy direct message request to relinquish CPU cycles.
+ * This resumes Cactus in the sleep routine.
+ */
+ ret_values = ffa_msg_send_direct_req64(SENDER, RECEIVER,
+ 0, 0, 0, 0, 0);
+
+ if (!is_ffa_direct_response(ret_values)) {
+ return TEST_RESULT_FAIL;
+ }
+
+ /* Make sure elapsed time not less than sleep time */
+ if (cactus_get_response(ret_values) < SLEEP_TIME) {
+ ERROR("Lapsed time less than requested sleep time\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ /* Disable Managed exit interrupt */
+ ret_values = cactus_interrupt_cmd(SENDER, RECEIVER, MANAGED_EXIT_INTERRUPT_ID,
+ false, 0);
+
+ if (!is_ffa_direct_response(ret_values)) {
+ return TEST_RESULT_FAIL;
+ }
+
+ if (cactus_get_response(ret_values) != CACTUS_SUCCESS) {
+ ERROR("Failed to disable Managed exit interrupt\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ return TEST_RESULT_SUCCESS;
+}
diff --git a/tftf/tests/runtime_services/secure_service/test_spci_blocking_interrupt.c b/tftf/tests/runtime_services/secure_service/test_spci_blocking_interrupt.c
deleted file mode 100644
index 44ee0f9a9..000000000
--- a/tftf/tests/runtime_services/secure_service/test_spci_blocking_interrupt.c
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (c) 2018, Arm Limited. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <arch_helpers.h>
-#include <assert.h>
-#include <cactus_def.h>
-#include <debug.h>
-#include <smccc.h>
-#include <spci_helpers.h>
-#include <spci_svc.h>
-#include <string.h>
-#include <test_helpers.h>
-#include <tftf_lib.h>
-#include <timer.h>
-
-static volatile int timer_irq_received;
-
-/*
- * ISR for the timer interrupt. Update a global variable to check it has been
- * called.
- */
-static int timer_handler(void *data)
-{
- assert(timer_irq_received == 0);
- timer_irq_received = 1;
- return 0;
-}
-
-/*
- * @Test_Aim@ Test that non-secure interrupts do not interrupt blocking
- * requests.
- *
- * 1. Register a handler for the non-secure timer interrupt. Program it to fire
- * in a certain time.
- *
- * 2. Send a blocking request to Cactus to sleep for more time than the timer.
- *
- * 3. While servicing the timer sleep request, the non-secure timer should
- * fire but not interrupt Cactus.
- *
- * 4. Once back in TFTF, check the response from Cactus, which shows whether the
- * secure service indeed ran to completion.
- *
- * 5. Also check whether the pending non-secure timer interrupt successfully got
- * handled in TFTF.
- */
-test_result_t test_spci_blocking_interrupt_by_ns(void)
-{
- int ret;
- uint16_t handle_cactus;
- test_result_t result = TEST_RESULT_SUCCESS;
-
- SKIP_TEST_IF_SPCI_VERSION_LESS_THAN(0, 1);
-
- /* Open handle */
-
- ret = spci_service_handle_open(TFTF_SPCI_CLIENT_ID, &handle_cactus,
- CACTUS_SERVICE1_UUID);
- if (ret != SPCI_SUCCESS) {
- tftf_testcase_printf("%d: SPM failed to return a valid handle. Returned: %d\n",
- __LINE__, ret);
- return TEST_RESULT_FAIL;
- }
-
- /* Program timer */
-
- timer_irq_received = 0;
- tftf_timer_register_handler(timer_handler);
-
- ret = tftf_program_timer(100);
- if (ret < 0) {
- tftf_testcase_printf("Failed to program timer (%d)\n", ret);
- result = TEST_RESULT_FAIL;
- }
-
- /* Send request to Cactus */
-
- ret = spci_service_request_blocking(CACTUS_SLEEP_MS, 200U,
- 0, 0, 0, 0,
- TFTF_SPCI_CLIENT_ID,
- handle_cactus,
- NULL, NULL, NULL);
- if (ret != SPCI_SUCCESS) {
- /*
- * If the interrupt is handled during the blocking call, there
- * will be a crash in EL3 because the function that invokes a
- * blocking call doesn't know how to handle it. The CPU won't
- * come back here (because it should never happen!).
- */
- tftf_testcase_printf("%d: SPM should have returned SPCI_SUCCESS. Returned: %d\n",
- __LINE__, ret);
- result = TEST_RESULT_FAIL;
- }
-
- /* Check that the interrupt has been handled */
-
- tftf_cancel_timer();
- tftf_timer_unregister_handler();
-
- if (timer_irq_received == 0) {
- tftf_testcase_printf("%d: Timer interrupt hasn't actually been handled.\n",
- __LINE__);
- result = TEST_RESULT_FAIL;
- }
-
- /* Close handle */
-
- ret = spci_service_handle_close(TFTF_SPCI_CLIENT_ID, handle_cactus);
- if (ret != SPCI_SUCCESS) {
- tftf_testcase_printf("%d: SPM failed to close the handle. Returned: %d\n",
- __LINE__, ret);
- result = TEST_RESULT_FAIL;
- }
-
- /* All tests finished */
-
- return result;
-}
diff --git a/tftf/tests/tests-spm.mk b/tftf/tests/tests-spm.mk
index 73d2baf3d..9364052b6 100644
--- a/tftf/tests/tests-spm.mk
+++ b/tftf/tests/tests-spm.mk
@@ -10,6 +10,7 @@ TESTS_SOURCES += \
spm_common.c \
test_ffa_direct_messaging.c \
test_ffa_features.c \
+ test_ffa_interrupts.c \
test_ffa_memory_sharing.c \
test_ffa_rxtx_map.c \
test_ffa_version.c \
diff --git a/tftf/tests/tests-spm.xml b/tftf/tests/tests-spm.xml
index ee3429278..01ebceab2 100644
--- a/tftf/tests/tests-spm.xml
+++ b/tftf/tests/tests-spm.xml
@@ -78,4 +78,10 @@
function="test_simd_vectors_preserved" />
</testsuite>
+ <testsuite name="FF-A Interrupt"
+ description="Test non-secure Interrupts" >
+ <testcase name="Test NS interrupts"
+ function="test_ffa_ns_interrupt" />
+ </testsuite>
+
</testsuites>