test(indirect message): add FFA_MSG_SEND2 tests

Add tests for following scenarios:
* FFA_MSG_SEND2 is a supported FFA_FEATURE.
* VM-VM: primary VM sends an indirect message to VM2. Since current
  implementation misses receiver notification, VM1 explicitly schedules
  VM2 (via FFA_RUN), which reads its RX buffer and echoes it back to
  VM1.
* VM-SP, SP-SP, SP-VM: SP testing infrastructure doesn't support
  indirect messages yet. For those scenarios the primary VM asks the
  sender to send an indirect message to the receiver. Receiver
  notification is missing, the sender sends then a direct message to the
  receiver, which checks its RX buffer and echoes back the message
  payload to the sender.

Change-Id: I699a80cf839a1eac3883fcb7e857a08210160f58
Signed-off-by: Federico Recanati <federico.recanati@arm.com>
diff --git a/test/hftest/sel0_secure_service.c b/test/hftest/sel0_secure_service.c
index 2e2f568..3ea9991 100644
--- a/test/hftest/sel0_secure_service.c
+++ b/test/hftest/sel0_secure_service.c
@@ -16,6 +16,7 @@
 #include "vmapi/hf/call.h"
 
 #include "test/hftest.h"
+#include "test/vmapi/ffa.h"
 
 alignas(4096) uint8_t kstack[4096];
 
