libsp: Add FF-A direct message routing extension

The routing extension is a layer on top of FF-A which enables SPs to
send direct messages to the normal world component while maintaining
the state of the SPs in the call chain.

Change-Id: Ic4d6d5cbbccbe1adb534e7b8d2502b78b3d9cb04
Signed-off-by: Imre Kis <imre.kis@arm.com>
diff --git a/components/messaging/ffa/libsp/component.cmake b/components/messaging/ffa/libsp/component.cmake
index 2fec443..a21c630 100644
--- a/components/messaging/ffa/libsp/component.cmake
+++ b/components/messaging/ffa/libsp/component.cmake
@@ -8,6 +8,8 @@
 	message(FATAL_ERROR "mandatory parameter TGT is not defined.")
 endif()
 
+set(FFA_DIRECT_MSG_ROUTING_EXTENSION ON CACHE BOOL "Enable FF-A direct message routing extension")
+
 target_sources(${TGT} PRIVATE
 	"${CMAKE_CURRENT_LIST_DIR}/aarch64/ffa_syscalls_a64.S"
 	"${CMAKE_CURRENT_LIST_DIR}/ffa.c"
@@ -34,6 +36,15 @@
 	${CMAKE_CURRENT_LIST_DIR}/include/sp_rxtx.h
 	)
 
