/*
 * Copyright 2023 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/arch/irq.h"
#include "hf/arch/vm/interrupts.h"

#include "hf/check.h"
#include "hf/ffa.h"

#include "vmapi/hf/call.h"
#include "vmapi/hf/ffa.h"

#include "primary_with_secondary.h"
#include "test/hftest.h"
#include "test/vmapi/arch/exception_handler.h"
#include "test/vmapi/ffa.h"

#define MAX_RESP_REGS (MAX_MSG_SIZE / sizeof(uint64_t))

static uint16_t get_uuid_count(struct hftest_context *ctx)
{
	if (ctx->is_ffa_manifest_parsed) {
		return ctx->partition_manifest.uuid_count;
	}

	return 0;
}

static struct ffa_uuid *get_uuids(struct hftest_context *ctx)
{
	if (ctx->is_ffa_manifest_parsed) {
		return (struct ffa_uuid *)&ctx->partition_manifest.uuids;
	}

	return NULL;
}

static bool is_uuid_in_list(uint16_t uuid_count, struct ffa_uuid target_uuid,
			    struct ffa_uuid *uuid_list)
{
	uint16_t i;

	/* Allow for nil uuid usage. */
	if (ffa_uuid_is_null(&target_uuid)) {
		return true;
	}

	for (i = 0; i < uuid_count; i++) {
		if (ffa_uuid_is_null(&target_uuid)) {
			break;
		}
		if (ffa_uuid_equal(&uuid_list[i], &target_uuid)) {
			return true;
		}
	}

	return false;
}

TEST_SERVICE(ffa_direct_message_resp_echo)
{
	struct ffa_value args = ffa_msg_wait();

	EXPECT_EQ(args.func, FFA_MSG_SEND_DIRECT_REQ_32);

	ffa_msg_send_direct_resp(ffa_receiver(args), ffa_sender(args),
				 args.arg3, args.arg4, args.arg5, args.arg6,
				 args.arg7);

	FAIL("Direct response not expected to return");
}

TEST_SERVICE(ffa_direct_message_req2_resp_echo)
{
	uint64_t msg[MAX_RESP_REGS];
	struct ffa_value res = ffa_msg_wait();
	EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_REQ2_64);

	memcpy_s(&msg, sizeof(uint64_t) * MAX_RESP_REGS, &res.arg4,
		 MAX_RESP_REGS * sizeof(uint64_t));

	ffa_msg_send_direct_resp2(ffa_receiver(res), ffa_sender(res),
				  (const uint64_t *)msg, MAX_RESP_REGS);

	FAIL("Direct response not expected to return");
}

TEST_SERVICE(ffa_yield_direct_message_resp_echo)
{
	struct ffa_value args = ffa_msg_wait();

	EXPECT_EQ(args.func, FFA_MSG_SEND_DIRECT_REQ_32);

	/*
	 * Give back control to VM/SP, that sent the direct request message,
	 * through FFA_YIELD ABI and specify timeout of 0x123456789.
	 */
	ffa_yield_timeout(0x1, 0x23456789);

	/* Send the echo through direct message response. */
	ffa_msg_send_direct_resp(ffa_receiver(args), ffa_sender(args),
				 args.arg3, args.arg4, args.arg5, args.arg6,
				 args.arg7);

	FAIL("Direct response not expected to return");
}

TEST_SERVICE(ffa_yield_direct_message_resp2_echo)
{
	struct ffa_value res = ffa_msg_wait();
	uint64_t msg[MAX_RESP_REGS] = {0};

	EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_REQ2_64);

	/*
	 * Give back control to VM/SP, that sent the direct request message,
	 * through FFA_YIELD ABI and specify timeout of 0x123456789.
	 */
	ffa_yield_timeout(0x1, 0x23456789);

	HFTEST_LOG("after yield");

	/* Send the echo through direct message response. */
	memcpy_s(&msg, sizeof(uint64_t) * MAX_RESP_REGS, &res.arg4,
		 MAX_RESP_REGS * sizeof(uint64_t));

	ffa_msg_send_direct_resp2(ffa_receiver(res), ffa_sender(res),
				  (const uint64_t *)msg, MAX_RESP_REGS);

	FAIL("Direct response not expected to return");
}

TEST_SERVICE(ffa_direct_message_echo_services)
{
	const uint32_t msg[] = {0x00001111, 0x22223333, 0x44445555, 0x66667777,
				0x88889999};
	void *recv_buf = SERVICE_RECV_BUFFER();
	struct ffa_value res;
	ffa_id_t target_id;

	/* Retrieve FF-A ID of the target endpoint. */
	receive_indirect_message((void *)&target_id, sizeof(target_id),
				 recv_buf);

	HFTEST_LOG("Echo test with: %x", target_id);

	res = ffa_msg_send_direct_req(hf_vm_get_id(), target_id, msg[0], msg[1],
				      msg[2], msg[3], msg[4]);

	EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32);

	EXPECT_EQ(res.arg3, msg[0]);
	EXPECT_EQ(res.arg4, msg[1]);
	EXPECT_EQ(res.arg5, msg[2]);
	EXPECT_EQ(res.arg6, msg[3]);
	EXPECT_EQ(res.arg7, msg[4]);

	ffa_yield();
}