@@ -43,6 +44,9 @@
 
 noreturn void kmain(void)
 {
+	/* Register RX/TX buffers via FFA_RXTX_MAP */
+	set_up_mailbox();
+
 	test_main_sp(true);
 
 	/* Do not expect to get to this point, so abort. */
diff --git a/test/hftest/sel1_secure_service.c b/test/hftest/sel1_secure_service.c
index cff2f00..76b74e3 100644
--- a/test/hftest/sel1_secure_service.c
+++ b/test/hftest/sel1_secure_service.c
@@ -17,6 +17,7 @@
 
 #include "test/abort.h"
 #include "test/hftest.h"
+#include "test/vmapi/ffa.h"
 
 alignas(4096) uint8_t kstack[MAX_CPUS][4096];
 
@@ -40,6 +41,9 @@
 	res = ffa_secondary_ep_register((uintptr_t)secondary_ep_entry);
 	EXPECT_EQ(res.func, FFA_SUCCESS_32);
 
+	/* Register RX/TX buffers via FFA_RXTX_MAP */
+	set_up_mailbox();
+
 	test_main_sp(true);
 
 	/* Do not expect to get to this point, so abort. */
diff --git a/test/inc/test/vmapi/ffa.h b/test/inc/test/vmapi/ffa.h
index 7438335..03f5690 100644
--- a/test/inc/test/vmapi/ffa.h
+++ b/test/inc/test/vmapi/ffa.h
@@ -81,3 +81,8 @@
 struct ffa_boot_info_desc *get_boot_info_desc(
 	struct ffa_boot_info_header *boot_info_heade, uint8_t type,
 	uint8_t type_id);
+
+struct ffa_value send_indirect_message(ffa_vm_id_t from, ffa_vm_id_t to,
+				       void *send, const void *payload,
+				       size_t payload_size,
+				       uint32_t send_flags);
diff --git a/test/vmapi/common/ffa.c b/test/vmapi/common/ffa.c
index 5622ee9..42b0e48 100644
--- a/test/vmapi/common/ffa.c
+++ b/test/vmapi/common/ffa.c
@@ -27,8 +27,12 @@
 
 struct mailbox_buffers set_up_mailbox(void)
 {
-	ASSERT_EQ(ffa_rxtx_map(send_page_addr, recv_page_addr).func,
-		  FFA_SUCCESS_32);
+	static bool set_up = false;
+	if (!set_up) {
+		ASSERT_EQ(ffa_rxtx_map(send_page_addr, recv_page_addr).func,
+			  FFA_SUCCESS_32);
+		set_up = true;
+	}
 	return (struct mailbox_buffers){
 		.send = send_page,
 		.recv = recv_page,
@@ -431,3 +435,20 @@
 
 	return NULL;
 }
+
+struct ffa_value send_indirect_message(ffa_vm_id_t from, ffa_vm_id_t to,
+				       void *send, const void *payload,
+				       size_t payload_size, uint32_t send_flags)
+{
+	struct ffa_partition_msg *message = (struct ffa_partition_msg *)send;
+
+	/* Initialize message header. */
+	ffa_rxtx_header_init(from, to, payload_size, &message->header);
+
+	/* Fill TX buffer with payload. */
+	memcpy_s(message->payload, FFA_PARTITION_MSG_PAYLOAD_MAX, payload,
+		 payload_size);
+
+	/* Send the message. */
+	return ffa_msg_send2(send_flags);
+}
diff --git a/test/vmapi/ffa_secure_partition_only/secure_partition.c b/test/vmapi/ffa_secure_partition_only/secure_partition.c
index 8bbab9c..68b4d51 100644
--- a/test/vmapi/ffa_secure_partition_only/secure_partition.c
+++ b/test/vmapi/ffa_secure_partition_only/secure_partition.c
@@ -137,6 +137,9 @@
 
 	ret = ffa_features(FFA_MEM_PERM_SET_64);
 	EXPECT_EQ(ret.func, FFA_SUCCESS_32);
+
+	ret = ffa_features(FFA_MSG_SEND2_32);
+	EXPECT_EQ(ret.func, FFA_SUCCESS_32);
 #endif
 }
 
diff --git a/test/vmapi/ffa_secure_partitions/BUILD.gn b/test/vmapi/ffa_secure_partitions/BUILD.gn
index 57a04ef..ff6e3ed 100644
--- a/test/vmapi/ffa_secure_partitions/BUILD.gn
+++ b/test/vmapi/ffa_secure_partitions/BUILD.gn
@@ -18,6 +18,7 @@
 
   sources = [
     "dir_msg.c",
+    "indirect_msg.c",
     "interrupts.c",
     "notifications.c",
     "power_mgt.c",
diff --git a/test/vmapi/ffa_secure_partitions/indirect_msg.c b/test/vmapi/ffa_secure_partitions/indirect_msg.c
new file mode 100644
index 0000000..0eafef8
--- /dev/null
+++ b/test/vmapi/ffa_secure_partitions/indirect_msg.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2022 The Hafnium Authors.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/BSD-3-Clause.
+ */
+
+#include "hf/dlog.h"
+#include "hf/ffa.h"
+
+#include "vmapi/hf/call.h"
+
+#include "partition_services.h"
+#include "test/hftest.h"
+#include "test/vmapi/ffa.h"
+
+/** Ask SP1 to send a message to SP2, which will echo it back to SP1. */
+TEST(indirect_msg, echo_sp)
+{
+	const ffa_vm_id_t own_id = hf_vm_get_id();
+	const ffa_vm_id_t receiver_id = SP_ID(1);
+	const ffa_vm_id_t msg_receiver_id = SP_ID(2);
+	const uint32_t payload = 0xAA55AA55;
+	struct ffa_value ret;
+
+	ret = sp_indirect_msg_cmd_send(own_id, receiver_id, msg_receiver_id,
+				       payload);
+
+	EXPECT_EQ(ret.func, FFA_MSG_SEND_DIRECT_RESP_32);
+	EXPECT_EQ(sp_resp(ret), SP_SUCCESS);
+}
+
+/** VM1 sends a message to SP1, which will echo it back to VM1. */
+TEST(indirect_msg, echo_cross_world)
+{
+	const ffa_vm_id_t own_id = hf_vm_get_id();
+	const ffa_vm_id_t receiver_id = SP_ID(1);
+	const uint32_t payload = 0xAA55AA55;
+	struct mailbox_buffers mb = set_up_mailbox();
+	struct ffa_partition_msg *message;
+	const uint32_t *echo_payload;
+	struct ffa_value ret;
+
+	ret = send_indirect_message(own_id, receiver_id, mb.send, &payload,
+				    sizeof(payload), 0);
+	EXPECT_EQ(ret.func, FFA_SUCCESS_32);
+
+	/* Notify the receiver that got an indirect message. */
+	ret = sp_echo_indirect_msg_cmd_send(own_id, receiver_id);
+	EXPECT_EQ(ret.func, FFA_MSG_SEND_DIRECT_RESP_32);
+	EXPECT_EQ(sp_resp(ret), SP_SUCCESS);
+
+	/* Check notification. */
+	ret = ffa_notification_get(own_id, 0, FFA_NOTIFICATION_FLAG_BITMAP_SPM);
+	ASSERT_EQ(ret.func, FFA_SUCCESS_32);
+	ASSERT_TRUE(is_ffa_spm_buffer_full_notification(
+		ffa_notification_get_from_framework(ret)));
+
+	/* Ensure echoed message is the same as sent. */
+	message = (struct ffa_partition_msg *)mb.recv;
+	echo_payload = (const uint32_t *)message->payload;
+	ASSERT_EQ(payload, *echo_payload);
+
+	EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32);
+}
diff --git a/test/vmapi/ffa_secure_partitions/partition_manifest_nwd_primary.dts b/test/vmapi/ffa_secure_partitions/partition_manifest_nwd_primary.dts
index 2434ca5..aada4d7 100644
--- a/test/vmapi/ffa_secure_partitions/partition_manifest_nwd_primary.dts
+++ b/test/vmapi/ffa_secure_partitions/partition_manifest_nwd_primary.dts
@@ -21,6 +21,6 @@
 	load-address = <0x90000000>;
 	entrypoint-offset = <0x2000>;
 	xlat-granule = <0>; /* 4KiB */
-	messaging-method = <0x3>; /* Supports sending/receiving direct requests. */
+	messaging-method = <0x7>; /* Supports direct and indirect requests. */
 	notification-support; /* Receipt of notifications. */
 };