+if (FFA_DIRECT_MSG_ROUTING_EXTENSION)
+	target_sources(${TGT} PRIVATE
+		"${CMAKE_CURRENT_LIST_DIR}/ffa_direct_msg_routing_extension.c"
+		)
+
+	target_compile_options(${TGT} PUBLIC
+		-DFFA_DIRECT_MSG_ROUTING_EXTENSION=1
+		)
+endif()
 
 target_include_directories(${TGT}
 	 PUBLIC
diff --git a/components/messaging/ffa/libsp/ffa_direct_msg_routing_extension.c b/components/messaging/ffa/libsp/ffa_direct_msg_routing_extension.c
new file mode 100644
index 0000000..6813573
--- /dev/null
+++ b/components/messaging/ffa/libsp/ffa_direct_msg_routing_extension.c
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Copyright (c) 2020-2021, Arm Limited and Contributors. All rights reserved.
+ */
+
+#include "ffa_direct_msg_routing_extension.h"
+#include "ffa_api.h"
+#include <stdbool.h>
+
+#define SP_ID_INVALID FFA_ID_GET_ID_MASK
+#define FFA_ROUTING_EXT_RC_BIT BIT(0)
+#define FFA_ROUTING_EXT_ERROR_BIT BIT(1)
+
+enum sp_rc_state { idle = 0, root, leaf, rc_root, internal, forwarding };
+
+enum sp_rc_state state = idle;
+static uint16_t own_id = SP_ID_INVALID;
+static uint16_t caller_id = SP_ID_INVALID;
+static uint16_t callee_id = SP_ID_INVALID;
+
+static bool is_rc_message(const struct ffa_direct_msg *msg)
+{
+	return msg->args[0] & FFA_ROUTING_EXT_RC_BIT;
+}
+
+static bool is_error_message(const struct ffa_direct_msg *msg)
+{
+	return msg->args[0] & FFA_ROUTING_EXT_ERROR_BIT;
+}
+
+static ffa_result get_error_code_from_message(const struct ffa_direct_msg *msg)
+{
+	return (ffa_result)msg->args[1];
+}
+
+static ffa_result send_rc_error_message(struct ffa_direct_msg *req,
+					ffa_result error_code)
+{
+	return ffa_msg_send_direct_resp(req->destination_id, req->source_id,
+					(FFA_ROUTING_EXT_ERROR_BIT |
+					 FFA_ROUTING_EXT_RC_BIT),
+					error_code, 0, 0, 0, req);
+}
+
+static ffa_result send_rc_error_message_to_rc_root(struct ffa_direct_msg *resp,
+						   ffa_result error_code)
+{
+	return ffa_msg_send_direct_req(own_id, callee_id,
+				       (FFA_ROUTING_EXT_RC_BIT |
+					FFA_ROUTING_EXT_ERROR_BIT),
+				       error_code, 0, 0, 0, resp);
+}
+
+static ffa_result deny_unexpected_rc_responses(struct ffa_direct_msg *req)
+{
+	ffa_result ffa_res = FFA_OK;
+
+	while (is_rc_message(req)) {
+		ffa_res = send_rc_error_message(req, FFA_DENIED);
+		if (ffa_res != FFA_OK)
+			return ffa_res;
+	}
+
+	return FFA_OK;
+}
+
+static ffa_result deny_unexpected_requests(struct ffa_direct_msg *req)
+{
+	ffa_result ffa_res = FFA_OK;
+
+	while (!is_rc_message(req) || req->source_id != caller_id) {
+		ffa_res = send_rc_error_message(req, FFA_BUSY);
+		if (ffa_res != FFA_OK) {
+			/* Sending error message as a response to an invalid
+			 * request has failed. Sending and FFA_MSG_WAIT to have
+			 * a chance for receiving a valid message.
+			 */
+			ffa_res = ffa_msg_wait(req);
+			if (ffa_res != FFA_OK) {
+				/* Even the FFA_MSG_WAIT failed so return. */
+				return ffa_res;
+			}
+		}
+	}
+
+	return FFA_OK;
+}
+
+static ffa_result request_received_hook(struct ffa_direct_msg *req)
+{
+	ffa_result ffa_res = FFA_OK;
+
+	ffa_res = deny_unexpected_rc_responses(req);
+	if (ffa_res != FFA_OK)
+		return ffa_res;
+
+	state = leaf;
+	own_id = req->destination_id;
+	caller_id = req->source_id;
+	callee_id = SP_ID_INVALID;
+
+	return FFA_OK;
+}
+
+ffa_result ffa_direct_msg_routing_ext_wait_post_hook(struct ffa_direct_msg *req)
+{
+	return request_received_hook(req);
+}
+
+void ffa_direct_msg_routing_ext_req_pre_hook(struct ffa_direct_msg *req)
+{
+	state = internal;
+	callee_id = req->destination_id;
+}
+
+ffa_result ffa_direct_msg_routing_ext_req_post_hook(struct ffa_direct_msg *resp)
+{
+	ffa_result ffa_res = FFA_OK;
+	struct ffa_direct_msg rc_resp = { 0 };
+
+	while (is_rc_message(resp)) {
+		if (is_error_message(resp)) {
+			/* The callee returned an error in an RC message. */
+			ffa_res = get_error_code_from_message(resp);
+			break;
+		}
+
+		/* Forwarding RC request towards the root (normal world) */
+		state = forwarding;
+
+		ffa_res = ffa_msg_send_direct_resp(own_id, caller_id,
+						   resp->args[0], resp->args[1],
+						   resp->args[2], resp->args[3],
+						   resp->args[4], &rc_resp);
+		if (ffa_res != FFA_OK)
+			goto forward_ffa_error_to_rc_root;
+
+		/*
+		 * Denying messages which are not RC responses or came from a
+		 * different endpoint than the original caller.
+		 */
+		ffa_res = deny_unexpected_requests(&rc_resp);
+		if (ffa_res != FFA_OK)
+			goto forward_ffa_error_to_rc_root;
+
+		/* Forwarding RC response towards the RC root. */
+		state = internal;
+		ffa_res = ffa_msg_send_direct_req(
+			own_id, callee_id, rc_resp.args[0], rc_resp.args[1],
+			rc_resp.args[2], rc_resp.args[3], rc_resp.args[4],
+			resp);
+
+		goto break_on_ffa_error;
+
+		/*
+		 * At this point an FF-A error message was received while it was
+		 * trying to forward the RC message. Forwarding erro to RC root.
+		 */
+forward_ffa_error_to_rc_root:
+		ffa_res = send_rc_error_message_to_rc_root(resp, ffa_res);
+
+break_on_ffa_error:
+		if (ffa_res != FFA_OK) {
+			/* Exit loop, set leaf state and return with error. */
+			break;
+		}
+	}
+
+	/* Non-RC message was received or a non-recoverable error happened. */
+	state = leaf;
+	callee_id = SP_ID_INVALID;
+
+	return ffa_res;
+}
+
+void ffa_direct_msg_routing_ext_req_error_hook(void)
+{
+	state = leaf;
+	callee_id = SP_ID_INVALID;
+}
+
+void ffa_direct_msg_routing_ext_resp_pre_hook(struct ffa_direct_msg *resp)
+{
+	state = idle;
+	caller_id = SP_ID_INVALID;
+	callee_id = SP_ID_INVALID;
+}
+
+ffa_result ffa_direct_msg_routing_ext_resp_post_hook(struct ffa_direct_msg *req)
+{
+	return request_received_hook(req);
+}
+
+void ffa_direct_msg_routing_ext_resp_error_hook(void)
+{
+}
+
+void ffa_direct_msg_routing_ext_rc_req_pre_hook(struct ffa_direct_msg *req)
+{
+	req->args[0] = FFA_ROUTING_EXT_RC_BIT;
+	state = rc_root;
+}
+
+ffa_result
+ffa_direct_msg_routing_ext_rc_req_post_hook(struct ffa_direct_msg *resp)
+{
+	ffa_result ffa_res = FFA_OK;
+
+	ffa_res = deny_unexpected_requests(resp);
+	state = leaf;
+
+	if (is_error_message(resp))
+		ffa_res = get_error_code_from_message(resp);
+
+	return ffa_res;
+}
+
+void ffa_direct_msg_routing_ext_rc_req_error_hook(void)
+{
+	state = leaf;
+}
diff --git a/components/messaging/ffa/libsp/include/ffa_direct_msg_routing_extension.h b/components/messaging/ffa/libsp/include/ffa_direct_msg_routing_extension.h
new file mode 100644
index 0000000..3715f50
--- /dev/null
+++ b/components/messaging/ffa/libsp/include/ffa_direct_msg_routing_extension.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ */
+
+#ifndef LIBSP_INCLUDE_FFA_DIRECT_MSG_ROUTING_EXTENSION_H_
+#define LIBSP_INCLUDE_FFA_DIRECT_MSG_ROUTING_EXTENSION_H_
+
+#include "ffa_api_types.h"
+#include "sp_api_defines.h"
+#include "sp_api_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ffa_result
+ffa_direct_msg_routing_ext_wait_post_hook(struct ffa_direct_msg *req);
+
+void ffa_direct_msg_routing_ext_req_pre_hook(struct ffa_direct_msg *req);
+ffa_result
+ffa_direct_msg_routing_ext_req_post_hook(struct ffa_direct_msg *resp);
+void ffa_direct_msg_routing_ext_req_error_hook(void);
+
+void ffa_direct_msg_routing_ext_resp_pre_hook(struct ffa_direct_msg *resp);
+ffa_result
+ffa_direct_msg_routing_ext_resp_post_hook(struct ffa_direct_msg *req);
+void ffa_direct_msg_routing_ext_resp_error_hook(void);
+
+void ffa_direct_msg_routing_ext_rc_req_pre_hook(struct ffa_direct_msg *req);
+ffa_result
+ffa_direct_msg_routing_ext_rc_req_post_hook(struct ffa_direct_msg *resp);
+void ffa_direct_msg_routing_ext_rc_req_error_hook(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBSP_INCLUDE_FFA_DIRECT_MSG_ROUTING_EXTENSION_H_ */
diff --git a/components/messaging/ffa/libsp/include/sp_messaging.h b/components/messaging/ffa/libsp/include/sp_messaging.h
index 3ee5aae..4bb45f7 100644
--- a/components/messaging/ffa/libsp/include/sp_messaging.h
+++ b/components/messaging/ffa/libsp/include/sp_messaging.h
@@ -57,6 +57,18 @@
 sp_result sp_msg_send_direct_resp(const struct sp_msg *resp,
 				  struct sp_msg *req);
 
+#if FFA_DIRECT_MSG_ROUTING_EXTENSION
+/**
+ * @brief      Sends a request on the return channel and waits for the response
+ *             message which it returns then.
+ *
+ * @param[in]  req   The request message
+ * @param[out] resp  The response message
+ * @return     The SP API result
+ */
+sp_result sp_msg_send_rc_req(const struct sp_msg *req, struct sp_msg *resp);
+#endif /* FFA_DIRECT_MSG_ROUTING_EXTENSION */
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/components/messaging/ffa/libsp/sp_messaging.c b/components/messaging/ffa/libsp/sp_messaging.c
index 9e29384..ba07ce1 100644
--- a/components/messaging/ffa/libsp/sp_messaging.c
+++ b/components/messaging/ffa/libsp/sp_messaging.c
@@ -6,6 +6,9 @@
 #include "ffa_api.h"
 #include "sp_api_defines.h"
 #include "sp_messaging.h"
+#if FFA_DIRECT_MSG_ROUTING_EXTENSION
+#include "ffa_direct_msg_routing_extension.h"
+#endif
 
 #include <string.h>
 
@@ -57,6 +60,14 @@
 		return SP_RESULT_FFA(ffa_res);
 	}
 
+#if FFA_DIRECT_MSG_ROUTING_EXTENSION
+	ffa_res = ffa_direct_msg_routing_ext_wait_post_hook(&ffa_msg);
+	if (ffa_res != FFA_OK) {
+		*msg = (struct sp_msg){ 0 };
+		return SP_RESULT_FFA(ffa_res);
+	}
+#endif
+
 	unpack_ffa_direct_msg(&ffa_msg, msg);
 
 	return SP_RESULT_OK;
@@ -78,6 +89,10 @@
 
 	pack_ffa_direct_msg(req, &ffa_req);
 
+#if FFA_DIRECT_MSG_ROUTING_EXTENSION
+	ffa_direct_msg_routing_ext_req_pre_hook(&ffa_req);
+#endif
+
 	ffa_res = ffa_msg_send_direct_req(ffa_req.source_id,
 					  ffa_req.destination_id,
 					  ffa_req.args[0], ffa_req.args[1],
@@ -85,10 +100,21 @@
 					  ffa_req.args[4], &ffa_resp);
 
 	if (ffa_res != FFA_OK) {
+#if FFA_DIRECT_MSG_ROUTING_EXTENSION
+		ffa_direct_msg_routing_ext_req_error_hook();
+#endif
 		*resp = (struct sp_msg){ 0 };
 		return SP_RESULT_FFA(ffa_res);
 	}
 
+#if FFA_DIRECT_MSG_ROUTING_EXTENSION
+	ffa_res = ffa_direct_msg_routing_ext_req_post_hook(&ffa_resp);
+	if (ffa_res != SP_RESULT_OK) {
+		*resp = (struct sp_msg){ 0 };
+		return SP_RESULT_FFA(ffa_res);
+	}
+#endif
+
 	unpack_ffa_direct_msg(&ffa_resp, resp);
 
 	return SP_RESULT_OK;
@@ -110,6 +136,10 @@
 
 	pack_ffa_direct_msg(resp, &ffa_resp);
 
+#if FFA_DIRECT_MSG_ROUTING_EXTENSION
+	ffa_direct_msg_routing_ext_resp_pre_hook(&ffa_resp);
+#endif
+
 	ffa_res = ffa_msg_send_direct_resp(ffa_resp.source_id,
 					   ffa_resp.destination_id,
 					   ffa_resp.args[0], ffa_resp.args[1],
@@ -117,11 +147,65 @@
 					   ffa_resp.args[4], &ffa_req);
 
 	if (ffa_res != FFA_OK) {
+#if FFA_DIRECT_MSG_ROUTING_EXTENSION
+		ffa_direct_msg_routing_ext_resp_error_hook();
+#endif
 		*req = (struct sp_msg){ 0 };
 		return SP_RESULT_FFA(ffa_res);
 	}
 
+#if FFA_DIRECT_MSG_ROUTING_EXTENSION
+	ffa_res = ffa_direct_msg_routing_ext_resp_post_hook(&ffa_req);
+	if (ffa_res != SP_RESULT_OK) {
+		*req = (struct sp_msg){ 0 };
+		return SP_RESULT_FFA(ffa_res);
+	}
+#endif
+
 	unpack_ffa_direct_msg(&ffa_req, req);
 
 	return SP_RESULT_OK;
 }