TEST_SERVICE(ffa_direct_message_req2_echo_services)
{
	const uint64_t msg[] = {0x00001111, 0x22223333, 0x44445555, 0x66667777,
				0x88889999, 0x01010101, 0x23232323, 0x45454545,
				0x67676767, 0x89898989, 0x11001100, 0x22332233,
				0x44554455, 0x66776677};
	void *recv_buf = SERVICE_RECV_BUFFER();
	struct ffa_value res;
	struct ffa_partition_info target_info;
	struct ffa_uuid target_uuid;

	/* Retrieve uuid of target endpoint. */
	receive_indirect_message((void *)&target_uuid, sizeof(target_uuid),
				 recv_buf);

	HFTEST_LOG("Target UUID: %X-%X-%X-%X", target_uuid.uuid[0],
		   target_uuid.uuid[1], target_uuid.uuid[2],
		   target_uuid.uuid[3]);

	/* From uuid to respective partition info. */
	ASSERT_EQ(get_ffa_partition_info(target_uuid, &target_info,
					 sizeof(target_info), recv_buf),
		  1);

	HFTEST_LOG("Echo test with: %x", target_info.vm_id);

	res = ffa_msg_send_direct_req2(hf_vm_get_id(), target_info.vm_id,
				       &target_uuid, (const uint64_t *)&msg,
				       ARRAY_SIZE(msg));

	EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP2_64);

	EXPECT_EQ(res.arg4, msg[0]);
	EXPECT_EQ(res.arg5, msg[1]);
	EXPECT_EQ(res.arg6, msg[2]);
	EXPECT_EQ(res.arg7, msg[3]);
	EXPECT_EQ(res.extended_val.arg8, msg[4]);
	EXPECT_EQ(res.extended_val.arg9, msg[5]);
	EXPECT_EQ(res.extended_val.arg10, msg[6]);
	EXPECT_EQ(res.extended_val.arg11, msg[7]);
	EXPECT_EQ(res.extended_val.arg12, msg[8]);
	EXPECT_EQ(res.extended_val.arg13, msg[9]);
	EXPECT_EQ(res.extended_val.arg14, msg[10]);
	EXPECT_EQ(res.extended_val.arg15, msg[11]);
	EXPECT_EQ(res.extended_val.arg16, msg[12]);
	EXPECT_EQ(res.extended_val.arg17, msg[13]);

	ffa_yield();
}

TEST_SERVICE(ffa_yield_direct_message_echo_services)
{
	const uint32_t msg[] = {0x00001111, 0x22223333, 0x44445555, 0x66667777,
				0x88889999};
	void *recv_buf = SERVICE_RECV_BUFFER();
	struct ffa_value res;
	ffa_id_t target_id;

	/* Retrieve FF-A ID of the target endpoint. */
	receive_indirect_message((void *)&target_id, sizeof(target_id),
				 recv_buf);

	HFTEST_LOG("Echo test with: %x", target_id);

	res = ffa_msg_send_direct_req(hf_vm_get_id(), target_id, msg[0], msg[1],
				      msg[2], msg[3], msg[4]);

	/*
	 * Be prepared to allocate CPU cycles to target vCPU if it yields while
	 * processing direct message.
	 */
	while (res.func == FFA_YIELD_32) {
		/* VM id/vCPU index are passed through arg1. */
		EXPECT_EQ(res.arg1, ffa_vm_vcpu(target_id, 0));

		/* Allocate CPU cycles to resume SP. */
		res = ffa_run(target_id, 0);
	}
	EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32);

	EXPECT_EQ(res.arg3, msg[0]);
	EXPECT_EQ(res.arg4, msg[1]);
	EXPECT_EQ(res.arg5, msg[2]);
	EXPECT_EQ(res.arg6, msg[3]);
	EXPECT_EQ(res.arg7, msg[4]);

	ffa_yield();
}

TEST_SERVICE(ffa_direct_msg_req_disallowed_smc)
{
	struct ffa_value args = ffa_msg_wait();
	struct ffa_value ret;
	void *recv_buf = SERVICE_RECV_BUFFER();
	struct ffa_partition_info *service1_info = service1(recv_buf);

	EXPECT_EQ(args.func, FFA_MSG_SEND_DIRECT_REQ_32);

	ret = ffa_msg_wait();
	EXPECT_FFA_ERROR(ret, FFA_DENIED);

	ret = ffa_msg_send_direct_req(service1_info->vm_id, ffa_sender(args), 0,
				      0, 0, 0, 0);
	EXPECT_FFA_ERROR(ret, FFA_INVALID_PARAMETERS);

	ffa_msg_send_direct_resp(ffa_receiver(args), ffa_sender(args),
				 args.arg3, args.arg4, args.arg5, args.arg6,
				 args.arg7);

	FAIL("Direct response not expected to return");
}

