[RFC PATCH V2 2/2] tee: add OP-TEE driver

Jens Wiklander jens.wiklander at linaro.org
Wed Apr 29 23:33:15 PDT 2015


Adds a OP-TEE driver which also can be compiled as a loadable module.

* Targets ARM and ARM64
* Supports using reserved memory from OP-TEE as shared memory
* CMA as shared memory is optional and only tried if OP-TEE doesn't
  supply a reserved shared memory region
* Probes OP-TEE version using SMCs
* Accepts requests on privileged and unprivileged device
* Uses OPTEE message protocol version 2

Signed-off-by: Jens Wiklander <jens.wiklander at linaro.org>
---
 drivers/tee/Kconfig               |  10 +
 drivers/tee/Makefile              |   1 +
 drivers/tee/optee/Kconfig         |  19 ++
 drivers/tee/optee/Makefile        |  13 +
 drivers/tee/optee/call.c          | 294 ++++++++++++++++++++++
 drivers/tee/optee/core.c          | 509 +++++++++++++++++++++++++++++++++++++
 drivers/tee/optee/optee_private.h | 138 +++++++++++
 drivers/tee/optee/optee_smc.h     | 510 ++++++++++++++++++++++++++++++++++++++
 drivers/tee/optee/rpc.c           | 282 +++++++++++++++++++++
 drivers/tee/optee/smc_a32.S       |  30 +++
 drivers/tee/optee/smc_a64.S       |  37 +++
 drivers/tee/optee/supp.c          | 327 ++++++++++++++++++++++++
 include/uapi/linux/optee_msg.h    | 368 +++++++++++++++++++++++++++
 13 files changed, 2538 insertions(+)
 create mode 100644 drivers/tee/optee/Kconfig
 create mode 100644 drivers/tee/optee/Makefile
 create mode 100644 drivers/tee/optee/call.c
 create mode 100644 drivers/tee/optee/core.c
 create mode 100644 drivers/tee/optee/optee_private.h
 create mode 100644 drivers/tee/optee/optee_smc.h
 create mode 100644 drivers/tee/optee/rpc.c
 create mode 100644 drivers/tee/optee/smc_a32.S
 create mode 100644 drivers/tee/optee/smc_a64.S
 create mode 100644 drivers/tee/optee/supp.c
 create mode 100644 include/uapi/linux/optee_msg.h

diff --git a/drivers/tee/Kconfig b/drivers/tee/Kconfig
index 64a8cd7..b269276 100644
--- a/drivers/tee/Kconfig
+++ b/drivers/tee/Kconfig
@@ -6,3 +6,13 @@ config TEE
 	help
 	  This implements a generic interface towards a Trusted Execution
 	  Environment (TEE).
+
+if TEE
+
+menu "TEE drivers"
+
+source "drivers/tee/optee/Kconfig"
+
+endmenu
+
+endif
diff --git a/drivers/tee/Makefile b/drivers/tee/Makefile
index 60d2dab..53f3c76 100644
--- a/drivers/tee/Makefile
+++ b/drivers/tee/Makefile
@@ -1,3 +1,4 @@
 obj-y += tee.o
 obj-y += tee_shm.o
 obj-y += tee_shm_pool.o