+
+#if FFA_DIRECT_MSG_ROUTING_EXTENSION
+sp_result sp_msg_send_rc_req(const struct sp_msg *req, struct sp_msg *resp)
+{
+	ffa_result ffa_res = FFA_OK;
+	struct ffa_direct_msg ffa_req = { 0 };
+	struct ffa_direct_msg ffa_resp = { 0 };
+
+	if (!resp)
+		return SP_RESULT_INVALID_PARAMETERS;
+
+	if (!req) {
+		*resp = (struct sp_msg){ 0 };
+		return SP_RESULT_INVALID_PARAMETERS;
+	}
+
+	pack_ffa_direct_msg(req, &ffa_req);
+
+	ffa_direct_msg_routing_ext_rc_req_pre_hook(&ffa_req);
+
+	ffa_res = ffa_msg_send_direct_resp(ffa_req.source_id,
+					   ffa_req.destination_id,
+					   ffa_req.args[0], ffa_req.args[1],
+					   ffa_req.args[2], ffa_req.args[3],
+					   ffa_req.args[4], &ffa_resp);
+
+	if (ffa_res != FFA_OK) {
+		ffa_direct_msg_routing_ext_rc_req_error_hook();
+		*resp = (struct sp_msg){ 0 };
+		return SP_RESULT_FFA(ffa_res);
+	}
+
+	ffa_res = ffa_direct_msg_routing_ext_rc_req_post_hook(&ffa_resp);
+	if (ffa_res != SP_RESULT_OK) {
+		*resp = (struct sp_msg){ 0 };
+		return SP_RESULT_FFA(ffa_res);
+	}
+
+	unpack_ffa_direct_msg(&ffa_resp, resp);
+
+	return SP_RESULT_OK;
+}
+#endif
diff --git a/components/messaging/ffa/libsp/test/test_sp_messaging.cpp b/components/messaging/ffa/libsp/test/test_sp_messaging.cpp
index 2ddf1ab..78bf8bf 100644
--- a/components/messaging/ffa/libsp/test/test_sp_messaging.cpp
+++ b/components/messaging/ffa/libsp/test/test_sp_messaging.cpp
@@ -12,6 +12,11 @@
 
 #define SP_MSG_ARG_OFFSET (1)
 
+#if FFA_DIRECT_MSG_ROUTING_EXTENSION
+#define ROUTING_EXT_RC_BIT BIT(0)
+#define ROUTING_EXT_ERR_BIT BIT(1)
+#endif
+
 TEST_GROUP(sp_messaging)
 {
 	TEST_SETUP()
@@ -76,6 +81,22 @@
 		}
 	}
 
+#if FFA_DIRECT_MSG_ROUTING_EXTENSION
+	void wait_and_receive_request(uint16_t source_id, uint16_t dest_id)
+	{
+		struct ffa_direct_msg expected_ffa_req = { 0 };
+		struct sp_msg req = { 0 };
+
+		fill_ffa_msg(&expected_ffa_req);
+		expected_ffa_req.source_id = source_id;
+		expected_ffa_req.destination_id = dest_id;
+		expect_ffa_msg_wait(&expected_ffa_req, FFA_OK);
+
+		LONGS_EQUAL(SP_RESULT_OK, sp_msg_wait(&req));
+		ffa_and_sp_msg_equal(&expected_ffa_req, &req);
+	}
+#endif
+
 	struct sp_msg req;
 	struct sp_msg resp;
 	struct ffa_direct_msg ffa_msg;
@@ -112,6 +133,45 @@
 	ffa_and_sp_msg_equal(&ffa_msg, &req);
 }
 