/**
 * Verify that services can't send direct message requests
 * when invoked by FFA_RUN.
 */
TEST_SERVICE(ffa_disallowed_direct_msg_req)
{
	struct ffa_value args;
	struct ffa_value ret;
	void *recv_buf = SERVICE_RECV_BUFFER();
	struct ffa_partition_info *service1_info = service1(recv_buf);

	ret = ffa_msg_send_direct_req(service1_info->vm_id, HF_PRIMARY_VM_ID, 0,
				      0, 0, 0, 0);
	EXPECT_FFA_ERROR(ret, FFA_INVALID_PARAMETERS);

	ret = ffa_msg_send_direct_req(service1_info->vm_id, HF_VM_ID_BASE + 10,
				      0, 0, 0, 0, 0);
	EXPECT_FFA_ERROR(ret, FFA_INVALID_PARAMETERS);

	args = ffa_msg_wait();
	EXPECT_EQ(args.func, FFA_MSG_SEND_DIRECT_REQ_32);

	ffa_msg_send_direct_resp(ffa_receiver(args), ffa_sender(args),
				 args.arg3, args.arg4, args.arg5, args.arg6,
				 args.arg7);

	FAIL("Direct response not expected to return");
}

/**
 * Verify a service can't send a direct message response when it hasn't
 * first been sent a request.
 */
TEST_SERVICE(ffa_disallowed_direct_msg_resp)
{
	struct ffa_value args;
	struct ffa_value ret;
	void *recv_buf = SERVICE_RECV_BUFFER();
	struct ffa_partition_info *service1_info = service1(recv_buf);

	ret = ffa_msg_send_direct_resp(service1_info->vm_id, HF_PRIMARY_VM_ID,
				       0, 0, 0, 0, 0);
	EXPECT_FFA_ERROR(ret, FFA_DENIED);

	args = ffa_msg_wait();
	EXPECT_EQ(args.func, FFA_MSG_SEND_DIRECT_REQ_32);

	ffa_msg_send_direct_resp(ffa_receiver(args), ffa_sender(args),
				 args.arg3, args.arg4, args.arg5, args.arg6,
				 args.arg7);

	FAIL("Direct response not expected to return");
}

/**
 * Verify a service can't send a response to a different VM than the one
 * that sent the request.
 * Verify a service cannot send a response with a sender ID different from
 * its own service ID.
 */
TEST_SERVICE(ffa_direct_msg_resp_invalid_sender_receiver)
{
	struct ffa_value res;
	void *recv_buf = SERVICE_RECV_BUFFER();
	struct ffa_partition_info *service2_info = service2(recv_buf);
	ffa_id_t invalid_receiver;
	struct ffa_value args = ffa_msg_wait();
	ffa_id_t own_id = hf_vm_get_id();
	EXPECT_EQ(args.func, FFA_MSG_SEND_DIRECT_REQ_32);

	ffa_id_t sender = ffa_sender(args);
	ASSERT_EQ(own_id, ffa_receiver(args));

	/* Other receiver ID. */
	invalid_receiver = ffa_is_vm_id(own_id) ? service2_info->vm_id : own_id;
	res = ffa_msg_send_direct_resp(own_id, invalid_receiver, 0, 0, 0, 0, 0);
	EXPECT_FFA_ERROR(res, FFA_INVALID_PARAMETERS);

	/* Spoof sender ID. */
	res = ffa_msg_send_direct_resp(service2_info->vm_id, sender, 0, 0, 0, 0,
				       0);
	EXPECT_FFA_ERROR(res, FFA_INVALID_PARAMETERS);

	ffa_msg_send_direct_resp(own_id, sender, 0, 0, 0, 0, 0);

	FAIL("Direct response not expected to return");
}

TEST_SERVICE(ffa_direct_message_cycle_denied)
{
	struct ffa_value res;
	struct ffa_value args = ffa_msg_wait();
	ffa_id_t sender;
	ffa_id_t receiver;
	ffa_id_t own_id = hf_vm_get_id();

	ASSERT_EQ(args.func, FFA_MSG_SEND_DIRECT_REQ_32);
	receiver = ffa_receiver(args);
	sender = ffa_sender(args);

	EXPECT_EQ(receiver, hf_vm_get_id());

	res = ffa_msg_send_direct_req(own_id, sender, 1, 2, 3, 4, 5);
	EXPECT_FFA_ERROR(res, FFA_DENIED);

	ffa_msg_send_direct_resp(ffa_receiver(args), ffa_sender(args),
				 args.arg3, args.arg4, args.arg5, args.arg6,
				 args.arg7);

	FAIL("Direct response not expected to return");
}

