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"