diff --git a/test/vmapi/ffa_secure_partitions/partition_manifest_service_sp_first.dts b/test/vmapi/ffa_secure_partitions/partition_manifest_service_sp_first.dts
index 0dade1b..58a31d6 100644
--- a/test/vmapi/ffa_secure_partitions/partition_manifest_service_sp_first.dts
+++ b/test/vmapi/ffa_secure_partitions/partition_manifest_service_sp_first.dts
@@ -21,7 +21,7 @@
 	load-address = <0x6480000>;
 	entrypoint-offset = <0x2000>;
 	xlat-granule = <0>; /* 4KiB */
-	messaging-method = <0x3>; /* Supports sending/receiving direct requests. */
+	messaging-method = <0x7>; /* Supports direct and indirect requests. */
 	boot-order = <1>;
 	notification-support; /* Receipt of notifications. */
 };
diff --git a/test/vmapi/ffa_secure_partitions/partition_manifest_service_sp_second.dts b/test/vmapi/ffa_secure_partitions/partition_manifest_service_sp_second.dts
index 5e33df3..ae83fe8 100644
--- a/test/vmapi/ffa_secure_partitions/partition_manifest_service_sp_second.dts
+++ b/test/vmapi/ffa_secure_partitions/partition_manifest_service_sp_second.dts
@@ -21,7 +21,7 @@
 	load-address = <0x6380000>;
 	entrypoint-offset = <0x2000>;
 	xlat-granule = <0>; /* 4KiB */
-	messaging-method = <0x3>; /* Supports sending/receiving direct requests. */
+	messaging-method = <0x7>; /* Supports direct and indirect messages. */
 	boot-order = <4>;
 	notification-support; /* Receipt of notifications. */
 };
diff --git a/test/vmapi/ffa_secure_partitions/partition_manifest_service_sp_second_el0.dts b/test/vmapi/ffa_secure_partitions/partition_manifest_service_sp_second_el0.dts
index f19bffe..c117dac 100644
--- a/test/vmapi/ffa_secure_partitions/partition_manifest_service_sp_second_el0.dts
+++ b/test/vmapi/ffa_secure_partitions/partition_manifest_service_sp_second_el0.dts
@@ -21,7 +21,7 @@
   load-address = <0x6290000>;
   entrypoint-offset = <0x2000>;
   xlat-granule = <0>; /* 4KiB */