TEST_SERVICE(ffa_direct_message_v_1_2_cycle_denied)
{
	struct ffa_value res;
	struct ffa_value args;
	ffa_id_t sender;
	ffa_id_t receiver;
	ffa_id_t own_id = hf_vm_get_id();
	const uint64_t invalid_msg[] = {1, 2, 3, 4, 5};
	uint64_t msg[MAX_RESP_REGS];

	void *recv_buf = SERVICE_RECV_BUFFER();
	struct ffa_uuid target_uuid;

	/* Setup handling of NPI, to handle RX buffer full notification. */
	exception_setup(check_npi, NULL);
	arch_irq_enable();

	/* Retrieve uuid of target endpoint. */
	receive_indirect_message((void *)&target_uuid, sizeof(target_uuid),
				 recv_buf);

	/* Wait for direct request. */
	args = ffa_msg_wait();

	ASSERT_EQ(args.func, FFA_MSG_SEND_DIRECT_REQ2_64);
	receiver = ffa_receiver(args);
	sender = ffa_sender(args);

	EXPECT_EQ(receiver, hf_vm_get_id());

	/* Try to send a request back instead of a response. */
	res = ffa_msg_send_direct_req2(own_id, sender, &target_uuid,
				       (const uint64_t *)&invalid_msg,
				       ARRAY_SIZE(invalid_msg));

	EXPECT_FFA_ERROR(res, FFA_DENIED);

	/* Send the echo through direct message response. */
	memcpy_s(&msg, sizeof(uint64_t) * MAX_RESP_REGS, &args.arg4,
		 MAX_RESP_REGS * sizeof(uint64_t));

	ffa_msg_send_direct_resp2(receiver, sender, (const uint64_t *)msg,
				  MAX_RESP_REGS);

	FAIL("Direct response not expected to return");
}

TEST_SERVICE(ffa_direct_message_cycle_req_req2_denied)
{
	struct ffa_value res;
	struct ffa_value args;
	ffa_id_t sender;
	ffa_id_t receiver;
	ffa_id_t own_id = hf_vm_get_id();
	const uint64_t invalid_msg[] = {1, 2, 3, 4, 5};
	void *recv_buf = SERVICE_RECV_BUFFER();
	struct ffa_uuid target_uuid;

	/* Setup handling of NPI, to handle RX buffer full notification. */
	exception_setup(check_npi, NULL);
	arch_irq_enable();

	/* Retrieve uuid of target endpoint. */
	receive_indirect_message((void *)&target_uuid, sizeof(target_uuid),
				 recv_buf);

	/* Wait for direct request. */
	args = ffa_msg_wait();
	ASSERT_EQ(args.func, FFA_MSG_SEND_DIRECT_REQ_32);
	receiver = ffa_receiver(args);
	sender = ffa_sender(args);

	EXPECT_EQ(receiver, hf_vm_get_id());

	/* Try to send a request back instead of a response. */
	res = ffa_msg_send_direct_req2(own_id, sender, &target_uuid,
				       (const uint64_t *)&invalid_msg,
				       ARRAY_SIZE(invalid_msg));
	EXPECT_FFA_ERROR(res, FFA_DENIED);

	ffa_msg_send_direct_resp(ffa_receiver(args), ffa_sender(args),
				 args.arg3, args.arg4, args.arg5, args.arg6,
				 args.arg7);

	FAIL("Direct response not expected to return");
}

TEST_SERVICE(ffa_yield_direct_message_v_1_2_echo_services)
{
	const uint64_t msg[] = {0x00001111, 0x22223333, 0x44445555, 0x66667777,
				0x88889999, 0x01010101, 0x23232323, 0x45454545,
				0x67676767, 0x89898989, 0x11001100, 0x22332233,
				0x44554455, 0x66776677};
	void *recv_buf = SERVICE_RECV_BUFFER();
	struct ffa_value res;
	struct ffa_uuid target_uuid;
	struct ffa_partition_info target_info;

	/* Retrieve FF-A ID of the target endpoint. */
	receive_indirect_message((void *)&target_uuid, sizeof(target_uuid),
				 recv_buf);

	/* From uuid to respective partition info. */
	ASSERT_EQ(get_ffa_partition_info(target_uuid, &target_info,
					 sizeof(target_info), recv_buf),
		  1);

	HFTEST_LOG("Echo test with: %x", target_info.vm_id);

	res = ffa_msg_send_direct_req2(hf_vm_get_id(), target_info.vm_id,
				       &target_uuid, (const uint64_t *)&msg,
				       ARRAY_SIZE(msg));
	/*
	 * Be prepared to allocate CPU cycles to target vCPU if it yields while
	 * processing direct message.
	 */
	while (res.func == FFA_YIELD_32) {
		/* VM id/vCPU index are passed through arg1. */
		EXPECT_EQ(res.arg1, ffa_vm_vcpu(target_info.vm_id, 0));

		/* Allocate CPU cycles to resume SP. */
		res = ffa_run(target_info.vm_id, 0);
	}
	EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP2_64);

	EXPECT_EQ(res.arg4, msg[0]);
	EXPECT_EQ(res.arg5, msg[1]);
	EXPECT_EQ(res.arg6, msg[2]);
	EXPECT_EQ(res.arg7, msg[3]);
	EXPECT_EQ(res.extended_val.arg8, msg[4]);
	EXPECT_EQ(res.extended_val.arg9, msg[5]);
	EXPECT_EQ(res.extended_val.arg10, msg[6]);
	EXPECT_EQ(res.extended_val.arg11, msg[7]);
	EXPECT_EQ(res.extended_val.arg12, msg[8]);
	EXPECT_EQ(res.extended_val.arg13, msg[9]);
	EXPECT_EQ(res.extended_val.arg14, msg[10]);
	EXPECT_EQ(res.extended_val.arg15, msg[11]);
	EXPECT_EQ(res.extended_val.arg16, msg[12]);
	EXPECT_EQ(res.extended_val.arg17, msg[13]);

	ffa_yield();
}

