All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jens Wiklander <jens.wiklander@linaro.org>
To: linux-kernel@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org,
	op-tee@lists.trustedfirmware.org
Cc: Sumit Garg <sumit.garg@linaro.org>,
	Sudeep Holla <sudeep.holla@arm.com>,
	Marc Bonnici <marc.bonnici@arm.com>,
	Jerome Forissier <jerome@forissier.org>,
	sughosh.ganu@linaro.org,
	Jens Wiklander <jens.wiklander@linaro.org>
Subject: [PATCH v5 5/5] optee: add FF-A support
Date: Tue, 31 Aug 2021 09:24:12 +0200	[thread overview]
Message-ID: <20210831072412.887565-6-jens.wiklander@linaro.org> (raw)
In-Reply-To: <20210831072412.887565-1-jens.wiklander@linaro.org>

Adds support for using FF-A [1] as transport to the OP-TEE driver.

Introduces struct optee_msg_param_fmem which carries all information
needed when OP-TEE is calling FFA_MEM_RETRIEVE_REQ to get the shared
memory reference mapped by the hypervisor in S-EL2. Register usage is
also updated to include the information needed.

The FF-A part of this driver is enabled if CONFIG_ARM_FFA_TRANSPORT is
enabled.

[1] https://developer.arm.com/documentation/den0077/latest
Acked-by: Sumit Garg <sumit.garg@linaro.org>
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
 drivers/tee/optee/Makefile        |   3 +-
 drivers/tee/optee/call.c          |  13 +-
 drivers/tee/optee/core.c          |  16 +-
 drivers/tee/optee/ffa_abi.c       | 907 ++++++++++++++++++++++++++++++
 drivers/tee/optee/optee_ffa.h     | 153 +++++
 drivers/tee/optee/optee_msg.h     |  27 +-
 drivers/tee/optee/optee_private.h |  43 +-
 7 files changed, 1148 insertions(+), 14 deletions(-)
 create mode 100644 drivers/tee/optee/ffa_abi.c
 create mode 100644 drivers/tee/optee/optee_ffa.h

diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile
index d4e4776d2dec..dbfd83d3c4ae 100644
--- a/drivers/tee/optee/Makefile
+++ b/drivers/tee/optee/Makefile
@@ -7,7 +7,8 @@ optee-objs += supp.o
 optee-objs += device.o
 
 optee-smc-abi-y = smc_abi.o
-optee-objs += $(optee-smc-abi-y)
+optee-ffa-abi-$(CONFIG_ARM_FFA_TRANSPORT) = ffa_abi.o
+optee-objs += $(optee-smc-abi-y) $(optee-ffa-abi-y)
 
 # for tracing framework to find optee_trace.h
 CFLAGS_smc_abi.o := -I$(src)
diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c
index 9ff4f0812825..b25cc1fac945 100644
--- a/drivers/tee/optee/call.c
+++ b/drivers/tee/optee/call.c
@@ -107,11 +107,20 @@ static struct optee_session *find_session(struct optee_context_data *ctxdata,
 struct tee_shm *optee_get_msg_arg(struct tee_context *ctx, size_t num_params,
 				  struct optee_msg_arg **msg_arg)
 {
+	struct optee *optee = tee_get_drvdata(ctx->teedev);
+	size_t sz = OPTEE_MSG_GET_ARG_SIZE(num_params);
 	struct tee_shm *shm;
 	struct optee_msg_arg *ma;
 
-	shm = tee_shm_alloc(ctx, OPTEE_MSG_GET_ARG_SIZE(num_params),
-			    TEE_SHM_MAPPED | TEE_SHM_PRIV);
+	/*
+	 * rpc_arg_count is set to the number of allocated parameters in
+	 * the RPC argument struct if a second MSG arg struct is expected.
+	 * The second arg struct will then be used for RPC.
+	 */
+	if (optee->rpc_arg_count)
+		sz += OPTEE_MSG_GET_ARG_SIZE(optee->rpc_arg_count);
+
+	shm = tee_shm_alloc(ctx, sz, TEE_SHM_MAPPED | TEE_SHM_PRIV);
 	if (IS_ERR(shm))
 		return shm;
 
diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
index 9e3b6cdbe371..44bd67b7b0eb 100644
--- a/drivers/tee/optee/core.c
+++ b/drivers/tee/optee/core.c
@@ -169,6 +169,9 @@ void optee_remove_common(struct optee *optee)
 	mutex_destroy(&optee->call_queue.mutex);
 }
 
+static int smc_abi_rc;
+static int ffa_abi_rc;
+
 static int optee_core_init(void)
 {
 	/*
@@ -181,13 +184,22 @@ static int optee_core_init(void)
 	if (is_kdump_kernel())
 		return -ENODEV;
 
-	return optee_smc_abi_register();
+	smc_abi_rc = optee_smc_abi_register();
+	ffa_abi_rc = optee_ffa_abi_register();
+
+	/* If both failed there's no point with this module */
+	if (smc_abi_rc && ffa_abi_rc)
+		return smc_abi_rc;
+	return 0;
 }
 module_init(optee_core_init);
 
 static void optee_core_exit(void)
 {
-	optee_smc_abi_unregister();
+	if (!smc_abi_rc)
+		optee_smc_abi_unregister();
+	if (!ffa_abi_rc)
+		optee_ffa_abi_unregister();
 }
 module_exit(optee_core_exit);
 
diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
new file mode 100644
index 000000000000..ef7ee7f04e8c
--- /dev/null
+++ b/drivers/tee/optee/ffa_abi.c
@@ -0,0 +1,907 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021, Linaro Limited
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/arm_ffa.h>
+#include <linux/errno.h>
+#include <linux/scatterlist.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/tee_drv.h>
+#include <linux/types.h>
+#include "optee_private.h"
+#include "optee_ffa.h"
+#include "optee_rpc_cmd.h"
+
+/*
+ * This file implement the FF-A ABI used when communicating with secure world
+ * OP-TEE OS via FF-A.
+ * This file is divided into the following sections:
+ * 1. Maintain a hash table for lookup of a global FF-A memory handle
+ * 2. Convert between struct tee_param and struct optee_msg_param
+ * 3. Low level support functions to register shared memory in secure world
+ * 4. Dynamic shared memory pool based on alloc_pages()
+ * 5. Do a normal scheduled call into secure world
+ * 6. Driver initialization.
+ */
+
+/*
+ * 1. Maintain a hash table for lookup of a global FF-A memory handle
+ *
+ * FF-A assigns a global memory handle for each piece shared memory.
+ * This handle is then used when communicating with secure world.
+ *
+ * Main functions are optee_shm_add_ffa_handle() and optee_shm_rem_ffa_handle()
+ */
+struct shm_rhash {
+	struct tee_shm *shm;
+	u64 global_id;
+	struct rhash_head linkage;
+};
+
+static void rh_free_fn(void *ptr, void *arg)
+{
+	kfree(ptr);
+}
+
+static const struct rhashtable_params shm_rhash_params = {
+	.head_offset = offsetof(struct shm_rhash, linkage),
+	.key_len     = sizeof(u64),
+	.key_offset  = offsetof(struct shm_rhash, global_id),
+	.automatic_shrinking = true,
+};
+
+static struct tee_shm *optee_shm_from_ffa_handle(struct optee *optee,
+						 u64 global_id)
+{
+	struct tee_shm *shm = NULL;
+	struct shm_rhash *r;
+
+	mutex_lock(&optee->ffa.mutex);
+	r = rhashtable_lookup_fast(&optee->ffa.global_ids, &global_id,
+				   shm_rhash_params);
+	if (r)
+		shm = r->shm;
+	mutex_unlock(&optee->ffa.mutex);
+
+	return shm;
+}
+
+static int optee_shm_add_ffa_handle(struct optee *optee, struct tee_shm *shm,
+				    u64 global_id)
+{
+	struct shm_rhash *r;
+	int rc;
+
+	r = kmalloc(sizeof(*r), GFP_KERNEL);
+	if (!r)
+		return -ENOMEM;
+	r->shm = shm;
+	r->global_id = global_id;
+
+	mutex_lock(&optee->ffa.mutex);
+	rc = rhashtable_lookup_insert_fast(&optee->ffa.global_ids, &r->linkage,
+					   shm_rhash_params);
+	mutex_unlock(&optee->ffa.mutex);
+
+	if (rc)
+		kfree(r);
+
+	return rc;
+}
+
+static int optee_shm_rem_ffa_handle(struct optee *optee, u64 global_id)
+{
+	struct shm_rhash *r;
+	int rc = -ENOENT;
+
+	mutex_lock(&optee->ffa.mutex);
+	r = rhashtable_lookup_fast(&optee->ffa.global_ids, &global_id,
+				   shm_rhash_params);
+	if (r)
+		rc = rhashtable_remove_fast(&optee->ffa.global_ids,
+					    &r->linkage, shm_rhash_params);
+	mutex_unlock(&optee->ffa.mutex);
+
+	if (!rc)
+		kfree(r);
+
+	return rc;
+}
+
+/*
+ * 2. Convert between struct tee_param and struct optee_msg_param
+ *
+ * optee_ffa_from_msg_param() and optee_ffa_to_msg_param() are the main
+ * functions.
+ */
+
+static void from_msg_param_ffa_mem(struct optee *optee, struct tee_param *p,
+				   u32 attr, const struct optee_msg_param *mp)
+{
+	struct tee_shm *shm = NULL;
+	u64 offs_high = 0;
+	u64 offs_low = 0;
+
+	p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT +
+		  attr - OPTEE_MSG_ATTR_TYPE_FMEM_INPUT;
+	p->u.memref.size = mp->u.fmem.size;
+
+	if (mp->u.fmem.global_id != OPTEE_MSG_FMEM_INVALID_GLOBAL_ID)
+		shm = optee_shm_from_ffa_handle(optee, mp->u.fmem.global_id);
+	p->u.memref.shm = shm;
+
+	if (shm) {
+		offs_low = mp->u.fmem.offs_low;
+		offs_high = mp->u.fmem.offs_high;
+	}
+	p->u.memref.shm_offs = offs_low | offs_high << 32;
+}
+
+/**
+ * optee_ffa_from_msg_param() - convert from OPTEE_MSG parameters to
+ *				struct tee_param
+ * @optee:	main service struct
+ * @params:	subsystem internal parameter representation
+ * @num_params:	number of elements in the parameter arrays
+ * @msg_params:	OPTEE_MSG parameters
+ *
+ * Returns 0 on success or <0 on failure
+ */
+static int optee_ffa_from_msg_param(struct optee *optee,
+				    struct tee_param *params, size_t num_params,
+				    const struct optee_msg_param *msg_params)
+{
+	size_t n;
+
+	for (n = 0; n < num_params; n++) {
+		struct tee_param *p = params + n;
+		const struct optee_msg_param *mp = msg_params + n;
+		u32 attr = mp->attr & OPTEE_MSG_ATTR_TYPE_MASK;
+
+		switch (attr) {
+		case OPTEE_MSG_ATTR_TYPE_NONE:
+			p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
+			memset(&p->u, 0, sizeof(p->u));
+			break;
+		case OPTEE_MSG_ATTR_TYPE_VALUE_INPUT:
+		case OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT:
+		case OPTEE_MSG_ATTR_TYPE_VALUE_INOUT:
+			optee_from_msg_param_value(p, attr, mp);
+			break;
+		case OPTEE_MSG_ATTR_TYPE_FMEM_INPUT:
+		case OPTEE_MSG_ATTR_TYPE_FMEM_OUTPUT:
+		case OPTEE_MSG_ATTR_TYPE_FMEM_INOUT:
+			from_msg_param_ffa_mem(optee, p, attr, mp);
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int to_msg_param_ffa_mem(struct optee_msg_param *mp,
+				const struct tee_param *p)
+{
+	struct tee_shm *shm = p->u.memref.shm;
+
+	mp->attr = OPTEE_MSG_ATTR_TYPE_FMEM_INPUT + p->attr -
+		   TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
+
+	if (shm) {
+		u64 shm_offs = p->u.memref.shm_offs;
+
+		mp->u.fmem.internal_offs = shm->offset;
+
+		mp->u.fmem.offs_low = shm_offs;
+		mp->u.fmem.offs_high = shm_offs >> 32;
+		/* Check that the entire offset could be stored. */
+		if (mp->u.fmem.offs_high != shm_offs >> 32)
+			return -EINVAL;
+
+		mp->u.fmem.global_id = shm->sec_world_id;
+	} else {
+		memset(&mp->u, 0, sizeof(mp->u));
+		mp->u.fmem.global_id = OPTEE_MSG_FMEM_INVALID_GLOBAL_ID;
+	}
+	mp->u.fmem.size = p->u.memref.size;
+
+	return 0;
+}
+
+/**
+ * optee_ffa_to_msg_param() - convert from struct tee_params to OPTEE_MSG
+ *			      parameters
+ * @optee:	main service struct
+ * @msg_params:	OPTEE_MSG parameters
+ * @num_params:	number of elements in the parameter arrays
+ * @params:	subsystem itnernal parameter representation
+ * Returns 0 on success or <0 on failure
+ */
+static int optee_ffa_to_msg_param(struct optee *optee,
+				  struct optee_msg_param *msg_params,
+				  size_t num_params,
+				  const struct tee_param *params)
+{
+	size_t n;
+
+	for (n = 0; n < num_params; n++) {
+		const struct tee_param *p = params + n;
+		struct optee_msg_param *mp = msg_params + n;
+
+		switch (p->attr) {
+		case TEE_IOCTL_PARAM_ATTR_TYPE_NONE:
+			mp->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
+			memset(&mp->u, 0, sizeof(mp->u));
+			break;
+		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT:
+		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT:
+		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT:
+			optee_to_msg_param_value(mp, p);
+			break;
+		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT:
+		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
+		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT:
+			if (to_msg_param_ffa_mem(mp, p))
+				return -EINVAL;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * 3. Low level support functions to register shared memory in secure world
+ *
+ * Functions to register and unregister shared memory both for normal
+ * clients and for tee-supplicant.
+ */
+
+static int optee_ffa_shm_register(struct tee_context *ctx, struct tee_shm *shm,
+				  struct page **pages, size_t num_pages,
+				  unsigned long start)
+{
+	struct optee *optee = tee_get_drvdata(ctx->teedev);
+	const struct ffa_dev_ops *ffa_ops = optee->ffa.ffa_ops;
+	struct ffa_device *ffa_dev = optee->ffa.ffa_dev;
+	struct ffa_mem_region_attributes mem_attr = {
+		.receiver = ffa_dev->vm_id,
+		.attrs = FFA_MEM_RW,
+	};
+	struct ffa_mem_ops_args args = {
+		.use_txbuf = true,
+		.attrs = &mem_attr,
+		.nattrs = 1,
+	};
+	struct sg_table sgt;
+	int rc;
+
+	rc = optee_check_mem_type(start, num_pages);
+	if (rc)
+		return rc;
+
+	rc = sg_alloc_table_from_pages(&sgt, pages, num_pages, 0,
+				       num_pages * PAGE_SIZE, GFP_KERNEL);
+	if (rc)
+		return rc;
+	args.sg = sgt.sgl;
+	rc = ffa_ops->memory_share(ffa_dev, &args);
+	sg_free_table(&sgt);
+	if (rc)
+		return rc;
+
+	rc = optee_shm_add_ffa_handle(optee, shm, args.g_handle);
+	if (rc) {
+		ffa_ops->memory_reclaim(args.g_handle, 0);
+		return rc;
+	}
+
+	shm->sec_world_id = args.g_handle;
+
+	return 0;
+}
+
+static int optee_ffa_shm_unregister(struct tee_context *ctx,
+				    struct tee_shm *shm)
+{
+	struct optee *optee = tee_get_drvdata(ctx->teedev);
+	const struct ffa_dev_ops *ffa_ops = optee->ffa.ffa_ops;
+	struct ffa_device *ffa_dev = optee->ffa.ffa_dev;
+	u64 global_handle = shm->sec_world_id;
+	struct ffa_send_direct_data data = {
+		.data0 = OPTEE_FFA_UNREGISTER_SHM,
+		.data1 = (u32)global_handle,
+		.data2 = (u32)(global_handle >> 32)
+	};
+	int rc;
+
+	optee_shm_rem_ffa_handle(optee, global_handle);
+	shm->sec_world_id = 0;
+
+	rc = ffa_ops->sync_send_receive(ffa_dev, &data);
+	if (rc)
+		pr_err("Unregister SHM id 0x%llx rc %d\n", global_handle, rc);
+
+	rc = ffa_ops->memory_reclaim(global_handle, 0);
+	if (rc)
+		pr_err("mem_reclain: 0x%llx %d", global_handle, rc);
+
+	return rc;
+}
+
+static int optee_ffa_shm_unregister_supp(struct tee_context *ctx,
+					 struct tee_shm *shm)
+{
+	struct optee *optee = tee_get_drvdata(ctx->teedev);
+	const struct ffa_dev_ops *ffa_ops = optee->ffa.ffa_ops;
+	u64 global_handle = shm->sec_world_id;
+	int rc;
+
+	/*
+	 * We're skipping the OPTEE_FFA_YIELDING_CALL_UNREGISTER_SHM call
+	 * since this is OP-TEE freeing via RPC so it has already retired
+	 * this ID.
+	 */
+
+	optee_shm_rem_ffa_handle(optee, global_handle);
+	rc = ffa_ops->memory_reclaim(global_handle, 0);
+	if (rc)
+		pr_err("mem_reclain: 0x%llx %d", global_handle, rc);
+
+	shm->sec_world_id = 0;
+
+	return rc;
+}
+
+/*
+ * 4. Dynamic shared memory pool based on alloc_pages()
+ *
+ * Implements an OP-TEE specific shared memory pool.
+ * The main function is optee_ffa_shm_pool_alloc_pages().
+ */
+
+static int pool_ffa_op_alloc(struct tee_shm_pool_mgr *poolm,
+			     struct tee_shm *shm, size_t size)
+{
+	return optee_pool_op_alloc_helper(poolm, shm, size,
+					  optee_ffa_shm_register);
+}
+
+static void pool_ffa_op_free(struct tee_shm_pool_mgr *poolm,
+			     struct tee_shm *shm)
+{
+	optee_ffa_shm_unregister(shm->ctx, shm);
+	free_pages((unsigned long)shm->kaddr, get_order(shm->size));
+	shm->kaddr = NULL;
+}
+
+static void pool_ffa_op_destroy_poolmgr(struct tee_shm_pool_mgr *poolm)
+{
+	kfree(poolm);
+}
+
+static const struct tee_shm_pool_mgr_ops pool_ffa_ops = {
+	.alloc = pool_ffa_op_alloc,
+	.free = pool_ffa_op_free,
+	.destroy_poolmgr = pool_ffa_op_destroy_poolmgr,
+};
+
+/**
+ * optee_ffa_shm_pool_alloc_pages() - create page-based allocator pool
+ *
+ * This pool is used with OP-TEE over FF-A. In this case command buffers
+ * and such are allocated from kernel's own memory.
+ */
+static struct tee_shm_pool_mgr *optee_ffa_shm_pool_alloc_pages(void)
+{
+	struct tee_shm_pool_mgr *mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
+
+	if (!mgr)
+		return ERR_PTR(-ENOMEM);
+
+	mgr->ops = &pool_ffa_ops;
+
+	return mgr;
+}
+
+/*
+ * 5. Do a normal scheduled call into secure world
+ *
+ * The function optee_ffa_do_call_with_arg() performs a normal scheduled
+ * call into secure world. During this call may normal world request help
+ * from normal world using RPCs, Remote Procedure Calls. This includes
+ * delivery of non-secure interrupts to for instance allow rescheduling of
+ * the current task.
+ */
+
+static void handle_ffa_rpc_func_cmd_shm_alloc(struct tee_context *ctx,
+					      struct optee_msg_arg *arg)
+{
+	struct tee_shm *shm;
+
+	if (arg->num_params != 1 ||
+	    arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) {
+		arg->ret = TEEC_ERROR_BAD_PARAMETERS;
+		return;
+	}
+
+	switch (arg->params[0].u.value.a) {
+	case OPTEE_RPC_SHM_TYPE_APPL:
+		shm = optee_rpc_cmd_alloc_suppl(ctx, arg->params[0].u.value.b);
+		break;
+	case OPTEE_RPC_SHM_TYPE_KERNEL:
+		shm = tee_shm_alloc(ctx, arg->params[0].u.value.b,
+				    TEE_SHM_MAPPED | TEE_SHM_PRIV);
+		break;
+	default:
+		arg->ret = TEEC_ERROR_BAD_PARAMETERS;
+		return;
+	}
+
+	if (IS_ERR(shm)) {
+		arg->ret = TEEC_ERROR_OUT_OF_MEMORY;
+		return;
+	}
+
+	arg->params[0] = (struct optee_msg_param){
+		.attr = OPTEE_MSG_ATTR_TYPE_FMEM_OUTPUT,
+		.u.fmem.size = tee_shm_get_size(shm),
+		.u.fmem.global_id = shm->sec_world_id,
+		.u.fmem.internal_offs = shm->offset,
+	};
+
+	arg->ret = TEEC_SUCCESS;
+}
+
+static void handle_ffa_rpc_func_cmd_shm_free(struct tee_context *ctx,
+					     struct optee *optee,
+					     struct optee_msg_arg *arg)
+{
+	struct tee_shm *shm;
+
+	if (arg->num_params != 1 ||
+	    arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT)
+		goto err_bad_param;
+
+	shm = optee_shm_from_ffa_handle(optee, arg->params[0].u.value.b);
+	if (!shm)
+		goto err_bad_param;
+	switch (arg->params[0].u.value.a) {
+	case OPTEE_RPC_SHM_TYPE_APPL:
+		optee_rpc_cmd_free_suppl(ctx, shm);
+		break;
+	case OPTEE_RPC_SHM_TYPE_KERNEL:
+		tee_shm_free(shm);
+		break;
+	default:
+		goto err_bad_param;
+	}
+	arg->ret = TEEC_SUCCESS;
+	return;
+
+err_bad_param:
+	arg->ret = TEEC_ERROR_BAD_PARAMETERS;
+}
+
+static void handle_ffa_rpc_func_cmd(struct tee_context *ctx,
+				    struct optee_msg_arg *arg)
+{
+	struct optee *optee = tee_get_drvdata(ctx->teedev);
+
+	arg->ret_origin = TEEC_ORIGIN_COMMS;
+	switch (arg->cmd) {
+	case OPTEE_RPC_CMD_SHM_ALLOC:
+		handle_ffa_rpc_func_cmd_shm_alloc(ctx, arg);
+		break;
+	case OPTEE_RPC_CMD_SHM_FREE:
+		handle_ffa_rpc_func_cmd_shm_free(ctx, optee, arg);
+		break;
+	default:
+		optee_rpc_cmd(ctx, optee, arg);
+	}
+}
+
+static void optee_handle_ffa_rpc(struct tee_context *ctx, u32 cmd,
+				 struct optee_msg_arg *arg)
+{
+	switch (cmd) {
+	case OPTEE_FFA_YIELDING_CALL_RETURN_RPC_CMD:
+		handle_ffa_rpc_func_cmd(ctx, arg);
+		break;
+	case OPTEE_FFA_YIELDING_CALL_RETURN_INTERRUPT:
+		/* Interrupt delivered by now */
+		break;
+	default:
+		pr_warn("Unknown RPC func 0x%x\n", cmd);
+		break;
+	}
+}
+
+static int optee_ffa_yielding_call(struct tee_context *ctx,
+				   struct ffa_send_direct_data *data,
+				   struct optee_msg_arg *rpc_arg)
+{
+	struct optee *optee = tee_get_drvdata(ctx->teedev);
+	const struct ffa_dev_ops *ffa_ops = optee->ffa.ffa_ops;
+	struct ffa_device *ffa_dev = optee->ffa.ffa_dev;
+	struct optee_call_waiter w;
+	u32 cmd = data->data0;
+	u32 w4 = data->data1;
+	u32 w5 = data->data2;
+	u32 w6 = data->data3;
+	int rc;
+
+	/* Initialize waiter */
+	optee_cq_wait_init(&optee->call_queue, &w);
+	while (true) {
+		rc = ffa_ops->sync_send_receive(ffa_dev, data);
+		if (rc)
+			goto done;
+
+		switch ((int)data->data0) {
+		case TEEC_SUCCESS:
+			break;
+		case TEEC_ERROR_BUSY:
+			if (cmd == OPTEE_FFA_YIELDING_CALL_RESUME) {
+				rc = -EIO;
+				goto done;
+			}
+
+			/*
+			 * Out of threads in secure world, wait for a thread
+			 * become available.
+			 */
+			optee_cq_wait_for_completion(&optee->call_queue, &w);
+			data->data0 = cmd;
+			data->data1 = w4;
+			data->data2 = w5;
+			data->data3 = w6;
+			continue;
+		default:
+			rc = -EIO;
+			goto done;
+		}
+
+		if (data->data1 == OPTEE_FFA_YIELDING_CALL_RETURN_DONE)
+			goto done;
+
+		/*
+		 * OP-TEE has returned with a RPC request.
+		 *
+		 * Note that data->data4 (passed in register w7) is already
+		 * filled in by ffa_ops->sync_send_receive() returning
+		 * above.
+		 */
+		cond_resched();
+		optee_handle_ffa_rpc(ctx, data->data1, rpc_arg);
+		cmd = OPTEE_FFA_YIELDING_CALL_RESUME;
+		data->data0 = cmd;
+		data->data1 = 0;
+		data->data2 = 0;
+		data->data3 = 0;
+	}
+done:
+	/*
+	 * We're done with our thread in secure world, if there's any
+	 * thread waiters wake up one.
+	 */
+	optee_cq_wait_final(&optee->call_queue, &w);
+
+	return rc;
+}
+
+/**
+ * optee_ffa_do_call_with_arg() - Do a FF-A call to enter OP-TEE in secure world
+ * @ctx:	calling context
+ * @shm:	shared memory holding the message to pass to secure world
+ *
+ * Does a FF-A call to OP-TEE in secure world and handles eventual resulting
+ * Remote Procedure Calls (RPC) from OP-TEE.
+ *
+ * Returns return code from FF-A, 0 is OK
+ */
+
+static int optee_ffa_do_call_with_arg(struct tee_context *ctx,
+				      struct tee_shm *shm)
+{
+	struct ffa_send_direct_data data = {
+		.data0 = OPTEE_FFA_YIELDING_CALL_WITH_ARG,
+		.data1 = (u32)shm->sec_world_id,
+		.data2 = (u32)(shm->sec_world_id >> 32),
+		.data3 = shm->offset,
+	};
+	struct optee_msg_arg *arg = tee_shm_get_va(shm, 0);
+	unsigned int rpc_arg_offs = OPTEE_MSG_GET_ARG_SIZE(arg->num_params);
+	struct optee_msg_arg *rpc_arg = tee_shm_get_va(shm, rpc_arg_offs);
+
+	return optee_ffa_yielding_call(ctx, &data, rpc_arg);
+}
+
+/*
+ * 6. Driver initialization
+ *
+ * During driver inititialization is the OP-TEE Secure Partition is probed
+ * to find out which features it supports so the driver can be initialized
+ * with a matching configuration.
+ */
+
+static bool optee_ffa_api_is_compatbile(struct ffa_device *ffa_dev,
+					const struct ffa_dev_ops *ops)
+{
+	struct ffa_send_direct_data data = { OPTEE_FFA_GET_API_VERSION };
+	int rc;
+
+	ops->mode_32bit_set(ffa_dev);
+
+	rc = ops->sync_send_receive(ffa_dev, &data);
+	if (rc) {
+		pr_err("Unexpected error %d\n", rc);
+		return false;
+	}
+	if (data.data0 != OPTEE_FFA_VERSION_MAJOR ||
+	    data.data1 < OPTEE_FFA_VERSION_MINOR) {
+		pr_err("Incompatible OP-TEE API version %lu.%lu",
+		       data.data0, data.data1);
+		return false;
+	}
+
+	data = (struct ffa_send_direct_data){ OPTEE_FFA_GET_OS_VERSION };
+	rc = ops->sync_send_receive(ffa_dev, &data);
+	if (rc) {
+		pr_err("Unexpected error %d\n", rc);
+		return false;
+	}
+	if (data.data2)
+		pr_info("revision %lu.%lu (%08lx)",
+			data.data0, data.data1, data.data2);
+	else
+		pr_info("revision %lu.%lu", data.data0, data.data1);
+
+	return true;
+}
+
+static bool optee_ffa_exchange_caps(struct ffa_device *ffa_dev,
+				    const struct ffa_dev_ops *ops,
+				    unsigned int *rpc_arg_count)
+{
+	struct ffa_send_direct_data data = { OPTEE_FFA_EXCHANGE_CAPABILITIES };
+	int rc;
+
+	rc = ops->sync_send_receive(ffa_dev, &data);
+	if (rc) {
+		pr_err("Unexpected error %d", rc);
+		return false;
+	}
+	if (data.data0) {
+		pr_err("Unexpected exchange error %lu", data.data0);
+		return false;
+	}
+
+	*rpc_arg_count = (u8)data.data1;
+
+	return true;
+}
+
+static struct tee_shm_pool *optee_ffa_config_dyn_shm(void)
+{
+	struct tee_shm_pool_mgr *priv_mgr;
+	struct tee_shm_pool_mgr *dmabuf_mgr;
+	void *rc;
+
+	rc = optee_ffa_shm_pool_alloc_pages();
+	if (IS_ERR(rc))
+		return rc;
+	priv_mgr = rc;
+
+	rc = optee_ffa_shm_pool_alloc_pages();
+	if (IS_ERR(rc)) {
+		tee_shm_pool_mgr_destroy(priv_mgr);
+		return rc;
+	}
+	dmabuf_mgr = rc;
+
+	rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr);
+	if (IS_ERR(rc)) {
+		tee_shm_pool_mgr_destroy(priv_mgr);
+		tee_shm_pool_mgr_destroy(dmabuf_mgr);
+	}
+
+	return rc;
+}
+
+static void optee_ffa_get_version(struct tee_device *teedev,
+				  struct tee_ioctl_version_data *vers)
+{
+	struct tee_ioctl_version_data v = {
+		.impl_id = TEE_IMPL_ID_OPTEE,
+		.impl_caps = TEE_OPTEE_CAP_TZ,
+		.gen_caps = TEE_GEN_CAP_GP | TEE_GEN_CAP_REG_MEM |
+			    TEE_GEN_CAP_MEMREF_NULL,
+	};
+
+	*vers = v;
+}
+
+static int optee_ffa_open(struct tee_context *ctx)
+{
+	return optee_open(ctx, true);
+}
+
+static const struct tee_driver_ops optee_ffa_clnt_ops = {
+	.get_version = optee_ffa_get_version,
+	.open = optee_ffa_open,
+	.release = optee_release,
+	.open_session = optee_open_session,
+	.close_session = optee_close_session,
+	.invoke_func = optee_invoke_func,
+	.cancel_req = optee_cancel_req,
+	.shm_register = optee_ffa_shm_register,
+	.shm_unregister = optee_ffa_shm_unregister,
+};
+
+static const struct tee_desc optee_ffa_clnt_desc = {
+	.name = DRIVER_NAME "-ffa-clnt",
+	.ops = &optee_ffa_clnt_ops,
+	.owner = THIS_MODULE,
+};
+
+static const struct tee_driver_ops optee_ffa_supp_ops = {
+	.get_version = optee_ffa_get_version,
+	.open = optee_ffa_open,
+	.release = optee_release_supp,
+	.supp_recv = optee_supp_recv,
+	.supp_send = optee_supp_send,
+	.shm_register = optee_ffa_shm_register, /* same as for clnt ops */
+	.shm_unregister = optee_ffa_shm_unregister_supp,
+};
+
+static const struct tee_desc optee_ffa_supp_desc = {
+	.name = DRIVER_NAME "-ffa-supp",
+	.ops = &optee_ffa_supp_ops,
+	.owner = THIS_MODULE,
+	.flags = TEE_DESC_PRIVILEGED,
+};
+
+static const struct optee_ops optee_ffa_ops = {
+	.do_call_with_arg = optee_ffa_do_call_with_arg,
+	.to_msg_param = optee_ffa_to_msg_param,
+	.from_msg_param = optee_ffa_from_msg_param,
+};
+
+static void optee_ffa_remove(struct ffa_device *ffa_dev)
+{
+	struct optee *optee = ffa_dev->dev.driver_data;
+
+	optee_remove_common(optee);
+
+	mutex_destroy(&optee->ffa.mutex);
+	rhashtable_free_and_destroy(&optee->ffa.global_ids, rh_free_fn, NULL);
+
+	kfree(optee);
+}
+
+static int optee_ffa_probe(struct ffa_device *ffa_dev)
+{
+	const struct ffa_dev_ops *ffa_ops;
+	unsigned int rpc_arg_count;
+	struct tee_device *teedev;
+	struct optee *optee;
+	int rc;
+
+	ffa_ops = ffa_dev_ops_get(ffa_dev);
+	if (!ffa_ops) {
+		pr_warn("failed \"method\" init: ffa\n");
+		return -ENOENT;
+	}
+
+	if (!optee_ffa_api_is_compatbile(ffa_dev, ffa_ops))
+		return -EINVAL;
+
+	if (!optee_ffa_exchange_caps(ffa_dev, ffa_ops, &rpc_arg_count))
+		return -EINVAL;
+
+	optee = kzalloc(sizeof(*optee), GFP_KERNEL);
+	if (!optee) {
+		rc = -ENOMEM;
+		goto err;
+	}
+	optee->pool = optee_ffa_config_dyn_shm();
+	if (IS_ERR(optee->pool)) {
+		rc = PTR_ERR(optee->pool);
+		optee->pool = NULL;
+		goto err;
+	}
+
+	optee->ops = &optee_ffa_ops;
+	optee->ffa.ffa_dev = ffa_dev;
+	optee->ffa.ffa_ops = ffa_ops;
+	optee->rpc_arg_count = rpc_arg_count;
+
+	teedev = tee_device_alloc(&optee_ffa_clnt_desc, NULL, optee->pool,
+				  optee);
+	if (IS_ERR(teedev)) {
+		rc = PTR_ERR(teedev);
+		goto err;
+	}
+	optee->teedev = teedev;
+
+	teedev = tee_device_alloc(&optee_ffa_supp_desc, NULL, optee->pool,
+				  optee);
+	if (IS_ERR(teedev)) {
+		rc = PTR_ERR(teedev);
+		goto err;
+	}
+	optee->supp_teedev = teedev;
+
+	rc = tee_device_register(optee->teedev);
+	if (rc)
+		goto err;
+
+	rc = tee_device_register(optee->supp_teedev);
+	if (rc)
+		goto err;
+
+	rc = rhashtable_init(&optee->ffa.global_ids, &shm_rhash_params);
+	if (rc)
+		goto err;
+	mutex_init(&optee->ffa.mutex);
+	mutex_init(&optee->call_queue.mutex);
+	INIT_LIST_HEAD(&optee->call_queue.waiters);
+	optee_wait_queue_init(&optee->wait_queue);
+	optee_supp_init(&optee->supp);
+	ffa_dev_set_drvdata(ffa_dev, optee);
+
+	rc = optee_enumerate_devices(PTA_CMD_GET_DEVICES);
+	if (rc) {
+		optee_ffa_remove(ffa_dev);
+		return rc;
+	}
+
+	pr_info("initialized driver\n");
+	return 0;
+err:
+	/*
+	 * tee_device_unregister() is safe to call even if the
+	 * devices hasn't been registered with
+	 * tee_device_register() yet.
+	 */
+	tee_device_unregister(optee->supp_teedev);
+	tee_device_unregister(optee->teedev);
+	if (optee->pool)
+		tee_shm_pool_free(optee->pool);
+	kfree(optee);
+	return rc;
+}
+
+static const struct ffa_device_id optee_ffa_device_id[] = {
+	/* 486178e0-e7f8-11e3-bc5e0002a5d5c51b */
+	{ UUID_INIT(0x486178e0, 0xe7f8, 0x11e3,
+		    0xbc, 0x5e, 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b) },
+	{}
+};
+
+static struct ffa_driver optee_ffa_driver = {
+	.name = "optee",
+	.probe = optee_ffa_probe,
+	.remove = optee_ffa_remove,
+	.id_table = optee_ffa_device_id,
+};
+
+int optee_ffa_abi_register(void)
+{
+	return ffa_register(&optee_ffa_driver);
+}
+
+void optee_ffa_abi_unregister(void)
+{
+	ffa_unregister(&optee_ffa_driver);
+}
diff --git a/drivers/tee/optee/optee_ffa.h b/drivers/tee/optee/optee_ffa.h
new file mode 100644
index 000000000000..ee3a03fc392c
--- /dev/null
+++ b/drivers/tee/optee/optee_ffa.h
@@ -0,0 +1,153 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (c) 2019-2021, Linaro Limited
+ */
+
+/*
+ * This file is exported by OP-TEE and is kept in sync between secure world
+ * and normal world drivers. We're using ARM FF-A 1.0 specification.
+ */
+
+#ifndef __OPTEE_FFA_H
+#define __OPTEE_FFA_H
+
+#include <linux/arm_ffa.h>
+
+/*
+ * Normal world sends requests with FFA_MSG_SEND_DIRECT_REQ and
+ * responses are returned with FFA_MSG_SEND_DIRECT_RESP for normal
+ * messages.
+ *
+ * All requests with FFA_MSG_SEND_DIRECT_REQ and FFA_MSG_SEND_DIRECT_RESP
+ * are using the AArch32 SMC calling convention with register usage as
+ * defined in FF-A specification:
+ * w0:    Function ID (0x8400006F or 0x84000070)
+ * w1:    Source/Destination IDs
+ * w2:    Reserved (MBZ)
+ * w3-w7: Implementation defined, free to be used below
+ */
+
+#define OPTEE_FFA_VERSION_MAJOR	1
+#define OPTEE_FFA_VERSION_MINOR	0
+
+#define OPTEE_FFA_BLOCKING_CALL(id)	(id)
+#define OPTEE_FFA_YIELDING_CALL_BIT	31
+#define OPTEE_FFA_YIELDING_CALL(id)	((id) | BIT(OPTEE_FFA_YIELDING_CALL_BIT))
+
+/*
+ * Returns the API version implemented, currently follows the FF-A version.
+ * Call register usage:
+ * w3:    Service ID, OPTEE_FFA_GET_API_VERSION
+ * w4-w7: Not used (MBZ)
+ *
+ * Return register usage:
+ * w3:    OPTEE_FFA_VERSION_MAJOR
+ * w4:    OPTEE_FFA_VERSION_MINOR
+ * w5-w7: Not used (MBZ)
+ */
+#define OPTEE_FFA_GET_API_VERSION	OPTEE_FFA_BLOCKING_CALL(0)
+
+/*
+ * Returns the revision of OP-TEE.
+ *
+ * Used by non-secure world to figure out which version of the Trusted OS
+ * is installed. Note that the returned revision is the revision of the
+ * Trusted OS, not of the API.
+ *
+ * Call register usage:
+ * w3:    Service ID, OPTEE_FFA_GET_OS_VERSION
+ * w4-w7: Unused (MBZ)
+ *
+ * Return register usage:
+ * w3:    CFG_OPTEE_REVISION_MAJOR
+ * w4:    CFG_OPTEE_REVISION_MINOR
+ * w5:    TEE_IMPL_GIT_SHA1 (or zero if not supported)
+ */
+#define OPTEE_FFA_GET_OS_VERSION	OPTEE_FFA_BLOCKING_CALL(1)
+
+/*
+ * Exchange capabilities between normal world and secure world.
+ *
+ * Currently there are no defined capabilities. When features are added new
+ * capabilities may be added.
+ *
+ * Call register usage:
+ * w3:    Service ID, OPTEE_FFA_EXCHANGE_CAPABILITIES
+ * w4-w7: Note used (MBZ)
+ *
+ * Return register usage:
+ * w3:    Error code, 0 on success
+ * w4:    Bit[7:0]:  Number of parameters needed for RPC to be supplied
+ *                   as the second MSG arg struct for
+ *                   OPTEE_FFA_YIELDING_CALL_WITH_ARG.
+ *        Bit[31:8]: Reserved (MBZ)
+ * w5-w7: Note used (MBZ)
+ */
+#define OPTEE_FFA_EXCHANGE_CAPABILITIES OPTEE_FFA_BLOCKING_CALL(2)
+
+/*
+ * Unregister shared memory
+ *
+ * Call register usage:
+ * w3:    Service ID, OPTEE_FFA_YIELDING_CALL_UNREGISTER_SHM
+ * w4:    Shared memory handle, lower bits
+ * w5:    Shared memory handle, higher bits
+ * w6-w7: Not used (MBZ)
+ *
+ * Return register usage:
+ * w3:    Error code, 0 on success
+ * w4-w7: Note used (MBZ)
+ */
+#define OPTEE_FFA_UNREGISTER_SHM	OPTEE_FFA_BLOCKING_CALL(3)
+
+/*
+ * Call with struct optee_msg_arg as argument in the supplied shared memory
+ * with a zero internal offset and normal cached memory attributes.
+ * Register usage:
+ * w3:    Service ID, OPTEE_FFA_YIELDING_CALL_WITH_ARG
+ * w4:    Lower 32 bits of a 64-bit Shared memory handle
+ * w5:    Upper 32 bits of a 64-bit Shared memory handle
+ * w6:    Offset into shared memory pointing to a struct optee_msg_arg
+ *	  right after the parameters of this struct (at offset
+ *	  OPTEE_MSG_GET_ARG_SIZE(num_params) follows a struct optee_msg_arg
+ *	  for RPC, this struct has reserved space for the number of RPC
+ *	  parameters as returned by OPTEE_FFA_EXCHANGE_CAPABILITIES.
+ * w7:    Not used (MBZ)
+ * Resume from RPC. Register usage:
+ * w3:    Service ID, OPTEE_FFA_YIELDING_CALL_RESUME
+ * w4-w6: Not used (MBZ)
+ * w7:    Resume info
+ *
+ * Normal return (yielding call is completed). Register usage:
+ * w3:    Error code, 0 on success
+ * w4:    OPTEE_FFA_YIELDING_CALL_RETURN_DONE
+ * w5-w7: Not used (MBZ)
+ *
+ * RPC interrupt return (RPC from secure world). Register usage:
+ * w3:    Error code == 0
+ * w4:    Any defined RPC code but OPTEE_FFA_YIELDING_CALL_RETURN_DONE
+ * w5-w6: Not used (MBZ)
+ * w7:    Resume info
+ *
+ * Possible error codes in register w3:
+ * 0:                       Success
+ * FFA_DENIED:              w4 isn't one of OPTEE_FFA_YIELDING_CALL_START
+ *                          OPTEE_FFA_YIELDING_CALL_RESUME
+ *
+ * Possible error codes for OPTEE_FFA_YIELDING_CALL_START,
+ * FFA_BUSY:               Number of OP-TEE OS threads exceeded,
+ *                         try again later
+ * FFA_DENIED:             RPC shared memory object not found
+ * FFA_INVALID_PARAMETER:  Bad shared memory handle or offset into the memory
+ *
+ * Possible error codes for OPTEE_FFA_YIELDING_CALL_RESUME
+ * FFA_INVALID_PARAMETER:  Bad resume info
+ */
+#define OPTEE_FFA_YIELDING_CALL_WITH_ARG	OPTEE_FFA_YIELDING_CALL(0)
+#define OPTEE_FFA_YIELDING_CALL_RESUME		OPTEE_FFA_YIELDING_CALL(1)
+
+#define OPTEE_FFA_YIELDING_CALL_RETURN_DONE		0
+#define OPTEE_FFA_YIELDING_CALL_RETURN_RPC_CMD		1
+#define OPTEE_FFA_YIELDING_CALL_RETURN_INTERRUPT	2
+
+#endif /*__OPTEE_FFA_H*/
diff --git a/drivers/tee/optee/optee_msg.h b/drivers/tee/optee/optee_msg.h
index e3d72d09c484..2422e185d400 100644
--- a/drivers/tee/optee/optee_msg.h
+++ b/drivers/tee/optee/optee_msg.h
@@ -28,6 +28,9 @@
 #define OPTEE_MSG_ATTR_TYPE_RMEM_INPUT		0x5
 #define OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT		0x6
 #define OPTEE_MSG_ATTR_TYPE_RMEM_INOUT		0x7
+#define OPTEE_MSG_ATTR_TYPE_FMEM_INPUT		OPTEE_MSG_ATTR_TYPE_RMEM_INPUT
+#define OPTEE_MSG_ATTR_TYPE_FMEM_OUTPUT		OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT
+#define OPTEE_MSG_ATTR_TYPE_FMEM_INOUT		OPTEE_MSG_ATTR_TYPE_RMEM_INOUT
 #define OPTEE_MSG_ATTR_TYPE_TMEM_INPUT		0x9
 #define OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT		0xa
 #define OPTEE_MSG_ATTR_TYPE_TMEM_INOUT		0xb
@@ -96,6 +99,8 @@
  */
 #define OPTEE_MSG_NONCONTIG_PAGE_SIZE		4096
 
+#define OPTEE_MSG_FMEM_INVALID_GLOBAL_ID	0xffffffffffffffff
+
 /**
  * struct optee_msg_param_tmem - temporary memory reference parameter
  * @buf_ptr:	Address of the buffer
@@ -127,6 +132,23 @@ struct optee_msg_param_rmem {
 	u64 shm_ref;
 };
 
+/**
+ * struct optee_msg_param_fmem - ffa memory reference parameter
+ * @offs_lower:	   Lower bits of offset into shared memory reference
+ * @offs_upper:	   Upper bits of offset into shared memory reference
+ * @internal_offs: Internal offset into the first page of shared memory
+ *		   reference
+ * @size:	   Size of the buffer
+ * @global_id:	   Global identifier of Shared memory
+ */
+struct optee_msg_param_fmem {
+	u32 offs_low;
+	u16 offs_high;
+	u16 internal_offs;
+	u64 size;
+	u64 global_id;
+};
+
 /**
  * struct optee_msg_param_value - opaque value parameter
  *
@@ -143,13 +165,15 @@ struct optee_msg_param_value {
  * @attr:	attributes
  * @tmem:	parameter by temporary memory reference
  * @rmem:	parameter by registered memory reference
+ * @fmem:	parameter by ffa registered memory reference
  * @value:	parameter by opaque value
  * @octets:	parameter by octet string
  *
  * @attr & OPTEE_MSG_ATTR_TYPE_MASK indicates if tmem, rmem or value is used in
  * the union. OPTEE_MSG_ATTR_TYPE_VALUE_* indicates value or octets,
  * OPTEE_MSG_ATTR_TYPE_TMEM_* indicates @tmem and
- * OPTEE_MSG_ATTR_TYPE_RMEM_* indicates @rmem,
+ * OPTEE_MSG_ATTR_TYPE_RMEM_* or the alias PTEE_MSG_ATTR_TYPE_FMEM_* indicates
+ * @rmem or @fmem depending on the conduit.
  * OPTEE_MSG_ATTR_TYPE_NONE indicates that none of the members are used.
  */
 struct optee_msg_param {
@@ -157,6 +181,7 @@ struct optee_msg_param {
 	union {
 		struct optee_msg_param_tmem tmem;
 		struct optee_msg_param_rmem rmem;
+		struct optee_msg_param_fmem fmem;
 		struct optee_msg_param_value value;
 		u8 octets[24];
 	} u;
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
index ca0213e330b5..2593742364da 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -7,6 +7,7 @@
 #define OPTEE_PRIVATE_H
 
 #include <linux/arm-smccc.h>
+#include <linux/rhashtable.h>
 #include <linux/semaphore.h>
 #include <linux/tee_drv.h>
 #include <linux/types.h>
@@ -22,6 +23,7 @@
 #define TEEC_ERROR_NOT_SUPPORTED	0xFFFF000A
 #define TEEC_ERROR_COMMUNICATION	0xFFFF000E
 #define TEEC_ERROR_OUT_OF_MEMORY	0xFFFF000C
+#define TEEC_ERROR_BUSY			0xFFFF000D
 #define TEEC_ERROR_SHORT_BUFFER		0xFFFF0010
 
 #define TEEC_ORIGIN_COMMS		0x00000002
@@ -73,19 +75,28 @@ struct optee_supp {
 	struct completion reqs_c;
 };
 
-/**
- * struct optee_smc - SMC ABI specifics
- * @invoke_fn:		function to issue smc or hvc
- * @memremaped_shm	virtual address of memory in shared memory pool
- * @sec_caps:		secure world capabilities defined by
- *			OPTEE_SMC_SEC_CAP_* in optee_smc.h
- */
 struct optee_smc {
 	optee_invoke_fn *invoke_fn;
 	void *memremaped_shm;
 	u32 sec_caps;
 };
 
+/**
+ * struct optee_ffa_data -  FFA communication struct
+ * @ffa_dev		FFA device, contains the destination id, the id of
+ *			OP-TEE in secure world
+ * @ffa_ops		FFA operations
+ * @mutex		Serializes access to @global_ids
+ * @global_ids		FF-A shared memory global handle translation
+ */
+struct optee_ffa {
+	struct ffa_device *ffa_dev;
+	const struct ffa_dev_ops *ffa_ops;
+	/* Serializes access to @global_ids */
+	struct mutex mutex;
+	struct rhashtable global_ids;
+};
+
 struct optee;
 
 /**
@@ -116,11 +127,13 @@ struct optee_ops {
  *			world
  * @teedev:		client device
  * @smc:		specific to SMC ABI
+ * @ffa:		specific to FF-A ABI
  * @call_queue:		queue of threads waiting to call @invoke_fn
  * @wait_queue:		queue of threads from secure world waiting for a
  *			secure world sync object
  * @supp:		supplicant synchronization struct for RPC to supplicant
  * @pool:		shared memory pool
+ * @rpc_arg_count:	If > 0 number of RPC parameters to make room for
  * @scan_bus_done	flag if device registation was already done.
  * @scan_bus_wq		workqueue to scan optee bus and register optee drivers
  * @scan_bus_work	workq to scan optee bus and register optee drivers
@@ -129,11 +142,17 @@ struct optee {
 	struct tee_device *supp_teedev;
 	struct tee_device *teedev;
 	const struct optee_ops *ops;
-	struct optee_smc smc;
+	union {
+		struct optee_smc smc;
+#ifdef CONFIG_ARM_FFA_TRANSPORT
+		struct optee_ffa ffa;
+#endif
+	};
 	struct optee_call_queue call_queue;
 	struct optee_wait_queue wait_queue;
 	struct optee_supp supp;
 	struct tee_shm_pool *pool;
+	unsigned int rpc_arg_count;
 	bool   scan_bus_done;
 	struct workqueue_struct *scan_bus_wq;
 	struct work_struct scan_bus_work;
@@ -266,4 +285,12 @@ static inline void reg_pair_from_64(u32 *reg0, u32 *reg1, u64 val)
 int optee_smc_abi_register(void);
 void optee_smc_abi_unregister(void);
 
+#ifdef CONFIG_ARM_FFA_TRANSPORT
+int optee_ffa_abi_register(void);
+void optee_ffa_abi_unregister(void);
+#else
+static inline int optee_ffa_abi_register(void) { return -EOPNOTSUPP; }
+static inline void optee_ffa_abi_unregister(void) {}
+#endif
+
 #endif /*OPTEE_PRIVATE_H*/
-- 
2.31.1


WARNING: multiple messages have this Message-ID (diff)
From: Jens Wiklander <jens.wiklander@linaro.org>
To: linux-kernel@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org,
	op-tee@lists.trustedfirmware.org
Cc: Sumit Garg <sumit.garg@linaro.org>,
	Sudeep Holla <sudeep.holla@arm.com>,
	Marc Bonnici <marc.bonnici@arm.com>,
	Jerome Forissier <jerome@forissier.org>,
	sughosh.ganu@linaro.org,
	Jens Wiklander <jens.wiklander@linaro.org>
Subject: [PATCH v5 5/5] optee: add FF-A support
Date: Tue, 31 Aug 2021 09:24:12 +0200	[thread overview]
Message-ID: <20210831072412.887565-6-jens.wiklander@linaro.org> (raw)
In-Reply-To: <20210831072412.887565-1-jens.wiklander@linaro.org>

Adds support for using FF-A [1] as transport to the OP-TEE driver.

Introduces struct optee_msg_param_fmem which carries all information
needed when OP-TEE is calling FFA_MEM_RETRIEVE_REQ to get the shared
memory reference mapped by the hypervisor in S-EL2. Register usage is
also updated to include the information needed.

The FF-A part of this driver is enabled if CONFIG_ARM_FFA_TRANSPORT is
enabled.

[1] https://developer.arm.com/documentation/den0077/latest
Acked-by: Sumit Garg <sumit.garg@linaro.org>
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
 drivers/tee/optee/Makefile        |   3 +-
 drivers/tee/optee/call.c          |  13 +-
 drivers/tee/optee/core.c          |  16 +-
 drivers/tee/optee/ffa_abi.c       | 907 ++++++++++++++++++++++++++++++
 drivers/tee/optee/optee_ffa.h     | 153 +++++
 drivers/tee/optee/optee_msg.h     |  27 +-
 drivers/tee/optee/optee_private.h |  43 +-
 7 files changed, 1148 insertions(+), 14 deletions(-)
 create mode 100644 drivers/tee/optee/ffa_abi.c
 create mode 100644 drivers/tee/optee/optee_ffa.h

diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile
index d4e4776d2dec..dbfd83d3c4ae 100644
--- a/drivers/tee/optee/Makefile
+++ b/drivers/tee/optee/Makefile
@@ -7,7 +7,8 @@ optee-objs += supp.o
 optee-objs += device.o
 
 optee-smc-abi-y = smc_abi.o
-optee-objs += $(optee-smc-abi-y)
+optee-ffa-abi-$(CONFIG_ARM_FFA_TRANSPORT) = ffa_abi.o
+optee-objs += $(optee-smc-abi-y) $(optee-ffa-abi-y)
 
 # for tracing framework to find optee_trace.h
 CFLAGS_smc_abi.o := -I$(src)
diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c
index 9ff4f0812825..b25cc1fac945 100644
--- a/drivers/tee/optee/call.c
+++ b/drivers/tee/optee/call.c
@@ -107,11 +107,20 @@ static struct optee_session *find_session(struct optee_context_data *ctxdata,
 struct tee_shm *optee_get_msg_arg(struct tee_context *ctx, size_t num_params,
 				  struct optee_msg_arg **msg_arg)
 {
+	struct optee *optee = tee_get_drvdata(ctx->teedev);
+	size_t sz = OPTEE_MSG_GET_ARG_SIZE(num_params);
 	struct tee_shm *shm;
 	struct optee_msg_arg *ma;
 
-	shm = tee_shm_alloc(ctx, OPTEE_MSG_GET_ARG_SIZE(num_params),
-			    TEE_SHM_MAPPED | TEE_SHM_PRIV);
+	/*
+	 * rpc_arg_count is set to the number of allocated parameters in
+	 * the RPC argument struct if a second MSG arg struct is expected.
+	 * The second arg struct will then be used for RPC.
+	 */
+	if (optee->rpc_arg_count)
+		sz += OPTEE_MSG_GET_ARG_SIZE(optee->rpc_arg_count);
+
+	shm = tee_shm_alloc(ctx, sz, TEE_SHM_MAPPED | TEE_SHM_PRIV);
 	if (IS_ERR(shm))
 		return shm;
 
diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
index 9e3b6cdbe371..44bd67b7b0eb 100644
--- a/drivers/tee/optee/core.c
+++ b/drivers/tee/optee/core.c
@@ -169,6 +169,9 @@ void optee_remove_common(struct optee *optee)
 	mutex_destroy(&optee->call_queue.mutex);
 }
 
+static int smc_abi_rc;
+static int ffa_abi_rc;
+
 static int optee_core_init(void)
 {
 	/*
@@ -181,13 +184,22 @@ static int optee_core_init(void)
 	if (is_kdump_kernel())
 		return -ENODEV;
 
-	return optee_smc_abi_register();
+	smc_abi_rc = optee_smc_abi_register();
+	ffa_abi_rc = optee_ffa_abi_register();
+
+	/* If both failed there's no point with this module */
+	if (smc_abi_rc && ffa_abi_rc)
+		return smc_abi_rc;
+	return 0;
 }
 module_init(optee_core_init);
 
 static void optee_core_exit(void)
 {
-	optee_smc_abi_unregister();
+	if (!smc_abi_rc)
+		optee_smc_abi_unregister();
+	if (!ffa_abi_rc)
+		optee_ffa_abi_unregister();
 }
 module_exit(optee_core_exit);
 
diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
new file mode 100644
index 000000000000..ef7ee7f04e8c
--- /dev/null
+++ b/drivers/tee/optee/ffa_abi.c
@@ -0,0 +1,907 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021, Linaro Limited
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/arm_ffa.h>
+#include <linux/errno.h>
+#include <linux/scatterlist.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/tee_drv.h>
+#include <linux/types.h>
+#include "optee_private.h"
+#include "optee_ffa.h"
+#include "optee_rpc_cmd.h"
+
+/*
+ * This file implement the FF-A ABI used when communicating with secure world
+ * OP-TEE OS via FF-A.
+ * This file is divided into the following sections:
+ * 1. Maintain a hash table for lookup of a global FF-A memory handle
+ * 2. Convert between struct tee_param and struct optee_msg_param
+ * 3. Low level support functions to register shared memory in secure world
+ * 4. Dynamic shared memory pool based on alloc_pages()
+ * 5. Do a normal scheduled call into secure world
+ * 6. Driver initialization.
+ */
+
+/*
+ * 1. Maintain a hash table for lookup of a global FF-A memory handle
+ *
+ * FF-A assigns a global memory handle for each piece shared memory.
+ * This handle is then used when communicating with secure world.
+ *
+ * Main functions are optee_shm_add_ffa_handle() and optee_shm_rem_ffa_handle()
+ */
+struct shm_rhash {
+	struct tee_shm *shm;
+	u64 global_id;
+	struct rhash_head linkage;
+};
+
+static void rh_free_fn(void *ptr, void *arg)
+{
+	kfree(ptr);
+}
+
+static const struct rhashtable_params shm_rhash_params = {
+	.head_offset = offsetof(struct shm_rhash, linkage),
+	.key_len     = sizeof(u64),
+	.key_offset  = offsetof(struct shm_rhash, global_id),
+	.automatic_shrinking = true,
+};
+
+static struct tee_shm *optee_shm_from_ffa_handle(struct optee *optee,
+						 u64 global_id)
+{
+	struct tee_shm *shm = NULL;
+	struct shm_rhash *r;
+
+	mutex_lock(&optee->ffa.mutex);
+	r = rhashtable_lookup_fast(&optee->ffa.global_ids, &global_id,
+				   shm_rhash_params);
+	if (r)
+		shm = r->shm;
+	mutex_unlock(&optee->ffa.mutex);
+
+	return shm;
+}
+
+static int optee_shm_add_ffa_handle(struct optee *optee, struct tee_shm *shm,
+				    u64 global_id)
+{
+	struct shm_rhash *r;
+	int rc;
+
+	r = kmalloc(sizeof(*r), GFP_KERNEL);
+	if (!r)
+		return -ENOMEM;
+	r->shm = shm;
+	r->global_id = global_id;
+
+	mutex_lock(&optee->ffa.mutex);
+	rc = rhashtable_lookup_insert_fast(&optee->ffa.global_ids, &r->linkage,
+					   shm_rhash_params);
+	mutex_unlock(&optee->ffa.mutex);
+
+	if (rc)
+		kfree(r);
+
+	return rc;
+}
+
+static int optee_shm_rem_ffa_handle(struct optee *optee, u64 global_id)
+{
+	struct shm_rhash *r;
+	int rc = -ENOENT;
+
+	mutex_lock(&optee->ffa.mutex);
+	r = rhashtable_lookup_fast(&optee->ffa.global_ids, &global_id,
+				   shm_rhash_params);
+	if (r)
+		rc = rhashtable_remove_fast(&optee->ffa.global_ids,
+					    &r->linkage, shm_rhash_params);
+	mutex_unlock(&optee->ffa.mutex);
+
+	if (!rc)
+		kfree(r);
+
+	return rc;
+}
+
+/*
+ * 2. Convert between struct tee_param and struct optee_msg_param
+ *
+ * optee_ffa_from_msg_param() and optee_ffa_to_msg_param() are the main
+ * functions.
+ */
+
+static void from_msg_param_ffa_mem(struct optee *optee, struct tee_param *p,
+				   u32 attr, const struct optee_msg_param *mp)
+{
+	struct tee_shm *shm = NULL;
+	u64 offs_high = 0;
+	u64 offs_low = 0;
+
+	p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT +
+		  attr - OPTEE_MSG_ATTR_TYPE_FMEM_INPUT;
+	p->u.memref.size = mp->u.fmem.size;
+
+	if (mp->u.fmem.global_id != OPTEE_MSG_FMEM_INVALID_GLOBAL_ID)
+		shm = optee_shm_from_ffa_handle(optee, mp->u.fmem.global_id);
+	p->u.memref.shm = shm;
+
+	if (shm) {
+		offs_low = mp->u.fmem.offs_low;
+		offs_high = mp->u.fmem.offs_high;
+	}
+	p->u.memref.shm_offs = offs_low | offs_high << 32;
+}
+
+/**
+ * optee_ffa_from_msg_param() - convert from OPTEE_MSG parameters to
+ *				struct tee_param
+ * @optee:	main service struct
+ * @params:	subsystem internal parameter representation
+ * @num_params:	number of elements in the parameter arrays
+ * @msg_params:	OPTEE_MSG parameters
+ *
+ * Returns 0 on success or <0 on failure
+ */
+static int optee_ffa_from_msg_param(struct optee *optee,
+				    struct tee_param *params, size_t num_params,
+				    const struct optee_msg_param *msg_params)
+{
+	size_t n;
+
+	for (n = 0; n < num_params; n++) {
+		struct tee_param *p = params + n;
+		const struct optee_msg_param *mp = msg_params + n;
+		u32 attr = mp->attr & OPTEE_MSG_ATTR_TYPE_MASK;
+
+		switch (attr) {
+		case OPTEE_MSG_ATTR_TYPE_NONE:
+			p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
+			memset(&p->u, 0, sizeof(p->u));
+			break;
+		case OPTEE_MSG_ATTR_TYPE_VALUE_INPUT:
+		case OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT:
+		case OPTEE_MSG_ATTR_TYPE_VALUE_INOUT:
+			optee_from_msg_param_value(p, attr, mp);
+			break;
+		case OPTEE_MSG_ATTR_TYPE_FMEM_INPUT:
+		case OPTEE_MSG_ATTR_TYPE_FMEM_OUTPUT:
+		case OPTEE_MSG_ATTR_TYPE_FMEM_INOUT:
+			from_msg_param_ffa_mem(optee, p, attr, mp);
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int to_msg_param_ffa_mem(struct optee_msg_param *mp,
+				const struct tee_param *p)
+{
+	struct tee_shm *shm = p->u.memref.shm;
+
+	mp->attr = OPTEE_MSG_ATTR_TYPE_FMEM_INPUT + p->attr -
+		   TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
+
+	if (shm) {
+		u64 shm_offs = p->u.memref.shm_offs;
+
+		mp->u.fmem.internal_offs = shm->offset;
+
+		mp->u.fmem.offs_low = shm_offs;
+		mp->u.fmem.offs_high = shm_offs >> 32;
+		/* Check that the entire offset could be stored. */
+		if (mp->u.fmem.offs_high != shm_offs >> 32)
+			return -EINVAL;
+
+		mp->u.fmem.global_id = shm->sec_world_id;
+	} else {
+		memset(&mp->u, 0, sizeof(mp->u));
+		mp->u.fmem.global_id = OPTEE_MSG_FMEM_INVALID_GLOBAL_ID;
+	}
+	mp->u.fmem.size = p->u.memref.size;
+
+	return 0;
+}
+
+/**
+ * optee_ffa_to_msg_param() - convert from struct tee_params to OPTEE_MSG
+ *			      parameters
+ * @optee:	main service struct
+ * @msg_params:	OPTEE_MSG parameters
+ * @num_params:	number of elements in the parameter arrays
+ * @params:	subsystem itnernal parameter representation
+ * Returns 0 on success or <0 on failure
+ */
+static int optee_ffa_to_msg_param(struct optee *optee,
+				  struct optee_msg_param *msg_params,
+				  size_t num_params,
+				  const struct tee_param *params)
+{
+	size_t n;
+
+	for (n = 0; n < num_params; n++) {
+		const struct tee_param *p = params + n;
+		struct optee_msg_param *mp = msg_params + n;
+
+		switch (p->attr) {
+		case TEE_IOCTL_PARAM_ATTR_TYPE_NONE:
+			mp->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
+			memset(&mp->u, 0, sizeof(mp->u));
+			break;
+		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT:
+		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT:
+		case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT:
+			optee_to_msg_param_value(mp, p);
+			break;
+		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT:
+		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
+		case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT:
+			if (to_msg_param_ffa_mem(mp, p))
+				return -EINVAL;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * 3. Low level support functions to register shared memory in secure world
+ *
+ * Functions to register and unregister shared memory both for normal
+ * clients and for tee-supplicant.
+ */
+
+static int optee_ffa_shm_register(struct tee_context *ctx, struct tee_shm *shm,
+				  struct page **pages, size_t num_pages,
+				  unsigned long start)
+{
+	struct optee *optee = tee_get_drvdata(ctx->teedev);
+	const struct ffa_dev_ops *ffa_ops = optee->ffa.ffa_ops;
+	struct ffa_device *ffa_dev = optee->ffa.ffa_dev;
+	struct ffa_mem_region_attributes mem_attr = {
+		.receiver = ffa_dev->vm_id,
+		.attrs = FFA_MEM_RW,
+	};
+	struct ffa_mem_ops_args args = {
+		.use_txbuf = true,
+		.attrs = &mem_attr,
+		.nattrs = 1,
+	};
+	struct sg_table sgt;
+	int rc;
+
+	rc = optee_check_mem_type(start, num_pages);
+	if (rc)
+		return rc;
+
+	rc = sg_alloc_table_from_pages(&sgt, pages, num_pages, 0,
+				       num_pages * PAGE_SIZE, GFP_KERNEL);
+	if (rc)
+		return rc;
+	args.sg = sgt.sgl;
+	rc = ffa_ops->memory_share(ffa_dev, &args);
+	sg_free_table(&sgt);
+	if (rc)
+		return rc;
+
+	rc = optee_shm_add_ffa_handle(optee, shm, args.g_handle);
+	if (rc) {
+		ffa_ops->memory_reclaim(args.g_handle, 0);
+		return rc;
+	}
+
+	shm->sec_world_id = args.g_handle;
+
+	return 0;
+}
+
+static int optee_ffa_shm_unregister(struct tee_context *ctx,
+				    struct tee_shm *shm)
+{
+	struct optee *optee = tee_get_drvdata(ctx->teedev);
+	const struct ffa_dev_ops *ffa_ops = optee->ffa.ffa_ops;
+	struct ffa_device *ffa_dev = optee->ffa.ffa_dev;
+	u64 global_handle = shm->sec_world_id;
+	struct ffa_send_direct_data data = {
+		.data0 = OPTEE_FFA_UNREGISTER_SHM,
+		.data1 = (u32)global_handle,
+		.data2 = (u32)(global_handle >> 32)
+	};
+	int rc;
+
+	optee_shm_rem_ffa_handle(optee, global_handle);
+	shm->sec_world_id = 0;
+
+	rc = ffa_ops->sync_send_receive(ffa_dev, &data);
+	if (rc)
+		pr_err("Unregister SHM id 0x%llx rc %d\n", global_handle, rc);
+
+	rc = ffa_ops->memory_reclaim(global_handle, 0);
+	if (rc)
+		pr_err("mem_reclain: 0x%llx %d", global_handle, rc);
+
+	return rc;
+}
+
+static int optee_ffa_shm_unregister_supp(struct tee_context *ctx,
+					 struct tee_shm *shm)
+{
+	struct optee *optee = tee_get_drvdata(ctx->teedev);
+	const struct ffa_dev_ops *ffa_ops = optee->ffa.ffa_ops;
+	u64 global_handle = shm->sec_world_id;
+	int rc;
+
+	/*
+	 * We're skipping the OPTEE_FFA_YIELDING_CALL_UNREGISTER_SHM call
+	 * since this is OP-TEE freeing via RPC so it has already retired
+	 * this ID.
+	 */
+
+	optee_shm_rem_ffa_handle(optee, global_handle);
+	rc = ffa_ops->memory_reclaim(global_handle, 0);
+	if (rc)
+		pr_err("mem_reclain: 0x%llx %d", global_handle, rc);
+
+	shm->sec_world_id = 0;
+
+	return rc;
+}
+
+/*
+ * 4. Dynamic shared memory pool based on alloc_pages()
+ *
+ * Implements an OP-TEE specific shared memory pool.
+ * The main function is optee_ffa_shm_pool_alloc_pages().
+ */
+
+static int pool_ffa_op_alloc(struct tee_shm_pool_mgr *poolm,
+			     struct tee_shm *shm, size_t size)
+{
+	return optee_pool_op_alloc_helper(poolm, shm, size,
+					  optee_ffa_shm_register);
+}
+
+static void pool_ffa_op_free(struct tee_shm_pool_mgr *poolm,
+			     struct tee_shm *shm)
+{
+	optee_ffa_shm_unregister(shm->ctx, shm);
+	free_pages((unsigned long)shm->kaddr, get_order(shm->size));
+	shm->kaddr = NULL;
+}
+
+static void pool_ffa_op_destroy_poolmgr(struct tee_shm_pool_mgr *poolm)
+{
+	kfree(poolm);
+}
+
+static const struct tee_shm_pool_mgr_ops pool_ffa_ops = {
+	.alloc = pool_ffa_op_alloc,
+	.free = pool_ffa_op_free,
+	.destroy_poolmgr = pool_ffa_op_destroy_poolmgr,
+};
+
+/**
+ * optee_ffa_shm_pool_alloc_pages() - create page-based allocator pool
+ *
+ * This pool is used with OP-TEE over FF-A. In this case command buffers
+ * and such are allocated from kernel's own memory.
+ */
+static struct tee_shm_pool_mgr *optee_ffa_shm_pool_alloc_pages(void)
+{
+	struct tee_shm_pool_mgr *mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
+
+	if (!mgr)
+		return ERR_PTR(-ENOMEM);
+
+	mgr->ops = &pool_ffa_ops;
+
+	return mgr;
+}
+
+/*
+ * 5. Do a normal scheduled call into secure world
+ *
+ * The function optee_ffa_do_call_with_arg() performs a normal scheduled
+ * call into secure world. During this call may normal world request help
+ * from normal world using RPCs, Remote Procedure Calls. This includes
+ * delivery of non-secure interrupts to for instance allow rescheduling of
+ * the current task.
+ */
+
+static void handle_ffa_rpc_func_cmd_shm_alloc(struct tee_context *ctx,
+					      struct optee_msg_arg *arg)
+{
+	struct tee_shm *shm;
+
+	if (arg->num_params != 1 ||
+	    arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) {
+		arg->ret = TEEC_ERROR_BAD_PARAMETERS;
+		return;
+	}
+
+	switch (arg->params[0].u.value.a) {
+	case OPTEE_RPC_SHM_TYPE_APPL:
+		shm = optee_rpc_cmd_alloc_suppl(ctx, arg->params[0].u.value.b);
+		break;
+	case OPTEE_RPC_SHM_TYPE_KERNEL:
+		shm = tee_shm_alloc(ctx, arg->params[0].u.value.b,
+				    TEE_SHM_MAPPED | TEE_SHM_PRIV);
+		break;
+	default:
+		arg->ret = TEEC_ERROR_BAD_PARAMETERS;
+		return;
+	}
+
+	if (IS_ERR(shm)) {
+		arg->ret = TEEC_ERROR_OUT_OF_MEMORY;
+		return;
+	}
+
+	arg->params[0] = (struct optee_msg_param){
+		.attr = OPTEE_MSG_ATTR_TYPE_FMEM_OUTPUT,
+		.u.fmem.size = tee_shm_get_size(shm),
+		.u.fmem.global_id = shm->sec_world_id,
+		.u.fmem.internal_offs = shm->offset,
+	};
+
+	arg->ret = TEEC_SUCCESS;
+}
+
+static void handle_ffa_rpc_func_cmd_shm_free(struct tee_context *ctx,
+					     struct optee *optee,
+					     struct optee_msg_arg *arg)
+{
+	struct tee_shm *shm;
+
+	if (arg->num_params != 1 ||
+	    arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT)
+		goto err_bad_param;
+
+	shm = optee_shm_from_ffa_handle(optee, arg->params[0].u.value.b);
+	if (!shm)
+		goto err_bad_param;
+	switch (arg->params[0].u.value.a) {
+	case OPTEE_RPC_SHM_TYPE_APPL:
+		optee_rpc_cmd_free_suppl(ctx, shm);
+		break;
+	case OPTEE_RPC_SHM_TYPE_KERNEL:
+		tee_shm_free(shm);
+		break;
+	default:
+		goto err_bad_param;
+	}
+	arg->ret = TEEC_SUCCESS;
+	return;
+
+err_bad_param:
+	arg->ret = TEEC_ERROR_BAD_PARAMETERS;
+}
+
+static void handle_ffa_rpc_func_cmd(struct tee_context *ctx,
+				    struct optee_msg_arg *arg)
+{
+	struct optee *optee = tee_get_drvdata(ctx->teedev);
+
+	arg->ret_origin = TEEC_ORIGIN_COMMS;
+	switch (arg->cmd) {
+	case OPTEE_RPC_CMD_SHM_ALLOC:
+		handle_ffa_rpc_func_cmd_shm_alloc(ctx, arg);
+		break;
+	case OPTEE_RPC_CMD_SHM_FREE:
+		handle_ffa_rpc_func_cmd_shm_free(ctx, optee, arg);
+		break;
+	default:
+		optee_rpc_cmd(ctx, optee, arg);
+	}
+}
+
+static void optee_handle_ffa_rpc(struct tee_context *ctx, u32 cmd,
+				 struct optee_msg_arg *arg)
+{
+	switch (cmd) {
+	case OPTEE_FFA_YIELDING_CALL_RETURN_RPC_CMD:
+		handle_ffa_rpc_func_cmd(ctx, arg);
+		break;
+	case OPTEE_FFA_YIELDING_CALL_RETURN_INTERRUPT:
+		/* Interrupt delivered by now */
+		break;
+	default:
+		pr_warn("Unknown RPC func 0x%x\n", cmd);
+		break;
+	}
+}
+
+static int optee_ffa_yielding_call(struct tee_context *ctx,
+				   struct ffa_send_direct_data *data,
+				   struct optee_msg_arg *rpc_arg)
+{
+	struct optee *optee = tee_get_drvdata(ctx->teedev);
+	const struct ffa_dev_ops *ffa_ops = optee->ffa.ffa_ops;
+	struct ffa_device *ffa_dev = optee->ffa.ffa_dev;
+	struct optee_call_waiter w;
+	u32 cmd = data->data0;
+	u32 w4 = data->data1;
+	u32 w5 = data->data2;
+	u32 w6 = data->data3;
+	int rc;
+
+	/* Initialize waiter */
+	optee_cq_wait_init(&optee->call_queue, &w);
+	while (true) {
+		rc = ffa_ops->sync_send_receive(ffa_dev, data);
+		if (rc)
+			goto done;
+
+		switch ((int)data->data0) {
+		case TEEC_SUCCESS:
+			break;
+		case TEEC_ERROR_BUSY:
+			if (cmd == OPTEE_FFA_YIELDING_CALL_RESUME) {
+				rc = -EIO;
+				goto done;
+			}
+
+			/*
+			 * Out of threads in secure world, wait for a thread
+			 * become available.
+			 */
+			optee_cq_wait_for_completion(&optee->call_queue, &w);
+			data->data0 = cmd;
+			data->data1 = w4;
+			data->data2 = w5;
+			data->data3 = w6;
+			continue;
+		default:
+			rc = -EIO;
+			goto done;
+		}
+
+		if (data->data1 == OPTEE_FFA_YIELDING_CALL_RETURN_DONE)
+			goto done;
+
+		/*
+		 * OP-TEE has returned with a RPC request.
+		 *
+		 * Note that data->data4 (passed in register w7) is already
+		 * filled in by ffa_ops->sync_send_receive() returning
+		 * above.
+		 */
+		cond_resched();
+		optee_handle_ffa_rpc(ctx, data->data1, rpc_arg);
+		cmd = OPTEE_FFA_YIELDING_CALL_RESUME;
+		data->data0 = cmd;
+		data->data1 = 0;
+		data->data2 = 0;
+		data->data3 = 0;
+	}
+done:
+	/*
+	 * We're done with our thread in secure world, if there's any
+	 * thread waiters wake up one.
+	 */
+	optee_cq_wait_final(&optee->call_queue, &w);
+
+	return rc;
+}
+
+/**
+ * optee_ffa_do_call_with_arg() - Do a FF-A call to enter OP-TEE in secure world
+ * @ctx:	calling context
+ * @shm:	shared memory holding the message to pass to secure world
+ *
+ * Does a FF-A call to OP-TEE in secure world and handles eventual resulting
+ * Remote Procedure Calls (RPC) from OP-TEE.
+ *
+ * Returns return code from FF-A, 0 is OK
+ */
+
+static int optee_ffa_do_call_with_arg(struct tee_context *ctx,
+				      struct tee_shm *shm)
+{
+	struct ffa_send_direct_data data = {
+		.data0 = OPTEE_FFA_YIELDING_CALL_WITH_ARG,
+		.data1 = (u32)shm->sec_world_id,
+		.data2 = (u32)(shm->sec_world_id >> 32),
+		.data3 = shm->offset,
+	};
+	struct optee_msg_arg *arg = tee_shm_get_va(shm, 0);
+	unsigned int rpc_arg_offs = OPTEE_MSG_GET_ARG_SIZE(arg->num_params);
+	struct optee_msg_arg *rpc_arg = tee_shm_get_va(shm, rpc_arg_offs);
+
+	return optee_ffa_yielding_call(ctx, &data, rpc_arg);
+}
+
+/*
+ * 6. Driver initialization
+ *
+ * During driver inititialization is the OP-TEE Secure Partition is probed
+ * to find out which features it supports so the driver can be initialized
+ * with a matching configuration.
+ */
+
+static bool optee_ffa_api_is_compatbile(struct ffa_device *ffa_dev,
+					const struct ffa_dev_ops *ops)
+{
+	struct ffa_send_direct_data data = { OPTEE_FFA_GET_API_VERSION };
+	int rc;
+
+	ops->mode_32bit_set(ffa_dev);
+
+	rc = ops->sync_send_receive(ffa_dev, &data);
+	if (rc) {
+		pr_err("Unexpected error %d\n", rc);
+		return false;
+	}
+	if (data.data0 != OPTEE_FFA_VERSION_MAJOR ||
+	    data.data1 < OPTEE_FFA_VERSION_MINOR) {
+		pr_err("Incompatible OP-TEE API version %lu.%lu",
+		       data.data0, data.data1);
+		return false;
+	}
+
+	data = (struct ffa_send_direct_data){ OPTEE_FFA_GET_OS_VERSION };
+	rc = ops->sync_send_receive(ffa_dev, &data);
+	if (rc) {
+		pr_err("Unexpected error %d\n", rc);
+		return false;
+	}
+	if (data.data2)
+		pr_info("revision %lu.%lu (%08lx)",
+			data.data0, data.data1, data.data2);
+	else
+		pr_info("revision %lu.%lu", data.data0, data.data1);
+
+	return true;
+}
+
+static bool optee_ffa_exchange_caps(struct ffa_device *ffa_dev,
+				    const struct ffa_dev_ops *ops,
+				    unsigned int *rpc_arg_count)
+{
+	struct ffa_send_direct_data data = { OPTEE_FFA_EXCHANGE_CAPABILITIES };
+	int rc;
+
+	rc = ops->sync_send_receive(ffa_dev, &data);
+	if (rc) {
+		pr_err("Unexpected error %d", rc);
+		return false;
+	}
+	if (data.data0) {
+		pr_err("Unexpected exchange error %lu", data.data0);
+		return false;
+	}
+
+	*rpc_arg_count = (u8)data.data1;
+
+	return true;
+}
+
+static struct tee_shm_pool *optee_ffa_config_dyn_shm(void)
+{
+	struct tee_shm_pool_mgr *priv_mgr;
+	struct tee_shm_pool_mgr *dmabuf_mgr;
+	void *rc;
+
+	rc = optee_ffa_shm_pool_alloc_pages();
+	if (IS_ERR(rc))
+		return rc;
+	priv_mgr = rc;
+
+	rc = optee_ffa_shm_pool_alloc_pages();
+	if (IS_ERR(rc)) {
+		tee_shm_pool_mgr_destroy(priv_mgr);
+		return rc;
+	}
+	dmabuf_mgr = rc;
+
+	rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr);
+	if (IS_ERR(rc)) {
+		tee_shm_pool_mgr_destroy(priv_mgr);
+		tee_shm_pool_mgr_destroy(dmabuf_mgr);
+	}
+
+	return rc;
+}
+
+static void optee_ffa_get_version(struct tee_device *teedev,
+				  struct tee_ioctl_version_data *vers)
+{
+	struct tee_ioctl_version_data v = {
+		.impl_id = TEE_IMPL_ID_OPTEE,
+		.impl_caps = TEE_OPTEE_CAP_TZ,
+		.gen_caps = TEE_GEN_CAP_GP | TEE_GEN_CAP_REG_MEM |
+			    TEE_GEN_CAP_MEMREF_NULL,
+	};
+
+	*vers = v;
+}
+
+static int optee_ffa_open(struct tee_context *ctx)
+{
+	return optee_open(ctx, true);
+}
+
+static const struct tee_driver_ops optee_ffa_clnt_ops = {
+	.get_version = optee_ffa_get_version,
+	.open = optee_ffa_open,
+	.release = optee_release,
+	.open_session = optee_open_session,
+	.close_session = optee_close_session,
+	.invoke_func = optee_invoke_func,
+	.cancel_req = optee_cancel_req,
+	.shm_register = optee_ffa_shm_register,
+	.shm_unregister = optee_ffa_shm_unregister,
+};
+
+static const struct tee_desc optee_ffa_clnt_desc = {
+	.name = DRIVER_NAME "-ffa-clnt",
+	.ops = &optee_ffa_clnt_ops,
+	.owner = THIS_MODULE,
+};
+
+static const struct tee_driver_ops optee_ffa_supp_ops = {
+	.get_version = optee_ffa_get_version,
+	.open = optee_ffa_open,
+	.release = optee_release_supp,
+	.supp_recv = optee_supp_recv,
+	.supp_send = optee_supp_send,
+	.shm_register = optee_ffa_shm_register, /* same as for clnt ops */
+	.shm_unregister = optee_ffa_shm_unregister_supp,
+};
+
+static const struct tee_desc optee_ffa_supp_desc = {
+	.name = DRIVER_NAME "-ffa-supp",
+	.ops = &optee_ffa_supp_ops,
+	.owner = THIS_MODULE,
+	.flags = TEE_DESC_PRIVILEGED,
+};
+
+static const struct optee_ops optee_ffa_ops = {
+	.do_call_with_arg = optee_ffa_do_call_with_arg,
+	.to_msg_param = optee_ffa_to_msg_param,
+	.from_msg_param = optee_ffa_from_msg_param,
+};
+
+static void optee_ffa_remove(struct ffa_device *ffa_dev)
+{
+	struct optee *optee = ffa_dev->dev.driver_data;
+
+	optee_remove_common(optee);
+
+	mutex_destroy(&optee->ffa.mutex);
+	rhashtable_free_and_destroy(&optee->ffa.global_ids, rh_free_fn, NULL);
+
+	kfree(optee);
+}
+
+static int optee_ffa_probe(struct ffa_device *ffa_dev)
+{
+	const struct ffa_dev_ops *ffa_ops;
+	unsigned int rpc_arg_count;
+	struct tee_device *teedev;
+	struct optee *optee;
+	int rc;
+
+	ffa_ops = ffa_dev_ops_get(ffa_dev);
+	if (!ffa_ops) {
+		pr_warn("failed \"method\" init: ffa\n");
+		return -ENOENT;
+	}
+
+	if (!optee_ffa_api_is_compatbile(ffa_dev, ffa_ops))
+		return -EINVAL;
+
+	if (!optee_ffa_exchange_caps(ffa_dev, ffa_ops, &rpc_arg_count))
+		return -EINVAL;
+
+	optee = kzalloc(sizeof(*optee), GFP_KERNEL);
+	if (!optee) {
+		rc = -ENOMEM;
+		goto err;
+	}
+	optee->pool = optee_ffa_config_dyn_shm();
+	if (IS_ERR(optee->pool)) {
+		rc = PTR_ERR(optee->pool);
+		optee->pool = NULL;
+		goto err;
+	}
+
+	optee->ops = &optee_ffa_ops;
+	optee->ffa.ffa_dev = ffa_dev;
+	optee->ffa.ffa_ops = ffa_ops;
+	optee->rpc_arg_count = rpc_arg_count;
+
+	teedev = tee_device_alloc(&optee_ffa_clnt_desc, NULL, optee->pool,
+				  optee);
+	if (IS_ERR(teedev)) {
+		rc = PTR_ERR(teedev);
+		goto err;
+	}
+	optee->teedev = teedev;
+
+	teedev = tee_device_alloc(&optee_ffa_supp_desc, NULL, optee->pool,
+				  optee);
+	if (IS_ERR(teedev)) {
+		rc = PTR_ERR(teedev);
+		goto err;
+	}
+	optee->supp_teedev = teedev;
+
+	rc = tee_device_register(optee->teedev);
+	if (rc)
+		goto err;
+
+	rc = tee_device_register(optee->supp_teedev);
+	if (rc)
+		goto err;
+
+	rc = rhashtable_init(&optee->ffa.global_ids, &shm_rhash_params);
+	if (rc)
+		goto err;
+	mutex_init(&optee->ffa.mutex);
+	mutex_init(&optee->call_queue.mutex);
+	INIT_LIST_HEAD(&optee->call_queue.waiters);
+	optee_wait_queue_init(&optee->wait_queue);
+	optee_supp_init(&optee->supp);
+	ffa_dev_set_drvdata(ffa_dev, optee);
+
+	rc = optee_enumerate_devices(PTA_CMD_GET_DEVICES);
+	if (rc) {
+		optee_ffa_remove(ffa_dev);
+		return rc;
+	}
+
+	pr_info("initialized driver\n");
+	return 0;
+err:
+	/*
+	 * tee_device_unregister() is safe to call even if the
+	 * devices hasn't been registered with
+	 * tee_device_register() yet.
+	 */
+	tee_device_unregister(optee->supp_teedev);
+	tee_device_unregister(optee->teedev);
+	if (optee->pool)
+		tee_shm_pool_free(optee->pool);
+	kfree(optee);
+	return rc;
+}
+
+static const struct ffa_device_id optee_ffa_device_id[] = {
+	/* 486178e0-e7f8-11e3-bc5e0002a5d5c51b */
+	{ UUID_INIT(0x486178e0, 0xe7f8, 0x11e3,
+		    0xbc, 0x5e, 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b) },
+	{}
+};
+
+static struct ffa_driver optee_ffa_driver = {
+	.name = "optee",
+	.probe = optee_ffa_probe,
+	.remove = optee_ffa_remove,
+	.id_table = optee_ffa_device_id,
+};
+
+int optee_ffa_abi_register(void)
+{
+	return ffa_register(&optee_ffa_driver);
+}
+
+void optee_ffa_abi_unregister(void)
+{
+	ffa_unregister(&optee_ffa_driver);
+}
diff --git a/drivers/tee/optee/optee_ffa.h b/drivers/tee/optee/optee_ffa.h
new file mode 100644
index 000000000000..ee3a03fc392c
--- /dev/null
+++ b/drivers/tee/optee/optee_ffa.h
@@ -0,0 +1,153 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (c) 2019-2021, Linaro Limited
+ */
+
+/*
+ * This file is exported by OP-TEE and is kept in sync between secure world
+ * and normal world drivers. We're using ARM FF-A 1.0 specification.
+ */
+
+#ifndef __OPTEE_FFA_H
+#define __OPTEE_FFA_H
+
+#include <linux/arm_ffa.h>
+
+/*
+ * Normal world sends requests with FFA_MSG_SEND_DIRECT_REQ and
+ * responses are returned with FFA_MSG_SEND_DIRECT_RESP for normal
+ * messages.
+ *
+ * All requests with FFA_MSG_SEND_DIRECT_REQ and FFA_MSG_SEND_DIRECT_RESP
+ * are using the AArch32 SMC calling convention with register usage as
+ * defined in FF-A specification:
+ * w0:    Function ID (0x8400006F or 0x84000070)
+ * w1:    Source/Destination IDs
+ * w2:    Reserved (MBZ)
+ * w3-w7: Implementation defined, free to be used below
+ */
+
+#define OPTEE_FFA_VERSION_MAJOR	1
+#define OPTEE_FFA_VERSION_MINOR	0
+
+#define OPTEE_FFA_BLOCKING_CALL(id)	(id)
+#define OPTEE_FFA_YIELDING_CALL_BIT	31
+#define OPTEE_FFA_YIELDING_CALL(id)	((id) | BIT(OPTEE_FFA_YIELDING_CALL_BIT))
+
+/*
+ * Returns the API version implemented, currently follows the FF-A version.
+ * Call register usage:
+ * w3:    Service ID, OPTEE_FFA_GET_API_VERSION
+ * w4-w7: Not used (MBZ)
+ *
+ * Return register usage:
+ * w3:    OPTEE_FFA_VERSION_MAJOR
+ * w4:    OPTEE_FFA_VERSION_MINOR
+ * w5-w7: Not used (MBZ)
+ */
+#define OPTEE_FFA_GET_API_VERSION	OPTEE_FFA_BLOCKING_CALL(0)
+
+/*
+ * Returns the revision of OP-TEE.
+ *
+ * Used by non-secure world to figure out which version of the Trusted OS
+ * is installed. Note that the returned revision is the revision of the
+ * Trusted OS, not of the API.
+ *
+ * Call register usage:
+ * w3:    Service ID, OPTEE_FFA_GET_OS_VERSION
+ * w4-w7: Unused (MBZ)
+ *
+ * Return register usage:
+ * w3:    CFG_OPTEE_REVISION_MAJOR
+ * w4:    CFG_OPTEE_REVISION_MINOR
+ * w5:    TEE_IMPL_GIT_SHA1 (or zero if not supported)
+ */
+#define OPTEE_FFA_GET_OS_VERSION	OPTEE_FFA_BLOCKING_CALL(1)
+
+/*
+ * Exchange capabilities between normal world and secure world.
+ *
+ * Currently there are no defined capabilities. When features are added new
+ * capabilities may be added.
+ *
+ * Call register usage:
+ * w3:    Service ID, OPTEE_FFA_EXCHANGE_CAPABILITIES
+ * w4-w7: Note used (MBZ)
+ *
+ * Return register usage:
+ * w3:    Error code, 0 on success
+ * w4:    Bit[7:0]:  Number of parameters needed for RPC to be supplied
+ *                   as the second MSG arg struct for
+ *                   OPTEE_FFA_YIELDING_CALL_WITH_ARG.
+ *        Bit[31:8]: Reserved (MBZ)
+ * w5-w7: Note used (MBZ)
+ */
+#define OPTEE_FFA_EXCHANGE_CAPABILITIES OPTEE_FFA_BLOCKING_CALL(2)
+
+/*
+ * Unregister shared memory
+ *
+ * Call register usage:
+ * w3:    Service ID, OPTEE_FFA_YIELDING_CALL_UNREGISTER_SHM
+ * w4:    Shared memory handle, lower bits
+ * w5:    Shared memory handle, higher bits
+ * w6-w7: Not used (MBZ)
+ *
+ * Return register usage:
+ * w3:    Error code, 0 on success
+ * w4-w7: Note used (MBZ)
+ */
+#define OPTEE_FFA_UNREGISTER_SHM	OPTEE_FFA_BLOCKING_CALL(3)
+
+/*
+ * Call with struct optee_msg_arg as argument in the supplied shared memory
+ * with a zero internal offset and normal cached memory attributes.
+ * Register usage:
+ * w3:    Service ID, OPTEE_FFA_YIELDING_CALL_WITH_ARG
+ * w4:    Lower 32 bits of a 64-bit Shared memory handle
+ * w5:    Upper 32 bits of a 64-bit Shared memory handle
+ * w6:    Offset into shared memory pointing to a struct optee_msg_arg
+ *	  right after the parameters of this struct (at offset
+ *	  OPTEE_MSG_GET_ARG_SIZE(num_params) follows a struct optee_msg_arg
+ *	  for RPC, this struct has reserved space for the number of RPC
+ *	  parameters as returned by OPTEE_FFA_EXCHANGE_CAPABILITIES.
+ * w7:    Not used (MBZ)
+ * Resume from RPC. Register usage:
+ * w3:    Service ID, OPTEE_FFA_YIELDING_CALL_RESUME
+ * w4-w6: Not used (MBZ)
+ * w7:    Resume info
+ *
+ * Normal return (yielding call is completed). Register usage:
+ * w3:    Error code, 0 on success
+ * w4:    OPTEE_FFA_YIELDING_CALL_RETURN_DONE
+ * w5-w7: Not used (MBZ)
+ *
+ * RPC interrupt return (RPC from secure world). Register usage:
+ * w3:    Error code == 0
+ * w4:    Any defined RPC code but OPTEE_FFA_YIELDING_CALL_RETURN_DONE
+ * w5-w6: Not used (MBZ)
+ * w7:    Resume info
+ *
+ * Possible error codes in register w3:
+ * 0:                       Success
+ * FFA_DENIED:              w4 isn't one of OPTEE_FFA_YIELDING_CALL_START
+ *                          OPTEE_FFA_YIELDING_CALL_RESUME
+ *
+ * Possible error codes for OPTEE_FFA_YIELDING_CALL_START,
+ * FFA_BUSY:               Number of OP-TEE OS threads exceeded,
+ *                         try again later
+ * FFA_DENIED:             RPC shared memory object not found
+ * FFA_INVALID_PARAMETER:  Bad shared memory handle or offset into the memory
+ *
+ * Possible error codes for OPTEE_FFA_YIELDING_CALL_RESUME
+ * FFA_INVALID_PARAMETER:  Bad resume info
+ */
+#define OPTEE_FFA_YIELDING_CALL_WITH_ARG	OPTEE_FFA_YIELDING_CALL(0)
+#define OPTEE_FFA_YIELDING_CALL_RESUME		OPTEE_FFA_YIELDING_CALL(1)
+
+#define OPTEE_FFA_YIELDING_CALL_RETURN_DONE		0
+#define OPTEE_FFA_YIELDING_CALL_RETURN_RPC_CMD		1
+#define OPTEE_FFA_YIELDING_CALL_RETURN_INTERRUPT	2
+
+#endif /*__OPTEE_FFA_H*/
diff --git a/drivers/tee/optee/optee_msg.h b/drivers/tee/optee/optee_msg.h
index e3d72d09c484..2422e185d400 100644
--- a/drivers/tee/optee/optee_msg.h
+++ b/drivers/tee/optee/optee_msg.h
@@ -28,6 +28,9 @@
 #define OPTEE_MSG_ATTR_TYPE_RMEM_INPUT		0x5
 #define OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT		0x6
 #define OPTEE_MSG_ATTR_TYPE_RMEM_INOUT		0x7
+#define OPTEE_MSG_ATTR_TYPE_FMEM_INPUT		OPTEE_MSG_ATTR_TYPE_RMEM_INPUT
+#define OPTEE_MSG_ATTR_TYPE_FMEM_OUTPUT		OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT
+#define OPTEE_MSG_ATTR_TYPE_FMEM_INOUT		OPTEE_MSG_ATTR_TYPE_RMEM_INOUT
 #define OPTEE_MSG_ATTR_TYPE_TMEM_INPUT		0x9
 #define OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT		0xa
 #define OPTEE_MSG_ATTR_TYPE_TMEM_INOUT		0xb
@@ -96,6 +99,8 @@
  */
 #define OPTEE_MSG_NONCONTIG_PAGE_SIZE		4096
 
+#define OPTEE_MSG_FMEM_INVALID_GLOBAL_ID	0xffffffffffffffff
+
 /**
  * struct optee_msg_param_tmem - temporary memory reference parameter
  * @buf_ptr:	Address of the buffer
@@ -127,6 +132,23 @@ struct optee_msg_param_rmem {
 	u64 shm_ref;
 };
 
+/**
+ * struct optee_msg_param_fmem - ffa memory reference parameter
+ * @offs_lower:	   Lower bits of offset into shared memory reference
+ * @offs_upper:	   Upper bits of offset into shared memory reference
+ * @internal_offs: Internal offset into the first page of shared memory
+ *		   reference
+ * @size:	   Size of the buffer
+ * @global_id:	   Global identifier of Shared memory
+ */
+struct optee_msg_param_fmem {
+	u32 offs_low;
+	u16 offs_high;
+	u16 internal_offs;
+	u64 size;
+	u64 global_id;
+};
+
 /**
  * struct optee_msg_param_value - opaque value parameter
  *
@@ -143,13 +165,15 @@ struct optee_msg_param_value {
  * @attr:	attributes
  * @tmem:	parameter by temporary memory reference
  * @rmem:	parameter by registered memory reference
+ * @fmem:	parameter by ffa registered memory reference
  * @value:	parameter by opaque value
  * @octets:	parameter by octet string
  *
  * @attr & OPTEE_MSG_ATTR_TYPE_MASK indicates if tmem, rmem or value is used in
  * the union. OPTEE_MSG_ATTR_TYPE_VALUE_* indicates value or octets,
  * OPTEE_MSG_ATTR_TYPE_TMEM_* indicates @tmem and
- * OPTEE_MSG_ATTR_TYPE_RMEM_* indicates @rmem,
+ * OPTEE_MSG_ATTR_TYPE_RMEM_* or the alias PTEE_MSG_ATTR_TYPE_FMEM_* indicates
+ * @rmem or @fmem depending on the conduit.
  * OPTEE_MSG_ATTR_TYPE_NONE indicates that none of the members are used.
  */
 struct optee_msg_param {
@@ -157,6 +181,7 @@ struct optee_msg_param {
 	union {
 		struct optee_msg_param_tmem tmem;
 		struct optee_msg_param_rmem rmem;
+		struct optee_msg_param_fmem fmem;
 		struct optee_msg_param_value value;
 		u8 octets[24];
 	} u;
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
index ca0213e330b5..2593742364da 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -7,6 +7,7 @@
 #define OPTEE_PRIVATE_H
 
 #include <linux/arm-smccc.h>
+#include <linux/rhashtable.h>
 #include <linux/semaphore.h>
 #include <linux/tee_drv.h>
 #include <linux/types.h>
@@ -22,6 +23,7 @@
 #define TEEC_ERROR_NOT_SUPPORTED	0xFFFF000A
 #define TEEC_ERROR_COMMUNICATION	0xFFFF000E
 #define TEEC_ERROR_OUT_OF_MEMORY	0xFFFF000C
+#define TEEC_ERROR_BUSY			0xFFFF000D
 #define TEEC_ERROR_SHORT_BUFFER		0xFFFF0010
 
 #define TEEC_ORIGIN_COMMS		0x00000002
@@ -73,19 +75,28 @@ struct optee_supp {
 	struct completion reqs_c;
 };
 
-/**
- * struct optee_smc - SMC ABI specifics
- * @invoke_fn:		function to issue smc or hvc
- * @memremaped_shm	virtual address of memory in shared memory pool
- * @sec_caps:		secure world capabilities defined by
- *			OPTEE_SMC_SEC_CAP_* in optee_smc.h
- */
 struct optee_smc {
 	optee_invoke_fn *invoke_fn;
 	void *memremaped_shm;
 	u32 sec_caps;
 };
 
+/**
+ * struct optee_ffa_data -  FFA communication struct
+ * @ffa_dev		FFA device, contains the destination id, the id of
+ *			OP-TEE in secure world
+ * @ffa_ops		FFA operations
+ * @mutex		Serializes access to @global_ids
+ * @global_ids		FF-A shared memory global handle translation
+ */
+struct optee_ffa {
+	struct ffa_device *ffa_dev;
+	const struct ffa_dev_ops *ffa_ops;
+	/* Serializes access to @global_ids */
+	struct mutex mutex;
+	struct rhashtable global_ids;
+};
+
 struct optee;
 
 /**
@@ -116,11 +127,13 @@ struct optee_ops {
  *			world
  * @teedev:		client device
  * @smc:		specific to SMC ABI
+ * @ffa:		specific to FF-A ABI
  * @call_queue:		queue of threads waiting to call @invoke_fn
  * @wait_queue:		queue of threads from secure world waiting for a
  *			secure world sync object
  * @supp:		supplicant synchronization struct for RPC to supplicant
  * @pool:		shared memory pool
+ * @rpc_arg_count:	If > 0 number of RPC parameters to make room for
  * @scan_bus_done	flag if device registation was already done.
  * @scan_bus_wq		workqueue to scan optee bus and register optee drivers
  * @scan_bus_work	workq to scan optee bus and register optee drivers
@@ -129,11 +142,17 @@ struct optee {
 	struct tee_device *supp_teedev;
 	struct tee_device *teedev;
 	const struct optee_ops *ops;
-	struct optee_smc smc;
+	union {
+		struct optee_smc smc;
+#ifdef CONFIG_ARM_FFA_TRANSPORT
+		struct optee_ffa ffa;
+#endif
+	};
 	struct optee_call_queue call_queue;
 	struct optee_wait_queue wait_queue;
 	struct optee_supp supp;
 	struct tee_shm_pool *pool;
+	unsigned int rpc_arg_count;
 	bool   scan_bus_done;
 	struct workqueue_struct *scan_bus_wq;
 	struct work_struct scan_bus_work;
@@ -266,4 +285,12 @@ static inline void reg_pair_from_64(u32 *reg0, u32 *reg1, u64 val)
 int optee_smc_abi_register(void);
 void optee_smc_abi_unregister(void);
 
+#ifdef CONFIG_ARM_FFA_TRANSPORT
+int optee_ffa_abi_register(void);
+void optee_ffa_abi_unregister(void);
+#else
+static inline int optee_ffa_abi_register(void) { return -EOPNOTSUPP; }
+static inline void optee_ffa_abi_unregister(void) {}
+#endif
+
 #endif /*OPTEE_PRIVATE_H*/
-- 
2.31.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

  parent reply	other threads:[~2021-08-31  7:25 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-08-31  7:24 [PATCH v5 0/5] Add FF-A support in OP-TEE driver Jens Wiklander
2021-08-31  7:24 ` Jens Wiklander
2021-08-31  7:24 ` [PATCH v5 1/5] tee: add sec_world_id to struct tee_shm Jens Wiklander
2021-08-31  7:24   ` Jens Wiklander
2021-08-31  7:24 ` [PATCH v5 2/5] optee: simplify optee_release() Jens Wiklander
2021-08-31  7:24   ` Jens Wiklander
2021-08-31  7:24 ` [PATCH v5 3/5] optee: refactor driver with internal callbacks Jens Wiklander
2021-08-31  7:24   ` Jens Wiklander
2021-08-31  7:24 ` [PATCH v5 4/5] optee: isolate smc abi Jens Wiklander
2021-08-31  7:24   ` Jens Wiklander
2021-08-31  7:24 ` Jens Wiklander [this message]
2021-08-31  7:24   ` [PATCH v5 5/5] optee: add FF-A support Jens Wiklander
2021-10-01  9:34   ` Sudeep Holla
2021-10-01  9:34     ` Sudeep Holla
2021-10-01  9:29 ` [PATCH v5 0/5] Add FF-A support in OP-TEE driver Sudeep Holla
2021-10-01  9:29   ` Sudeep Holla

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210831072412.887565-6-jens.wiklander@linaro.org \
    --to=jens.wiklander@linaro.org \
    --cc=jerome@forissier.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=marc.bonnici@arm.com \
    --cc=op-tee@lists.trustedfirmware.org \
    --cc=sudeep.holla@arm.com \
    --cc=sughosh.ganu@linaro.org \
    --cc=sumit.garg@linaro.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.