-  messaging-method = <0x3>; /* Direct messaging only */
+  messaging-method = <0x7>; /* Support direct and indirect messaging. */
   boot-order = <4>;
   notification-support; /* Receipt of notifications. */
 };
diff --git a/test/vmapi/ffa_secure_partitions/services/BUILD.gn b/test/vmapi/ffa_secure_partitions/services/BUILD.gn
index e1345fc..a400e5c 100644
--- a/test/vmapi/ffa_secure_partitions/services/BUILD.gn
+++ b/test/vmapi/ffa_secure_partitions/services/BUILD.gn
@@ -34,6 +34,7 @@
 
   deps = [
     ":common",
+    "//test/vmapi/common:common",
   ]
 }
 
diff --git a/test/vmapi/ffa_secure_partitions/services/inc/partition_services.h b/test/vmapi/ffa_secure_partitions/services/inc/partition_services.h
index b271560..941e31e 100644
--- a/test/vmapi/ffa_secure_partitions/services/inc/partition_services.h
+++ b/test/vmapi/ffa_secure_partitions/services/inc/partition_services.h
@@ -204,6 +204,47 @@
 				     ffa_vm_id_t notif_sender,
 				     ffa_notifications_bitmap_t bitmap);
 
+/**
+ * Command to request an SP to send an indirect message.
+ */
+#define SP_INDIR_MSG_CMD 0x494dU
+
+static inline struct ffa_value sp_indirect_msg_cmd_send(
+	ffa_vm_id_t sender, ffa_vm_id_t receiver, ffa_vm_id_t msg_receiver,
+	uint32_t payload)
+{
+	return ffa_msg_send_direct_req(sender, receiver, SP_INDIR_MSG_CMD,
+				       msg_receiver, payload, 0, 0);
+}
+
+static inline ffa_vm_id_t sp_indirect_msg_receiver(struct ffa_value cmd)
+{
+	return (ffa_vm_id_t)cmd.arg4;
+}
+
+static inline uint32_t sp_indirect_msg_payload(struct ffa_value cmd)
+{
+	return cmd.arg5;
+}
+
+struct ffa_value sp_indirect_msg_cmd(ffa_vm_id_t test_source,
+				     ffa_vm_id_t receiver_id, uint32_t payload);
+
+/**
+ * Command to notify an SP an indirect message is available in it's RX buffer,
+ * echoes it back to sender.
+ */
+#define SP_ECHO_INDIR_MSG_CMD 0x43494eU
+
+static inline struct ffa_value sp_echo_indirect_msg_cmd_send(
+	ffa_vm_id_t sender, ffa_vm_id_t receiver)
+{
+	return ffa_msg_send_direct_req(sender, receiver, SP_ECHO_INDIR_MSG_CMD,
+				       0, 0, 0, 0);
+}
+
+struct ffa_value sp_echo_indirect_msg_cmd(ffa_vm_id_t test_source);
+
 struct ffa_value sp_check_ffa_return_resp(ffa_vm_id_t test_source,
 					  ffa_vm_id_t own_id,
 					  struct ffa_value res);
diff --git a/test/vmapi/ffa_secure_partitions/services/message_loop.c b/test/vmapi/ffa_secure_partitions/services/message_loop.c
index a00fd52..8b5bf37 100644
--- a/test/vmapi/ffa_secure_partitions/services/message_loop.c
+++ b/test/vmapi/ffa_secure_partitions/services/message_loop.c
@@ -64,6 +64,14 @@
 			res = sp_check_cpu_idx_cmd(ffa_sender(res),
 						   sp_check_cpu_idx(res));
 			break;