/**
 * Verify a service can't send a direct message response when it hasn't
 * first been sent a request.
 */
TEST_SERVICE(ffa_disallowed_direct_msg_resp2)
{
	struct ffa_value args;
	struct ffa_value ret;
	void *recv_buf = SERVICE_RECV_BUFFER();
	struct ffa_partition_info *service1_info = service1(recv_buf);
	uint64_t msg[MAX_RESP_REGS];

	ret = ffa_msg_send_direct_resp2(service1_info->vm_id, HF_PRIMARY_VM_ID,
					(uint64_t *)msg, ARRAY_SIZE(msg));
	EXPECT_FFA_ERROR(ret, FFA_DENIED);

	args = ffa_msg_wait();
	EXPECT_EQ(args.func, FFA_MSG_SEND_DIRECT_REQ2_64);

	ffa_msg_send_direct_resp2(ffa_receiver(args), ffa_sender(args),
				  (uint64_t *)msg, ARRAY_SIZE(msg));

	FAIL("Direct response not expected to return");
}

TEST_SERVICE(ffa_direct_msg_req2_disallowed_smc)
{
	struct ffa_value args = ffa_msg_wait();
	struct ffa_value ret;
	void *recv_buf = SERVICE_RECV_BUFFER();
	struct ffa_partition_info *service1_info = service1(recv_buf);
	uint64_t msg[MAX_RESP_REGS] = {0};
	struct ffa_uuid sender_uuid;
	ffa_uuid_init(0, 0, 0, 0, &sender_uuid);

	EXPECT_EQ(args.func, FFA_MSG_SEND_DIRECT_REQ2_64);

	ret = ffa_msg_wait();
	EXPECT_FFA_ERROR(ret, FFA_DENIED);

	ret = ffa_msg_send_direct_req2(service1_info->vm_id, ffa_sender(args),
				       &sender_uuid, (uint64_t *)msg,
				       ARRAY_SIZE(msg));
	EXPECT_FFA_ERROR(ret, FFA_INVALID_PARAMETERS);

	ffa_msg_send_direct_resp2(ffa_receiver(args), ffa_sender(args),
				  (uint64_t *)msg, ARRAY_SIZE(msg));

	FAIL("Direct response not expected to return");
}

/**
 * Verify that services can't send direct message requests
 * via FFA_MSG_SEND_DIRECT_REQ2 after being invoked by FFA_RUN.
 */
TEST_SERVICE(ffa_disallowed_direct_msg_req2)
{
	struct ffa_value args;
	struct ffa_value ret;
	void *recv_buf = SERVICE_RECV_BUFFER();
	struct ffa_partition_info *service1_info;
	uint64_t msg[MAX_RESP_REGS] = {0};
	struct ffa_uuid target_uuid;

	/* Setup handling of NPI, to handle RX buffer full notification. */
	exception_setup(check_npi, NULL);
	arch_irq_enable();

	/* Retrieve uuid of NWd PVM. */
	receive_indirect_message((void *)&target_uuid, sizeof(target_uuid),
				 recv_buf);

	service1_info = service1(recv_buf);

	/* Attempt request to NWd VM. */
	ret = ffa_msg_send_direct_req2(service1_info->vm_id, HF_PRIMARY_VM_ID,
				       &target_uuid, (uint64_t *)msg,
				       ARRAY_SIZE(msg));
	EXPECT_FFA_ERROR(ret, FFA_INVALID_PARAMETERS);

	ret = ffa_msg_send_direct_req2(service1_info->vm_id, HF_VM_ID_BASE + 10,
				       &target_uuid, (uint64_t *)msg,
				       ARRAY_SIZE(msg));
	EXPECT_FFA_ERROR(ret, FFA_INVALID_PARAMETERS);

	args = ffa_msg_wait();
	EXPECT_EQ(args.func, FFA_MSG_SEND_DIRECT_REQ2_64);

	ffa_msg_send_direct_resp2(ffa_receiver(args), ffa_sender(args),
				  (uint64_t *)msg, ARRAY_SIZE(msg));

	FAIL("Direct response not expected to return");
}