+obj-$(CONFIG_OPTEE) += optee/
diff --git a/drivers/tee/optee/Kconfig b/drivers/tee/optee/Kconfig
new file mode 100644
index 0000000..3faa855
--- /dev/null
+++ b/drivers/tee/optee/Kconfig
@@ -0,0 +1,19 @@
+# OP-TEE Trusted Execution Environment Configuration
+config OPTEE
+	tristate "OP-TEE"
+	default n
+	depends on ARM || ARM64
+	help
+	  This implements the OP-TEE Trusted Execution Environment (TEE)
+	  driver.
+
+if OPTEE
+menu "OP-TEE options"
+config OPTEE_USE_CMA
+	bool "Use CMA"
+	default n
+	select DMA_CMA
+	help
+	  Configures OP-TEE driver to use CMA for shared memory allocations.
+endmenu
+endif
diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile
new file mode 100644
index 0000000..096651d
--- /dev/null
+++ b/drivers/tee/optee/Makefile
@@ -0,0 +1,13 @@
+obj-$(CONFIG_OPTEE) += optee.o
+optee-objs += core.o
+optee-objs += call.o
+ifdef CONFIG_ARM
+plus_sec := $(call as-instr,.arch_extension sec,+sec)
+AFLAGS_smc_a32.o := -Wa,-march=armv7-a$(plus_sec)
+optee-objs += smc_a32.o
+endif
+ifdef CONFIG_ARM64
+optee-objs += smc_a64.o
+endif
+optee-objs += rpc.o
+optee-objs += supp.o
diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c
new file mode 100644
index 0000000..d931a82a
--- /dev/null
+++ b/drivers/tee/optee/call.c
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 2015, Linaro Limited
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/types.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/device.h>
+#include <linux/tee_drv.h>
+#include "optee_private.h"
+#include "optee_smc.h"
+
+static void optee_call_lock(struct optee_call_sync *callsync)
+{
+	mutex_lock(&callsync->mutex);
+}
+
+static void optee_call_lock_wait_completion(struct optee_call_sync *callsync)
+{
+	/*
+	 * Release the lock until "something happens" and then reacquire it
+	 * again.
+	 *
+	 * This is needed when TEE returns "busy" and we need to try again
+	 * later.
+	 */
+	callsync->c_waiters++;
+	mutex_unlock(&callsync->mutex);
+	/*
+	 * Wait at most one second. Secure world is normally never busy
+	 * more than that so we should normally never timeout.
+	 */
+	wait_for_completion_timeout(&callsync->c, HZ);
+	mutex_lock(&callsync->mutex);
+	callsync->c_waiters--;
+}
+
+static void optee_call_unlock(struct optee_call_sync *callsync)
+{
+	/*
+	 * If at least one thread is waiting for "something to happen" let
+	 * one thread know that "something has happened".
+	 */
+	if (callsync->c_waiters)
+		complete(&callsync->c);
+	mutex_unlock(&callsync->mutex);
+}
+
+static int optee_arg_from_user(struct opteem_arg *arg, size_t size,
+			struct tee_shm **put_shm)
+{
+	struct opteem_param *param;
+	size_t n;
+
+	if (!arg->num_params || !put_shm)
+		return -EINVAL;
+
+	param = OPTEEM_GET_PARAMS(arg);
+
+	for (n = 0; n < arg->num_params; n++) {
+		struct tee_shm *shm;
+		u32 shm_offs;
+		phys_addr_t pa;
+		int ret;
+
+		if (param[n].attr & ~(OPTEEM_ATTR_TYPE_MASK | OPTEEM_ATTR_META))
+			return -EINVAL;
+
+		if (optee_param_is(param + n, PARAM_MEMREF | PARAM_INOUT)) {
+			shm_offs = param[n].u.memref.buf_ptr;
+			shm = tee_shm_get_from_fd(
+					(int)param[n].u.memref.shm_ref);
+			if (IS_ERR(shm))
+				return PTR_ERR(shm);
+			put_shm[n] = shm;
+			ret = tee_shm_get_pa(shm, shm_offs, &pa);
+			if (ret)
+				return ret;
+			param[n].u.memref.buf_ptr = pa;
+		}
+	}
+
+	return 0;
+}
+
+static int optee_arg_to_user(struct opteem_arg *arg,
+			struct opteem_arg __user *uarg)
+{
+	struct opteem_param *param = OPTEEM_GET_PARAMS(arg);
+	struct opteem_param __user *uparam = (void __user *)(uarg + 1);
+	size_t n;
+
+	if (arg->cmd == OPTEEM_CMD_OPEN_SESSION &&
+	    put_user(arg->session, &uarg->session))
+		return -EINVAL;
+	if (put_user(arg->ret, &uarg->ret) ||
+	    put_user(arg->ret_origin, &uarg->ret_origin))
+		return -EINVAL;
+
+	for (n = 0; n < arg->num_params; n++) {
+		struct opteem_param *p = param + n;
+		struct opteem_param __user *up = uparam + n;
+
+		if (optee_param_is(p, PARAM_VALUE | PARAM_OUT)) {
+			if (put_user(p->u.value.a, &up->u.value.a) ||
+			    put_user(p->u.value.b, &up->u.value.b))
+				return -EINVAL;
+		} else if (optee_param_is(p, PARAM_MEMREF | PARAM_OUT)) {
+			if (put_user(p->u.memref.size, &up->u.memref.size))
+				return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+/* Requires the filpstate mutex to be held */
+static struct optee_session *find_session(struct optee_context_data *ctxdata,
+			u32 session_id)
+{
+	struct optee_session *sess;
+
+	list_for_each_entry(sess, &ctxdata->sess_list, list_node)
+		if (sess->session_id == session_id)
+			return sess;
+	return NULL;
+}
+
+u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg)
+{
+	struct optee *optee = tee_get_drvdata(ctx->teedev);
+	struct optee_smc_param param = { };
+	u32 ret;
+	u32 cmdid = OPTEE_SMC_CALL_WITH_ARG;
+
+	reg_pair_from_64(&param.a1, &param.a2, parg);
+	optee_call_lock(&optee->callsync);
+	while (true) {
+		param.a0 = cmdid;
+
+		optee_smc(&param);
+		ret = param.a0;
+
+		if (ret == OPTEE_SMC_RETURN_EBUSY) {
+			/*
+			 * Since secure world returned busy, release the
+			 * lock we had when entering this function and wait
+			 * for "something to happen" (something else to
+			 * exit from secure world and needed resources may
+			 * have become available).
+			 */
+			optee_call_lock_wait_completion(&optee->callsync);
+		} else if (OPTEE_SMC_RETURN_IS_RPC(ret)) {
+			/*
+			 * Process the RPC. We're unlocking the path to
+			 * secure world to allow another request while
+			 * processing the RPC.
+			 */
+			optee_call_unlock(&optee->callsync);
+			cmdid = optee_handle_rpc(ctx, &param);
+			optee_call_lock(&optee->callsync);
+		} else {
+			break;
+		}
+	}
+	optee_call_unlock(&optee->callsync);
+	return ret;
+}
+
+int optee_cmd_call_with_arg(struct tee_context *ctx, struct tee_shm *shm,
+			struct opteem_cmd_prefix *arg,
+			struct opteem_cmd_prefix __user *uarg, size_t len)
+{
+	struct optee_context_data *ctxdata = ctx->data;
+	struct tee_shm **put_shm = NULL;
+	struct opteem_arg *opteem_arg;
+	struct opteem_arg __user *opteem_uarg;
+	struct optee_session *sess;
+	phys_addr_t opteem_parg;
+	size_t opteem_arg_size;
+	int rc;
+	size_t n;
+
+	opteem_arg = (struct opteem_arg *)(arg + 1);
+	opteem_uarg = (struct opteem_arg __user *)(uarg + 1);
+
+	opteem_arg_size = len - sizeof(*arg);
+
+	/* Check that the header is complete */
+	if (opteem_arg_size < sizeof(struct opteem_arg))
+		return -EINVAL;
+	/* Check that there's room for the specified number of params */
+	if (opteem_arg_size != OPTEEM_GET_ARG_SIZE(opteem_arg->num_params))
+		return -EINVAL;
+
+	if (opteem_arg->num_params) {
+		put_shm = kcalloc(opteem_arg->num_params,
+				  sizeof(struct tee_shm *), GFP_KERNEL);
+		if (!put_shm)
+			return -ENOMEM;
+		/*
+		 * The params are updated with physical addresses and the ref
+		 * counters on the shared memory is increased. The shms to
+		 * decreased ref counts on when the call is over are stored in
+		 * put_shm.
+		 */
+		rc = optee_arg_from_user(opteem_arg, opteem_arg_size, put_shm);
+		if (rc)
+			goto out;
+	}
+
+	rc = tee_shm_va2pa(shm, opteem_arg, &opteem_parg);
+	if (rc)
+		goto out;
+
+	switch (opteem_arg->cmd) {
+	case OPTEEM_CMD_OPEN_SESSION:
+		/*
+		 * Allocate memory now to be able to store the new session
+		 * below.
+		 */
+		sess = kzalloc(sizeof(struct optee_session), GFP_KERNEL);
+		if (!sess) {
+			rc = -ENOMEM;
+			goto out;
+		}
+		break;
+	case OPTEEM_CMD_CLOSE_SESSION:
+		/* A session is about to be closed, remove it from the list */
+		mutex_lock(&ctxdata->mutex);
+		sess = find_session(ctxdata, opteem_arg->session);
+		if (sess)
+			list_del(&sess->list_node);
+		mutex_unlock(&ctxdata->mutex);
+		if (!sess) {
+			rc = -EINVAL;
+			goto out;
+		}
+		kfree(sess);
+		sess = NULL;
+		break;
+
+	case OPTEEM_CMD_INVOKE_COMMAND:
+	case OPTEEM_CMD_CANCEL:
+		mutex_lock(&ctxdata->mutex);
+		sess = find_session(ctxdata, opteem_arg->session);
+		mutex_unlock(&ctxdata->mutex);
+		if (!sess) {
+			rc = -EINVAL;
+			goto out;
+		}
+		sess = NULL;
+		break;
+
+	default:
+		rc = -EINVAL;
+		goto out;
+	}
+
+	if (optee_do_call_with_arg(ctx, opteem_parg)) {
+		opteem_arg->ret = TEEC_ERROR_COMMUNICATION;
+		opteem_arg->ret_origin = TEEC_ORIGIN_COMMS;
+	}
+
+	rc = optee_arg_to_user(opteem_arg, opteem_uarg);
+
+	if (sess && opteem_arg->ret == TEEC_SUCCESS) {
+		/* A new session has been created, add it to the list. */
+		sess->session_id = opteem_arg->session;
+		mutex_lock(&ctxdata->mutex);
+		list_add(&sess->list_node, &ctxdata->sess_list);
+		mutex_unlock(&ctxdata->mutex);
+		sess = NULL;
+	}
+out:
+	kfree(sess);
+	if (put_shm) {
+		for (n = 0; n < opteem_arg->num_params; n++)
+			if (put_shm[n])
+				tee_shm_put(put_shm[n]);
+		kfree(put_shm);
+	}
+	return rc;
+}
diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
new file mode 100644
index 0000000..3df389e
--- /dev/null
+++ b/drivers/tee/optee/core.c
@@ -0,0 +1,509 @@
+/*
+ * Copyright (c) 2015, Linaro Limited
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/dma-contiguous.h>
+#ifdef CONFIG_OPTEE_USE_CMA
+#include <linux/cma.h>
+#endif
+#include <linux/io.h>
+#include <linux/tee_drv.h>
+#include "optee_private.h"
+#include "optee_smc.h"
+
+#define DRIVER_NAME "optee"
+
+bool optee_param_is(struct opteem_param *param, uint32_t flags)
+{
+	static const u8 attr_flags[] = {
+		[OPTEEM_ATTR_TYPE_NONE]		 = 0,
+		[OPTEEM_ATTR_TYPE_VALUE_INPUT]	 = PARAM_VALUE | PARAM_IN,
+		[OPTEEM_ATTR_TYPE_VALUE_OUTPUT]	 = PARAM_VALUE | PARAM_OUT,
+		[OPTEEM_ATTR_TYPE_VALUE_INOUT]	 = PARAM_VALUE | PARAM_IN |
+						   PARAM_OUT,
+		[OPTEEM_ATTR_TYPE_MEMREF_INPUT]	 = PARAM_MEMREF | PARAM_IN,
+		[OPTEEM_ATTR_TYPE_MEMREF_OUTPUT] = PARAM_MEMREF | PARAM_OUT,
+		[OPTEEM_ATTR_TYPE_MEMREF_INOUT]	 = PARAM_MEMREF | PARAM_IN |
+						   PARAM_OUT,
+	};
+	int idx = param->attr & OPTEEM_ATTR_TYPE_MASK;
+	u32 masked;
+
+	if (idx >= sizeof(attr_flags))
+		return false;
+
+	masked = attr_flags[idx] & flags;
+	return (masked & PARAM_ANY) && (masked & PARAM_INOUT);
+}
+
+static void optee_get_smc_version(struct optee_smc_param *param)
+{
+	param->a0 = OPTEE_SMC_CALLS_UID;
+	optee_smc(param);
+}
+
+static int optee_get_version(struct tee_context *ctx,
+			struct tee_ioctl_version_data __user *vers)
+{
+	struct optee_smc_param param;
+
+	optee_get_smc_version(&param);
+	/* The first 4 words in param are the UUID of protocol */
+	return copy_to_user(vers, &param, sizeof(*vers));
+}
+
+static int optee_open(struct tee_context *ctx)
+{
+	struct optee_context_data *ctxdata;
+
+	ctxdata = kzalloc(sizeof(*ctxdata), GFP_KERNEL);
+	if (!ctxdata)
+		return -ENOMEM;
+
+	mutex_init(&ctxdata->mutex);
+	INIT_LIST_HEAD(&ctxdata->sess_list);
+
+	ctx->data = ctxdata;
+	return 0;
+}
+
+static void optee_release(struct tee_context *ctx)
+{
+	struct optee_context_data *ctxdata = ctx->data;
+	struct tee_shm *shm;
+	struct opteem_arg *arg = NULL;
+	phys_addr_t parg;
+
+	if (!ctxdata)
+		return;
+
+	shm = tee_shm_alloc(ctx->teedev, sizeof(struct opteem_arg),
+			    TEE_SHM_MAPPED);
+	if (!IS_ERR(shm)) {
+		arg = tee_shm_get_va(shm, 0);
+		/*
+		 * If va2pa fails for some reason, we can't call
+		 * optee_close_session(), only free the memory. Secure OS
+		 * will leak sessions and finally refuse more session, but
+		 * we will at least let normal world reclaim its memory.
+		 */
+		if (!IS_ERR(arg))
+			tee_shm_va2pa(shm, arg, &parg);
+	}
+
+	while (true) {
+		struct optee_session *sess;
+
+		sess = list_first_entry_or_null(&ctxdata->sess_list,
+						struct optee_session,
+						list_node);
+		if (!sess)
+			break;
+		list_del(&sess->list_node);
+		if (!IS_ERR_OR_NULL(arg)) {
+			memset(arg, 0, sizeof(*arg));
+			arg->cmd = OPTEEM_CMD_CLOSE_SESSION;
+			arg->session = sess->session_id;
+			optee_do_call_with_arg(ctx, parg);
+		}
+		kfree(sess);
+	}
+	kfree(ctxdata);
+
+	if (!IS_ERR(shm))
+		tee_shm_free(shm);
+
+	ctx->data = NULL;
+}
+
+static int optee_cmd_raw_fastcall(u32 smc_id, struct opteem_cmd_prefix *arg,
+		size_t len)
+{
+	struct optee_smc_param param = { .a0 = smc_id };
+	u32 *data = (u32 *)(arg + 1);
+	size_t data_len = len - sizeof(*arg);
+
+	if (data_len < 4 * sizeof(u32))
+		return -EINVAL;
+
+	/* This is a fast-call no need to take a mutex */
+
+	optee_smc(&param);
+	data[0] = param.a0;
+	data[1] = param.a1;
+	data[2] = param.a2;
+	data[3] = param.a3;
+	return 0;
+}
+
+static int optee_cmd(struct tee_context *ctx, void __user *buf, size_t len)
+{
+	struct opteem_cmd_prefix *arg;
+	struct tee_shm *shm;
+	int ret;
+
+	if (len > OPTEE_MAX_ARG_SIZE || len < sizeof(*arg))
+		return -EINVAL;
+
+	shm = tee_shm_alloc(ctx->teedev, len, TEE_SHM_MAPPED);
+	if (IS_ERR(shm))
+		return PTR_ERR(shm);
+
+	arg = tee_shm_get_va(shm, 0);
+	if (IS_ERR(arg) || copy_from_user(arg, buf, len)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	switch (arg->func_id) {
+	case OPTEEM_FUNCID_CALLS_UID:
+		ret = optee_cmd_raw_fastcall(OPTEE_SMC_CALLS_UID, arg, len);
+		break;
+	case OPTEEM_FUNCID_GET_OS_UUID:
+		ret = optee_cmd_raw_fastcall(OPTEE_SMC_CALL_GET_OS_UUID,
+					     arg, len);
+		break;
+	case OPTEEM_FUNCID_CALLS_REVISION:
+		ret = optee_cmd_raw_fastcall(OPTEE_SMC_CALLS_REVISION,
+					     arg, len);
+		break;
+	case OPTEEM_FUNCID_GET_OS_REVISION:
+		ret = optee_cmd_raw_fastcall(OPTEE_SMC_CALL_GET_OS_REVISION,
+					     arg, len);
+		break;
+	case OPTEEM_FUNCID_CALL_WITH_ARG:
+		ret = optee_cmd_call_with_arg(ctx, shm, arg, buf, len);
+		goto out_from_call;
+	default:
+		ret = -EINVAL;
+		goto out;
+	}
+
+out:
+	if (!ret) {
+		if (copy_to_user(buf, arg, len))
+			ret = -EINVAL;
+	}
+out_from_call:
+	tee_shm_free(shm);
+	return ret;
+}
+
+static struct tee_driver_ops optee_ops = {
+	.get_version = optee_get_version,
+	.open = optee_open,
+	.release = optee_release,
+	.cmd = optee_cmd,
+};
+
+static struct tee_desc optee_desc = {
+	.name = DRIVER_NAME "-clnt",
+	.ops = &optee_ops,
+	.owner = THIS_MODULE,
+};
+
+static int optee_supp_req(struct tee_context *ctx, void __user *buf,
+			size_t len)
+{
+	struct opteem_cmd_prefix *arg;
+	struct tee_shm *shm;
+	int ret;
+
+	if (len > OPTEE_MAX_ARG_SIZE || len < sizeof(*arg))
+		return -EINVAL;
+
+	shm = tee_shm_alloc(ctx->teedev, len, TEE_SHM_MAPPED);
+	if (IS_ERR(shm)) {
+		ret = PTR_ERR(shm);
+		goto out;
+	}
+
+	arg = tee_shm_get_va(shm, 0);
+	if (IS_ERR(arg) || copy_from_user(arg, buf, len)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	switch (arg->func_id) {
+	case OPTEEM_FUNCID_CALLS_UID:
+		ret = optee_cmd_raw_fastcall(OPTEE_SMC_CALLS_UID, arg, len);
+		break;
+	case OPTEEM_FUNCID_GET_OS_UUID:
+		ret = optee_cmd_raw_fastcall(OPTEE_SMC_CALL_GET_OS_UUID,
+					     arg, len);
+		break;
+	case OPTEEM_FUNCID_CALLS_REVISION:
+		ret = optee_cmd_raw_fastcall(OPTEE_SMC_CALLS_REVISION,
+					     arg, len);
+		break;
+	case OPTEEM_FUNCID_GET_OS_REVISION:
+		ret = optee_cmd_raw_fastcall(OPTEE_SMC_CALL_GET_OS_REVISION,
+					     arg, len);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	if (ret)
+		goto out;
+
+	if (copy_to_user(buf, arg, len))
+		ret = -EINVAL;
+out:
+	if (!IS_ERR(shm))
+		tee_shm_free(shm);
+	return ret;
+}
+
+static int optee_supp_cmd(struct tee_context *ctx, void __user *buf,
+			size_t len)
+{
+	struct opteem_cmd_prefix arg;
+
+	if (len < sizeof(arg) || copy_from_user(&arg, buf, sizeof(arg)))
+		return -EINVAL;
+
+	switch (arg.func_id) {
+	case OPTEEM_FUNCID_SUPP_CMD_WRITE:
+		return optee_supp_write(ctx, buf + sizeof(arg),
+					len - sizeof(arg));
+	case OPTEEM_FUNCID_SUPP_CMD_READ:
+		return optee_supp_read(ctx, buf + sizeof(arg),
+				       len - sizeof(arg));
+	default:
+		return optee_supp_req(ctx, buf, len);
+	}
+}
+
+static struct tee_driver_ops optee_supp_ops = {
+	.get_version = optee_get_version,
+	.open = optee_open,
+	.release = optee_release,
+	.cmd = optee_supp_cmd,
+};
+
+static struct tee_desc optee_supp_desc = {
+	.name = DRIVER_NAME "-supp",
+	.ops = &optee_supp_ops,
+	.owner = THIS_MODULE,
+	.flags = TEE_DESC_PRIVILEGED,
+};
+
+static bool opteem_api_uid_is_optee_api(void)
+{
+	struct optee_smc_param param;
+
+	optee_get_smc_version(&param);
+
+	if (param.a0 == OPTEEM_UID_0 && param.a1 == OPTEEM_UID_1 &&
+	    param.a2 == OPTEEM_UID_2 && param.a3 == OPTEEM_UID_3)
+		return true;
+	return false;
+}
+
+static bool opteem_api_revision_is_compatible(void)
+{
+	struct optee_smc_param param = { .a0 = OPTEE_SMC_CALLS_REVISION };
+
+	optee_smc(&param);
+
+	if (param.a0 == OPTEEM_REVISION_MAJOR &&
+	    (int)param.a1 >= OPTEEM_REVISION_MINOR)
+		return true;
+	return false;
+}
+
+static struct tee_shm_pool *optee_config_shm_ioremap(struct device *dev,
+			void **ioremaped_shm)
+{
+	struct optee_smc_param param = { .a0 = OPTEE_SMC_GET_SHM_CONFIG };
+	struct tee_shm_pool *pool;
+	u_long vaddr;
+	phys_addr_t paddr;
+	size_t size;
+	phys_addr_t begin;
+	phys_addr_t end;
+	void *va;
+
+	optee_smc(&param);
+	if (param.a0 != OPTEE_SMC_RETURN_OK) {
+		dev_info(dev, "shm service not available\n");
+		return ERR_PTR(-ENOENT);
+	}
+
+	if (param.a3 != OPTEE_SMC_SHM_CACHED) {
+		dev_err(dev, "only normal cached shared memory supported\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	begin = roundup(param.a1, PAGE_SIZE);
+	end = rounddown(param.a1 + param.a2, PAGE_SIZE);
+	paddr = begin;
+	size = end - begin;
+
+	va = ioremap_cache(paddr, size);
+	if (!va) {
+		dev_err(dev, "shared memory ioremap failed\n");
+		return ERR_PTR(-EINVAL);
+	}
+	vaddr = (u_long)va;
+
+	pool = tee_shm_pool_alloc_res_mem(dev, vaddr, paddr, size);
+	if (IS_ERR(pool))
+		iounmap(va);
+	else
+		*ioremaped_shm = va;
+	return pool;
+}
+
+#ifdef CONFIG_OPTEE_USE_CMA
+static struct tee_shm_pool *optee_config_shm_cma(struct device *dev)
+{
+	struct optee_smc_param param = { .a0 = OPTEE_SMC_REGISTER_SHM };
+	u_long vaddr;
+	phys_addr_t paddr;
+	size_t size;
+	struct tee_shm_pool *pool;
+
+	pool = tee_shm_pool_alloc(dev, &vaddr, &paddr, &size);
+	if (IS_ERR(pool))
+		return pool;
+
+	reg_pair_from_64(&param.a1, &param.a2, paddr);
+	param.a3 = size;
+	param.a4 = OPTEE_SMC_SHM_CACHED;
+	optee_smc(&param);
+	if (param.a0 != OPTEE_SMC_RETURN_OK) {
+		dev_err(dev, "can't register shared memory\n");
+		tee_shm_pool_free(pool);
+		return ERR_PTR(-EINVAL);
+	}
+	return pool;
+}
+#else
+static struct tee_shm_pool *optee_config_shm_cma(struct device *dev)
+{
+	return ERR_PTR(-ENOENT);
+}
+#endif
+
+static int optee_probe(struct platform_device *pdev)
+{
+	struct tee_shm_pool *pool;
+	struct optee *optee = NULL;
+	void *ioremaped_shm = NULL;
+	int rc;
+
+	if (!opteem_api_uid_is_optee_api() ||
+	    !opteem_api_revision_is_compatible())
+		return -EINVAL;
+
+	pool = optee_config_shm_ioremap(&pdev->dev, &ioremaped_shm);
+	if (IS_ERR(pool))
+		pool = optee_config_shm_cma(&pdev->dev);
+	if (IS_ERR(pool))
+		return PTR_ERR(pool);
+
+	optee = devm_kzalloc(&pdev->dev, sizeof(*optee), GFP_KERNEL);
+	if (!optee) {
+		rc = -ENOMEM;
+		goto err;
+	}
+
+	optee->dev = &pdev->dev;
+
+	optee->teedev = tee_device_alloc(&optee_desc, &pdev->dev, pool, optee);
+	if (IS_ERR(optee->teedev)) {
+		rc = PTR_ERR(optee->teedev);
+		goto err;
+	}
+
+	optee->supp_teedev = tee_device_alloc(&optee_supp_desc, &pdev->dev,
+					      pool, optee);
+	if (IS_ERR(optee->supp_teedev)) {
+		rc = PTR_ERR(optee->supp_teedev);
+		goto err;
+	}
+
+	rc = tee_device_register(optee->teedev);
+	if (rc)
+		goto err;
+
+	rc = tee_device_register(optee->supp_teedev);
+	if (rc)
+		goto err;
+
+	mutex_init(&optee->callsync.mutex);
+	init_completion(&optee->callsync.c);
+	optee->callsync.c_waiters = 0;
+	optee_mutex_wait_init(&optee->mutex_wait);
+	optee_supp_init(&optee->supp);
+	optee->ioremaped_shm = ioremaped_shm;
+	optee->pool = pool;
+
+	platform_set_drvdata(pdev, optee);
+
+	dev_info(&pdev->dev, "initialized driver\n");
+	return 0;
+err:
+	tee_device_unregister(optee->teedev);
+	tee_device_unregister(optee->supp_teedev);
+	if (pool)
+		tee_shm_pool_free(pool);
+	if (ioremaped_shm)
+		iounmap(optee->ioremaped_shm);
+	return rc;
+}
+
+static int optee_remove(struct platform_device *pdev)
+{
+	struct optee *optee = platform_get_drvdata(pdev);
+
+	tee_device_unregister(optee->teedev);
+	tee_device_unregister(optee->supp_teedev);
+	tee_shm_pool_free(optee->pool);
+	if (optee->ioremaped_shm)
+		iounmap(optee->ioremaped_shm);
+	optee_mutex_wait_uninit(&optee->mutex_wait);
+	optee_supp_uninit(&optee->supp);
+	mutex_destroy(&optee->callsync.mutex);
+	return 0;
+}
+
+static const struct of_device_id optee_match[] = {
+	{ .compatible = "optee,optee-tz" },
+	{},
+};
+
+static struct platform_driver optee_driver = {
+	.driver = {
+		.name = DRIVER_NAME,
+		.of_match_table = optee_match,
+	},
+	.probe = optee_probe,
+	.remove = optee_remove,
+};
+
+module_platform_driver(optee_driver);
+
+MODULE_AUTHOR("Linaro");
+MODULE_DESCRIPTION("OP-TEE driver");
+MODULE_SUPPORTED_DEVICE("");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
new file mode 100644
index 0000000..d357743
--- /dev/null
+++ b/drivers/tee/optee/optee_private.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2015, Linaro Limited
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef OPTEE_PRIVATE_H
+#define OPTEE_PRIVATE_H
+
+#include <linux/types.h>
+#include <linux/semaphore.h>
+#include <linux/tee_drv.h>
+#include <linux/optee_msg.h>
+
+#define OPTEE_MAX_ARG_SIZE	1024
+
+/* Some Global Platform error codes used in this driver */
+#define TEEC_SUCCESS			0x00000000
+#define TEEC_ERROR_BAD_PARAMETERS	0xFFFF0006
+#define TEEC_ERROR_COMMUNICATION	0xFFFF000E
+#define TEEC_ERROR_OUT_OF_MEMORY	0xFFFF000C
+
+#define TEEC_ORIGIN_COMMS		0x00000002
+
+struct optee_call_sync {
+	struct mutex mutex;
+	struct completion c;
+	int c_waiters;
+};
+
+struct optee_mutex_wait {
+	struct mutex mu;
+	struct list_head db;
+};
+
+struct optee_supp {
+	bool supp_next_write;
+	size_t data_size;
+	const struct opteem_arg *data_to_supp;
+	struct opteem_arg *data_from_supp;
+	struct mutex thrd_mutex;
+	struct mutex supp_mutex;
+	struct semaphore data_to_supp_sem;
+	struct semaphore data_from_supp_sem;
+};
+
+struct optee {
+	struct tee_device *supp_teedev;
+	struct tee_device *teedev;
+	struct device *dev;
+	struct optee_call_sync callsync;
+	struct optee_mutex_wait mutex_wait;
+	struct optee_supp supp;
+	struct tee_shm_pool *pool;
+	void *ioremaped_shm;
+};
+
+struct optee_session {
+	struct list_head list_node;
+	u32 session_id;
+};
+
+struct optee_context_data {
+	struct mutex mutex;
+	struct list_head sess_list;
+};
+
+/* Note that 32bit arguments are passed also when running in 64bit */
+struct optee_smc_param {
+	u32 a0;
+	u32 a1;
+	u32 a2;
+	u32 a3;
+	u32 a4;
+	u32 a5;
+	u32 a6;
+	u32 a7;
+};
+
+void optee_smc(struct optee_smc_param *param);
+
+u32 optee_handle_rpc(struct tee_context *ctx, struct optee_smc_param *param);
+
+void optee_mutex_wait_init(struct optee_mutex_wait *muw);
+void optee_mutex_wait_uninit(struct optee_mutex_wait *muw);
+
+void optee_supp_thrd_req(struct tee_context *ctx, struct opteem_arg *arg);
+int optee_supp_read(struct tee_context *ctx, void __user *buf, size_t len);
+int optee_supp_write(struct tee_context *ctx, void __user *buf, size_t len);
+void optee_supp_init(struct optee_supp *supp);
+void optee_supp_uninit(struct optee_supp *supp);
+
+
+u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg);
+int optee_cmd_call_with_arg(struct tee_context *ctx, struct tee_shm *shm,
+			struct opteem_cmd_prefix *arg,
+			struct opteem_cmd_prefix __user *uarg, size_t len);
+
+/*
+ * Small helpers
+ */
+#define PARAM_VALUE	0x1
+#define PARAM_MEMREF	0x2
+#define PARAM_ANY	(PARAM_VALUE | PARAM_MEMREF)
+#define PARAM_IN	0x4
+#define PARAM_OUT	0x8
+#define PARAM_INOUT	(PARAM_IN | PARAM_OUT)
+
+/**
+ * optee_param_is() - report kind of opteem parameter
+ * @param:	the opteem parameter
+ * @flags:	which properties of the parameter to check
+ * @returns true if any of PARAM_VALUE PARAM_MEMREF is satified _and_
+ *	any of the PARAM_IN PARAM_OUT is satisfied
+ */
+bool optee_param_is(struct opteem_param *param, uint32_t flags);
+
+static inline void *reg_pair_to_ptr(u32 reg0, u32 reg1)
+{
+	return (void *)(u_long)(((u64)reg0 << 32) | reg1);
+}
+
+static inline void reg_pair_from_64(u32 *reg0, u32 *reg1, u64 val)
+{
+	*reg0 = val >> 32;
+	*reg1 = val;
+}
+
+
+#endif /*OPTEE_PRIVATE_H*/
diff --git a/drivers/tee/optee/optee_smc.h b/drivers/tee/optee/optee_smc.h
new file mode 100644
index 0000000..6254bcf
--- /dev/null
+++ b/drivers/tee/optee/optee_smc.h
@@ -0,0 +1,510 @@
+/*
+ * Copyright (c) 2015, Linaro Limited
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef OPTEE_SMC_H
+#define OPTEE_SMC_H
+
+/*
+ * This file is exported by OP-TEE and is in kept in sync between secure
+ * world and normal world kernel driver. We're following ARM SMC Calling
+ * Convention as specified in
+ * http://infocenter.arm.com/help/topic/com.arm.doc.den0028a/index.html
+ *
+ * This file depends on optee_msg.h being included to expand the SMC id
+ * macros below.
+ */
+
+#define OPTEE_SMC_32			0
+#define OPTEE_SMC_64			0x40000000
+#define OPTEE_SMC_FAST_CALL		0x80000000
+#define OPTEE_SMC_STD_CALL		0
+
+#define OPTEE_SMC_OWNER_MASK		0x3F
+#define OPTEE_SMC_OWNER_SHIFT		24
+
+#define OPTEE_SMC_FUNC_MASK		0xFFFF
+
+#define OPTEE_SMC_IS_FAST_CALL(smc_val)	((smc_val) & OPTEE_SMC_FAST_CALL)
+#define OPTEE_SMC_IS_64(smc_val)	((smc_val) & OPTEE_SMC_64)
+#define OPTEE_SMC_FUNC_NUM(smc_val)	((smc_val) & OPTEE_SMC_FUNC_MASK)
+#define OPTEE_SMC_OWNER_NUM(smc_val) \
+	(((smc_val) >> OPTEE_SMC_OWNER_SHIFT) & OPTEE_SMC_OWNER_MASK)
+
+#define OPTEE_SMC_CALL_VAL(type, calling_convention, owner, func_num) \
+			((type) | (calling_convention) | \
+			(((owner) & OPTEE_SMC_OWNER_MASK) << \
+				OPTEE_SMC_OWNER_SHIFT) |\
+			((func_num) & OPTEE_SMC_FUNC_MASK))
+
+#define OPTEE_SMC_STD_CALL_VAL(func_num) \
+	OPTEE_SMC_CALL_VAL(OPTEE_SMC_32, OPTEE_SMC_STD_CALL, \
+			   OPTEE_SMC_OWNER_TRUSTED_OS, (func_num))
+#define OPTEE_SMC_FAST_CALL_VAL(func_num) \
+	OPTEE_SMC_CALL_VAL(OPTEE_SMC_32, OPTEE_SMC_FAST_CALL, \
+			   OPTEE_SMC_OWNER_TRUSTED_OS, (func_num))
+
+#define OPTEE_SMC_OWNER_ARCH		0
+#define OPTEE_SMC_OWNER_CPU		1
+#define OPTEE_SMC_OWNER_SIP		2
+#define OPTEE_SMC_OWNER_OEM		3
+#define OPTEE_SMC_OWNER_STANDARD	4
+#define OPTEE_SMC_OWNER_TRUSTED_APP	48
+#define OPTEE_SMC_OWNER_TRUSTED_OS	50
+
+#define OPTEE_SMC_OWNER_TRUSTED_OS_OPTEED 62
+#define OPTEE_SMC_OWNER_TRUSTED_OS_API	63
+
+/*
+ * Function specified by SMC Calling convention.
+ */
+#define OPTEE_SMC_FUNCID_CALLS_COUNT	0xFF00
+#define OPTEE_SMC_CALLS_COUNT \
+	OPTEE_SMC_CALL_VAL(OPTEE_SMC_32, OPTEE_SMC_FAST_CALL, \
+			   OPTEE_SMC_OWNER_TRUSTED_OS_API, \
+			   OPTEE_SMC_FUNCID_CALLS_COUNT)
+
+
+/*
+ * Cache settings for shared memory
+ */
+#define OPTEE_SMC_SHM_NONCACHED		0ULL
+#define OPTEE_SMC_SHM_CACHED		1ULL
+
+/*
+ * a0..a7 is used as register names in the descriptions below, on arm32
+ * that translates to r0..r7 and on arm64 to w0..w7. In both cases it's
+ * 32-bit registers.
+ */
+
+/*
+ * Function specified by SMC Calling convention
+ *
+ * Return one of the following UIDs if using API specified in this file
+ * without further extentions:
+ * 65cb6b93-af0c-4617-8ed6-644a8d1140f8
+ * see also OPTEE_SMC_UID_* in optee_msg.h
+ */
+#define OPTEE_SMC_FUNCID_CALLS_UID OPTEEM_FUNCID_CALLS_UID
+#define OPTEE_SMC_CALLS_UID \
+	OPTEE_SMC_CALL_VAL(OPTEE_SMC_32, OPTEE_SMC_FAST_CALL, \
+			   OPTEE_SMC_OWNER_TRUSTED_OS_API, \
+			   OPTEE_SMC_FUNCID_CALLS_UID)
+
+/*
+ * Function specified by SMC Calling convention
+ *
+ * Returns 2.0 if using API specified in this file without further extentions.
+ * see also OPTEEM_REVISION_* in optee_msg.h
+ */
+#define OPTEE_SMC_FUNCID_CALLS_REVISION OPTEEM_FUNCID_CALLS_REVISION
+#define OPTEE_SMC_CALLS_REVISION \
+	OPTEE_SMC_CALL_VAL(OPTEE_SMC_32, OPTEE_SMC_FAST_CALL, \
+			   OPTEE_SMC_OWNER_TRUSTED_OS_API, \
+			   OPTEE_SMC_FUNCID_CALLS_REVISION)
+
+/*
+ * Get UUID of Trusted OS.
+ *
+ * Used by non-secure world to figure out which Trusted OS is installed.
+ * Note that returned UUID is the UUID of the Trusted OS, not of the API.
+ *
+ * Returns UUID in a0-4 in the same way as OPTEE_SMC_CALLS_UID
+ * described above.
+ */
+#define OPTEE_SMC_FUNCID_GET_OS_UUID OPTEEM_FUNCID_GET_OS_UUID
+#define OPTEE_SMC_CALL_GET_OS_UUID \
+	OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_OS_UUID)
+
+/*
+ * Get revision of Trusted OS.
+ *
+ * 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.
+ *
+ * Returns revision in a0-1 in the same way as OPTEE_SMC_CALLS_REVISION
+ * described above.
+ */
+#define OPTEE_SMC_FUNCID_GET_OS_REVISION OPTEEM_FUNCID_GET_OS_REVISION
+#define OPTEE_SMC_CALL_GET_OS_REVISION \
+	OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_OS_REVISION)
+
+/*
+ * Call with struct opteem_arg as argument
+ *
+ * Call register usage:
+ * a0	SMC Function ID, OPTEE_SMC*CALL_WITH_ARG
+ * a1	Upper 32bit of a 64bit physical pointer to a struct opteem_arg
+ * a2	Lower 32bit of a 64bit physical pointer to a struct opteem_arg
+ * a3-6	Not used
+ * a7	Hypervisor Client ID register
+ *
+ * Normal return register usage:
+ * a0	Return value, OPTEE_SMC_RETURN_*
+ * a1-3	Not used
+ * a4-7	Preserved
+ *
+ * Ebusy return register usage:
+ * a0	Return value, OPTEE_SMC_RETURN_EBUSY
+ * a1-3	Preserved
+ * a4-7	Preserved
+ *
+ * RPC return register usage:
+ * a0	Return value, OPTEE_SMC_RETURN_IS_RPC(val)
+ * a1-2	RPC parameters
+ * a3-7	Resume information, must be preserved
+ *
+ * Possible return values:
+ * OPTEE_SMC_RETURN_UNKNOWN_FUNCTION	Trusted OS does not recognize this
+ *					function.
+ * OPTEE_SMC_RETURN_OK			Call completed, result updated in
+ *					the previously supplied struct
+ *					opteem_arg.
+ * OPTEE_SMC_RETURN_EBUSY		Trusted OS busy, try again later.
+ * OPTEE_SMC_RETURN_EBADADDR		Bad physcial pointer to struct
+ *					opteem_arg.
+ * OPTEE_SMC_RETURN_EBADCMD		Bad/unknown cmd in struct opteem_arg
+ * OPTEE_SMC_RETURN_IS_RPC()		Call suspended by RPC call to normal
+ *					world.
+ */
+#define OPTEE_SMC_FUNCID_CALL_WITH_ARG OPTEEM_FUNCID_CALL_WITH_ARG
+#define OPTEE_SMC_CALL_WITH_ARG \
+	OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_CALL_WITH_ARG)
+/* Same as OPTEE_SMC_CALL_WITH_ARG but a "fast call". */
+#define OPTEE_SMC_FASTCALL_WITH_ARG \
+	OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_CALL_WITH_ARG)
+
+/*
+ * Register a secure/non-secure shared memory region
+ *
+ * Call register usage:
+ * a0	SMC Function ID, OPTEE_SMC*_REGISTER_SHM
+ * a1	Upper 32bits of 64bit physical address of start of SHM
+ * a2	Lower 32bits of 64bit physical address of start of SHM
+ * a3	Size of SHM
+ * a4	Cache settings of memory, as defined by the
+ *	OPTEE_SMC_SHM_* values above
+ * a5-6	Not used
+ * a7	Hypervisor Client ID register
+ *
+ * Normal return register usage:
+ * a0	OPTEE_SMC_RETURN_OK if OK
+ *	OPTEE_SMC_RETURN_EBUSY can't obtain access to register SHM
+ *	OPTEE_SMC_RETURN_ENOMEM not enough memory to register SHM
+ *	OPTEE_SMC_RETURN_EBADADDR bad parameters
+ *	OPTEE_SMC_RETURN_EBADCMD call not available
+ * a1-2	Not used
+ * a3-7	Preserved
+ */
+#define OPTEE_SMC_FUNCID_REGISTER_SHM	5
+#define OPTEE_SMC_REGISTER_SHM \
+	OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_REGISTER_SHM)
+
+/*
+ * Unregister a secure/non-secure shared memory region
+ *
+ * Call register usage:
+ * a0	SMC Function ID, OPTEE_SMC*_*UNREGISTER_SHM
+ * a1	Upper 32bits of 64bit physical address of start of SHM
+ * a2	Lower 32bits of 64bit physical address of start of SHM
+ * a3	Size of SHM
+ * a3-6	Not used
+ * a7	Hypervisor Client ID register
+ *
+ * Normal return register usage:
+ * a00	OPTEE_SMC_RETURN_OK if OK
+ *	OPTEE_SMC_RETURN_EBUSY can't obtain access to register SHM
+ *	OPTEE_SMC_RETURN_ENOMEM not enough memory to register SHM
+ *	OPTEE_SMC_RETURN_EBADCMD call not available
+ * a1-3	Not used
+ * a4-7	Preserved
+ */
+#define OPTEE_SMC_FUNCID_UNREGISTER_SHM	6
+#define OPTEE_SMC_UNREGISTER_SHM \
+	OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_UNREGISTER_SHM)
+
+/*
+ * Get Shared Memory Config
+ *
+ * Returns the Secure/Non-secure shared memory config.
+ *
+ * Call register usage:
+ * a0	SMC Function ID, OPTEE_SMC_GET_SHM_CONFIG
+ * a1-6	Not used
+ * a7	Hypervisor Client ID register
+ *
+ * Have config return register usage:
+ * a0	OPTEE_SMC_RETURN_OK
+ * a1	Physical address of start of SHM
+ * a2	Size of of SHM
+ * a3	Cache settings of memory, as defined by the
+ *	OPTEE_SMC_SHM_* values above
+ * a4-7	Preserved
+ *
+ * Not available register usage:
+ * a0	OPTEE_SMC_RETURN_NOTAVAIL
+ * a1-3 Not used
+ * a4-7	Preserved
+ */
+#define OPTEE_SMC_FUNCID_GET_SHM_CONFIG	7
+#define OPTEE_SMC_GET_SHM_CONFIG \
+	OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_SHM_CONFIG)
+
+/*
+ * Configures L2CC mutex
+ *
+ * Disables, enables usage of L2CC mutex. Returns or sets physical address
+ * of L2CC mutex.
+ *
+ * Call register usage:
+ * a0	SMC Function ID, OPTEE_SMC_L2CC_MUTEX
+ * a1	OPTEE_SMC_L2CC_MUTEX_GET_ADDR	Get physical address of mutex
+ *	OPTEE_SMC_L2CC_MUTEX_SET_ADDR	Set physical address of mutex
+ *	OPTEE_SMC_L2CC_MUTEX_ENABLE	Enable usage of mutex
+ *	OPTEE_SMC_L2CC_MUTEX_DISABLE	Disable usage of mutex
+ * a2	if a1 == OPTEE_SMC_L2CC_MUTEX_SET_ADDR, physical address of mutex
+ * a3-6	Not used
+ * a7	Hypervisor Client ID register
+ *
+ * Have config return register usage:
+ * a0	OPTEE_SMC_RETURN_OK
+ * a1	Preserved
+ * a2	if a1 == OPTEE_SMC_L2CC_MUTEX_GET_ADDR, physical address of L2CC mutex
+ * a3-7	Preserved
+ *
+ * Error return register usage:
+ * a0	OPTEE_SMC_RETURN_NOTAVAIL	Physical address not available
+ *	OPTEE_SMC_RETURN_EBADADDR	Bad supplied physical address
+ *	OPTEE_SMC_RETURN_EBADCMD	Unsupported value in a1
+ * a1-7	Preserved
+ */
+#define OPTEE_SMC_L2CC_MUTEX_GET_ADDR	0
+#define OPTEE_SMC_L2CC_MUTEX_SET_ADDR	1
+#define OPTEE_SMC_L2CC_MUTEX_ENABLE	2
+#define OPTEE_SMC_L2CC_MUTEX_DISABLE	3
+#define OPTEE_SMC_FUNCID_L2CC_MUTEX	8
+#define OPTEE_SMC_L2CC_MUTEX \
+	OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_L2CC_MUTEX)
+
+/*
+ * Resume from RPC (for example after processing an IRQ)
+ *
+ * Call register usage:
+ * a0	SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC
+ * a1-3	Value of a1-3 when OPTEE_SMC_CALL_WITH_ARG returned
+ *	OPTEE_SMC_RETURN_RPC in a0
+ *
+ * Return register usage is the same as for OPTEE_SMC_*CALL_WITH_ARG above.
+ *
+ * Possible return values
+ * OPTEE_SMC_RETURN_UNKNOWN_FUNCTION	Trusted OS does not recognize this
+ *					function.
+ * OPTEE_SMC_RETURN_OK			Original call completed, result
+ *					updated in the previously supplied.
+ *					struct opteem_arg
+ * OPTEE_SMC_RETURN_RPC			Call suspended by RPC call to normal
+ *					world.
+ * OPTEE_SMC_RETURN_EBUSY		Trusted OS busy, try again later.
+ * OPTEE_SMC_RETURN_ERESUME		Resume failed, the opaque resume
+ *					information was corrupt.
+ */
+#define OPTEE_SMC_FUNCID_RETURN_FROM_RPC	3
+#define OPTEE_SMC_CALL_RETURN_FROM_RPC \
+	OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_RETURN_FROM_RPC)
+
+#define OPTEE_SMC_RETURN_RPC_PREFIX_MASK	0xFFFF0000
+#define OPTEE_SMC_RETURN_RPC_PREFIX		0xFFFF0000
+#define OPTEE_SMC_RETURN_RPC_FUNC_MASK		0x0000FFFF
+
+#define OPTEE_SMC_RETURN_GET_RPC_FUNC(ret) \
+	((ret) & OPTEE_SMC_RETURN_RPC_FUNC_MASK)
+
+#define OPTEE_SMC_RPC_VAL(func)		((func) | OPTEE_SMC_RETURN_RPC_PREFIX)
+
+/*
+ * Allocate argument memory for RPC parameter passing.
+ * Argument memory is used to hold a struct opteem_arg.
+ *
+ * "Call" register usage:
+ * a0	This value, OPTEE_SMC_RETURN_RPC_ALLOC_ARG
+ * a1	Size in bytes of required argument memory
+ * a2	Not used
+ * a3	Resume information, must be preserved
+ * a4-5	Not used
+ * a6-7	Resume information, must be preserved
+ *
+ * "Return" register usage:
+ * a0	SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC.
+ * a1	Upper 32bits of 64bit physical pointer to allocated argument
+ *	memory, (a1 == 0 && a2 == 0) if size was 0 or if memory can't
+ *	be allocated.
+ * a2	Lower 32bits of 64bit physical pointer to allocated argument
+ *	memory, (a1 == 0 && a2 == 0) if size was 0 or if memory can't
+ *	be allocated
+ * a3	Preserved
+ * a4	Upper 32bits of 64bit Shared memory cookie used when freeing
+ *	the memory or doing an RPC
+ * a5	Lower 32bits of 64bit Shared memory cookie used when freeing
+ *	the memory or doing an RPC
+ * a6-7	Preserved
+ */
+#define OPTEE_SMC_RPC_FUNC_ALLOC_ARG	0
+#define OPTEE_SMC_RETURN_RPC_ALLOC_ARG \
+	OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_ALLOC_ARG)
+
+/*
+ * Allocate payload memory for RPC parameter passing.
+ * Payload memory is used to hold the memory referred to by struct
+ * opteem_param_memref.
+ *
+ * "Call" register usage:
+ * a0	This value, OPTEE_SMC_RETURN_RPC_ALLOC_PAYLOAD
+ * a1	Size in bytes of required argument memory
+ * a2	Not used
+ * a3	Resume information, must be preserved
+ * a4-5	Not used
+ * a6-7	Resume information, must be preserved
+ *
+ * "Return" register usage:
+ * a0	SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC.
+ * a1	Upper 32bits of 64bit physical pointer to allocated argument
+ *	memory, (a1 == 0 && a2 == 0) if size was 0 or if memory can't
+ *	be allocated
+ * a2	Lower 32bits of 64bit physical pointer to allocated argument
+ *	memory, (a1 == 0 && a2 == 0) if size was 0 or if memory can't
+ *	be allocated
+ * a3	Preserved
+ * a4	Upper 32bits of 64bit Shared memory cookie used when freeing
+ *	the memory
+ * a5	Lower 32bits of 64bit Shared memory cookie used when freeing
+ *	the memory
+ * a6-7	Preserved
+ */
+#define OPTEE_SMC_RPC_FUNC_ALLOC_PAYLOAD	1
+#define OPTEE_SMC_RETURN_RPC_ALLOC_PAYLOAD \
+	OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_ALLOC_PAYLOAD)
+
+/*
+ * Free memory previously allocated by OPTEE_SMC_RETURN_RPC_ALLOC_ARG.
+ *
+ * "Call" register usage:
+ * a0	This value, OPTEE_SMC_RETURN_RPC_FREE_ARG
+ * a1	Upper 32bits of 64bit shared memory cookie belonging to this
+ *	argument memory
+ * a2	Lower 32bits of 64bit shared memory cookie belonging to this
+ *	argument memory
+ * a3-7	Resume information, must be preserved
+ *
+ * "Return" register usage:
+ * a0	SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC.
+ * a1-2	Not used
+ * a3-7	Preserved
+ */
+#define OPTEE_SMC_RPC_FUNC_FREE_ARG	2
+#define OPTEE_SMC_RETURN_RPC_FREE_ARG \
+	OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_FREE_ARG)
+
+/*
+ * Free memory previously allocated by OPTEE_SMC_RETURN_RPC_ALLOC_PAYLOAD.
+ *
+ * "Call" register usage:
+ * a0	This value, OPTEE_SMC_RETURN_RPC_FREE_PAYLOAD
+ * a1	Upper 32bit of 64bit shared memory cookie belonging to this
+ *	payload memory
+ * a2	Lower 32bit of 64bit shared memory cookie belonging to this
+ *	payload memory
+ * a3-7	Resume information, must be preserved
+ *
+ * "Return" register usage:
+ * a0	SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC.
+ * a1-2	Not used
+ * a3-7	Preserved
+ */
+#define OPTEE_SMC_RPC_FUNC_FREE_PAYLOAD	3
+#define OPTEE_SMC_RETURN_RPC_FREE_PAYLOAD \
+	OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_FREE_PAYLOAD)
+
+/*
+ * Deliver an IRQ in normal world.
+ *
+ * "Call" register usage:
+ * a0	OPTEE_SMC_RETURN_RPC_IRQ
+ * a1-7	Resume information, must be preserved
+ *
+ * "Return" register usage:
+ * a0	SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC.
+ * a1-7	Preserved
+ */
+#define OPTEE_SMC_RPC_FUNC_IRQ		4
+#define OPTEE_SMC_RETURN_RPC_IRQ \
+	OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_IRQ)
+
+/*
+ * Do an RPC request. The supplied struct opteem_arg tells which
+ * request to do and the parameters for the request. The following fields
+ * are used (the rest are unused):
+ * - cmd		the Request ID
+ * - ret		return value of the request, filled in by normal world
+ * - num_params		number of parameters for the request
+ * - params		the parameters
+ * - param_attrs	attributes of the parameters
+ *
+ * "Call" register usage:
+ * a0	OPTEE_SMC_RETURN_RPC_CMD
+ * a1	Upper 32bit of a 64bit Shared memory cookie holding a
+ *	struct opteem_arg, must be preserved, only the data should
+ *	be updated
+ * a2	Lower 32bit of a 64bit Shared memory cookie holding a
+ *	struct opteem_arg, must be preserved, only the data should
+ *	be updated
+ * a3-7	Resume information, must be preserved
+ *
+ * "Return" register usage:
+ * a0	SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC.
+ * a1-2	Not used
+ * a3-7	Preserved
+ */
+#define OPTEE_SMC_RPC_FUNC_CMD		5
+#define OPTEE_SMC_RETURN_RPC_CMD \
+	OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_CMD)
+
+/* Returned in a0 */
+#define OPTEE_SMC_RETURN_UNKNOWN_FUNCTION 0xFFFFFFFF
+
+/* Returned in a0 only from Trusted OS functions */
+#define OPTEE_SMC_RETURN_OK		0x0
+#define OPTEE_SMC_RETURN_EBUSY		0x1
+#define OPTEE_SMC_RETURN_ERESUME	0x2
+#define OPTEE_SMC_RETURN_EBADADDR	0x3
+#define OPTEE_SMC_RETURN_EBADCMD	0x4
+#define OPTEE_SMC_RETURN_ENOMEM		0x5
+#define OPTEE_SMC_RETURN_NOTAVAIL	0x6
+#define OPTEE_SMC_RETURN_IS_RPC(ret) \
+	(((ret) != OPTEE_SMC_RETURN_UNKNOWN_FUNCTION) && \
+	((((ret) & OPTEE_SMC_RETURN_RPC_PREFIX_MASK) == \
+		OPTEE_SMC_RETURN_RPC_PREFIX)))
+
+#endif /* OPTEE_SMC_H */
diff --git a/drivers/tee/optee/rpc.c b/drivers/tee/optee/rpc.c
new file mode 100644
index 0000000..640af3d
--- /dev/null
+++ b/drivers/tee/optee/rpc.c
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2015, Linaro Limited
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/tee_drv.h>
+#include "optee_private.h"
+#include "optee_smc.h"
+
+struct optee_mutex_wait_entry {
+	struct list_head link;
+	struct completion comp;
+	struct mutex mu;
+	u32 wait_after;
+	u32 key;
+};
+
+/*
+ * Compares two serial numbers using Serial Number Arithmetic
+ * (https://www.ietf.org/rfc/rfc1982.txt).
+ */
+#define TICK_GT(t1, t2) \
+	(((t1) < (t2) && (t2) - (t1) > 0xFFFFFFFFu) || \
+	((t1) > (t2) && (t1) - (t2) < 0xFFFFFFFFu))
+
+static struct optee_mutex_wait_entry *muw_find_entry(
+			struct optee_mutex_wait *muw, u32 key)
+{
+	struct optee_mutex_wait_entry *w;
+
+	mutex_lock(&muw->mu);
+
+	list_for_each_entry(w, &muw->db, link)
+		if (w->key == key)
+			goto out;
+
+	w = kmalloc(sizeof(struct optee_mutex_wait), GFP_KERNEL);
+	if (!w)
+		goto out;
+
+	init_completion(&w->comp);
+	mutex_init(&w->mu);
+	w->wait_after = 0;
+	w->key = key;
+	list_add_tail(&w->link, &muw->db);
+out:
+	mutex_unlock(&muw->mu);
+	return w;
+}
+
+static void muw_delete_entry(struct optee_mutex_wait_entry *w)
+{
+	list_del(&w->link);
+	mutex_destroy(&w->mu);
+	kfree(w);
+}
+
+static void muw_delete(struct optee_mutex_wait *muw, u32 key)
+{
+	struct optee_mutex_wait_entry *w;
+
+	mutex_lock(&muw->mu);
+
+	list_for_each_entry(w, &muw->db, link) {
+		if (w->key == key) {
+			muw_delete_entry(w);
+			break;
+		}
+	}
+
+	mutex_unlock(&muw->mu);
+}
+
+static void muw_wakeup(struct optee_mutex_wait *muw, u32 key,
+			u32 wait_after)
+{
+	struct optee_mutex_wait_entry *w = muw_find_entry(muw, key);
+
+	if (!w)
+		return;
+
+	mutex_lock(&w->mu);
+	w->wait_after = wait_after;
+	mutex_unlock(&w->mu);
+	complete(&w->comp);
+}
+
+static void muw_sleep(struct optee_mutex_wait *muw, u32 key, u32 wait_tick)
+{
+	struct optee_mutex_wait_entry *w = muw_find_entry(muw, key);
+	u32 wait_after;
+
+	if (!w)
+		return;
+
+	mutex_lock(&w->mu);
+	wait_after = w->wait_after;
+	mutex_unlock(&w->mu);
+
+	/*
+	 * Only wait if the wait_tick is larger than wait_after, that is
+	 * the mutex_wait hasn't been updated while this function was about
+	 * to be called.
+	 */
+	if (TICK_GT(wait_tick, wait_after))
+		wait_for_completion_timeout(&w->comp, HZ);
+}
+
+void optee_mutex_wait_init(struct optee_mutex_wait *muw)
+{
+	mutex_init(&muw->mu);
+	INIT_LIST_HEAD(&muw->db);
+}
+
+void optee_mutex_wait_uninit(struct optee_mutex_wait *muw)
+{
+	/*
+	 * It's the callers responsibility to ensure that no one is using
+	 * anything inside muw.
+	 */
+
+	mutex_destroy(&muw->mu);
+	while (!list_empty(&muw->db)) {
+		struct optee_mutex_wait_entry *w;
+
+		w = list_first_entry(&muw->db, struct optee_mutex_wait_entry,
+				     link);
+		muw_delete_entry(w);
+	}
+}
+
+static void handle_rpc_func_cmd_mutex_wait(struct optee *optee,
+			struct opteem_arg *arg)
+{
+	struct opteem_param *params;
+
+	if (arg->num_params != OPTEEM_RPC_NUM_PARAMS)
+		goto bad;
+
+	params = OPTEEM_GET_PARAMS(arg);
+
+	if ((params[0].attr & OPTEEM_ATTR_TYPE_MASK) !=
+			OPTEEM_ATTR_TYPE_VALUE_INPUT)
+		goto bad;
+	if (params[1].attr != OPTEEM_ATTR_TYPE_NONE)
+		goto bad;
+
+	switch (arg->func) {
+	case OPTEEM_RPC_SLEEP_MUTEX_WAIT:
+		muw_sleep(&optee->mutex_wait, params[0].u.value.a,
+			  params[0].u.value.b);
+		break;
+	case OPTEEM_RPC_SLEEP_MUTEX_WAKEUP:
+		muw_wakeup(&optee->mutex_wait, params[0].u.value.a,
+			   params[0].u.value.b);
+		break;
+	case OPTEEM_RPC_SLEEP_MUTEX_DELETE:
+		muw_delete(&optee->mutex_wait, params[0].u.value.a);
+		break;
+	default:
+		goto bad;
+	}
+
+	arg->ret = TEEC_SUCCESS;
+	return;
+bad:
+	arg->ret = TEEC_ERROR_BAD_PARAMETERS;
+}
+
+static void handle_rpc_func_cmd_wait(struct opteem_arg *arg)
+{
+	struct opteem_param *params;
+	u32 msec_to_wait;
+
+	if (arg->num_params != OPTEEM_RPC_NUM_PARAMS)
+		goto bad;
+
+	params = OPTEEM_GET_PARAMS(arg);
+	if (params[0].attr != OPTEEM_ATTR_TYPE_VALUE_INPUT)
+		goto bad;
+
+	msec_to_wait = params[0].u.value.a;
+
+	/* set task's state to interruptible sleep */
+	set_current_state(TASK_INTERRUPTIBLE);
+
+	/* take a nap */
+	schedule_timeout(msecs_to_jiffies(msec_to_wait));
+
+	arg->ret = TEEC_SUCCESS;
+	return;
+bad:
+	arg->ret = TEEC_ERROR_BAD_PARAMETERS;
+}
+
+static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee,
+			struct tee_shm *shm)
+{
+	struct opteem_arg *arg;
+
+	arg = tee_shm_get_va(shm, 0);
+	if (IS_ERR(arg)) {
+		dev_err(optee->dev, "%s: tee_shm_get_va %p failed\n",
+			__func__, shm);
+		return;
+	}
+
+	switch (arg->cmd) {
+	case OPTEEM_RPC_CMD_SLEEP_MUTEX:
+		handle_rpc_func_cmd_mutex_wait(optee, arg);
+		break;
+	case OPTEEM_RPC_CMD_SUSPEND:
+		handle_rpc_func_cmd_wait(arg);
+		break;
+	default:
+		optee_supp_thrd_req(ctx, arg);
+	}
+}
+
+u32 optee_handle_rpc(struct tee_context *ctx, struct optee_smc_param *param)
+{
+	struct tee_device *teedev = ctx->teedev;
+	struct optee *optee = tee_get_drvdata(teedev);
+	struct tee_shm *shm;
+	phys_addr_t pa;
+
+	switch (OPTEE_SMC_RETURN_GET_RPC_FUNC(param->a0)) {
+	case OPTEE_SMC_RPC_FUNC_ALLOC_ARG:
+		shm = tee_shm_alloc(teedev, param->a1, TEE_SHM_MAPPED);
+		if (!IS_ERR(shm) && !tee_shm_get_pa(shm, 0, &pa)) {
+			reg_pair_from_64(&param->a1, &param->a2, pa);
+			reg_pair_from_64(&param->a4, &param->a5, (u_long)shm);
+		} else {
+			param->a1 = 0;
+			param->a2 = 0;
+			param->a4 = 0;
+			param->a5 = 0;
+		}
+		break;
+	case OPTEE_SMC_RPC_FUNC_ALLOC_PAYLOAD:
+		shm = tee_shm_alloc(teedev, param->a1,
+				    TEE_SHM_MAPPED | TEE_SHM_DMA_BUF);
+		if (!IS_ERR(shm) && !tee_shm_get_pa(shm, 0, &pa)) {
+			reg_pair_from_64(&param->a1, &param->a2, pa);
+			reg_pair_from_64(&param->a4, &param->a5, (u_long)shm);
+		} else {
+			param->a1 = 0;
+			param->a2 = 0;
+			param->a4 = 0;
+			param->a5 = 0;
+		}
+		break;
+	case OPTEE_SMC_RPC_FUNC_FREE_ARG:
+	case OPTEE_SMC_RPC_FUNC_FREE_PAYLOAD:
+		shm = reg_pair_to_ptr(param->a1, param->a2);
+		tee_shm_free(shm);
+		break;
+	case OPTEE_SMC_RPC_FUNC_IRQ:
+		break;
+	case OPTEE_SMC_RPC_FUNC_CMD:
+		shm = reg_pair_to_ptr(param->a1, param->a2);
+		handle_rpc_func_cmd(ctx, optee, shm);
+		break;
+	default:
+		dev_warn(optee->dev, "Unknown RPC func 0x%x\n",
+			 (u32)OPTEE_SMC_RETURN_GET_RPC_FUNC(param->a0));
+		break;
+	}
+
+	return OPTEE_SMC_CALL_RETURN_FROM_RPC;
+}
diff --git a/drivers/tee/optee/smc_a32.S b/drivers/tee/optee/smc_a32.S
new file mode 100644
index 0000000..30fe0e5
--- /dev/null
+++ b/drivers/tee/optee/smc_a32.S
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2015, Linaro Limited
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/linkage.h>
+
+	.text
+	.balign 4
+	.code 32
+
+	/* void optee_smc(struct optee_smc_param *param); */
+	.globl	optee_smc
+ENTRY(optee_smc)
+	push	{r4-r8, lr}
+	mov	r8, r0
+	ldm	r8, {r0-r7}
+.arch_extension sec
+	smc	#0
+	stm	r8, {r0-r7}
+	pop	{r4-r8, pc}
+ENDPROC(optee_smc)
diff --git a/drivers/tee/optee/smc_a64.S b/drivers/tee/optee/smc_a64.S
new file mode 100644
index 0000000..458a138
--- /dev/null
+++ b/drivers/tee/optee/smc_a64.S
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2015, Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/linkage.h>
+
+	.text
+
+#define SMC_PARAM_W0_OFFS	0
+#define SMC_PARAM_W2_OFFS	8
+#define SMC_PARAM_W4_OFFS	16
+#define SMC_PARAM_W6_OFFS	24
+
+	/* void optee_smc(struct smc_param *param); */
+	.globl	optee_smc
+ENTRY(optee_smc)
+	stp	x28, x30, [sp, #-16]!
+	mov	x28, x0
+	ldp	w0, w1, [x28, #SMC_PARAM_W0_OFFS]
+	ldp	w2, w3, [x28, #SMC_PARAM_W2_OFFS]
+	ldp	w4, w5, [x28, #SMC_PARAM_W4_OFFS]
+	ldp	w6, w7, [x28, #SMC_PARAM_W6_OFFS]
+	smc	#0
+	stp	w0, w1, [x28, #SMC_PARAM_W0_OFFS]
+	stp	w2, w3, [x28, #SMC_PARAM_W2_OFFS]
+	ldp	x28, x30, [sp], #16
+	ret
+ENDPROC(optee_smc)
diff --git a/drivers/tee/optee/supp.c b/drivers/tee/optee/supp.c
new file mode 100644
index 0000000..97c0a9a
--- /dev/null
+++ b/drivers/tee/optee/supp.c
@@ -0,0 +1,327 @@
+/*
+ * Copyright (c) 2015, Linaro Limited
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include "optee_private.h"
+
+void optee_supp_init(struct optee_supp *supp)
+{
+	memset(supp, 0, sizeof(*supp));
+	mutex_init(&supp->thrd_mutex);
+	mutex_init(&supp->supp_mutex);
+	sema_init(&supp->data_to_supp_sem, 0);
+	sema_init(&supp->data_from_supp_sem, 0);
+}
+
+void optee_supp_uninit(struct optee_supp *supp)
+{
+	mutex_destroy(&supp->thrd_mutex);
+	mutex_destroy(&supp->supp_mutex);
+}
+
+static void optee_supp_send(struct optee *optee,
+			const struct opteem_arg *arg,
+			struct opteem_arg *resp)
+{
+	struct optee_supp *supp = &optee->supp;
+
+	/*
+	 * Other threads blocks here until we've copied our answer from
+	 * supplicant.
+	 */
+	mutex_lock(&supp->thrd_mutex);
+
+	/*
+	 * We have exclusive access to data_to_supp and data_from_supp
+	 * since the supplicant is at this point either trying to down()
+	 * data_to_supp_sem or still in userspace about to do the ioctl()
+	 * to enter optee_supp_read() below.
+	 */
+
+	supp->data_to_supp = arg;
+	supp->data_from_supp = resp;
+
+	/* Let supplicant get the data */
+	up(&supp->data_to_supp_sem);
+
+	/*
+	 * Wait for supplicant to process and return result, once we've
+	 * down()'ed data_from_supp_sem we have exclusive access again.
+	 */
+	down(&supp->data_from_supp_sem);
+
+	/* We're done, let someone else talk to the supplicant now. */
+	mutex_unlock(&supp->thrd_mutex);
+}
+
+static void copy_back_outdata(struct opteem_arg *arg,
+			const struct opteem_arg *resp)
+{
+	struct opteem_param *arg_params = OPTEEM_GET_PARAMS(arg);
+	struct opteem_param *resp_params = OPTEEM_GET_PARAMS(resp);
+	size_t n;
+
+	/* Copy back out and inout parameters */
+	for (n = 0; n < arg->num_params; n++) {
+		struct opteem_param *ap = arg_params + n;
+
+		if (optee_param_is(ap, PARAM_VALUE | PARAM_OUT))
+			ap->u.value = resp_params[n].u.value;
+		else if (optee_param_is(ap, PARAM_MEMREF | PARAM_OUT))
+			ap->u.memref.size = resp_params[n].u.memref.size;
+	}
+	arg->ret = resp->ret;
+
+}
+
+void optee_supp_thrd_req(struct tee_context *ctx, struct opteem_arg *arg)
+{
+	struct optee *optee = tee_get_drvdata(ctx->teedev);
+	const size_t s = OPTEEM_GET_ARG_SIZE(OPTEEM_RPC_NUM_PARAMS);
+	struct opteem_arg *resp;
+
+	if (arg->num_params != OPTEEM_RPC_NUM_PARAMS) {
+		arg->ret = TEEC_ERROR_BAD_PARAMETERS;
+		return;
+	}
+
+	resp = kzalloc(s, GFP_KERNEL);
+	if (!resp) {
+		arg->ret = TEEC_ERROR_OUT_OF_MEMORY;
+		return;
+	}
+
+	optee_supp_send(optee, arg, resp);
+	copy_back_outdata(arg, resp);
+
+	kfree(resp);
+}
+
+static u32 memref_to_user(struct tee_shm *shm,
+			struct opteem_param_memref *ph_mem,
+			struct opteem_param_memref *user_mem, int *fd)
+{
+	int res;
+	phys_addr_t pa;
+
+	if (!shm) {
+		*fd = -1;
+		return TEEC_SUCCESS;
+	}
+
+	res = tee_shm_get_pa(shm, 0, &pa);
+	if (res)
+		return TEEC_ERROR_BAD_PARAMETERS;
+
+	if (pa > ph_mem->buf_ptr)
+		return TEEC_ERROR_BAD_PARAMETERS;
+
+	user_mem->buf_ptr = ph_mem->buf_ptr - pa;
+	res = tee_shm_get_fd(shm);
+	if (res < 0)
+		return TEEC_ERROR_OUT_OF_MEMORY;
+
+	*fd = res;
+	return TEEC_SUCCESS;
+}
+
+static int optee_supp_copy_to_user(void __user *buf,
+			const struct opteem_arg *arg, struct opteem_arg *tmp)
+{
+	size_t s = OPTEEM_GET_ARG_SIZE(OPTEEM_RPC_NUM_PARAMS);
+	struct opteem_param *arg_params;
+	struct opteem_param *tmp_params;
+	size_t n;
+	int ret;
+
+	memcpy(tmp, arg, s);
+	arg_params = OPTEEM_GET_PARAMS(arg);
+	tmp_params = OPTEEM_GET_PARAMS(tmp);
+
+	for (n = 0; n < arg->num_params; n++) {
+		if (optee_param_is(arg_params + n, PARAM_MEMREF | PARAM_INOUT))
+			tmp_params[n].u.memref.shm_ref = -1;
+	}
+
+	for (n = 0; n < arg->num_params; n++) {
+		if (optee_param_is(arg_params + n,
+				   PARAM_MEMREF | PARAM_INOUT)) {
+			int fd = -1;
+			struct tee_shm *shm;
+			uint32_t res;
+			struct opteem_param_memref *memref;
+
+			memref = &arg_params[n].u.memref;
+			shm = (struct tee_shm *)(uintptr_t)memref->shm_ref;
+			res = memref_to_user(shm, memref,
+					     &tmp_params[n].u.memref, &fd);
+			/* Propagate kind of error to requesting thread. */
+			if (res != TEEC_SUCCESS) {
+				tmp->ret = res;
+				if (res == TEEC_ERROR_OUT_OF_MEMORY) {
+					/*
+					 * For out of memory it could help
+					 * if tee-supplicant was restarted,
+					 * maybe it leaks something.
+					 */
+					ret = -ENOMEM;
+					goto err;
+				}
+				/* Let supplicant grab next request. */
+				ret = -EAGAIN;
+			}
+			tmp_params[n].u.memref.shm_ref = fd;
+		}
+	}
+
+	if (copy_to_user(buf, tmp, s)) {
+		/* Something is wrong, let supplicant restart and try again */
+		ret = -EINVAL;
+		goto err;
+	}
+	return 0;
+err:
+	for (n = 0; n < arg->num_params; n++) {
+		int fd;
+
+		if (!optee_param_is(arg_params + n, PARAM_MEMREF | PARAM_INOUT))
+			continue;
+		fd = tmp_params[n].u.memref.shm_ref;
+		if (fd >= 0)
+			tee_shm_put_fd(fd);
+	}
+	return ret;
+}
+
+int optee_supp_read(struct tee_context *ctx, void __user *buf, size_t len)
+{
+	struct tee_device *teedev = ctx->teedev;
+	struct optee *optee = tee_get_drvdata(teedev);
+	struct optee_supp *supp = &optee->supp;
+	const size_t s = OPTEEM_GET_ARG_SIZE(OPTEEM_RPC_NUM_PARAMS);
+	int ret;
+
+	if (len != s)
+		return -EINVAL;
+
+	/*
+	 * In case two supplicants or two threads in one supplicant is
+	 * calling this function simultaneously we need to protect the
+	 * data with a mutex which we'll release before returning.
+	 */
+	mutex_lock(&supp->supp_mutex);
+	while (true) {
+		if (supp->supp_next_write) {
+			/*
+			 * optee_supp_read() has been called again without
+			 * a optee_supp_write() in between. Supplicant has
+			 * probably been restarted before it was able to
+			 * write back last result. Abort last request and
+			 * wait for a new.
+			 */
+			if (supp->data_to_supp) {
+				memcpy(supp->data_from_supp,
+				       supp->data_to_supp, s);
+				supp->data_from_supp->ret =
+					TEEC_ERROR_COMMUNICATION;
+				supp->data_to_supp = NULL;
+				supp->supp_next_write = false;
+				up(&supp->data_from_supp_sem);
+			}
+		}
+
+		/*
+		 * This is where supplicant will be hanging most of the
+		 * time, let's make this interruptable so we can easily
+		 * restart supplicant if needed.
+		 */
+		if (down_interruptible(&supp->data_to_supp_sem)) {
+			ret = -ERESTARTSYS;
+			goto out;
+		}
+
+		/* We have exlusive access to the data */
+		ret = optee_supp_copy_to_user(buf, supp->data_to_supp,
+					      supp->data_from_supp);
+		if (!ret)
+			break;
+		supp->data_to_supp = NULL;
+		up(&supp->data_from_supp_sem);
+		if (ret != -EAGAIN)
+			goto out;
+	}
+
+	/* We've consumed the data, set it to NULL */
+	supp->data_to_supp = NULL;
+
+	/* Allow optee_supp_write() below to do its work */
+	supp->supp_next_write = true;
+
+	ret = 0;
+out:
+	mutex_unlock(&supp->supp_mutex);
+	return ret;
+}
+
+int optee_supp_write(struct tee_context *ctx, void __user *buf, size_t len)
+{
+	struct tee_device *teedev = ctx->teedev;
+	struct optee *optee = tee_get_drvdata(teedev);
+	struct optee_supp *supp = &optee->supp;
+	const size_t s = OPTEEM_GET_ARG_SIZE(OPTEEM_RPC_NUM_PARAMS);
+	int ret = 0;
+
+	if (len != s)
+		return -EINVAL;
+
+	/*
+	 * We still have exclusive access to the data since that's how we
+	 * left it when returning from optee_supp_read().
+	 */
+
+	/* See comment on mutex in optee_supp_read() above */
+	mutex_lock(&supp->supp_mutex);
+
+	if (!supp->supp_next_write) {
+		/*
+		 * Something strange is going on, supplicant shouldn't
+		 * enter optee_supp_write() in this state
+		 */
+		ret = -ENOENT;
+		goto out;
+	}
+
+	if (copy_from_user(supp->data_from_supp, buf, s)) {
+		/*
+		 * Something is wrong, let supplicant restart. Next call to
+		 * optee_supp_read() will give an error to the requesting
+		 * thread and release it.
+		 */
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* Data has been populated, set the pointer to NULL */
+	supp->data_from_supp = NULL;
+
+	/* Allow optee_supp_read() above to do its work */
+	supp->supp_next_write = false;
+
+	/* Let the requesting thread continue */
+	up(&supp->data_from_supp_sem);
+out:
+	mutex_unlock(&supp->supp_mutex);
+	return ret;
+}
diff --git a/include/uapi/linux/optee_msg.h b/include/uapi/linux/optee_msg.h
new file mode 100644
index 0000000..338d862
--- /dev/null
+++ b/include/uapi/linux/optee_msg.h
@@ -0,0 +1,368 @@
+/*
+ * Copyright (c) 2015, Linaro Limited
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef OPTEE_MSG_H
+#define OPTEE_MSG_H
+
+#include <linux/types.h>
+
+/*
+ * This file is exported by OP-TEE and is kept in sync between secure
+ * world, normal world kernel driver, and user space client lib.
+ *
+ * This file is divided into three sections.
+ * 1. Formatting of messages.
+ * 2. Requests from normal world
+ * 3. Requests from secure world, Remote Procedure Call (RPC)
+ */
+
+/*****************************************************************************
+ * Part 1 - formatting of messages
+ *****************************************************************************/
+
+/*
+ * Same values as TEE_PARAM_* from TEE Internal API
+ */
+#define OPTEEM_ATTR_TYPE_NONE		0
+#define OPTEEM_ATTR_TYPE_VALUE_INPUT	1
+#define OPTEEM_ATTR_TYPE_VALUE_OUTPUT	2
+#define OPTEEM_ATTR_TYPE_VALUE_INOUT	3
+#define OPTEEM_ATTR_TYPE_MEMREF_INPUT	5
+#define OPTEEM_ATTR_TYPE_MEMREF_OUTPUT	6
+#define OPTEEM_ATTR_TYPE_MEMREF_INOUT	7
+
+#define OPTEEM_ATTR_TYPE_MASK		0x7
+
+/*
+ * Meta parameter to be absorbed by the Secure OS and not passed
+ * to the Trusted Application.
+ *
+ * Currently only used for struct opteem_meta_open_session which
+ * is added to OPTEEM_CMD_OPEN_SESSION.
+ */
+#define OPTEEM_ATTR_META		0x8
+
+
+/**
+ * struct opteem_param_memref - memory reference
+ * @buf_ptr:	Address of the buffer
+ * @size:	Size of the buffer
+ * @shm_ref:	Shared memory reference only used by normal world
+ *
+ * Secure and normal world communicates pointers as physical address
+ * instead of the virtual address. This is because secure and normal world
+ * have completely independent memory mapping. Normal world can even have a
+ * hypervisor which need to translate the guest physical address (AKA IPA
+ * in ARM documentation) to a real physical address before passing the
+ * structure to secure world.
+ */
+struct opteem_param_memref {
+	__u64 buf_ptr;
+	__u64 size;
+	__u64 shm_ref;
+};
+
+/**
+ * struct opteem_param_value - values
+ * @a: first value
+ * @b: second value
+ * @c: third value
+ */
+struct opteem_param_value {
+	__u64 a;
+	__u64 b;
+	__u64 c;
+};
+
+/**
+ * struct opteem_param - parameter
+ * @attr: attributes
+ * @memref: a memory reference
+ * @value: a value
+ *
+ * @attr & OPTEEM_ATTR_TYPE_MASK indicates if memref or value is used in
+ * the union. OPTEEM_ATTR_TYPE_VALUE_* indicates value and
+ * OPTEEM_ATTR_TYPE_MEMREF_* indicates memref. OPTEEM_ATTR_TYPE_NONE
+ * indicates that none of the members are used.
+ */
+struct opteem_param {
+	__u64 attr;
+	union {
+		struct opteem_param_memref memref;
+		struct opteem_param_value value;
+	} u;
+};
+
+/**
+ * struct opteem_arg - call argument
+ * @cmd: Command, one of OPTEEM_CMD_* or OPTEEM_RPC_CMD_*
+ * @func: Trusted Application function, specific to the Trusted Application,
+ *	     used if cmd == OPTEEM_CMD_INVOKE_COMMAND
+ * @session: In parameter for all OPTEEM_CMD_* except
+ *	     OPTEEM_CMD_OPEN_SESSION where it's an output parameter instead
+ * @ret: return value
+ * @ret_origin: origin of the return value
+ * @num_params: number of parameters supplied to the OS Command
+ * @params: the parameters supplied to the OS Command
+ *
+ * All normal calls to Trusted OS uses this struct. If cmd requires further
+ * information than what these field holds it can be passed as a parameter
+ * tagged as meta (setting the OPTEEM_ATTR_META bit in corresponding
+ * param_attrs). All parameters tagged as meta has to come first.
+ */
+struct opteem_arg {
+	__u32 cmd;
+	__u32 func;
+	__u32 session;
+	__u32 ret;
+	__u32 ret_origin;
+	__u32 num_params __aligned(8);
+
+	/*
+	 * num_params is 8 byte aligned since the 'struct opteem_param'
+	 * which follows requires 8 byte alignment.
+	 *
+	 * Commented out element used to visualize the layout dynamic part
+	 * of the struct. This field is not available at all if
+	 * num_params == 0.
+	 *
+	 * params is accessed through the macro OPTEEM_GET_PARAMS
+	 *
+	 * struct opteem_param params[num_params];
+	 */
+};
+
+/**
+ * OPTEEM_GET_PARAMS - return pointer to struct opteem_param *
+ *
+ * @x: Pointer to a struct opteem_arg
+ *
+ * Returns a pointer to the params[] inside a struct opteem_arg.
+ */
+#define OPTEEM_GET_PARAMS(x) \
+	(struct opteem_param *)(((struct opteem_arg *)(x)) + 1)
+
+/**
+ * OPTEEM_GET_ARG_SIZE - return size of struct opteem_arg
+ *
+ * @num_params: Number of parameters embedded in the struct opteem_arg
+ *
+ * Returns the size of the struct opteem_arg together with the number
+ * of embedded parameters.
+ */
+#define OPTEEM_GET_ARG_SIZE(num_params) \
+	(sizeof(struct opteem_arg) + \
+	 sizeof(struct opteem_param) * (num_params))
+
+/* Length in bytes of a UUID */
+#define OPTEEM_UUID_LEN	16
+
+/**
+ * struct opteem_meta_open_session - additional parameters for
+ *				     OPTEEM_CMD_OPEN_SESSION
+ * @uuid: UUID of the Trusted Application
+ * @clnt_uuid: UUID of client
+ * @clnt_login: Login class of client, TEE_LOGIN_* if being Global Platform
+ *		compliant
+ *
+ * This struct is passed in the first parameter as an input memref tagged
+ * as meta on an OPTEEM_CMD_OPEN_SESSION cmd.
+ */
+struct opteem_meta_open_session {
+	__u8 uuid[OPTEEM_UUID_LEN];
+	__u8 clnt_uuid[OPTEEM_UUID_LEN];
+	__u32 clnt_login;
+};
+
+/**
+ * struct optee_cmd_prefix - initial header for all user space buffers
+ * @func_id:	Function Id OPTEEM_FUNCID_* below
+ * @pad:	padding to make the struct size a multiple of 8 bytes
+ *
+ * This struct is 8 byte aligned since it's always followed by a struct
+ * opteem_arg which requires 8 byte alignment.
+ */
+struct opteem_cmd_prefix {
+	__u32 func_id;
+	__u32 pad __aligned(8);
+};
+
+/*****************************************************************************
+ * Part 2 - requests from normal world
+ *****************************************************************************/
+
+/*
+ * Return the following UID if using API specified in this file without
+ * further extentions:
+ * 384fb3e0-e7f8-11e3-af63-0002a5d5c51b.
+ * Represented in 4 32-bit words in OPTEEM_UID_0, OPTEEM_UID_1,
+ * OPTEEM_UID_2, OPTEEM_UID_3.
+ */
+#define OPTEEM_UID_0		0x384fb3e0
+#define OPTEEM_UID_1		0xe7f811e3
+#define OPTEEM_UID_2		0xaf630002
+#define OPTEEM_UID_3		0xa5d5c51b
+#define OPTEEM_FUNCID_CALLS_UID	0xFF01
+
+/*
+ * Returns 2.0 if using API specified in this file without further extentions.
+ * Represented in 2 32-bit words in OPTEEM_REVISION_MAJOR and
+ * OPTEEM_REVISION_MINOR
+ */
+#define OPTEEM_REVISION_MAJOR	2
+#define OPTEEM_REVISION_MINOR	0
+#define OPTEEM_FUNCID_CALLS_REVISION	0xFF03
+
+/*
+ * Get UUID of Trusted OS.
+ *
+ * Used by non-secure world to figure out which Trusted OS is installed.
+ * Note that returned UUID is the UUID of the Trusted OS, not of the API.
+ *
+ * Returns UUID in 4 32-bit words in the same way as OPTEEM_FUNCID_CALLS_UID
+ * described above.
+ */
+#define OPTEEM_OS_OPTEE_UUID_0		0x486178e0
+#define OPTEEM_OS_OPTEE_UUID_1		0xe7f811e3
+#define OPTEEM_OS_OPTEE_UUID_2		0xbc5e0002
+#define OPTEEM_OS_OPTEE_UUID_3		0xa5d5c51b
+#define OPTEEM_FUNCID_GET_OS_UUID	0x0000
+
+/*
+ * Get revision of Trusted OS.
+ *
+ * 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.
+ *
+ * Returns revision in 2 32-bit words in the same way as OPTEEM_CALLS_REVISION
+ * described above.
+ */
+#define OPTEEM_OS_OPTEE_REVISION_MAJOR	1
+#define OPTEEM_OS_OPTEE_REVISION_MINOR	0
+#define OPTEEM_FUNCID_GET_OS_REVISION	0x0001
+
+/*
+ * Do a secure call with struct opteem_arg as argument
+ * The OPTEEM_CMD_* below defines what goes in struct opteem_arg::cmd
+ *
+ * For OPTEEM_CMD_OPEN_SESSION the first parameter is tagged as meta, holding
+ * a memref with a struct opteem_meta_open_session which is needed find the
+ * Trusted Application and to indicate the credentials of the client.
+ *
+ * For OPTEEM_CMD_INVOKE_COMMAND struct opteem_arg::func is Trusted
+ * Application function, specific to the Trusted Application.
+ */
+#define OPTEEM_CMD_OPEN_SESSION		0
+#define OPTEEM_CMD_INVOKE_COMMAND	1
+#define OPTEEM_CMD_CLOSE_SESSION	2
+#define OPTEEM_CMD_CANCEL		3
+#define OPTEEM_FUNCID_CALL_WITH_ARG	0x0004
+
+/*
+ * Do a write response from tee-supplicant with struct opteem_arg as argument
+ */
+#define OPTEEM_FUNCID_SUPP_CMD_WRITE	0x1000
+
+/*
+ * Do a read request from tee-supplicant with struct opteem_arg as argument
+ */
+#define OPTEEM_FUNCID_SUPP_CMD_READ	0x1001
+
+/*****************************************************************************
+ * Part 3 - Requests from secure world, RPC
+ *****************************************************************************/
+
+/*
+ * All RPC is done with a struct opteem_arg as bearer of information,
+ * struct opteem_arg::arg holds values defined by OPTEEM_RPC_CMD_* below
+ */
+
+/*
+ * Number of parameters used in RPC communication, always this number but
+ * for some commands a parameter may be set to unused.
+ */
+#define OPTEEM_RPC_NUM_PARAMS 2
+
+/*
+ * Load a TA into memory
+ * [in] param[0]	memref holding a uuid (OPTEEM_UUID_LEN bytes) of the
+ *			TA to load
+ * [out] param[1]	memref allocated to hold the TA content. memref.buf
+ *			may be == NULL to query the size of the TA content.
+ *			memref.size is always updated with the actual size
+ *			of the TA content. If returned memref.size is larger
+ *			than the supplied memref.size, not content is loaded.
+ * [out] arg.ret	return value of request, 0 on success.
+ */
+#define OPTEEM_RPC_CMD_LOAD_TA		0
+
+/*
+ * Reserved
+ */
+#define OPTEEM_RPC_CMD_RPMB		1
+
+/*
+ * File system access, defined in tee-supplicant
+ */
+#define OPTEEM_RPC_CMD_FS		2
+
+/*
+ * Get time, defined in tee-supplicant
+ */
+#define OPTEEM_RPC_CMD_GET_TIME		3
+
+/*
+ * Sleep mutex, helper for secure world to implement a sleeping mutex.
+ * struct opteem_arg::func	one of OPTEEM_RPC_SLEEP_MUTEX_* below
+ *
+ * OPTEEM_RPC_SLEEP_MUTEX_WAIT
+ * [in] param[0].value	.a sleep mutex key
+ *			.b wait tick
+ * [not used] param[1]
+ *
+ * OPTEEM_RPC_SLEEP_MUTEX_WAKEUP
+ * [in] param[0].value	.a sleep mutex key
+ *			.b wait after
+ * [not used] param[1]
+ *
+ * OPTEEM_RPC_SLEEP_MUTEX_DELETE
+ * [in] param[0].value	.a sleep mutex key
+ * [not used] param[1]
+ */
+#define OPTEEM_RPC_SLEEP_MUTEX_WAIT	0
+#define OPTEEM_RPC_SLEEP_MUTEX_WAKEUP	1
+#define OPTEEM_RPC_SLEEP_MUTEX_DELETE	2
+#define OPTEEM_RPC_CMD_SLEEP_MUTEX	4
+
+/*
+ * Suspend execution
+ *
+ * [in] param[0].value	.a number of milliseconds to suspend
+ */
+#define OPTEEM_RPC_CMD_SUSPEND		5
+
+#endif /* OPTEE_MSG_H */
-- 
1.9.1




More information about the linux-arm-kernel mailing list