+		case SP_INDIR_MSG_CMD:
+			res = sp_indirect_msg_cmd(ffa_sender(res),
+						  sp_indirect_msg_receiver(res),
+						  sp_indirect_msg_payload(res));
+			break;
+		case SP_ECHO_INDIR_MSG_CMD:
+			res = sp_echo_indirect_msg_cmd(ffa_sender(res));
+			break;
 		case SP_WAIT_BUSY_LOOP_CMD:
 			for (volatile uint64_t loop = 0; loop < res.arg4;
 			     loop++) {
diff --git a/test/vmapi/ffa_secure_partitions/services/partition_services.c b/test/vmapi/ffa_secure_partitions/services/partition_services.c
index e423ee2..4d6878c 100644
--- a/test/vmapi/ffa_secure_partitions/services/partition_services.c
+++ b/test/vmapi/ffa_secure_partitions/services/partition_services.c
@@ -1,5 +1,5 @@
 /*
- * Copyright 2021 The Hafnium Authors.
+ * Copyright 2022 The Hafnium Authors.
  *
  * Use of this source code is governed by a BSD-style
  * license that can be found in the LICENSE file or at
@@ -58,3 +58,87 @@
 
 	return sp_success(own_id, test_source, 0);
 }
+
+/**
+ * VM asking an SP to send an indirect message to another endpoint.
+ * Message is sent via FFA_MSG_SEND2 ABI, and the receiver is notified through
+ * a direct message.
+ * The message is expected to be echoed back by an indirect message.
+ */
+struct ffa_value sp_indirect_msg_cmd(ffa_vm_id_t test_source,
+				     ffa_vm_id_t receiver_id, uint32_t payload)
+{
+	ffa_vm_id_t own_id = hf_vm_get_id();
+	struct mailbox_buffers mb = set_up_mailbox();
+	struct ffa_partition_msg *message;
+	const uint32_t *echo_payload;
+	struct ffa_value ret;
+
+	ret = send_indirect_message(own_id, receiver_id, mb.send, &payload,
+				    sizeof(payload),
+				    FFA_NOTIFICATIONS_FLAG_DELAY_SRI);
+	EXPECT_EQ(ret.func, FFA_SUCCESS_32);
+
+	/*
+	 * Notify the receiver that got an indirect message using a direct
+	 * message.
+	 */
+	ret = sp_echo_indirect_msg_cmd_send(own_id, receiver_id);
+	EXPECT_EQ(ret.func, FFA_MSG_SEND_DIRECT_RESP_32);
+	EXPECT_EQ(sp_resp(ret), SP_SUCCESS);
+
+	/* Check notification. */
+	ret = ffa_notification_get(own_id, 0, FFA_NOTIFICATION_FLAG_BITMAP_SPM);
+	ASSERT_EQ(ret.func, FFA_SUCCESS_32);
+	ASSERT_TRUE(is_ffa_spm_buffer_full_notification(
+		ffa_notification_get_from_framework(ret)));
+
+	/* Ensure echoed message is the same as sent. */
+	message = (struct ffa_partition_msg *)mb.recv;
+	echo_payload = (const uint32_t *)message->payload;
+	ASSERT_EQ(payload, *echo_payload);
+
+	EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32);
+
+	return sp_success(own_id, test_source, 0);
+}
+
+/**
+ * Echo the indirect message back to sender.
+ */
+struct ffa_value sp_echo_indirect_msg_cmd(ffa_vm_id_t test_source)
+{
+	ffa_vm_id_t own_id = hf_vm_get_id();
+	ffa_vm_id_t target_vm_id;
+	ffa_vm_id_t source_vm_id;
+	struct mailbox_buffers mb = set_up_mailbox();
+	struct ffa_partition_msg *message;
+	const uint32_t *payload;
+	struct ffa_value ret;
+
+	/* Check notification. */
+	ret = ffa_notification_get(own_id, 0,
+				   FFA_NOTIFICATION_FLAG_BITMAP_HYP |
+					   FFA_NOTIFICATION_FLAG_BITMAP_SPM);
+	ASSERT_EQ(ret.func, FFA_SUCCESS_32);
+	ASSERT_TRUE(is_ffa_hyp_buffer_full_notification(
+			    ffa_notification_get_from_framework(ret)) |
+		    is_ffa_spm_buffer_full_notification(
+			    ffa_notification_get_from_framework(ret)));
+
+	message = (struct ffa_partition_msg *)mb.recv;
+	source_vm_id = ffa_rxtx_header_sender(&message->header);
+	target_vm_id = ffa_rxtx_header_receiver(&message->header);
+	EXPECT_EQ(own_id, target_vm_id);
+
+	payload = (const uint32_t *)message->payload;
+
+	EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32);
+
+	/* Echo message back. */
+	send_indirect_message(target_vm_id, source_vm_id, mb.send, payload,
+			      sizeof(*payload),
+			      FFA_NOTIFICATIONS_FLAG_DELAY_SRI);
+
+	return sp_success(own_id, test_source, 0);
+}
diff --git a/test/vmapi/primary_only/primary_only.c b/test/vmapi/primary_only/primary_only.c
index b907202..8c90c61 100644
--- a/test/vmapi/primary_only/primary_only.c
+++ b/test/vmapi/primary_only/primary_only.c
@@ -278,6 +278,9 @@
 
 	ret = ffa_features(FFA_MEM_PERM_SET_64);
 	EXPECT_EQ(ret.func, FFA_SUCCESS_32);