/**
 * Verify a service can't send a response to a different VM than the one
 * that sent the request.
 * Verify a service cannot send a response with a sender ID different from
 * its own service ID.
 */
TEST_SERVICE(ffa_direct_msg_resp2_invalid_sender_receiver)
{
	struct ffa_value res;
	void *recv_buf = SERVICE_RECV_BUFFER();
	struct ffa_partition_info *service2_info = service2(recv_buf);
	ffa_id_t invalid_receiver;
	uint64_t msg[MAX_RESP_REGS] = {0};
	ffa_id_t own_id;
	ffa_id_t sender;
	struct ffa_value args = ffa_msg_wait();

	EXPECT_EQ(args.func, FFA_MSG_SEND_DIRECT_REQ2_64);

	sender = ffa_sender(args);
	own_id = hf_vm_get_id();
	ASSERT_EQ(own_id, ffa_receiver(args));

	/* Other receiver ID. */
	invalid_receiver = ffa_is_vm_id(own_id) ? service2_info->vm_id : own_id;
	res = ffa_msg_send_direct_resp2(own_id, invalid_receiver,
					(uint64_t *)msg, ARRAY_SIZE(msg));
	EXPECT_FFA_ERROR(res, FFA_INVALID_PARAMETERS);

	/* Spoof sender ID. */
	res = ffa_msg_send_direct_resp2(service2_info->vm_id, sender,
					(uint64_t *)msg, ARRAY_SIZE(msg));
	EXPECT_FFA_ERROR(res, FFA_INVALID_PARAMETERS);

	ffa_msg_send_direct_resp2(own_id, sender, (uint64_t *)msg,
				  ARRAY_SIZE(msg));

	FAIL("Direct response not expected to return");
}

TEST_SERVICE(ffa_direct_msg_req2_resp_failure)
{
	struct ffa_value res;
	struct ffa_value args = ffa_msg_wait();
	uint64_t msg[MAX_RESP_REGS] = {0};

	EXPECT_EQ(args.func, FFA_MSG_SEND_DIRECT_REQ2_64);

	/* Respond to FFA_MSG_SEND_DIRECT_REQ2 with FFA_MSG_SEND_DIRECT_RESP. */
	res = ffa_msg_send_direct_resp(ffa_receiver(args), ffa_sender(args),
				       args.arg3, args.arg4, args.arg5,
				       args.arg6, args.arg7);

	EXPECT_FFA_ERROR(res, FFA_DENIED);

	memcpy_s(&msg, sizeof(uint64_t) * MAX_RESP_REGS, &args.arg4,
		 MAX_RESP_REGS * sizeof(uint64_t));
	ffa_msg_send_direct_resp2(ffa_receiver(args), ffa_sender(args),
				  (uint64_t *)msg, ARRAY_SIZE(msg));

	FAIL("Direct response not expected to return");
}

TEST_SERVICE(ffa_direct_msg_req_resp2_failure)
{
	struct ffa_value res;
	struct ffa_value args = ffa_msg_wait();
	uint64_t msg[MAX_RESP_REGS] = {0};

	EXPECT_EQ(args.func, FFA_MSG_SEND_DIRECT_REQ_32);

	memcpy_s(&msg, sizeof(uint64_t) * MAX_RESP_REGS, &args.arg4,
		 MAX_RESP_REGS * sizeof(uint64_t));
	/* Respond to FFA_MSG_SEND_DIRECT_REQ with FFA_MSG_SEND_DIRECT_RESP2. */
	res = ffa_msg_send_direct_resp2(ffa_receiver(args), ffa_sender(args),
					(uint64_t *)msg, ARRAY_SIZE(msg));

	EXPECT_FFA_ERROR(res, FFA_DENIED);

	ffa_msg_send_direct_resp(ffa_receiver(args), ffa_sender(args),
				 args.arg3, args.arg4, args.arg5, args.arg6,
				 args.arg7);

	FAIL("Direct response not expected to return");
}

/**
 * Put SP in waiting state via FFA_MSG_SEND_DIRECT_RESP.
 * Make sure extended registers are preserved when SP is brought
 * into running state with receipt of FFA_MSG_SEND_DIRECT_REQ2.
 *
 * Run twice to cover the reverse scenario (SP waits with
 * FFA_MSG_SEND_DIRECT_RESP2 and wakes up with receipt of
 * FFA_MSG_SEND_DIRECT_REQ).
 */
