Add RPC caller and endpoint for FF-A

Adds RPC caller and endpoint using FF-A messaging: using direct requests
and shared memory buffers for communication.

The endpoint is only for running in SPs. The caller is implemented in
two flavours: for Linux and for SPs. The Linux version relies on the
FF-A user space interface described in external/ffa_tool/buf_common.h

Change-Id: I3b51bcaf114d06bbf3dad2d63529fa59d8977e19
Signed-off-by: Balint Dobszay <balint.dobszay@arm.com>
diff --git a/components/rpc/ffarpc/caller/linux/component.cmake b/components/rpc/ffarpc/caller/linux/component.cmake
new file mode 100644
index 0000000..3bab840
--- /dev/null
+++ b/components/rpc/ffarpc/caller/linux/component.cmake
@@ -0,0 +1,14 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if (NOT DEFINED TGT)
+	message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+target_sources(${TGT} PRIVATE
+	"${CMAKE_CURRENT_LIST_DIR}/ffarpc_caller.c"
+	)
+
diff --git a/components/rpc/ffarpc/caller/linux/ffarpc_caller.c b/components/rpc/ffarpc/caller/linux/ffarpc_caller.c
new file mode 100644
index 0000000..af86664
--- /dev/null
+++ b/components/rpc/ffarpc/caller/linux/ffarpc_caller.c
@@ -0,0 +1,319 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "ffarpc_caller.h"
+#include <external/ffa_tool/buf_common.h>
+#include <rpc/ffarpc/endpoint/ffarpc_call_args.h>
+#include <rpc/ffarpc/endpoint/ffarpc_call_ops.h>
+#include <protocols/rpc/common/packed-c/status.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define DEFAULT_SHMEM_BUF_SIZE			(4096)
+
+static rpc_call_handle call_begin(void *context, uint8_t **req_buf, size_t req_len);
+static rpc_status_t call_invoke(void *context, rpc_call_handle handle, uint32_t opcode,
+			    int *opstatus, uint8_t **resp_buf, size_t *resp_len);
+static void call_end(void *context, rpc_call_handle handle);
+
+static int kernel_write_req_buf(struct ffarpc_caller *s);
+static int kernel_read_resp_buf(struct ffarpc_caller *s);
+static int share_mem_with_partition(struct ffarpc_caller *s);
+static int unshare_mem_with_partition(struct ffarpc_caller *s);
+
+
+
+struct rpc_caller *ffarpc_caller_init(struct ffarpc_caller *s, const char *device_path)
+{
+	struct rpc_caller *base = &s->rpc_caller;
+
+	base->context = s;
+	base->call_begin = call_begin;
+	base->call_invoke = call_invoke;
+	base->call_end = call_end;
+
+	s->device_path = device_path;
+	s->fd = -1;
+	s->call_ep_id = 0;
+	s->shared_mem_handle = 0;
+	s->shared_mem_required_size = DEFAULT_SHMEM_BUF_SIZE;
+	s->req_buf = NULL;
+	s->req_len = 0;
+	s->resp_buf = NULL;
+	s->resp_len = 0;
+	s->is_call_transaction_in_progess = false;
+
+	return base;
+}
+
+void ffarpc_caller_deinit(struct ffarpc_caller *s)
+{
+	s->rpc_caller.context = NULL;
+	s->rpc_caller.call_begin = NULL;
+	s->rpc_caller.call_invoke = NULL;
+	s->rpc_caller.call_end = NULL;
+
+	call_end(s, s);
+	ffarpc_caller_close(s);
+}
+
+size_t ffarpc_caller_discover(const struct ffarpc_caller *s, const uint8_t *uuid,
+						uint16_t *partition_ids, size_t discover_limit)
+{
+	size_t discover_count = 0;
+
+	if (uuid && partition_ids && s->device_path) {
+		int fd;
+
+		fd = open(s->device_path, O_RDWR);
+
+		if (fd >= 0) {
+			int ioctl_status;
+			struct endpoint_id discovered_partition;
+
+			discovered_partition.uuid_0 = uuid[3]  << 24 | uuid[2]  << 16 | uuid[1]  << 8 | uuid[0];
+			discovered_partition.uuid_1 = uuid[7]  << 24 | uuid[6]  << 16 | uuid[5]  << 8 | uuid[4];
+			discovered_partition.uuid_2 = uuid[11] << 24 | uuid[10] << 16 | uuid[9]  << 8 | uuid[8];
+			discovered_partition.uuid_3 = uuid[15] << 24 | uuid[14] << 16 | uuid[13] << 8 | uuid[12];
+			discovered_partition.id = 0;
+
+			ioctl_status = ioctl(fd, GET_PART_ID, &discovered_partition);
+
+			if ((ioctl_status == 0) && (discover_count < discover_limit)) {
+				partition_ids[discover_count] = discovered_partition.id;
+				++discover_count;
+			}
+
+			close(fd);
+		}
+	}
+
+    return discover_count;
+}
+
+int ffarpc_caller_open(struct ffarpc_caller *s, uint16_t call_ep_id)
+{
+    int ioctl_status = -1;
+
+	if (s->device_path) {
+
+		s->fd = open(s->device_path, O_RDWR);
+
+		if (s->fd >= 0) {
+			/* Allocate resource for session */
+			ioctl_status = ioctl(s->fd, SHARE_INIT, &s->shared_mem_handle);
+
+			if (ioctl_status == 0) {
+				/* Session successfully opened */
+				s->call_ep_id = call_ep_id;
+				ioctl_status = share_mem_with_partition(s);
+			}
+
+			if (ioctl_status != 0)  {
+				/* Resource allocation or sharing error */
+				close(s->fd);
+				s->fd = -1;
+			}
+		}
+	}
+
+    return ioctl_status;
+}
+
+int ffarpc_caller_close(struct ffarpc_caller *s)
+{
+    int ioctl_status = -1;
+
+    if (s->fd >= 0) {
+
+        unshare_mem_with_partition(s);
+        ioctl_status = ioctl(s->fd, SHARE_DEINIT);
+        close(s->fd);
+        s->fd = -1;
+        s->call_ep_id = 0;
+    }
+
+    return ioctl_status;
+}
+
+static rpc_call_handle call_begin(void *context, uint8_t **req_buf, size_t req_len)
+{
+	rpc_call_handle handle = NULL;
+	struct ffarpc_caller *s = (struct ffarpc_caller*)context;
+
+    if (!s->is_call_transaction_in_progess) {
+
+        s->is_call_transaction_in_progess = true;
+        handle = s;
+
+        if (req_len > 0) {
+
+            s->req_buf = malloc(req_len);
+
+            if (s->req_buf) {
+
+                *req_buf = s->req_buf;
+                s->req_len = req_len;
+            }
+            else {
+                /* Failed to allocate req buffer */
+                handle = NULL;
+                s->is_call_transaction_in_progess = false;
+            }
+        }
+        else {
+
+            *req_buf = NULL;
+            s->req_buf = NULL;
+            s->req_len = req_len;
+        }
+    }
+
+    return handle;
+}
+
+static rpc_status_t call_invoke(void *context, rpc_call_handle handle, uint32_t opcode,
+			    int *opstatus, uint8_t **resp_buf, size_t *resp_len)
+{
+    rpc_status_t rpc_status = TS_RPC_ERROR_INTERNAL;
+	struct ffarpc_caller *s = (struct ffarpc_caller*)context;
+
+    if ((handle == s) && s->is_call_transaction_in_progess) {
+        int kernel_op_status = 0;
+
+        if (s->req_len > 0) {
+            kernel_op_status = kernel_write_req_buf(s);
+        }
+
+        if (kernel_op_status == 0) {
+            /* Make direct call to send the request */
+            struct msg_args direct_msg;
+            memset(&direct_msg, 0, sizeof(direct_msg));
+
+		    direct_msg.dst_id = s->call_ep_id;
+		    direct_msg.args[FFA_CALL_ARGS_OPCODE] = (uint64_t)opcode;
+		    direct_msg.args[FFA_CALL_ARGS_REQ_DATA_LEN] = (uint64_t)s->req_len;
+
+            kernel_op_status = ioctl(s->fd, SEND_SYNC_MSG, &direct_msg);
+
+            if (kernel_op_status == 0) {
+                /* Send completed normally - ffa return args in msg_args struct */
+                s->resp_len = (size_t)direct_msg.args[FFA_CALL_ARGS_RESP_DATA_LEN];
+                rpc_status = (int)direct_msg.args[FFA_CALL_ARGS_RESP_RPC_STATUS];
+                *opstatus = (int)direct_msg.args[FFA_CALL_ARGS_RESP_OP_STATUS];
+
+                if (s->resp_len > 0) {
+                    s->resp_buf = malloc(s->resp_len);
+
+                    if (s->resp_buf) {
+                        kernel_op_status = kernel_read_resp_buf(s);
+
+                        if (kernel_op_status != 0) {
+                            /* Failed to read response buffer */
+                            rpc_status = TS_RPC_ERROR_INTERNAL;
+                        }
+                    }
+                    else {
+                        /* Failed to allocate response buffer */
+                        s->resp_len = 0;
+                        rpc_status = TS_RPC_ERROR_INTERNAL;
+                    }
+                }
+                else {
+					/* No response parameters */
+                    s->resp_buf = NULL;
+                }
+
+                *resp_len = s->resp_len;
+                *resp_buf = s->resp_buf;
+	        }
+        }
+	}
+
+    return rpc_status;
+}
+
+static void call_end(void *context, rpc_call_handle handle)
+{
+	struct ffarpc_caller *s = (struct ffarpc_caller*)context;
+
+	if ((handle == s) && s->is_call_transaction_in_progess) {
+
+        /* Call transaction complete so free resource */
+        free(s->req_buf);
+        s->req_buf = NULL;
+        s->req_len = 0;
+
+        free(s->resp_buf);
+        s->resp_buf = NULL;
+        s->resp_len = 0;
+
+        s->is_call_transaction_in_progess = false;
+    }
+}
+
+static int kernel_write_req_buf(struct ffarpc_caller *s) {
+
+    int ioctl_status;
+    struct buf_descr req_descr;
+
+    req_descr.buf = s->req_buf;
+    req_descr.length = s->req_len;
+    ioctl_status = ioctl(s->fd, SHARE_WRITE, &req_descr);
+
+    return ioctl_status;
+}
+
+
+static int kernel_read_resp_buf(struct ffarpc_caller *s) {
+
+    int ioctl_status;
+    struct buf_descr resp_descr;
+
+    resp_descr.buf = s->resp_buf;
+    resp_descr.length = s->resp_len;
+    ioctl_status = ioctl(s->fd, SHARE_READ, &resp_descr);
+
+    return ioctl_status;
+}
+
+static int share_mem_with_partition(struct ffarpc_caller *s) {
+
+    int ioctl_status;
+    struct msg_args direct_msg;
+    memset(&direct_msg, 0, sizeof(direct_msg));
+
+    direct_msg.dst_id = s->call_ep_id;
+    direct_msg.args[FFA_CALL_ARGS_OPCODE] = (uint64_t)FFA_CALL_OPCODE_SHARE_BUF;
+    direct_msg.args[FFA_CALL_ARGS_SHARE_MEM_HANDLE_LSW] = (uint32_t)s->shared_mem_handle;
+    direct_msg.args[FFA_CALL_ARGS_SHARE_MEM_HANDLE_MSW] = (uint32_t)(s->shared_mem_handle >> 32);
+    direct_msg.args[FFA_CALL_ARGS_SHARE_MEM_SIZE] = (uint64_t)s->shared_mem_required_size;
+
+    ioctl_status = ioctl(s->fd, SEND_SYNC_MSG, &direct_msg);
+
+    return ioctl_status;
+}
+
+static int unshare_mem_with_partition(struct ffarpc_caller *s) {
+
+    int ioctl_status;
+    struct msg_args direct_msg;
+    memset(&direct_msg, 0, sizeof(direct_msg));
+
+    direct_msg.dst_id = s->call_ep_id;
+    direct_msg.args[FFA_CALL_ARGS_OPCODE] = (uint64_t)FFA_CALL_OPCODE_UNSHARE_BUF;
+    direct_msg.args[FFA_CALL_ARGS_SHARE_MEM_HANDLE_LSW] = (uint32_t)s->shared_mem_handle;
+    direct_msg.args[FFA_CALL_ARGS_SHARE_MEM_HANDLE_MSW] = (uint32_t)(s->shared_mem_handle >> 32);
+
+    ioctl_status = ioctl(s->fd, SEND_SYNC_MSG, &direct_msg);
+
+    return ioctl_status;
+}
diff --git a/components/rpc/ffarpc/caller/linux/ffarpc_caller.h b/components/rpc/ffarpc/caller/linux/ffarpc_caller.h
new file mode 100644
index 0000000..69410ab
--- /dev/null
+++ b/components/rpc/ffarpc/caller/linux/ffarpc_caller.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef FFARPC_CALLER_H
+#define FFARPC_CALLER_H
+
+#include <rpc_caller.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * An RPC caller for Linux user-space clients.  Uses the FFA kernel driver
+ * to communicate with RPC endpoints deployed in partitions accessible
+ * via FFA.
+ */
+struct ffarpc_caller {
+	struct rpc_caller rpc_caller;
+	int fd;
+	const char *device_path;
+	uint16_t call_ep_id;
+	uint64_t shared_mem_handle;
+	size_t shared_mem_required_size;
+	uint8_t *req_buf;
+	uint8_t *resp_buf;
+	size_t req_len;
+	size_t resp_len;
+	bool is_call_transaction_in_progess;
+};
+
+struct rpc_caller *ffarpc_caller_init(struct ffarpc_caller *s, const char *device_path);
+void ffarpc_caller_deinit(struct ffarpc_caller *s);
+size_t ffarpc_caller_discover(const struct ffarpc_caller *s, const uint8_t *uuid,
+						uint16_t *partition_ids, size_t discover_limit);
+int ffarpc_caller_open(struct ffarpc_caller *s, uint16_t call_ep_id);
+int ffarpc_caller_close(struct ffarpc_caller *s);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FFARPC_CALLER_H */
diff --git a/components/rpc/ffarpc/caller/sp/component.cmake b/components/rpc/ffarpc/caller/sp/component.cmake
new file mode 100644
index 0000000..3bab840
--- /dev/null
+++ b/components/rpc/ffarpc/caller/sp/component.cmake
@@ -0,0 +1,14 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if (NOT DEFINED TGT)
+	message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+target_sources(${TGT} PRIVATE
+	"${CMAKE_CURRENT_LIST_DIR}/ffarpc_caller.c"
+	)
+
diff --git a/components/rpc/ffarpc/caller/sp/ffarpc_caller.c b/components/rpc/ffarpc/caller/sp/ffarpc_caller.c
new file mode 100644
index 0000000..9d98512
--- /dev/null
+++ b/components/rpc/ffarpc/caller/sp/ffarpc_caller.c
@@ -0,0 +1,287 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "ffarpc_caller.h"
+#include <components/rpc/ffarpc/endpoint/ffarpc_call_args.h>
+#include <components/rpc/ffarpc/endpoint/ffarpc_call_ops.h>
+#include <protocols/rpc/common/packed-c/status.h>
+#include <ffa_api.h>
+#include <sp_memory_management.h>
+#include <sp_rxtx.h>
+#include <trace.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+uint8_t shared_buffer[4096] __aligned(4096);
+extern uint16_t own_id; //TODO: replace this with nicer solution
+
+static rpc_call_handle call_begin(void *context, uint8_t **req_buf, size_t req_len)
+{
+	struct ffarpc_caller *this_context = (struct ffarpc_caller *)context;
+	rpc_call_handle handle = NULL;
+
+	if (req_buf == NULL) {
+		EMSG("call_begin(): invalid arguments");
+		goto out;
+	}
+
+	if (this_context->is_call_transaction_in_progess) {
+		EMSG("call_begin(): transaction already in progress");
+		goto out;
+	}
+
+	this_context->is_call_transaction_in_progess = true;
+	handle = this_context;
+
+	if (req_len > 0) {
+		this_context->req_buf = shared_buffer;
+		*req_buf = this_context->req_buf;
+		this_context->req_len = req_len;
+	} else {
+		*req_buf = NULL;
+		this_context->req_buf = NULL;
+		this_context->req_len = req_len;
+	}
+out:
+	return handle;
+}
+
+static rpc_status_t call_invoke(void *context, rpc_call_handle handle, uint32_t opcode,
+			    int *opstatus, uint8_t **resp_buf, size_t *resp_len)
+{
+	struct ffarpc_caller *this_context = (struct ffarpc_caller *)context;
+	ffa_result res = FFA_OK;
+	struct ffa_direct_msg req = { };
+	struct ffa_direct_msg resp = { };
+	rpc_status_t status = TS_RPC_ERROR_INTERNAL;
+
+	if (handle != this_context || opstatus == NULL ||
+	    resp_buf == NULL || resp_len == NULL) {
+		EMSG("call_invoke(): invalid arguments");
+		status = TS_RPC_ERROR_INVALID_PARAMETER;
+		goto out;
+	}
+
+	if (!this_context->is_call_transaction_in_progess) {
+		EMSG("call_invoke(): transaction was not started");
+		status = TS_RPC_ERROR_NOT_READY;
+		goto out;
+	}
+
+	req.destination_id = this_context->call_ep_id;
+	req.source_id = own_id;
+	req.args[FFA_CALL_ARGS_OPCODE] = opcode;
+	//TODO: downcast problem?
+	req.args[FFA_CALL_ARGS_REQ_DATA_LEN] = (uint32_t)this_context->req_len;
+
+	res = ffa_msg_send_direct_req(req.source_id, req.destination_id,
+				      req.args[0], req.args[1],
+				      req.args[2], req.args[3],
+				      req.args[4], &resp);
+
+	if (res != FFA_OK) {
+		EMSG("ffa_msg_send_direct_req(): error %"PRId32, res);
+		goto out;
+	}
+
+	this_context->resp_len = (size_t)resp.args[FFA_CALL_ARGS_RESP_DATA_LEN];
+	status = resp.args[FFA_CALL_ARGS_RESP_RPC_STATUS];
+	*opstatus = resp.args[FFA_CALL_ARGS_RESP_OP_STATUS];
+
+	if (this_context->resp_len > 0) {
+		this_context->resp_buf = shared_buffer;
+	} else {
+		this_context->resp_buf = NULL;
+	}
+
+	*resp_buf = this_context->resp_buf;
+	*resp_len = this_context->resp_len;
+out:
+	return status;
+}
+
+static void call_end(void *context, rpc_call_handle handle)
+{
+	struct ffarpc_caller *this_context = (struct ffarpc_caller *)context;
+
+	if (handle != this_context) {
+		EMSG("call_end(): invalid arguments");
+		return;
+	}
+
+	this_context->req_buf = NULL;
+	this_context->req_len = 0;
+	this_context->resp_buf = NULL;
+	this_context->resp_len = 0;
+	this_context->is_call_transaction_in_progess = false;
+}
+
+struct rpc_caller *ffarpc_caller_init(struct ffarpc_caller *s)
+{
+	struct rpc_caller *base = &s->rpc_caller;
+
+	base->context = s;
+	base->call_begin = call_begin;
+	base->call_invoke = call_invoke;
+	base->call_end = call_end;
+
+	s->call_ep_id = 0;
+	s->shared_mem_handle = 0;
+	s->shared_mem_required_size = sizeof(shared_buffer);
+	s->req_buf = NULL;
+	s->req_len = 0;
+	s->resp_buf = NULL;
+	s->resp_len = 0;
+	s->is_call_transaction_in_progess = false;
+
+	return base;
+}
+
+void ffarpc_caller_deinit(struct ffarpc_caller *s)
+{
+	s->rpc_caller.context = NULL;
+	s->rpc_caller.call_begin = NULL;
+	s->rpc_caller.call_invoke = NULL;
+	s->rpc_caller.call_end = NULL;
+}
+
+uint32_t ffarpc_caller_discover(const uint8_t *uuid, uint16_t *sp_ids, uint32_t sp_max_cnt)
+{
+	ffa_result ffa_res;
+	sp_result sp_res;
+	const void *rx_buf_addr = NULL;
+	size_t rx_buf_size = 0;
+	uint32_t sp_cnt = 0;
+	uint32_t i;
+
+	if (uuid == NULL || sp_ids == NULL || sp_max_cnt == 0) {
+		EMSG("ffarpc_caller_discover(): invalid arguments");
+		goto out;
+	}
+
+	//TODO: not sure if this cast is acceptable
+	ffa_res = ffa_partition_info_get((struct ffa_uuid *)uuid, &sp_cnt);
+	if (ffa_res != FFA_OK) {
+		EMSG("ffa_partition_info_get(): error %"PRId32, ffa_res);
+		goto out;
+	}
+
+	sp_res = sp_rxtx_buffer_rx_get(&rx_buf_addr, &rx_buf_size);
+	if (sp_res != SP_RESULT_OK) {
+		EMSG("sp_rxtx_buffer_rx_get(): error %"PRId32, sp_res);
+		goto out;
+	}
+
+	const struct ffa_partition_information *partitions =
+		(const struct ffa_partition_information *)rx_buf_addr;
+
+	for (i = 0; i < sp_cnt && i < sp_max_cnt; i++) {
+		sp_ids[i] = partitions[i].partition_id;
+	}
+
+	ffa_res = ffa_rx_release();
+	if (ffa_res != FFA_OK) {
+		EMSG("ffa_rx_release(): error %"PRId32, ffa_res);
+		goto out;
+	}
+out:
+	return sp_cnt;
+}
+
+int ffarpc_caller_open(struct ffarpc_caller *s, uint16_t call_ep_id)
+{
+	//TODO: revise return type, error handling
+	ffa_result ffa_res;
+	struct ffa_direct_msg req = { };
+	struct ffa_direct_msg resp = { };
+
+	sp_result sp_res;
+	struct sp_memory_descriptor desc = { };
+	struct sp_memory_access_descriptor acc_desc = { };
+	struct sp_memory_region region = { };
+
+	uint64_t handle = 0;
+
+	desc.sender_id = own_id;
+	desc.memory_type = sp_memory_type_normal_memory;
+	desc.mem_region_attr.normal_memory.cacheability = sp_cacheability_write_back;
+	desc.mem_region_attr.normal_memory.shareability = sp_shareability_inner_shareable;
+
+	acc_desc.data_access = sp_data_access_read_write;
+	acc_desc.instruction_access = sp_instruction_access_not_executable;
+	acc_desc.receiver_id = call_ep_id;
+
+	region.address = shared_buffer;
+	region.page_count = 1;
+
+	sp_res = sp_memory_share(&desc, &acc_desc, 1, &region, 1, &handle);
+	if (sp_res != SP_RESULT_OK) {
+		EMSG("sp_memory_share(): error %"PRId32, sp_res);
+		return -1;
+	}
+
+	req.source_id = own_id;
+	req.destination_id = call_ep_id;
+	req.args[FFA_CALL_ARGS_OPCODE] = FFA_CALL_OPCODE_SHARE_BUF;
+	req.args[FFA_CALL_ARGS_SHARE_MEM_HANDLE_LSW] = (uint32_t)(handle & 0xffff);
+	req.args[FFA_CALL_ARGS_SHARE_MEM_HANDLE_MSW] = (uint32_t)(handle >> 32);
+	//TODO: downcast
+	req.args[FFA_CALL_ARGS_SHARE_MEM_SIZE] = (uint32_t)(s->shared_mem_required_size);
+
+	ffa_res = ffa_msg_send_direct_req(req.source_id, req.destination_id,
+					  req.args[0], req.args[1],
+					  req.args[2], req.args[3],
+					  req.args[4], &resp);
+	if (ffa_res != FFA_OK) {
+		EMSG("ffa_msg_send_direct_req(): error %"PRId32, ffa_res);
+		return -1;
+	}
+
+	s->call_ep_id = call_ep_id;
+	s->shared_mem_handle = handle;
+
+	return 0;
+}
+
+int ffarpc_caller_close(struct ffarpc_caller *s)
+{
+	//TODO: revise return type, error handling
+	ffa_result ffa_res;
+	struct ffa_direct_msg req = { 0 };
+	struct ffa_direct_msg resp = { 0 };
+
+	sp_result sp_res;
+	uint32_t handle_lo, handle_hi;
+
+	handle_lo = (uint32_t)(s->shared_mem_handle & UINT32_MAX);
+	handle_hi = (uint32_t)(s->shared_mem_handle >> 32);
+
+	req.source_id = own_id;
+	req.destination_id = s->call_ep_id;
+	req.args[FFA_CALL_ARGS_OPCODE] = FFA_CALL_OPCODE_UNSHARE_BUF;
+	req.args[FFA_CALL_ARGS_SHARE_MEM_HANDLE_LSW] = handle_lo;
+	req.args[FFA_CALL_ARGS_SHARE_MEM_HANDLE_MSW] = handle_hi;
+
+	ffa_res = ffa_msg_send_direct_req(req.source_id, req.destination_id,
+				      req.args[0], req.args[1],
+				      req.args[2], req.args[3],
+				      req.args[4], &resp);
+	if (ffa_res != FFA_OK) {
+		EMSG("ffa_msg_send_direct_req(): error %"PRId32, ffa_res);
+		return -1;
+	}
+
+	sp_res = sp_memory_reclaim(s->shared_mem_handle, 0);
+	if (sp_res != SP_RESULT_OK) {
+		EMSG("sp_memory_reclaim(): error %"PRId32, sp_res);
+		return -1;
+	}
+
+	s->call_ep_id = 0;
+	s->shared_mem_handle = 0;
+
+	return 0;
+}
diff --git a/components/rpc/ffarpc/caller/sp/ffarpc_caller.h b/components/rpc/ffarpc/caller/sp/ffarpc_caller.h
new file mode 100644
index 0000000..ba810e9
--- /dev/null
+++ b/components/rpc/ffarpc/caller/sp/ffarpc_caller.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef FFA_SERVICE_CALLER_H
+#define FFA_SERVICE_CALLER_H
+
+#include <rpc_caller.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ffarpc_caller {
+	struct rpc_caller rpc_caller;
+	uint16_t call_ep_id;
+	uint64_t shared_mem_handle;
+	size_t shared_mem_required_size;
+	uint8_t *req_buf;
+	uint8_t *resp_buf;
+	size_t req_len;
+	size_t resp_len;
+	bool is_call_transaction_in_progess;
+};
+
+struct rpc_caller *ffarpc_caller_init(struct ffarpc_caller *s);
+void ffarpc_caller_deinit(struct ffarpc_caller *s);
+uint32_t ffarpc_caller_discover(const uint8_t *uuid, uint16_t *sp_ids, uint32_t sp_max_cnt);
+int ffarpc_caller_open(struct ffarpc_caller *s, uint16_t call_ep_id);
+int ffarpc_caller_close(struct ffarpc_caller *s);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FFA_SERVICE_CALLER_H */
diff --git a/components/rpc/ffarpc/endpoint/component.cmake b/components/rpc/ffarpc/endpoint/component.cmake
new file mode 100644
index 0000000..9e43ac5
--- /dev/null
+++ b/components/rpc/ffarpc/endpoint/component.cmake
@@ -0,0 +1,14 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if (NOT DEFINED TGT)
+	message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+target_sources(${TGT} PRIVATE
+	"${CMAKE_CURRENT_LIST_DIR}/ffarpc_call_ep.c"
+	)
+
diff --git a/components/rpc/ffarpc/endpoint/ffarpc_call_args.h b/components/rpc/ffarpc/endpoint/ffarpc_call_args.h
new file mode 100644
index 0000000..402e4f5
--- /dev/null
+++ b/components/rpc/ffarpc/endpoint/ffarpc_call_args.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef FFA_CALL_ARGS_H
+#define FFA_CALL_ARGS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Defines convention for use of FFA direct call arguments by
+ * the SP service framework. This header file is used by the
+ * normal world RPC caller and the RPC listener in the SP.
+ */
+
+/* Common req & resp arg offests into msg_args structure */
+#define FFA_CALL_ARGS_OPCODE			    (0)
+
+/* Req arg offsets */
+#define FFA_CALL_ARGS_REQ_DATA_LEN		    (1)
+
+/* Resp arg offsets */
+#define FFA_CALL_ARGS_RESP_DATA_LEN		    (1)
+#define FFA_CALL_ARGS_RESP_RPC_STATUS	    (2)
+#define FFA_CALL_ARGS_RESP_OP_STATUS		(3)
+
+/* Share/unshare offsets */
+#define FFA_CALL_ARGS_SHARE_MEM_HANDLE_LSW	(1)
+#define FFA_CALL_ARGS_SHARE_MEM_HANDLE_MSW	(2)
+#define FFA_CALL_ARGS_SHARE_MEM_SIZE		(3)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FFA_CALL_ARGS_H */
diff --git a/components/rpc/ffarpc/endpoint/ffarpc_call_ep.c b/components/rpc/ffarpc/endpoint/ffarpc_call_ep.c
new file mode 100644
index 0000000..bb40cf3
--- /dev/null
+++ b/components/rpc/ffarpc/endpoint/ffarpc_call_ep.c
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "ffarpc_call_args.h"
+#include "ffarpc_call_ep.h"
+#include "ffarpc_call_ops.h"
+#include <ffa_api.h>
+#include <sp_memory_management.h>
+#include <protocols/rpc/common/packed-c/status.h>
+#include <trace.h>
+#include <stddef.h>
+
+/* TODO: remove this when own ID will be available in libsp */
+extern uint16_t own_id;
+
+static void set_resp_args(uint32_t *resp_args, uint32_t opcode, uint32_t data_len,
+			  rpc_status_t rpc_status, uint32_t opstatus)
+{
+	resp_args[FFA_CALL_ARGS_OPCODE] = opcode;
+	resp_args[FFA_CALL_ARGS_RESP_DATA_LEN] = data_len;
+	resp_args[FFA_CALL_ARGS_RESP_RPC_STATUS] = rpc_status;
+	resp_args[FFA_CALL_ARGS_RESP_OP_STATUS] = opstatus;
+}
+
+static void set_mgmt_resp_args(uint32_t *resp_args, uint32_t opcode,
+			       rpc_status_t rpc_status)
+{
+	/*
+	 * Sets arguments for responses that originate from the ffa_call_ep
+	 * rather than from a higher layer service. These responses are not
+	 * associated with a shared buffer for any additional message payload.
+	 */
+	set_resp_args(resp_args, opcode, 0, rpc_status, 0);
+}
+
+static void init_shmem_buf(struct ffa_call_ep *call_ep, uint16_t source_id,
+			   const uint32_t *req_args, uint32_t *resp_args)
+{
+	sp_result sp_res = SP_RESULT_INTERNAL_ERROR;
+	struct sp_memory_descriptor desc = { };
+	struct sp_memory_access_descriptor acc_desc = { };
+	struct sp_memory_region region = { };
+	uint32_t in_region_count = 1;
+	uint32_t out_region_count = 1;
+	uint64_t handle = 0;
+	rpc_status_t rpc_status = TS_RPC_ERROR_INTERNAL;
+
+	desc.sender_id = source_id;
+	desc.memory_type = sp_memory_type_not_specified;
+	desc.flags.transaction_type = sp_memory_transaction_type_share;
+	acc_desc.receiver_id = own_id;
+	acc_desc.data_access = sp_data_access_read_write;
+	handle = req_args[FFA_CALL_ARGS_SHARE_MEM_HANDLE_MSW];
+	handle = (handle << 32) | req_args[FFA_CALL_ARGS_SHARE_MEM_HANDLE_LSW];
+
+	sp_res = sp_memory_retrieve(&desc, &acc_desc, &region, in_region_count,
+				    &out_region_count, handle);
+
+	if (sp_res == SP_RESULT_OK) {
+		call_ep->shmem_buf = region.address;
+		call_ep->shmem_buf_handle = handle;
+		call_ep->shmem_buf_size = (size_t)req_args[FFA_CALL_ARGS_SHARE_MEM_SIZE];
+		rpc_status = TS_RPC_CALL_ACCEPTED;
+	} else {
+		EMSG("memory retrieve error: %d", sp_res);
+	}
+
+	set_mgmt_resp_args(resp_args, req_args[FFA_CALL_ARGS_OPCODE], rpc_status);
+}
+
+static void deinit_shmem_buf(struct ffa_call_ep *call_ep, const uint32_t *req_args,
+			     uint32_t *resp_args)
+{
+	sp_result sp_res = SP_RESULT_INTERNAL_ERROR;
+	rpc_status_t rpc_status = TS_RPC_ERROR_INTERNAL;
+	uint64_t handle = call_ep->shmem_buf_handle;
+	uint16_t endpoints[1] = { own_id };
+	uint32_t endpoint_cnt = 1;
+	struct sp_memory_transaction_flags flags = {
+		.zero_memory = false,
+		.operation_time_slicing = false,
+	};
+
+	sp_res = sp_memory_relinquish(handle, endpoints, endpoint_cnt, &flags);
+	if (sp_res == SP_RESULT_OK) {
+		call_ep->shmem_buf = NULL;
+		call_ep->shmem_buf_handle = 0;
+		call_ep->shmem_buf_size = 0;
+		rpc_status = TS_RPC_CALL_ACCEPTED;
+	} else {
+		EMSG("memory relinquish error: %d", sp_res);
+	}
+
+	set_mgmt_resp_args(resp_args, req_args[FFA_CALL_ARGS_OPCODE], rpc_status);
+}
+
+static void handle_service_msg(struct ffa_call_ep *call_ep, uint16_t source_id,
+			       const uint32_t *req_args, uint32_t *resp_args)
+{
+	rpc_status_t rpc_status;
+	struct call_req call_req;
+
+	call_req.caller_id = source_id;
+	call_req.opcode = req_args[FFA_CALL_ARGS_OPCODE];
+
+	call_req.req_buf.data = call_ep->shmem_buf;
+	call_req.req_buf.data_len = req_args[FFA_CALL_ARGS_REQ_DATA_LEN];
+	call_req.req_buf.size = call_ep->shmem_buf_size;
+
+	call_req.resp_buf.data = call_ep->shmem_buf;
+	call_req.resp_buf.data_len = 0;
+	call_req.resp_buf.size = call_ep->shmem_buf_size;
+
+	rpc_status = call_ep_receive(call_ep->call_ep, &call_req);
+
+	set_resp_args(resp_args,
+		      call_req.opcode,
+		      call_req.resp_buf.data_len,
+		      rpc_status,
+		      call_req.opstatus);
+}
+
+static void handle_mgmt_msg(struct ffa_call_ep *call_ep, uint16_t source_id,
+			    const uint32_t *req_args, uint32_t *resp_args)
+{
+	uint32_t opcode = req_args[FFA_CALL_ARGS_OPCODE];
+
+	/*
+	 * TODO: shouldn't this be used to keep track of multiple
+	 * shared buffers for different endpoints?
+	 */
+	(void)source_id;
+
+	switch (opcode) {
+	case FFA_CALL_OPCODE_SHARE_BUF:
+		init_shmem_buf(call_ep, source_id, req_args, resp_args);
+		break;
+	case FFA_CALL_OPCODE_UNSHARE_BUF:
+		deinit_shmem_buf(call_ep, req_args, resp_args);
+		break;
+	default:
+		set_mgmt_resp_args(resp_args, opcode, TS_RPC_ERROR_INVALID_OPCODE);
+		break;
+	}
+}
+
+void ffa_call_ep_init(struct ffa_call_ep *ffa_call_ep, struct call_ep *call_ep)
+{
+	ffa_call_ep->call_ep = call_ep;
+	ffa_call_ep->shmem_buf_handle = 0;
+	ffa_call_ep->shmem_buf_size = 0;
+	ffa_call_ep->shmem_buf = NULL;
+}
+
+void ffa_call_ep_receive(struct ffa_call_ep *call_ep,
+			 const struct ffa_direct_msg *req_msg,
+			 struct ffa_direct_msg *resp_msg)
+{
+	const uint32_t *req_args = req_msg->args;
+	uint32_t *resp_args = resp_msg->args;
+
+	uint16_t source_id = req_msg->source_id;
+	uint32_t opcode = req_args[FFA_CALL_ARGS_OPCODE];
+
+	if (FFA_CALL_OPCODE_IS_MGMT(opcode)) {
+		/* It's an RPC layer management request */
+		handle_mgmt_msg(call_ep, source_id, req_args, resp_args);
+	} else {
+		/*
+		 * Assume anything else is a service request. Service requests
+		 * rely on a buffer being shared from the requesting client.
+		 * If it hasn't been set-up, fail the request.
+		 */
+		if (call_ep->shmem_buf)
+			handle_service_msg(call_ep, source_id, req_args, resp_args);
+		else
+			set_mgmt_resp_args(resp_args, opcode, TS_RPC_ERROR_NOT_READY);
+	}
+}
diff --git a/components/rpc/ffarpc/endpoint/ffarpc_call_ep.h b/components/rpc/ffarpc/endpoint/ffarpc_call_ep.h
new file mode 100644
index 0000000..b4bcdd6
--- /dev/null
+++ b/components/rpc/ffarpc/endpoint/ffarpc_call_ep.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef FFA_CALL_EP_H
+#define FFA_CALL_EP_H
+
+#include <ffa_api.h>
+#include <components/rpc/common/endpoint/call_ep.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ffa_call_ep {
+	struct call_ep *call_ep;
+	unsigned long shmem_buf_handle;
+	volatile uint8_t *shmem_buf;
+	size_t shmem_buf_size;
+ };
+
+void ffa_call_ep_init(struct ffa_call_ep *ffa_call_ep, struct call_ep *call_ep);
+void ffa_call_ep_receive(struct ffa_call_ep *call_ep,
+			 const struct ffa_direct_msg *req_msg,
+			 struct ffa_direct_msg *resp_msg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FFA_CALL_EP_H */
diff --git a/components/rpc/ffarpc/endpoint/ffarpc_call_ops.h b/components/rpc/ffarpc/endpoint/ffarpc_call_ops.h
new file mode 100644
index 0000000..3f14fc7
--- /dev/null
+++ b/components/rpc/ffarpc/endpoint/ffarpc_call_ops.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef FFA_CALL_OPS_H
+#define FFA_CALL_OPS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Common opcodes used by the FFA based RPC layer for management operations */
+#define FFA_CALL_OPCODE_BASE		(0x10)
+#define FFA_CALL_OPCODE_SHARE_BUF	(FFA_CALL_OPCODE_BASE + 0)
+#define FFA_CALL_OPCODE_UNSHARE_BUF	(FFA_CALL_OPCODE_BASE + 1)
+#define FFA_CALL_OPCODE_LIMIT		(FFA_CALL_OPCODE_BASE + 2)
+
+#define FFA_CALL_OPCODE_IS_MGMT(opcode) \
+	((opcode >= FFA_CALL_OPCODE_BASE) && (opcode < FFA_CALL_OPCODE_LIMIT))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FFA_CALL_OPS_H */
diff --git a/external/ffa_tool/buf_common.h b/external/ffa_tool/buf_common.h
new file mode 100644
index 0000000..326bd93
--- /dev/null
+++ b/external/ffa_tool/buf_common.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef __BUF_COMMON_H
+#define __BUF_COMMON_H
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#include <uapi/asm-generic/ioctl.h>
+#else
+#include <stdint.h>
+#include <sys/ioctl.h>
+#endif
+
+struct buf_descr {
+	uint8_t *buf;
+	size_t length;
+};
+
+struct msg_args {
+	uint16_t dst_id;
+	uint32_t args[5];
+};
+
+struct endpoint_id {
+	uint32_t uuid_0;
+	uint32_t uuid_1;
+	uint32_t uuid_2;
+	uint32_t uuid_3;
+	uint16_t id;
+};
+
+#define IOCTL_TYPE	0xf0
+
+#define GET_PART_ID	_IOWR(IOCTL_TYPE, 0x00, struct endpoint_id)
+#define SEND_SYNC_MSG	_IOWR(IOCTL_TYPE, 0x01, struct msg_args)
+
+#define SHARE_INIT	_IOR(IOCTL_TYPE, 0x10, unsigned long)
+#define SHARE_DEINIT	_IO(IOCTL_TYPE, 0x11)
+#define SHARE_READ	_IOW(IOCTL_TYPE, 0x12, struct buf_descr)
+#define SHARE_WRITE	_IOW(IOCTL_TYPE, 0x13, struct buf_descr)
+
+#endif /* __BUF_COMMON_H */