+
+	ret = ffa_features(FFA_MSG_SEND2_32);
+	EXPECT_EQ(ret.func, FFA_SUCCESS_32);
 #endif
 }
 
diff --git a/test/vmapi/primary_with_secondaries/BUILD.gn b/test/vmapi/primary_with_secondaries/BUILD.gn
index 55d7861..094762a 100644
--- a/test/vmapi/primary_with_secondaries/BUILD.gn
+++ b/test/vmapi/primary_with_secondaries/BUILD.gn
@@ -83,6 +83,7 @@
     "debug_el1.c",
     "ffa.c",
     "floating_point.c",
+    "indirect_messaging.c",
     "interrupts.c",
     "mailbox.c",
     "mailbox_common.c",
diff --git a/test/vmapi/primary_with_secondaries/indirect_messaging.c b/test/vmapi/primary_with_secondaries/indirect_messaging.c
new file mode 100644
index 0000000..096cd83
--- /dev/null
+++ b/test/vmapi/primary_with_secondaries/indirect_messaging.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2022 The Hafnium Authors.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/BSD-3-Clause.
+ */
+
+#include <stdint.h>
+
+#include "hf/ffa.h"
+#include "hf/std.h"
+
+#include "vmapi/hf/call.h"
+
+#include "primary_with_secondary.h"
+#include "test/hftest.h"
+#include "test/vmapi/ffa.h"
+
+SET_UP(indirect_messaging)
+{
+	/* Call FFA_VERSION to inform the hypervisor we are v1.1. */
+	ffa_version(MAKE_FFA_VERSION(1, 1));
+}
+
+/**
+ * Send and receive the same message from the echo VM using
+ * FFA v1.1 FFA_MSG_SEND2 ABI.
+ */
+TEST(indirect_messaging, echo)
+{
+	struct ffa_value ret;
+	struct mailbox_buffers mb;
+	const uint32_t payload = 0xAA55AA55;
+	struct ffa_partition_msg *echo;
+	const uint32_t *echo_payload;
+	ffa_vm_id_t own_id = hf_vm_get_id();
+
+	mb = set_up_mailbox();
+	SERVICE_SELECT(SERVICE_VM1, "echo_msg_send2", mb.send);
+
+	/* Send the message. */
+	ret = send_indirect_message(own_id, SERVICE_VM1, mb.send, &payload,
+				    sizeof(payload), 0);
+	EXPECT_EQ(ret.func, FFA_SUCCESS_32);
+
+	/* Schedule message receiver. */
+	ret = ffa_run(SERVICE_VM1, 0);
+	EXPECT_EQ(ret.func, FFA_YIELD_32);
+
+	/* Check notification. */
+	ret = ffa_notification_get(own_id, 0, FFA_NOTIFICATION_FLAG_BITMAP_HYP);
+	ASSERT_EQ(ret.func, FFA_SUCCESS_32);
+	ASSERT_TRUE(is_ffa_hyp_buffer_full_notification(
+		ffa_notification_get_from_framework(ret)));
+
+	echo = (struct ffa_partition_msg *)mb.recv;
+	echo_payload = (const uint32_t *)echo->payload;
+	HFTEST_LOG("Message echoed back: %#x", *echo_payload);
+	EXPECT_EQ(*echo_payload, payload);
+
+	EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32);
+}
+
+/** Sender haven't mapped TX buffer. */
+TEST(indirect_messaging, unmapped_tx)
+{
+	struct ffa_value ret;
+	struct mailbox_buffers mb;
+	const uint32_t payload = 0xAA55AA55;
+	ffa_vm_id_t own_id = hf_vm_get_id();
+
+	mb = set_up_mailbox();
+	SERVICE_SELECT(SERVICE_VM1, "ffa_indirect_msg_error", mb.send);
+
+	EXPECT_EQ(ffa_rxtx_unmap().func, FFA_SUCCESS_32);
+
+	/* Send the message. */
+	ret = send_indirect_message(own_id, SERVICE_VM1, mb.send, &payload,
+				    sizeof(payload), 0);
+	EXPECT_FFA_ERROR(ret, FFA_DENIED);
+}
+
+/** Receiver haven't mapped RX buffer. */
+TEST(indirect_messaging, unmapped_rx)
+{
+	struct ffa_value ret;
+	struct mailbox_buffers mb;
+	const uint32_t payload = 0xAA55AA55;
+	ffa_vm_id_t own_id = hf_vm_get_id();
+
+	mb = set_up_mailbox();
+	SERVICE_SELECT(SERVICE_VM1, "ffa_indirect_msg_error", mb.send);
+
+	/* Schedule message receiver. */
+	ret = ffa_run(SERVICE_VM1, 0);
+	EXPECT_EQ(ret.func, FFA_MSG_WAIT_32);
+
+	/* Send the message. */
+	ret = send_indirect_message(own_id, SERVICE_VM1, mb.send, &payload,
+				    sizeof(payload), 0);
+	EXPECT_FFA_ERROR(ret, FFA_BUSY);
+}
+
+/** Receiver haven't read a previous message. */
+TEST(indirect_messaging, unread_message)
+{
+	struct ffa_value ret;
+	struct mailbox_buffers mb;
+	const uint32_t payload = 0xAA55AA55;
+	ffa_vm_id_t own_id = hf_vm_get_id();
+
+	mb = set_up_mailbox();
+	SERVICE_SELECT(SERVICE_VM1, "ffa_indirect_msg_error", mb.send);
+
+	/* Send the message. */
+	ret = send_indirect_message(own_id, SERVICE_VM1, mb.send, &payload,
+				    sizeof(payload), 0);
+	EXPECT_EQ(ret.func, FFA_SUCCESS_32);
+
+	/* Immediately send another message. */
+	ret = send_indirect_message(own_id, SERVICE_VM1, mb.send, &payload,
+				    sizeof(payload), 0);
+	EXPECT_FFA_ERROR(ret, FFA_BUSY);
+}
+
+static void msg_send2_invalid_parameters(ffa_vm_id_t sender,
+					 ffa_vm_id_t receiver, uint32_t size)
+{
+	struct ffa_value ret;
+	struct mailbox_buffers mb;
+	struct ffa_partition_msg *message;
+
+	mb = set_up_mailbox();
+	SERVICE_SELECT(SERVICE_VM1, "ffa_indirect_msg_error", mb.send);
+
+	message = (struct ffa_partition_msg *)mb.send;
+	ffa_rxtx_header_init(sender, receiver, size, &message->header);
+
+	/* The header is expected to be invalid, do not set any payload. */
+
+	ret = ffa_msg_send2(0);
+	EXPECT_FFA_ERROR(ret, FFA_INVALID_PARAMETERS);
+}
+
+/** Sender sends message with a non existing VM IDs. */
+TEST(indirect_messaging, non_existing_sender)
+{
+	msg_send2_invalid_parameters(SERVICE_VM2, SERVICE_VM1, 0);
+}
+
+/** Sender sends message with another sender VM IDs. */
+TEST(indirect_messaging, corrupted_sender)
+{
+	ffa_vm_id_t own_id = hf_vm_get_id();
+	msg_send2_invalid_parameters(SERVICE_VM1, own_id, 0);
+}
+
+/** Sender sends message to itself. */
+TEST(indirect_messaging, self_message)
+{
+	ffa_vm_id_t own_id = hf_vm_get_id();
+	msg_send2_invalid_parameters(own_id, own_id, 0);
+}
+
+/** Sender sends message with invalid size. */
+TEST(indirect_messaging, invalid_size)
+{
+	ffa_vm_id_t own_id = hf_vm_get_id();
+	msg_send2_invalid_parameters(own_id, SERVICE_VM1, 1024 * 1024);
+}
diff --git a/test/vmapi/primary_with_secondaries/services/echo.c b/test/vmapi/primary_with_secondaries/services/echo.c
index 61f0caf..0a14b8c 100644
--- a/test/vmapi/primary_with_secondaries/services/echo.c
+++ b/test/vmapi/primary_with_secondaries/services/echo.c
@@ -12,6 +12,7 @@
 #include "vmapi/hf/call.h"
 
 #include "test/hftest.h"