+#if FFA_DIRECT_MSG_ROUTING_EXTENSION
+TEST(sp_messaging, sp_msg_wait_deny_rc_failure)
+{
+	struct ffa_direct_msg rc_msg = { 0 };
+	ffa_result result = FFA_ABORTED;
+
+	fill_ffa_msg(&rc_msg);
+	rc_msg.args[0] = ROUTING_EXT_RC_BIT;
+	expect_ffa_msg_wait(&rc_msg, FFA_OK);
+
+	fill_ffa_msg(&ffa_msg);
+	expect_ffa_msg_send_direct_resp(
+		rc_msg.destination_id, rc_msg.source_id,
+		ROUTING_EXT_RC_BIT | ROUTING_EXT_ERR_BIT,
+		SP_RESULT_FFA(FFA_DENIED), 0, 0, 0, &ffa_msg, result);
+
+	LONGS_EQUAL(SP_RESULT_FFA(result), sp_msg_wait(&req));
+	MEMCMP_EQUAL(&empty_sp_msg, &req, sizeof(empty_sp_msg));
+}
+
+TEST(sp_messaging, sp_msg_wait_deny_rc)
+{
+	struct ffa_direct_msg rc_msg = { 0 };
+
+	fill_ffa_msg(&rc_msg);
+	rc_msg.args[0] = ROUTING_EXT_RC_BIT;
+	expect_ffa_msg_wait(&rc_msg, FFA_OK);
+
+	fill_ffa_msg(&ffa_msg);
+	expect_ffa_msg_send_direct_resp(
+		rc_msg.destination_id, rc_msg.source_id,
+		ROUTING_EXT_RC_BIT | ROUTING_EXT_ERR_BIT,
+		SP_RESULT_FFA(FFA_DENIED), 0, 0, 0, &ffa_msg, FFA_OK);
+
+	LONGS_EQUAL(SP_RESULT_OK, sp_msg_wait(&req));
+	ffa_and_sp_msg_equal(&ffa_msg, &req);
+}
+#endif
+
 TEST(sp_messaging, sp_msg_send_direct_req_resp_null)
 {
 	LONGS_EQUAL(SP_RESULT_INVALID_PARAMETERS,
@@ -175,6 +235,564 @@
 	MEMCMP_EQUAL(&empty_sp_msg, &resp, sizeof(empty_sp_msg));
 }
 
+#if FFA_DIRECT_MSG_ROUTING_EXTENSION
+TEST(sp_messaging, sp_msg_send_direct_req_rc_forwarding_success)
+{
+	const uint16_t root_id = 1;
+	const uint16_t own_id = 2;
+	const uint16_t rc_root_id = 3;
+	ffa_direct_msg req = { 0 };
+	ffa_direct_msg rc_req = { 0 };
+	ffa_direct_msg rc_resp = { 0 };
+	ffa_direct_msg resp = { 0 };
+	sp_msg sp_req = { 0 };
+	sp_msg sp_resp = { 0 };
+
+	fill_sp_msg(&sp_req);
+	sp_req.source_id = own_id;
+	sp_req.destination_id = rc_root_id;
+
+	req.function_id = FFA_MSG_SEND_DIRECT_REQ_32;
+	req.source_id = own_id;
+	req.destination_id = rc_root_id;
+	copy_sp_to_ffa_args(sp_req.args, req.args);
+
+	fill_ffa_msg(&rc_req);
+	rc_req.function_id = FFA_MSG_SEND_DIRECT_RESP_32;
+	rc_req.source_id = rc_root_id;
+	rc_req.destination_id = own_id;
+	rc_req.args[0] = ROUTING_EXT_RC_BIT;
+
+	fill_ffa_msg(&rc_resp);
+	rc_resp.function_id = FFA_MSG_SEND_DIRECT_REQ_32;
+	rc_resp.source_id = root_id;
+	rc_resp.destination_id = own_id;
+	rc_resp.args[0] = ROUTING_EXT_RC_BIT;
+
+	fill_sp_msg(&sp_resp);
+	sp_resp.source_id = rc_root_id;
+	sp_resp.destination_id = own_id;
+
+	resp.function_id = FFA_MSG_SEND_DIRECT_RESP_32;
+	resp.source_id = rc_root_id;
+	resp.destination_id = own_id;
+	copy_sp_to_ffa_args(sp_resp.args, resp.args);
+
+	/* Initial request to current SP to set own_id */
+	wait_and_receive_request(root_id, own_id);
+
+	/* Sending request and receiving RC request from RC root */
+	expect_ffa_msg_send_direct_req(own_id, rc_root_id, 0, req.args[1],
+				       req.args[2], req.args[3], req.args[4],
+				       &rc_req, FFA_OK);
+
+	/* Forwarding RC request to root and receiving RC response */
+	expect_ffa_msg_send_direct_resp(own_id, root_id, rc_req.args[0],
+					rc_req.args[1], rc_req.args[2],
+					rc_req.args[3], rc_req.args[4],
+					&rc_resp, FFA_OK);
+
+	/* Fowarding RC response to RC root and receiving response */
+	expect_ffa_msg_send_direct_req(own_id, rc_root_id, rc_resp.args[0],
+				       rc_resp.args[1], rc_resp.args[2],
+				       rc_resp.args[3], rc_resp.args[4], &resp,
+				       FFA_OK);
+
+	LONGS_EQUAL(SP_RESULT_OK, sp_msg_send_direct_req(&sp_req, &sp_resp));
+	ffa_and_sp_msg_equal(&resp, &sp_resp);
+}
+
+TEST(sp_messaging, sp_msg_send_direct_req_rc_error)
+{
+	const uint16_t root_id = 1;
+	const uint16_t own_id = 2;
+	const uint16_t rc_root_id = 3;
+	ffa_direct_msg req = { 0 };
+	ffa_direct_msg rc_err = { 0 };
+	sp_msg sp_req = { 0 };
+	sp_msg sp_resp = { 0 };
+
+	fill_sp_msg(&sp_req);
+	sp_req.source_id = own_id;
+	sp_req.destination_id = rc_root_id;
+
+	req.function_id = FFA_MSG_SEND_DIRECT_REQ_32;
+	req.source_id = own_id;
+	req.destination_id = rc_root_id;
+	copy_sp_to_ffa_args(sp_req.args, req.args);
+
+	fill_ffa_msg(&rc_err);
+	rc_err.function_id = FFA_MSG_SEND_DIRECT_RESP_32;
+	rc_err.source_id = rc_root_id;
+	rc_err.destination_id = own_id;
+	rc_err.args[0] = ROUTING_EXT_RC_BIT | ROUTING_EXT_ERR_BIT;
+	rc_err.args[1] = result;
+
+	/* Initial request to current SP to set own_id */
+	wait_and_receive_request(root_id, own_id);
+
+	/* Sending request and receiving RC request from RC root */
+	expect_ffa_msg_send_direct_req(own_id, rc_root_id, 0, req.args[1],
+				       req.args[2], req.args[3], req.args[4],
+				       &rc_err, FFA_OK);
+
+	LONGS_EQUAL(SP_RESULT_FFA(result),
+		    sp_msg_send_direct_req(&sp_req, &sp_resp));
+	MEMCMP_EQUAL(&empty_sp_msg, &sp_resp, sizeof(empty_sp_msg));
+}
+
+TEST(sp_messaging, sp_msg_send_direct_req_rc_forwarding_success_deny_request)
+{
+	const uint16_t root_id = 1;
+	const uint16_t own_id = 2;
+	const uint16_t rc_root_id = 3;
+	ffa_direct_msg req = { 0 };
+	ffa_direct_msg rc_req = { 0 };
+	ffa_direct_msg request_to_deny = { 0 };
+	ffa_direct_msg rc_resp = { 0 };
+	ffa_direct_msg resp = { 0 };
+	sp_msg sp_req = { 0 };
+	sp_msg sp_resp = { 0 };
+
+	fill_sp_msg(&sp_req);
+	sp_req.source_id = own_id;
+	sp_req.destination_id = rc_root_id;
+
+	req.function_id = FFA_MSG_SEND_DIRECT_REQ_32;
+	req.source_id = own_id;
+	req.destination_id = rc_root_id;
+	copy_sp_to_ffa_args(sp_req.args, req.args);
+
+	fill_ffa_msg(&rc_req);
+	rc_req.function_id = FFA_MSG_SEND_DIRECT_RESP_32;
+	rc_req.source_id = rc_root_id;
+	rc_req.destination_id = own_id;
+	rc_req.args[0] = ROUTING_EXT_RC_BIT;
+
+	request_to_deny.function_id = FFA_MSG_SEND_DIRECT_REQ_32;
+	request_to_deny.source_id = root_id;
+	request_to_deny.destination_id = own_id;
+	request_to_deny.args[0] = 0;
+
+	fill_ffa_msg(&rc_resp);
+	rc_resp.function_id = FFA_MSG_SEND_DIRECT_REQ_32;
+	rc_resp.source_id = root_id;
+	rc_resp.destination_id = own_id;
+	rc_resp.args[0] = ROUTING_EXT_RC_BIT;
+
+	fill_sp_msg(&sp_resp);
+	sp_resp.source_id = rc_root_id;
+	sp_resp.destination_id = own_id;
+
+	resp.function_id = FFA_MSG_SEND_DIRECT_RESP_32;
+	resp.source_id = rc_root_id;
+	resp.destination_id = own_id;
+	copy_sp_to_ffa_args(sp_resp.args, resp.args);
+
+	/* Initial request to current SP to set own_id */
+	wait_and_receive_request(root_id, own_id);
+
+	/* Sending request and receiving RC request from RC root */
+	expect_ffa_msg_send_direct_req(own_id, rc_root_id, 0, req.args[1],
+				       req.args[2], req.args[3], req.args[4],
+				       &rc_req, FFA_OK);
+
+	/* Forwarding RC request to root and receiving a request to deny */
+	expect_ffa_msg_send_direct_resp(own_id, root_id, rc_req.args[0],
+					rc_req.args[1], rc_req.args[2],
+					rc_req.args[3], rc_req.args[4],
+					&request_to_deny, FFA_OK);
+
+	/* Sending error to root and receiving RC response */
+	expect_ffa_msg_send_direct_resp(
+		own_id, root_id, ROUTING_EXT_RC_BIT | ROUTING_EXT_ERR_BIT,
+		SP_RESULT_FFA(FFA_BUSY), 0, 0, 0, &rc_resp, FFA_OK);
+
+	/* Fowarding RC response to RC root and receiving response */
+	expect_ffa_msg_send_direct_req(own_id, rc_root_id, rc_resp.args[0],
+				       rc_resp.args[1], rc_resp.args[2],
+				       rc_resp.args[3], rc_resp.args[4], &resp,
+				       FFA_OK);
+
+	LONGS_EQUAL(SP_RESULT_OK, sp_msg_send_direct_req(&sp_req, &sp_resp));
+	ffa_and_sp_msg_equal(&resp, &sp_resp);
+}
+
+TEST(sp_messaging, sp_msg_send_direct_req_rc_forwarding_success_invalid_req_src)
+{
+	const uint16_t root_id = 1;
+	const uint16_t own_id = 2;
+	const uint16_t rc_root_id = 3;
+	ffa_direct_msg req = { 0 };
+	ffa_direct_msg rc_req = { 0 };
+	ffa_direct_msg request_to_deny = { 0 };
+	ffa_direct_msg rc_resp = { 0 };
+	ffa_direct_msg resp = { 0 };
+	sp_msg sp_req = { 0 };
+	sp_msg sp_resp = { 0 };
+
+	fill_sp_msg(&sp_req);
+	sp_req.source_id = own_id;
+	sp_req.destination_id = rc_root_id;
+
+	req.function_id = FFA_MSG_SEND_DIRECT_REQ_32;
+	req.source_id = own_id;
+	req.destination_id = rc_root_id;
+	copy_sp_to_ffa_args(sp_req.args, req.args);
+
+	fill_ffa_msg(&rc_req);
+	rc_req.function_id = FFA_MSG_SEND_DIRECT_RESP_32;
+	rc_req.source_id = rc_root_id;
+	rc_req.destination_id = own_id;
+	rc_req.args[0] = ROUTING_EXT_RC_BIT;
+
+	request_to_deny.function_id = FFA_MSG_SEND_DIRECT_REQ_32;
+	/* This source ID should be denied in the current state. */
+	request_to_deny.source_id = rc_root_id;
+	request_to_deny.destination_id = own_id;
+	request_to_deny.args[0] = ROUTING_EXT_RC_BIT;
+
+	fill_ffa_msg(&rc_resp);
+	rc_resp.function_id = FFA_MSG_SEND_DIRECT_REQ_32;
+	rc_resp.source_id = root_id;
+	rc_resp.destination_id = own_id;
+	rc_resp.args[0] = ROUTING_EXT_RC_BIT;
+
+	fill_sp_msg(&sp_resp);
+	sp_resp.source_id = rc_root_id;
+	sp_resp.destination_id = own_id;
+
+	resp.function_id = FFA_MSG_SEND_DIRECT_RESP_32;
+	resp.source_id = rc_root_id;
+	resp.destination_id = own_id;
+	copy_sp_to_ffa_args(sp_resp.args, resp.args);
+
+	/* Initial request to current SP to set own_id */
+	wait_and_receive_request(root_id, own_id);
+
+	/* Sending request and receiving RC request from RC root */
+	expect_ffa_msg_send_direct_req(own_id, rc_root_id, 0, req.args[1],
+				       req.args[2], req.args[3], req.args[4],
+				       &rc_req, FFA_OK);
+
+	/* Forwarding RC request to root and receiving RC response */
+	expect_ffa_msg_send_direct_resp(own_id, root_id, rc_req.args[0],
+					rc_req.args[1], rc_req.args[2],
+					rc_req.args[3], rc_req.args[4],
+					&request_to_deny, FFA_OK);
+
+	/* Sending error to root and receiving RC response */
+	expect_ffa_msg_send_direct_resp(
+		own_id, rc_root_id, ROUTING_EXT_ERR_BIT | ROUTING_EXT_RC_BIT,
+		SP_RESULT_FFA(FFA_BUSY), 0, 0, 0, &rc_resp, FFA_OK);
+
+	/* Fowarding RC response to RC root and receiving response */
+	expect_ffa_msg_send_direct_req(own_id, rc_root_id, rc_resp.args[0],
+				       rc_resp.args[1], rc_resp.args[2],
+				       rc_resp.args[3], rc_resp.args[4], &resp,
+				       FFA_OK);
+
+	LONGS_EQUAL(SP_RESULT_OK, sp_msg_send_direct_req(&sp_req, &sp_resp));
+	ffa_and_sp_msg_equal(&resp, &sp_resp);
+}
+
+TEST(sp_messaging, sp_msg_send_direct_req_deny_fail_wait_success)
+{
+	const uint16_t root_id = 1;
+	const uint16_t own_id = 2;
+	const uint16_t rc_root_id = 3;
+	ffa_direct_msg req = { 0 };
+	ffa_direct_msg rc_req = { 0 };
+	ffa_direct_msg request_to_deny = { 0 };
+	ffa_direct_msg rc_resp = { 0 };
+	ffa_direct_msg resp = { 0 };
+	sp_msg sp_req = { 0 };
+	sp_msg sp_resp = { 0 };
+
+	fill_sp_msg(&sp_req);
+	sp_req.source_id = own_id;
+	sp_req.destination_id = rc_root_id;
+
+	req.function_id = FFA_MSG_SEND_DIRECT_REQ_32;
+	req.source_id = own_id;
+	req.destination_id = rc_root_id;
+	copy_sp_to_ffa_args(sp_req.args, req.args);
+
+	fill_ffa_msg(&rc_req);
+	rc_req.function_id = FFA_MSG_SEND_DIRECT_RESP_32;
+	rc_req.source_id = rc_root_id;
+	rc_req.destination_id = own_id;
+	rc_req.args[0] = ROUTING_EXT_RC_BIT;
+
+	request_to_deny.function_id = FFA_MSG_SEND_DIRECT_REQ_32;
+	/* This source ID should be denied in the current state. */
+	request_to_deny.source_id = rc_root_id;
+	request_to_deny.destination_id = own_id;
+	request_to_deny.args[0] = ROUTING_EXT_RC_BIT;
+
+	fill_ffa_msg(&rc_resp);
+	rc_resp.function_id = FFA_MSG_SEND_DIRECT_REQ_32;
+	rc_resp.source_id = root_id;
+	rc_resp.destination_id = own_id;
+	rc_resp.args[0] = ROUTING_EXT_RC_BIT;
+
+	fill_sp_msg(&sp_resp);
+	sp_resp.source_id = rc_root_id;
+	sp_resp.destination_id = own_id;
+
+	resp.function_id = FFA_MSG_SEND_DIRECT_RESP_32;
+	resp.source_id = rc_root_id;
+	resp.destination_id = own_id;
+	copy_sp_to_ffa_args(sp_resp.args, resp.args);
+
+	/* Initial request to current SP to set own_id */
+	wait_and_receive_request(root_id, own_id);
+
+	/* Sending request and receiving RC request from RC root */
+	expect_ffa_msg_send_direct_req(own_id, rc_root_id, 0, req.args[1],
+				       req.args[2], req.args[3], req.args[4],
+				       &rc_req, FFA_OK);
+
+	/* Forwarding RC request to root and receiving RC response */
+	expect_ffa_msg_send_direct_resp(own_id, root_id, rc_req.args[0],
+					rc_req.args[1], rc_req.args[2],
+					rc_req.args[3], rc_req.args[4],
+					&request_to_deny, FFA_OK);
+
+	/* Sending error to root which fails */
+	expect_ffa_msg_send_direct_resp(
+		own_id, rc_root_id, (ROUTING_EXT_ERR_BIT | ROUTING_EXT_RC_BIT),
+		SP_RESULT_FFA(FFA_BUSY), 0, 0, 0, &rc_resp, FFA_DENIED);
+
+	/* Sending FFA_MSG_WAIT as we are still waiting for the RC response */
+	expect_ffa_msg_wait(&rc_resp, FFA_OK);
+
+	/* Fowarding RC response to RC root and receiving response */
+	expect_ffa_msg_send_direct_req(own_id, rc_root_id, rc_resp.args[0],
+				       rc_resp.args[1], rc_resp.args[2],
+				       rc_resp.args[3], rc_resp.args[4], &resp,
+				       FFA_OK);
+
+	LONGS_EQUAL(SP_RESULT_OK, sp_msg_send_direct_req(&sp_req, &sp_resp));
+	ffa_and_sp_msg_equal(&resp, &sp_resp);
+}
+
+TEST(sp_messaging, sp_msg_send_direct_req_deny_fail_wait_fail_forwarding)
+{
+	const uint16_t root_id = 1;
+	const uint16_t own_id = 2;
+	const uint16_t rc_root_id = 3;
+	ffa_direct_msg req = { 0 };
+	ffa_direct_msg rc_req = { 0 };
+	ffa_direct_msg request_to_deny = { 0 };
+	ffa_direct_msg rc_resp = { 0 };
+	ffa_direct_msg resp = { 0 };
+	sp_msg sp_req = { 0 };
+	sp_msg sp_resp = { 0 };
+
+	fill_sp_msg(&sp_req);
+	sp_req.source_id = own_id;
+	sp_req.destination_id = rc_root_id;
+
+	req.function_id = FFA_MSG_SEND_DIRECT_REQ_32;
+	req.source_id = own_id;
+	req.destination_id = rc_root_id;
+	copy_sp_to_ffa_args(sp_req.args, req.args);
+
+	fill_ffa_msg(&rc_req);
+	rc_req.function_id = FFA_MSG_SEND_DIRECT_RESP_32;
+	rc_req.source_id = rc_root_id;
+	rc_req.destination_id = own_id;
+	rc_req.args[0] = ROUTING_EXT_RC_BIT;
+
+	request_to_deny.function_id = FFA_MSG_SEND_DIRECT_REQ_32;
+	/* This source ID should be denied in the current state. */
+	request_to_deny.source_id = rc_root_id;
+	request_to_deny.destination_id = own_id;
+	request_to_deny.args[0] = ROUTING_EXT_RC_BIT;
+
+	fill_ffa_msg(&rc_resp);
+	rc_resp.function_id = FFA_MSG_SEND_DIRECT_REQ_32;
+	rc_resp.source_id = root_id;
+	rc_resp.destination_id = own_id;
+	rc_resp.args[0] = ROUTING_EXT_RC_BIT;
+
+	fill_sp_msg(&sp_resp);
+	sp_resp.source_id = rc_root_id;
+	sp_resp.destination_id = own_id;
+
+	resp.function_id = FFA_MSG_SEND_DIRECT_RESP_32;
+	resp.source_id = rc_root_id;
+	resp.destination_id = own_id;
+	copy_sp_to_ffa_args(sp_resp.args, resp.args);
+
+	/* Initial request to current SP to set own_id */
+	wait_and_receive_request(root_id, own_id);
+
+	/* Sending request and receiving RC request from RC root */
+	expect_ffa_msg_send_direct_req(own_id, rc_root_id, 0, req.args[1],
+				       req.args[2], req.args[3], req.args[4],
+				       &rc_req, FFA_OK);
+
+	/* Forwarding RC request to root and receiving RC response */
+	expect_ffa_msg_send_direct_resp(own_id, root_id, rc_req.args[0],
+					rc_req.args[1], rc_req.args[2],
+					rc_req.args[3], rc_req.args[4],
+					&request_to_deny, FFA_OK);
+
+	/* Sending error to root which fails */
+	expect_ffa_msg_send_direct_resp(
+		own_id, rc_root_id, ROUTING_EXT_ERR_BIT | ROUTING_EXT_RC_BIT,
+		SP_RESULT_FFA(FFA_BUSY), 0, 0, 0, &rc_resp, FFA_DENIED);
+
+	/* Sending FFA_MSG_WAIT as we are still waiting for the RC response */
+	expect_ffa_msg_wait(&rc_resp, result);
+
+	/* Fowarding RC error as FFA_MSG_WAIT failed  */
+	expect_ffa_msg_send_direct_req(
+		own_id, rc_root_id, (ROUTING_EXT_RC_BIT | ROUTING_EXT_ERR_BIT),
+		result, 0, 0, 0, &resp, FFA_OK);
+
+	LONGS_EQUAL(SP_RESULT_OK, sp_msg_send_direct_req(&sp_req, &sp_resp));
+	ffa_and_sp_msg_equal(&resp, &sp_resp);
+}
+
+TEST(sp_messaging, sp_msg_send_direct_req_rc_return_rc_error_msg)
+{
+	const uint16_t root_id = 1;
+	const uint16_t own_id = 2;
+	const uint16_t rc_root_id = 3;
+	ffa_direct_msg req = { 0 };
+	ffa_direct_msg rc_req = { 0 };
+	ffa_direct_msg rc_resp = { 0 };
+	ffa_direct_msg resp = { 0 };
+	sp_msg sp_req = { 0 };
+	sp_msg sp_resp = { 0 };
+	ffa_result result = FFA_ABORTED;
+
+	fill_sp_msg(&sp_req);
+	sp_req.source_id = own_id;
+	sp_req.destination_id = rc_root_id;
+
+	req.function_id = FFA_MSG_SEND_DIRECT_REQ_32;
+	req.source_id = own_id;
+	req.destination_id = rc_root_id;
+	copy_sp_to_ffa_args(sp_req.args, req.args);
+
+	fill_ffa_msg(&rc_req);
+	rc_req.function_id = FFA_MSG_SEND_DIRECT_RESP_32;
+	rc_req.source_id = rc_root_id;
+	rc_req.destination_id = own_id;
+	rc_req.args[0] = ROUTING_EXT_RC_BIT;
+
+	fill_ffa_msg(&rc_resp);
+	rc_resp.function_id = FFA_MSG_SEND_DIRECT_REQ_32;
+	rc_resp.source_id = root_id;
+	rc_resp.destination_id = own_id;
+	rc_resp.args[0] = ROUTING_EXT_RC_BIT;
+
+	fill_sp_msg(&sp_resp);
+	sp_resp.source_id = rc_root_id;
+	sp_resp.destination_id = own_id;
+
+	resp.function_id = FFA_MSG_SEND_DIRECT_RESP_32;
+	resp.source_id = rc_root_id;
+	resp.destination_id = own_id;
+	copy_sp_to_ffa_args(sp_resp.args, resp.args);
+
+	/* Initial request to current SP to set own_id */
+	wait_and_receive_request(root_id, own_id);
+
+	/* Sending request and receiving RC request from RC root */
+	expect_ffa_msg_send_direct_req(own_id, rc_root_id, 0, req.args[1],
+				       req.args[2], req.args[3], req.args[4],
+				       &rc_req, FFA_OK);
+
+	/* Forwarding RC request to root and receiving RC response */
+	expect_ffa_msg_send_direct_resp(own_id, root_id, rc_req.args[0],
+					rc_req.args[1], rc_req.args[2],
+					rc_req.args[3], rc_req.args[4],
+					&rc_resp, result);
+
+	/* Fowarding RC error to RC root and receiving response */
+	expect_ffa_msg_send_direct_req(own_id, rc_root_id,
+				       ROUTING_EXT_RC_BIT | ROUTING_EXT_ERR_BIT,
+				       SP_RESULT_FFA(result), 0, 0, 0, &resp,
+				       FFA_OK);
+
+	LONGS_EQUAL(SP_RESULT_OK, sp_msg_send_direct_req(&sp_req, &sp_resp));
+	ffa_and_sp_msg_equal(&resp, &sp_resp);
+}
+
+TEST(sp_messaging, sp_msg_send_direct_req_rc_return_resp_fail)
+{
+	const uint16_t root_id = 1;
+	const uint16_t own_id = 2;
+	const uint16_t rc_root_id = 3;
+	ffa_direct_msg req = { 0 };
+	ffa_direct_msg rc_req = { 0 };
+	ffa_direct_msg rc_resp = { 0 };
+	ffa_direct_msg resp = { 0 };
+	sp_msg sp_req = { 0 };
+	sp_msg sp_resp = { 0 };
+	ffa_result result = FFA_ABORTED;
+
+	fill_sp_msg(&sp_req);
+	sp_req.source_id = own_id;
+	sp_req.destination_id = rc_root_id;
+
+	req.function_id = FFA_MSG_SEND_DIRECT_REQ_32;
+	req.source_id = own_id;
+	req.destination_id = rc_root_id;
+	copy_sp_to_ffa_args(sp_req.args, req.args);
+
+	fill_ffa_msg(&rc_req);
+	rc_req.function_id = FFA_MSG_SEND_DIRECT_RESP_32;
+	rc_req.source_id = rc_root_id;
+	rc_req.destination_id = own_id;
+	rc_req.args[0] = ROUTING_EXT_RC_BIT;
+
+	fill_ffa_msg(&rc_resp);
+	rc_resp.function_id = FFA_MSG_SEND_DIRECT_REQ_32;
+	rc_resp.source_id = root_id;
+	rc_resp.destination_id = own_id;
+	rc_resp.args[0] = ROUTING_EXT_RC_BIT;
+
+	fill_sp_msg(&sp_resp);
+	sp_resp.source_id = rc_root_id;
+	sp_resp.destination_id = own_id;
+
+	resp.function_id = FFA_MSG_SEND_DIRECT_RESP_32;
+	resp.source_id = rc_root_id;
+	resp.destination_id = own_id;
+	copy_sp_to_ffa_args(sp_resp.args, resp.args);
+
+	/* Initial request to current SP to set own_id */
+	wait_and_receive_request(root_id, own_id);
+
+	/* Sending request and receiving RC request from RC root */
+	expect_ffa_msg_send_direct_req(own_id, rc_root_id, 0, req.args[1],
+				       req.args[2], req.args[3], req.args[4],
+				       &rc_req, FFA_OK);
+
+	/* Forwarding RC request to root and receiving RC response */
+	expect_ffa_msg_send_direct_resp(own_id, root_id, rc_req.args[0],
+					rc_req.args[1], rc_req.args[2],
+					rc_req.args[3], rc_req.args[4],
+					&rc_resp, FFA_OK);
+
+	/* Fowarding RC response to RC root and receiving response */
+	expect_ffa_msg_send_direct_req(own_id, rc_root_id, rc_resp.args[0],
+				       rc_resp.args[1], rc_resp.args[2],
+				       rc_resp.args[3], rc_resp.args[4], &resp,
+				       result);
+
+	LONGS_EQUAL(SP_RESULT_FFA(result),
+		    sp_msg_send_direct_req(&sp_req, &sp_resp));
+	MEMCMP_EQUAL(&empty_sp_msg, &sp_resp, sizeof(empty_sp_msg));
+}
+#endif
+
 TEST(sp_messaging, sp_msg_send_direct_resp_req_null)
 {
 	LONGS_EQUAL(SP_RESULT_INVALID_PARAMETERS,
@@ -239,3 +857,177 @@
 	LONGS_EQUAL(SP_RESULT_OK, sp_msg_send_direct_resp(&resp, &req));
 	MEMCMP_EQUAL(&empty_sp_msg, &req, sizeof(empty_sp_msg));
 }
+
+#if FFA_DIRECT_MSG_ROUTING_EXTENSION
+TEST(sp_messaging, sp_msg_send_direct_resp_deny_rc_failure)
+{
+	uint32_t expected_ffa_args[5] = { 0 };
+	struct ffa_direct_msg rc_msg = { 0 };
+
+	fill_sp_msg(&resp);
+
+	fill_ffa_msg(&rc_msg);
+	rc_msg.args[0] = ROUTING_EXT_RC_BIT;
+
+	fill_ffa_msg(&ffa_msg);
+	copy_sp_to_ffa_args(resp.args, expected_ffa_args);
+
+	expect_ffa_msg_send_direct_resp(
+		resp.source_id, resp.destination_id, expected_ffa_args[0],
+		expected_ffa_args[1], expected_ffa_args[2],
+		expected_ffa_args[3], expected_ffa_args[4], &rc_msg, FFA_OK);
+
+	expect_ffa_msg_send_direct_resp(
+		rc_msg.destination_id, rc_msg.source_id,
+		ROUTING_EXT_RC_BIT | ROUTING_EXT_ERR_BIT,
+		SP_RESULT_FFA(FFA_DENIED), 0, 0, 0, &ffa_msg, result);
+
+	LONGS_EQUAL(SP_RESULT_FFA(result),
+		    sp_msg_send_direct_resp(&resp, &req));
+	MEMCMP_EQUAL(&empty_sp_msg, &req, sizeof(empty_sp_msg));
+}
+
+TEST(sp_messaging, sp_msg_send_direct_resp_deny_rc)
+{
+	uint32_t expected_ffa_args[5] = { 0 };
+	struct ffa_direct_msg rc_msg = { 0 };
+
+	fill_sp_msg(&resp);
+
+	fill_ffa_msg(&rc_msg);
+	rc_msg.args[0] = ROUTING_EXT_RC_BIT;
+
+	fill_ffa_msg(&ffa_msg);
+	copy_sp_to_ffa_args(resp.args, expected_ffa_args);
+
+	expect_ffa_msg_send_direct_resp(resp.source_id, resp.destination_id, 0,
+					expected_ffa_args[1],
+					expected_ffa_args[2],
+					expected_ffa_args[3],
+					expected_ffa_args[4], &rc_msg, FFA_OK);
+
+	expect_ffa_msg_send_direct_resp(
+		rc_msg.destination_id, rc_msg.source_id,
+		ROUTING_EXT_RC_BIT | ROUTING_EXT_ERR_BIT,
+		SP_RESULT_FFA(FFA_DENIED), 0, 0, 0, &ffa_msg, FFA_OK);
+
+	LONGS_EQUAL(SP_RESULT_OK, sp_msg_send_direct_resp(&resp, &req));
+	ffa_and_sp_msg_equal(&ffa_msg, &req);
+}
+
+TEST(sp_messaging, sp_msg_send_rc_req_req_null)
+{
+	LONGS_EQUAL(SP_RESULT_INVALID_PARAMETERS,
+		    sp_msg_send_rc_req(&req, NULL));
+}
+
+TEST(sp_messaging, sp_msg_send_rc_req_resp_null)
+{
+	memset(&resp, 0x5a, sizeof(resp));
+	LONGS_EQUAL(SP_RESULT_INVALID_PARAMETERS,
+		    sp_msg_send_rc_req(NULL, &resp));
+	MEMCMP_EQUAL(&empty_sp_msg, &resp, sizeof(empty_sp_msg));
+}
+
+TEST(sp_messaging, sp_msg_send_rc_req_ffa_error)
+{
+	ffa_result result = FFA_ABORTED;
+
+	fill_sp_msg(&resp);
+	memset(&req, 0x5a, sizeof(req));
+	fill_ffa_msg(&ffa_msg);
+
+	expect_ffa_msg_send_direct_resp(req.source_id, req.destination_id,
+					ROUTING_EXT_RC_BIT, req.args[0],
+					req.args[1], req.args[2], req.args[3],
+					&ffa_msg, result);
+
+	LONGS_EQUAL(SP_RESULT_FFA(result), sp_msg_send_rc_req(&req, &resp));
+	MEMCMP_EQUAL(&empty_sp_msg, &resp, sizeof(empty_sp_msg));
+}
+
+TEST(sp_messaging, sp_msg_send_rc_req_deny_fail_wait_fail)
+{
+	const uint16_t root_id = 1;
+	const uint16_t own_id = 2;
+
+	wait_and_receive_request(root_id, own_id);
+
+	fill_sp_msg(&req);
+	req.source_id = own_id;
+	req.destination_id = root_id;
+
+	fill_ffa_msg(&ffa_msg);
+	ffa_msg.source_id = root_id;
+	ffa_msg.destination_id = own_id;
+	/* Should be RC message so it will be denied */
+	ffa_msg.args[0] = 0;
+
+	expect_ffa_msg_send_direct_resp(req.source_id, req.destination_id,
+					ROUTING_EXT_RC_BIT, req.args[0],
+					req.args[1], req.args[2], req.args[3],
+					&ffa_msg, FFA_OK);
+
+	expect_ffa_msg_send_direct_resp(
+		req.source_id, req.destination_id,
+		ROUTING_EXT_RC_BIT | ROUTING_EXT_ERR_BIT,
+		SP_RESULT_FFA(FFA_BUSY), 0, 0, 0, &ffa_msg, result);
+
+	expect_ffa_msg_wait(&ffa_msg, result);
+
+	LONGS_EQUAL(SP_RESULT_FFA(result), sp_msg_send_rc_req(&req, &resp));
+	MEMCMP_EQUAL(&empty_sp_msg, &resp, sizeof(empty_sp_msg));
+}
+
+TEST(sp_messaging, sp_msg_send_rc_req_rc_error)
+{
+	const uint16_t root_id = 1;
+	const uint16_t own_id = 2;
+	sp_result sp_err = SP_RESULT_NOT_FOUND;
+
+	wait_and_receive_request(root_id, own_id);
+
+	fill_sp_msg(&req);
+	req.source_id = own_id;
+	req.destination_id = root_id;
+
+	fill_ffa_msg(&ffa_msg);
+	ffa_msg.source_id = root_id;
+	ffa_msg.destination_id = own_id;
+	ffa_msg.args[0] = ROUTING_EXT_RC_BIT | ROUTING_EXT_ERR_BIT;
+	ffa_msg.args[1] = sp_err;
+
+	expect_ffa_msg_send_direct_resp(req.source_id, req.destination_id,
+					ROUTING_EXT_RC_BIT, req.args[0],
+					req.args[1], req.args[2], req.args[3],
+					&ffa_msg, FFA_OK);
+
+	LONGS_EQUAL(sp_err, sp_msg_send_rc_req(&req, &resp));
+	MEMCMP_EQUAL(&empty_sp_msg, &resp, sizeof(empty_sp_msg));
+}
+
+TEST(sp_messaging, sp_msg_send_rc_req_success)
+{
+	const uint16_t root_id = 1;
+	const uint16_t own_id = 2;
+
+	wait_and_receive_request(root_id, own_id);
+
+	fill_sp_msg(&req);
+	req.source_id = own_id;
+	req.destination_id = root_id;
+
+	fill_ffa_msg(&ffa_msg);
+	ffa_msg.source_id = root_id;
+	ffa_msg.destination_id = own_id;
+	ffa_msg.args[0] = ROUTING_EXT_RC_BIT;
+
+	expect_ffa_msg_send_direct_resp(req.source_id, req.destination_id,
+					ROUTING_EXT_RC_BIT, req.args[0],
+					req.args[1], req.args[2], req.args[3],
+					&ffa_msg, FFA_OK);
+
+	LONGS_EQUAL(SP_RESULT_OK, sp_msg_send_rc_req(&req, &resp));
+	ffa_and_sp_msg_equal(&ffa_msg, &resp);
+}
+#endif
diff --git a/components/messaging/ffa/libsp/tests.cmake b/components/messaging/ffa/libsp/tests.cmake
index 092acef..d851442 100644
--- a/components/messaging/ffa/libsp/tests.cmake
+++ b/components/messaging/ffa/libsp/tests.cmake
@@ -138,4 +138,19 @@
 		${UNIT_TEST_PROJECT_PATH}/components/common/utils/include
 	COMPILE_DEFINITIONS
 		-DARM64
+)
+
+unit_test_add_suite(
+	NAME libsp_sp_messaging_with_routing_extension
+	SOURCES
+		${CMAKE_CURRENT_LIST_DIR}/test/test_sp_messaging.cpp
+		${CMAKE_CURRENT_LIST_DIR}/test/mock_ffa_api.cpp
+		${CMAKE_CURRENT_LIST_DIR}/sp_messaging.c
+		${CMAKE_CURRENT_LIST_DIR}/ffa_direct_msg_routing_extension.c
+	INCLUDE_DIRECTORIES
+		${CMAKE_CURRENT_LIST_DIR}/include/
+		${UNIT_TEST_PROJECT_PATH}/components/common/utils/include
+	COMPILE_DEFINITIONS
+		-DARM64
+		-DFFA_DIRECT_MSG_ROUTING_EXTENSION=1
 )
\ No newline at end of file