TEST_SERVICE(ffa_direct_msg_resp_ext_registers_preserved)
{
	struct ffa_value args = ffa_msg_wait();
	uint64_t msg[MAX_RESP_REGS];

	for (uint32_t i = 0; i < 2; i++) {
		EXPECT_EQ(args.func, FFA_MSG_SEND_DIRECT_REQ_32);

		args = ffa_msg_send_direct_resp(
			ffa_receiver(args), ffa_sender(args), args.arg3,
			args.arg4, args.arg5, args.arg6, args.arg7);

		/*
		 * Wake up from waiting state with receipt of
		 * FFA_MSG_SEND_DIRECT_REQ2.
		 */
		EXPECT_EQ(args.func, FFA_MSG_SEND_DIRECT_REQ2_64);
		memcpy_s(&msg, sizeof(uint64_t) * MAX_RESP_REGS, &args.arg4,
			 MAX_RESP_REGS * sizeof(uint64_t));

		args = ffa_msg_send_direct_resp2(
			ffa_receiver(args), ffa_sender(args),
			(const uint64_t *)msg, MAX_RESP_REGS);
	}

	FAIL("Direct response not expected to return");
}

/**
 * FF-A v1.1 endpoint attempts to use FFA_MSG_SEND_DIRECT_REQ2
 * and fails.
 */
TEST_SERVICE(version_does_not_support_req2)
{
	const uint32_t msg[] = {0x00001111, 0x22223333, 0x44445555, 0x66667777,
				0x88889999};
	void *recv_buf = SERVICE_RECV_BUFFER();
	struct ffa_value res;
	struct ffa_partition_info target_info;
	struct ffa_uuid target_uuid;

	/* Retrieve uuid of target endpoint. */
	receive_indirect_message((void *)&target_uuid, sizeof(target_uuid),
				 recv_buf);

	HFTEST_LOG("Target UUID: %X-%X-%X-%X", target_uuid.uuid[0],
		   target_uuid.uuid[1], target_uuid.uuid[2],
		   target_uuid.uuid[3]);

	/* From uuid to respective partition info. */
	ASSERT_EQ(get_ffa_partition_info(target_uuid, &target_info,
					 sizeof(target_info), recv_buf),
		  1);

	/* Attempt to send FFA_MSG_SEND_DIRECT_REQ2 and fail. */
	res = ffa_msg_send_direct_req2(hf_vm_get_id(), target_info.vm_id,
				       &target_uuid, (const uint64_t *)&msg,
				       ARRAY_SIZE(msg));
	EXPECT_FFA_ERROR(res, FFA_DENIED);
	ffa_yield();
}

/**
 * Service traps execution in a loop, and expects to always wake up with a
 * FFA_MSG_SEND_DIRECT_REQ2. Verify that target UUID was specified in the target
 * partition's manifest before echoing message back to sender.
 */
TEST_SERVICE(ffa_direct_message_req2_resp_loop)
{
	struct hftest_context *ctx = hftest_get_context();
	struct ffa_uuid *uuids = get_uuids(ctx);
	uint16_t uuid_count = get_uuid_count(ctx);
	struct ffa_value res;

	if (!ctx->is_ffa_manifest_parsed) {
		FAIL("Manifest not parsed");
	}

	res = ffa_msg_wait();

	while (true) {
		uint64_t msg[MAX_RESP_REGS];
		struct ffa_uuid target_uuid;

		EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_REQ2_64);

		ffa_uuid_from_u64x2(res.arg2, res.arg3, &target_uuid);

		HFTEST_LOG("Target UUID: %X-%X-%X-%X", target_uuid.uuid[0],
			   target_uuid.uuid[1], target_uuid.uuid[2],
			   target_uuid.uuid[3]);

		EXPECT_TRUE(is_uuid_in_list(uuid_count, target_uuid, uuids));

		memcpy_s(&msg, sizeof(uint64_t) * MAX_RESP_REGS, &res.arg4,
			 MAX_RESP_REGS * sizeof(uint64_t));

		res = ffa_msg_send_direct_resp2(
			ffa_receiver(res), ffa_sender(res),
			(const uint64_t *)msg, MAX_RESP_REGS);
	}
}

/**
 * Identifies the VM availiability message.
 * See section 18.3 of v1.2 FF-A specification.
 */
enum ffa_vm_availability_message {
	VM_MSG_CREATED,
	VM_MSG_DESTROYED,
};

/**
 * The state of a VM.
 * See section 18.3.2.3 of v1.2 FF-A specification.
 */
enum ffa_vm_availability_state {
	VM_STATE_UNAVAILABLE,
	VM_STATE_AVAILABLE,
	VM_STATE_ERROR,
};

/**
 * In the scope of the VM availability messages, the test SP is maintaining the
 * state of 4 VMs.
 */
static enum ffa_vm_availability_state vm_states[4] = {0};