+#include "test/vmapi/ffa.h"
 
 TEST_SERVICE(echo)
 {
@@ -32,3 +33,39 @@
 			     0);
 	}
 }
+
+TEST_SERVICE(echo_msg_send2)
+{
+	void *send_buf = SERVICE_SEND_BUFFER();
+	void *recv_buf = SERVICE_RECV_BUFFER();
+	const struct ffa_partition_msg *message;
+	ffa_vm_id_t target_vm_id;
+	ffa_vm_id_t source_vm_id;
+	const uint32_t *payload;
+	struct ffa_value ret;
+
+	ret = ffa_msg_wait();
+	ASSERT_EQ(ret.func, FFA_RUN_32);
+
+	/* Check notification */
+	ret = ffa_notification_get(ffa_receiver(ret), 0,
+				   FFA_NOTIFICATION_FLAG_BITMAP_HYP);
+	ASSERT_EQ(ret.func, FFA_SUCCESS_32);
+	ASSERT_TRUE(is_ffa_hyp_buffer_full_notification(
+		ffa_notification_get_from_framework(ret)));
+
+	message = (const struct ffa_partition_msg *)recv_buf;
+	source_vm_id = ffa_rxtx_header_sender(&message->header);
+	target_vm_id = ffa_rxtx_header_receiver(&message->header);
+	payload = (const uint32_t *)message->payload;
+	HFTEST_LOG("Message got: %#x", *payload);
+
+	EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32);
+
+	/* Echo message back. */
+	send_indirect_message(target_vm_id, source_vm_id, send_buf, payload,
+			      sizeof(*payload), 0);
+
+	/* Give back control to caller VM. */
+	ffa_yield();
+}
diff --git a/test/vmapi/primary_with_secondaries/services/ffa_check.c b/test/vmapi/primary_with_secondaries/services/ffa_check.c
index 628b599..c56cb6b 100644
--- a/test/vmapi/primary_with_secondaries/services/ffa_check.c
+++ b/test/vmapi/primary_with_secondaries/services/ffa_check.c
@@ -193,3 +193,14 @@
 	ffa_msg_send_direct_resp(ffa_receiver(res), ffa_sender(res), 4, 0, 0, 0,
 				 0);
 }
+
+/**
+ * Service for indirect message error checking.
+ * The VM unmap its RX/TX and waits for a message.
+ */
+TEST_SERVICE(ffa_indirect_msg_error)
+{
+	EXPECT_EQ(ffa_rxtx_unmap().func, FFA_SUCCESS_32);
+
+	ffa_msg_wait();
+}