static enum ffa_vm_availability_state next_vm_availability_state(
	enum ffa_vm_availability_state current_state,
	enum ffa_vm_availability_message msg)
{
	/* clang-format off */
	static const enum ffa_vm_availability_state table[3][2] = {
		/* State                VM created          VM destoyed          */
		/* VM unavailable  */ { VM_STATE_AVAILABLE, VM_STATE_ERROR       },
		/* VM available    */ { VM_STATE_ERROR,     VM_STATE_UNAVAILABLE },
		/* Error           */ { VM_STATE_ERROR,     VM_STATE_ERROR       },
	};
	/* clang-format on */

	return table[current_state][msg];
}

/**
 * Receive VM availability messages and update VM's state accordingly.
 */
TEST_SERVICE(vm_availability_messaging)
{
	struct ffa_value args = ffa_msg_wait();

	for (;;) {
		enum ffa_framework_msg_func func =
			ffa_framework_msg_get_func(args);
		ffa_id_t sender_id = ffa_sender(args);
		ffa_id_t receiver_id = ffa_receiver(args);
		ffa_id_t vm_id = ffa_vm_availability_message_vm_id(args);
		enum ffa_vm_availability_state current_state;
		enum ffa_vm_availability_state new_state;
		enum ffa_vm_availability_message msg;

		switch (func) {
		case FFA_FRAMEWORK_MSG_VM_CREATION_REQ:
			msg = VM_MSG_CREATED;
			break;
		case FFA_FRAMEWORK_MSG_VM_DESTRUCTION_REQ:
			msg = VM_MSG_DESTROYED;
			break;
		default:
			dlog_verbose("Unknown framework message func: %#x\n",
				     func);
			return;
		}

		current_state = vm_states[vm_id];
		new_state = next_vm_availability_state(current_state, msg);
		vm_states[vm_id] = new_state;

		args = ffa_framework_message_send_direct_resp(
			receiver_id, sender_id,
			((msg == VM_MSG_CREATED)
				 ? FFA_FRAMEWORK_MSG_VM_CREATION_RESP
				 : FFA_FRAMEWORK_MSG_VM_DESTRUCTION_RESP),
			(new_state != VM_STATE_ERROR) ? 0
						      : FFA_INVALID_PARAMETERS);
	}
}

/**
 * Forward a framework message from primary VM, to demonstrate that SP cannot
 * send framework messages.
 */
TEST_SERVICE(vm_availability_messaging_send_from_sp)
{
	struct ffa_value args = ffa_msg_wait();

	for (;;) {
		enum ffa_framework_msg_func func =
			ffa_framework_msg_get_func(args);
		ffa_id_t sp_id = ffa_receiver(args);
		ffa_id_t pvm_id = ffa_sender(args);
		ffa_id_t vm_id = ffa_vm_availability_message_vm_id(args);

		struct ffa_value ret = ffa_framework_msg_send_direct_req(
			sp_id, pvm_id, func, vm_id);

		/* Verify that the framework message request failed. */
		ASSERT_EQ(ret.func, FFA_ERROR_32);
		ASSERT_EQ(ret.arg3, 0);

		/*
		 * Send a valid response, so that the PVM is not blocked
		 * forever.
		 */
		args = ffa_msg_send_direct_resp(sp_id, pvm_id, ret.func,
						ret.arg1, ret.arg2, ret.arg3,
						ret.arg4);
	}
}

/**
 * Forward a framework message from primary VM, to demonstrate that SP cannot
 * send non-framework messages in response to framework messages.
 */
TEST_SERVICE(vm_availability_messaging_send_non_framework_from_sp)
{
	struct ffa_value args = ffa_msg_wait();
	struct ffa_value ret;
	enum ffa_framework_msg_func func = ffa_framework_msg_get_func(args);
	enum ffa_framework_msg_func func_resp = FFA_FRAMEWORK_MSG_INVALID;
	ffa_id_t sp_id = ffa_receiver(args);
	ffa_id_t pvm_id = ffa_sender(args);

	if (func == FFA_FRAMEWORK_MSG_VM_CREATION_REQ) {
		func_resp = FFA_FRAMEWORK_MSG_VM_CREATION_RESP;
	} else if (func == FFA_FRAMEWORK_MSG_VM_DESTRUCTION_REQ) {
		func_resp = FFA_FRAMEWORK_MSG_VM_DESTRUCTION_RESP;
	} else {
		FAIL("Unsupported framework message function%#x received",
		     func);
	}

	/* Attempt to send a standard direct response message. */
	ret = ffa_msg_send_direct_resp(sp_id, pvm_id, args.arg3, args.arg4,
				       args.arg5, args.arg6, args.arg7);

	/* Verify that the direct message response failed. */
	ASSERT_EQ(ret.func, FFA_ERROR_32);
	ASSERT_EQ((enum ffa_error)ret.arg2, FFA_DENIED);

	/*
	 * Send a valid framework direct response message, so that the VM is not
	 * blocked forever.
	 */
	ffa_framework_message_send_direct_resp(sp_id, pvm_id, func_resp, 0);
	FAIL("Direct response not expected to return");
}
