Note that the TEESMC protocol is version 2
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org --- Hi,
Here's the full OP-TEE driver implementation. This patch is also available at http://shorl.com/relybehadrele
Corresponding patches for optee_client here https://github.com/jenswi-linaro/optee_client/tree/gen_driver and for optee_os here https://github.com/jenswi-linaro/optee_os/tree/gen_driver
Regards, Jens --- drivers/sec-hw/optee/Makefile | 3 + drivers/sec-hw/optee/optee.c | 530 ++++++++++++++++++++++++++- drivers/sec-hw/optee/optee_private.h | 110 ++++++ drivers/sec-hw/optee/optee_rpc.c | 301 +++++++++++++++ drivers/sec-hw/optee/optee_smc_a32.S | 30 ++ drivers/sec-hw/optee/optee_supp.c | 352 ++++++++++++++++++ include/linux/sec-hw/optee/optee.h | 28 ++ include/linux/sec-hw/optee/teesmc.h | 584 ++++++++++++++++++++++++++++++ include/linux/sec-hw/optee/teesmc_optee.h | 122 +++++++ 9 files changed, 2040 insertions(+), 20 deletions(-) create mode 100644 drivers/sec-hw/optee/optee_private.h create mode 100644 drivers/sec-hw/optee/optee_rpc.c create mode 100644 drivers/sec-hw/optee/optee_smc_a32.S create mode 100644 drivers/sec-hw/optee/optee_supp.c create mode 100644 include/linux/sec-hw/optee/optee.h create mode 100644 include/linux/sec-hw/optee/teesmc.h create mode 100644 include/linux/sec-hw/optee/teesmc_optee.h
diff --git a/drivers/sec-hw/optee/Makefile b/drivers/sec-hw/optee/Makefile index 11a3dfc..e9264c1 100644 --- a/drivers/sec-hw/optee/Makefile +++ b/drivers/sec-hw/optee/Makefile @@ -1 +1,4 @@ obj-y += optee.o +obj-y += optee_smc_a32.o +obj-y += optee_rpc.o +obj-y += optee_supp.o diff --git a/drivers/sec-hw/optee/optee.c b/drivers/sec-hw/optee/optee.c index f60f061..f9e6337 100644 --- a/drivers/sec-hw/optee/optee.c +++ b/drivers/sec-hw/optee/optee.c @@ -14,59 +14,466 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/slab.h> +#include <linux/uaccess.h> #include <linux/sec-hw/tee_drv.h> +#include "optee_private.h"
#define DRIVER_NAME "tee-optee" #define OPTEE_VERSION 1
-struct optee { - struct tee_device *supp_teedev; - struct tee_device *teedev; - struct device *dev; +struct put_shm { + struct tee_shm **shm; + size_t num_shm; };
-struct optee_filp_state { - int dummy; -}; +static u32 do_call_with_arg(struct tee_filp *teefilp, u32 funcid, + phys_addr_t parg);
-static struct optee_filp_state *optee_new_filp_state(void) +static void optee_call_lock(struct optee_call_sync *callsync) { - struct optee_filp_state *filpstate; + mutex_lock(&callsync->mutex); +}
- filpstate = kzalloc(sizeof(struct optee_filp_state), GFP_KERNEL); - return filpstate; +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_destroy_filp_state(struct optee_filp_state *filpstate) +static void optee_call_unlock(struct optee_call_sync *callsync) { - if (filpstate) - kzfree(filpstate); + /* + * 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 void optee_get_version(struct tee_filp *teefilp, u32 *version, u8 *uuid) { - *version = OPTEE_VERSION; + *version = TEESMC_OPTEE_REVISION_MAJOR; memset(uuid, 0, TEE_UUID_SIZE); }
static int optee_open(struct tee_filp *teefilp) { - teefilp->filp_data = optee_new_filp_state(); - if (!teefilp->filp_data) + struct optee_filp_state *filpstate; + + filpstate = kzalloc(sizeof(struct optee_filp_state), GFP_KERNEL); + if (!filpstate) return -ENOMEM; + + mutex_init(&filpstate->mutex); + INIT_LIST_HEAD(&filpstate->sess_list); + + teefilp->filp_data = filpstate; return 0; }
static void optee_release(struct tee_filp *teefilp) { - optee_destroy_filp_state(teefilp->filp_data); + struct optee *optee = tee_get_drvdata(teefilp->teedev); + struct optee_filp_state *filpstate = teefilp->filp_data; + struct tee_shm *shm; + struct teesmc_arg *arg; + phys_addr_t parg; + + if (!filpstate) + return; + + shm = tee_shm_alloc(teefilp->teedev, NULL, sizeof(struct teesmc_arg), + TEE_SHM_MAPPED); + if (shm) { + arg = tee_shm_get_va(shm, 0); + /* + * If va2pa fails for some reason, set free shm and set it + * NULL to know that we should not 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)) { + tee_shm_free(shm); + shm = NULL; + } + } + + while (true) { + struct optee_session *sess; + + /* + * TODO: Do we need to lock the filpstate when it's about + * to be released? + */ + sess = list_first_entry_or_null(&filpstate->sess_list, + struct optee_session, + list_node); + if (!sess) + break; + list_del(&sess->list_node); + if (shm) { + dev_dbg(optee->dev, "%s: closing session 0x%x\n", + __func__, sess->session_id); + memset(arg, 0, sizeof(*arg)); + arg->cmd = TEESMC_CMD_CLOSE_SESSION; + arg->session = sess->session_id; + do_call_with_arg(teefilp, TEESMC32_CALL_WITH_ARG, parg); + } + kfree(sess); + } + kfree(filpstate); + + if (shm) + tee_shm_free(shm); + teefilp->filp_data = NULL; }
+static int optee_cmd_uuid(struct tee_filp *teefilp, + struct optee_cmd_prefix *arg, size_t len) +{ + struct optee_smc_param param = { .a0 = arg->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(¶m); + data[0] = param.a0; + data[1] = param.a1; + data[2] = param.a2; + data[3] = param.a3; + return 0; +} + +static int optee_cmd_revision(struct tee_filp *teefilp, + struct optee_cmd_prefix *arg, size_t len) +{ + struct optee_smc_param param = { .a0 = arg->smc_id }; + u32 *data = (u32 *)(arg + 1); + size_t data_len = len - sizeof(*arg); + + if (data_len < 2 * sizeof(u32)) + return -EINVAL; + + /* This is a fast-call no need to take a mutex */ + + optee_smc(¶m); + data[0] = param.a0; + data[1] = param.a1; + return 0; +} + +static int teesmc_arg_from_user(struct teesmc_arg *arg, size_t size, + struct put_shm *put_shm) +{ + struct teesmc_param *param; + size_t n; + size_t s = TEESMC_GET_ARG_SIZE(arg->num_params); + + if (size < sizeof(struct teesmc_arg)) + return -EINVAL; + if (size != s) + return -EINVAL; + + if (!arg->num_params) { + put_shm->shm = NULL; + return 0; + } + param = TEESMC_GET_PARAMS(arg); + + put_shm->shm = kcalloc(arg->num_params, sizeof(struct tee_shm *), + GFP_KERNEL); + if (!put_shm->shm) + return -ENOMEM; + put_shm->num_shm = arg->num_params; + + 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 & ~(TEESMC_ATTR_TYPE_MASK | TEESMC_ATTR_META)) + return -EINVAL; + + switch (param[n].attr & TEESMC_ATTR_TYPE_MASK) { + case TEESMC_ATTR_TYPE_MEMREF_INPUT: + case TEESMC_ATTR_TYPE_MEMREF_OUTPUT: + case TEESMC_ATTR_TYPE_MEMREF_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->shm[n] = shm; + ret = tee_shm_get_pa(shm, shm_offs, &pa); + if (ret) + return ret; + param[n].attr |= TEESMC_ATTR_CACHE_DEFAULT << + TEESMC_ATTR_CACHE_SHIFT; + param[n].u.memref.buf_ptr = pa; + break; + default: + break; + } + } + + return 0; +} + +static int teesmc_arg_to_user(struct teesmc_arg *arg, + struct teesmc_arg __user *uarg) +{ + struct teesmc_param *param = TEESMC_GET_PARAMS(arg); + struct teesmc_param __user *uparam = (void __user *)(uarg + 1); + size_t n; + + if (arg->cmd == TEESMC_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++) { + switch (param[n].attr & TEESMC_ATTR_TYPE_MASK) { + case TEESMC_ATTR_TYPE_VALUE_OUTPUT: + case TEESMC_ATTR_TYPE_VALUE_INOUT: + if (put_user(param[n].u.value.a, + &uparam[n].u.value.a) || + put_user(param[n].u.value.b, &uparam[n].u.value.b)) + return -EINVAL; + break; + case TEESMC_ATTR_TYPE_MEMREF_OUTPUT: + case TEESMC_ATTR_TYPE_MEMREF_INOUT: + if (put_user(param[n].u.memref.size, + &uparam[n].u.memref.size)) + return -EINVAL; + break; + default: + break; + } + + } + return 0; +} + +static u32 do_call_with_arg(struct tee_filp *teefilp, u32 funcid, + phys_addr_t parg) +{ + struct optee *optee = tee_get_drvdata(teefilp->teedev); + u32 ret; + struct optee_smc_param param = { }; + + if (irqs_disabled()) + funcid |= TEESMC_FAST_CALL; + + param.a1 = parg; + optee_call_lock(&optee->callsync); + while (true) { + param.a0 = funcid; + + optee_smc(¶m); + ret = param.a0; + + if (ret == TEESMC_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 (TEESMC_RETURN_IS_RPC(ret)) { + /* Process the RPC. */ + optee_call_unlock(&optee->callsync); + funcid = optee_handle_rpc(teefilp, ¶m); + optee_call_lock(&optee->callsync); + } else { + break; + } + } + optee_call_unlock(&optee->callsync); + return ret; +} + +/* Requires the filpstate mutex to be held */ +static struct optee_session *find_session(struct optee_filp_state *filpstate, + u32 session_id) +{ + struct optee_session *sess; + + list_for_each_entry(sess, &filpstate->sess_list, list_node) + if (sess->session_id == session_id) + return sess; + return NULL; +} + +static int optee_cmd_call_with_arg(struct tee_filp *teefilp, + struct tee_shm *shm, struct optee_cmd_prefix *arg, + struct optee_cmd_prefix __user *uarg, size_t len) +{ + struct optee *optee = tee_get_drvdata(teefilp->teedev); + struct optee_filp_state *filpstate = teefilp->filp_data; + struct put_shm put_shm = { .shm = NULL }; + struct teesmc_arg *teesmc_arg; + struct teesmc_arg __user *teesmc_uarg; + struct optee_session *sess; + phys_addr_t teesmc_parg; + int ret; + size_t n; + + teesmc_arg = (struct teesmc_arg *)(arg + 1); + teesmc_uarg = (struct teesmc_arg __user *)(uarg + 1); + + ret = teesmc_arg_from_user(teesmc_arg, len - sizeof(*arg), + &put_shm); + if (ret) + goto out; + + ret = tee_shm_va2pa(shm, teesmc_arg, &teesmc_parg); + if (ret) + goto out; + + switch (teesmc_arg->cmd) { + case TEESMC_CMD_OPEN_SESSION: + /* Allocate memory to be able to store the new session below. */ + sess = kzalloc(sizeof(struct optee_session), GFP_KERNEL); + if (!sess) { + ret = -ENOMEM; + goto out; + } + break; + case TEESMC_CMD_CLOSE_SESSION: + /* A session is about to be closed, remove it from the list */ + dev_dbg(optee->dev, "%s: closing session 0x%x\n", + __func__, teesmc_arg->session); + mutex_lock(&filpstate->mutex); + sess = find_session(filpstate, teesmc_arg->session); + if (sess) { + list_del(&sess->list_node); + kfree(sess); + } + mutex_unlock(&filpstate->mutex); + if (!sess) { + ret = -EINVAL; + goto out; + } + sess = NULL; + break; + + case TEESMC_CMD_INVOKE_COMMAND: + case TEESMC_CMD_CANCEL: + mutex_lock(&filpstate->mutex); + sess = find_session(filpstate, teesmc_arg->session); + mutex_unlock(&filpstate->mutex); + if (!sess) { + ret = -EINVAL; + goto out; + } + sess = NULL; + break; + + default: + ret = -EINVAL; + goto out; + } + + if (do_call_with_arg(teefilp, arg->smc_id, teesmc_parg)) { + teesmc_arg->ret = TEEC_ERROR_COMMUNICATION; + teesmc_arg->ret_origin = TEEC_ORIGIN_COMMS; + } + + ret = teesmc_arg_to_user(teesmc_arg, teesmc_uarg); + + if (sess) { + /* A new session has been created, add it to the list. */ + if (teesmc_arg->ret == TEEC_SUCCESS) { + sess->session_id = teesmc_arg->session; + mutex_lock(&filpstate->mutex); + list_add(&sess->list_node, &filpstate->sess_list); + mutex_unlock(&filpstate->mutex); + } else + kfree(sess); + } +out: + if (put_shm.shm) { + for (n = 0; n < put_shm.num_shm; n++) + if (put_shm.shm[n]) + tee_shm_put(put_shm.shm[n]); + kfree(put_shm.shm); + } + return ret; +} + static int optee_cmd(struct tee_filp *teefilp, void __user *buf, size_t len) { - return -EINVAL; + struct optee_cmd_prefix *arg; + struct tee_shm *shm; + int ret; + + if (len > OPTEE_MAX_ARG_SIZE || len < sizeof(*arg)) + return -EINVAL; + + shm = tee_shm_alloc(teefilp->teedev, NULL, len, TEE_SHM_MAPPED); + if (!shm) + return -ENOMEM; + + arg = tee_shm_get_va(shm, 0); + if (IS_ERR(arg) || copy_from_user(arg, buf, len)) { + ret = -EINVAL; + goto out; + } + + switch (arg->smc_id) { + case TEESMC32_CALLS_UID: + case TEESMC32_CALL_GET_OS_UUID: + ret = optee_cmd_uuid(teefilp, arg, len); + goto out; + case TEESMC32_CALLS_REVISION: + case TEESMC32_CALL_GET_OS_REVISION: + ret = optee_cmd_revision(teefilp, arg, len); + goto out; + case TEESMC32_CALL_WITH_ARG: + case TEESMC32_FASTCALL_WITH_ARG: + ret = optee_cmd_call_with_arg(teefilp, 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: + if (shm) + tee_shm_free(shm); + return ret; }
static int optee_shm_share(struct tee_shm *shm) @@ -94,10 +501,68 @@ static struct tee_desc optee_desc = { .owner = THIS_MODULE, };
+static int optee_supp_req(struct tee_filp *teefilp, void __user *buf, + size_t len) +{ + struct optee_cmd_prefix *arg; + struct tee_shm *shm; + int ret; + + if (len > OPTEE_MAX_ARG_SIZE || len < sizeof(*arg)) + return -EINVAL; + + shm = tee_shm_alloc(teefilp->teedev, NULL, len, TEE_SHM_MAPPED); + if (!shm) + return -ENOMEM; + + arg = tee_shm_get_va(shm, 0); + if (IS_ERR(arg) || copy_from_user(arg, buf, len)) { + ret = -EINVAL; + goto out; + } + + switch (arg->smc_id) { + case TEESMC32_CALLS_UID: + case TEESMC32_CALL_GET_OS_UUID: + ret = optee_cmd_uuid(teefilp, arg, len); + break; + case TEESMC32_CALLS_REVISION: + case TEESMC32_CALL_GET_OS_REVISION: + ret = optee_cmd_revision(teefilp, arg, len); + break; + default: + ret = -EINVAL; + break; + } + +out: + if (!ret) { + if (copy_to_user(buf, arg, len)) + ret = -EINVAL; + } + if (shm) + tee_shm_free(shm); + return ret; +} + static int optee_supp_cmd(struct tee_filp *teefilp, void __user *buf, size_t len) { - return -EINVAL; + struct optee_cmd_prefix arg; + + if (len < sizeof(arg) || copy_from_user(&arg, buf, sizeof(arg))) + return -EINVAL; + + switch (arg.smc_id) { + case 0: /* RPC_CMD_WRITE */ + return optee_supp_write(teefilp, buf + sizeof(arg), + len - sizeof(arg)); + case 1: /* RPC_CMD_READ */ + return optee_supp_read(teefilp, buf + sizeof(arg), + len - sizeof(arg)); + default: + return optee_supp_req(teefilp, buf, len); + } }
static struct tee_driver_ops optee_supp_ops = { @@ -116,11 +581,28 @@ static struct tee_desc optee_supp_desc = { .flags = TEE_DESC_PRIVILEGED, };
+static bool teesmc_api_uid_is_optee_api(void) +{ + struct optee_smc_param param = { .a0 = TEESMC32_CALLS_UID }; + + optee_smc(¶m); + + if (param.a0 == TEESMC_OPTEE_UID_R0 && + param.a1 == TEESMC_OPTEE_UID_R1 && + param.a2 == TEESMC_OPTEE_UID_R2 && + param.a3 == TEESMC_OPTEE_UID32_R3) + return true; + return false; +} + static int optee_probe(struct platform_device *pdev) { struct optee *optee; int ret;
+ if (!teesmc_api_uid_is_optee_api()) + return -EINVAL; + optee = devm_kzalloc(&pdev->dev, sizeof(*optee), GFP_KERNEL); if (!optee) return -ENOMEM; @@ -142,11 +624,18 @@ static int optee_probe(struct platform_device *pdev) 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); + platform_set_drvdata(pdev, optee);
dev_info(&pdev->dev, "initialized driver\n"); return 0; err: + /* TODO proper cleanup */ if (optee->teedev) tee_unregister(optee->teedev); devm_kfree(&pdev->dev, optee); @@ -157,6 +646,7 @@ static int optee_remove(struct platform_device *pdev) { struct optee *optee = platform_get_drvdata(pdev);
+ /* TODO proper cleanup */ tee_unregister(optee->teedev); tee_unregister(optee->supp_teedev);
diff --git a/drivers/sec-hw/optee/optee_private.h b/drivers/sec-hw/optee/optee_private.h new file mode 100644 index 0000000..aab4ed7 --- /dev/null +++ b/drivers/sec-hw/optee/optee_private.h @@ -0,0 +1,110 @@ +/* + * 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/sec-hw/tee_drv.h> +#include <linux/sec-hw/optee/teesmc.h> +#include <linux/sec-hw/optee/teesmc_optee.h> +#include <linux/sec-hw/optee/optee.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_private { + struct mutex mu; + struct list_head db; +}; + +struct optee_mutex_wait { + struct list_head link; + struct completion comp; + struct mutex mu; + u32 wait_after; + u32 key; +}; + + +struct optee_supp { + bool supp_next_write; + size_t data_size; + const struct teesmc_arg *data_to_supp; + struct teesmc_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_private mutex_wait; + struct optee_supp supp; +}; + +struct optee_session { + struct list_head list_node; + u32 session_id; +}; + +struct optee_filp_state { + struct mutex mutex; + struct list_head sess_list; +}; + + +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_filp *teefilp, struct optee_smc_param *param); + +void optee_mutex_wait_init(struct optee_mutex_wait_private *priv); +void optee_mutex_wait_exit(struct optee_mutex_wait_private *priv); + +void optee_supp_thrd_req(struct tee_filp *teefilp, struct teesmc_arg *arg); +int optee_supp_read(struct tee_filp *teefilp, void __user *buf, size_t len); +int optee_supp_write(struct tee_filp *teefilp, void __user *buf, size_t len); +void optee_supp_init(struct optee_supp *supp); + +#endif /*OPTEE_PRIVATE_H*/ diff --git a/drivers/sec-hw/optee/optee_rpc.c b/drivers/sec-hw/optee/optee_rpc.c new file mode 100644 index 0000000..5ca30b8 --- /dev/null +++ b/drivers/sec-hw/optee/optee_rpc.c @@ -0,0 +1,301 @@ +/* + * 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/sec-hw/tee_drv.h> +#include "optee_private.h" + +/* + * Handled within the driver only + * Keep aligned with optee_os (secure space) + */ +#define TEE_RPC_MUTEX_WAIT 0x20000000 +#define TEE_RPC_WAIT 0x30000000 + +/* Parameters for TEE_RPC_WAIT_MUTEX above */ +#define TEE_MUTEX_WAIT_SLEEP 0 +#define TEE_MUTEX_WAIT_WAKEUP 1 +#define TEE_MUTEX_WAIT_DELETE 2 + +/* + * 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 *optee_mutex_wait_get(struct device *dev, + struct optee_mutex_wait_private *priv, u32 key) +{ + struct optee_mutex_wait *w; + + mutex_lock(&priv->mu); + + list_for_each_entry(w, &priv->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, &priv->db); +out: + mutex_unlock(&priv->mu); + return w; +} + +static void optee_mutex_wait_delete_entry(struct optee_mutex_wait *w) +{ + list_del(&w->link); + mutex_destroy(&w->mu); + kfree(w); +} + +static void optee_mutex_wait_delete(struct device *dev, + struct optee_mutex_wait_private *priv, + u32 key) +{ + struct optee_mutex_wait *w; + + mutex_lock(&priv->mu); + + list_for_each_entry(w, &priv->db, link) { + if (w->key == key) { + optee_mutex_wait_delete_entry(w); + break; + } + } + + mutex_unlock(&priv->mu); +} + +static void optee_mutex_wait_wakeup(struct device *dev, + struct optee_mutex_wait_private *priv, + u32 key, u32 wait_after) +{ + struct optee_mutex_wait *w = optee_mutex_wait_get(dev, priv, key); + + if (!w) + return; + + mutex_lock(&w->mu); + w->wait_after = wait_after; + mutex_unlock(&w->mu); + complete(&w->comp); +} + +static void optee_mutex_wait_sleep(struct device *dev, + struct optee_mutex_wait_private *priv, + u32 key, u32 wait_tick) +{ + struct optee_mutex_wait *w = optee_mutex_wait_get(dev, priv, key); + u32 wait_after; + + if (!w) + return; + + mutex_lock(&w->mu); + wait_after = w->wait_after; + mutex_unlock(&w->mu); + + if (TICK_GT(wait_tick, wait_after)) + wait_for_completion_timeout(&w->comp, HZ); +} + +void optee_mutex_wait_init(struct optee_mutex_wait_private *priv) +{ + mutex_init(&priv->mu); + INIT_LIST_HEAD(&priv->db); +} + +void optee_mutex_wait_exit(struct optee_mutex_wait_private *priv) +{ + /* + * It's the callers responibility to ensure that no one is using + * anything inside priv. + */ + + mutex_destroy(&priv->mu); + while (!list_empty(&priv->db)) { + struct optee_mutex_wait *w = + list_first_entry(&priv->db, + struct optee_mutex_wait, + link); + optee_mutex_wait_delete_entry(w); + } +} + +static void handle_rpc_func_cmd_mutex_wait(struct tee_filp *teefilp, + struct optee *optee, struct teesmc_arg *arg) +{ + struct device *dev = optee->dev; + struct teesmc_param *params; + + if (arg->num_params != 2) + goto bad; + + params = TEESMC_GET_PARAMS(arg); + + if ((params[0].attr & TEESMC_ATTR_TYPE_MASK) != + TEESMC_ATTR_TYPE_VALUE_INPUT) + goto bad; + if (params[1].attr != TEESMC_ATTR_TYPE_NONE) + goto bad; + + switch (params[0].u.value.a) { + case TEE_MUTEX_WAIT_SLEEP: + optee_mutex_wait_sleep(dev, &optee->mutex_wait, + params[0].u.value.b, + params[0].u.value.c); + break; + case TEE_MUTEX_WAIT_WAKEUP: + optee_mutex_wait_wakeup(dev, &optee->mutex_wait, + params[0].u.value.b, + params[0].u.value.c); + break; + case TEE_MUTEX_WAIT_DELETE: + optee_mutex_wait_delete(dev, &optee->mutex_wait, + params[0].u.value.b); + break; + default: + goto bad; + } + + arg->ret = TEEC_SUCCESS; + return; +bad: + arg->ret = TEEC_ERROR_BAD_PARAMETERS; +} + +static void handle_rpc_func_cmd_wait(struct teesmc_arg *arg) +{ + struct teesmc_param *params; + u32 msec_to_wait; + + if (arg->num_params != 2) + goto bad; + + params = TEESMC_GET_PARAMS(arg); + if (params[0].attr != TEESMC_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_filp *teefilp, struct optee *optee, + u32 parg) +{ + struct teesmc_arg *arg; + void *va; + struct tee_shm *shm; + + shm = tee_shm_find_by_pa(teefilp->teedev, 0, parg); + if (!shm) { + dev_err(optee->dev, "%s: cannot find shm for parg 0x%x\n", + __func__, parg); + return; + } + if (tee_shm_pa2va(shm, parg, &va)) { + dev_err(optee->dev, "%s: pa2va 0x%x failed\n", + __func__, parg); + return; + } + arg = va; + + switch (arg->cmd) { + case TEE_RPC_MUTEX_WAIT: + handle_rpc_func_cmd_mutex_wait(teefilp, optee, arg); + break; + case TEE_RPC_WAIT: + handle_rpc_func_cmd_wait(arg); + break; + default: + optee_supp_thrd_req(teefilp, arg); + } +} + +u32 optee_handle_rpc(struct tee_filp *teefilp, struct optee_smc_param *param) +{ + struct tee_device *teedev = teefilp->teedev; + struct optee *optee = tee_get_drvdata(teedev); + struct tee_shm *shm; + phys_addr_t pa; + + switch (TEESMC_RETURN_GET_RPC_FUNC(param->a0)) { + case TEESMC_RPC_FUNC_ALLOC_ARG: + shm = tee_shm_alloc(teedev, NULL, param->a1, TEE_SHM_MAPPED); + if (shm) { + if (tee_shm_get_pa(shm, 0, &pa)) + pa = 0; + } else + pa = 0; + param->a1 = pa; + param->a2 = (u64)(uintptr_t)shm; + break; + case TEESMC_RPC_FUNC_ALLOC_PAYLOAD: + shm = tee_shm_alloc(teedev, teefilp, param->a1, + TEE_SHM_MAPPED | TEE_SHM_DMA_BUF); + if (!shm) { + param->a1 = 0; + break; + } + if (tee_shm_get_pa(shm, 0, &pa)) { + tee_shm_free(shm); + param->a1 = 0; + break; + } + + param->a1 = pa; + param->a2 = (u64)(uintptr_t)shm; + break; + case TEESMC_RPC_FUNC_FREE_ARG: + case TEESMC_RPC_FUNC_FREE_PAYLOAD: + shm = (struct tee_shm *)(uintptr_t)param->a1; + tee_shm_free(shm); + break; + case TEESMC_RPC_FUNC_IRQ: + break; + case TEESMC_RPC_FUNC_CMD: + handle_rpc_func_cmd(teefilp, optee, param->a1); + break; + default: + dev_warn(optee->dev, "Unknown RPC func 0x%x\n", + (u32)TEESMC_RETURN_GET_RPC_FUNC(param->a0)); + break; + } + + /* TODO TEESMC64 */ + if (irqs_disabled()) + return TEESMC32_FASTCALL_RETURN_FROM_RPC; + else + return TEESMC32_CALL_RETURN_FROM_RPC; +} diff --git a/drivers/sec-hw/optee/optee_smc_a32.S b/drivers/sec-hw/optee/optee_smc_a32.S new file mode 100644 index 0000000..30fe0e5 --- /dev/null +++ b/drivers/sec-hw/optee/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/sec-hw/optee/optee_supp.c b/drivers/sec-hw/optee/optee_supp.c new file mode 100644 index 0000000..5bcd212 --- /dev/null +++ b/drivers/sec-hw/optee/optee_supp.c @@ -0,0 +1,352 @@ +/* + * 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/fdtable.h> +#include <linux/sched.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); +} + + +static void optee_supp_send(struct optee *optee, + const struct teesmc_arg *arg, + struct teesmc_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); +} + +void copy_back_outdata(struct teesmc_arg *arg, + const struct teesmc_arg *resp) +{ + struct teesmc_param *arg_params = TEESMC_GET_PARAMS(arg); + struct teesmc_param *resp_params = TEESMC_GET_PARAMS(resp); + size_t n; + + /* Copy back out and inout parameters */ + for (n = 0; n < arg->num_params; n++) { + switch (arg_params[n].attr & TEESMC_ATTR_TYPE_MASK) { + case TEESMC_ATTR_TYPE_VALUE_OUTPUT: + case TEESMC_ATTR_TYPE_VALUE_INOUT: + arg_params[n].u.value = resp_params[n].u.value; + break; + case TEESMC_ATTR_TYPE_MEMREF_OUTPUT: + case TEESMC_ATTR_TYPE_MEMREF_INOUT: + arg_params[n].u.memref.size = + resp_params[n].u.memref.size; + break; + default: + break; + } + } + arg->ret = resp->ret; + +} + +void optee_supp_thrd_req(struct tee_filp *teefilp, struct teesmc_arg *arg) +{ + struct optee *optee = tee_get_drvdata(teefilp->teedev); + const size_t s = TEESMC_GET_ARG_SIZE(OPTEE_RPC_NUM_BUFS); + struct teesmc_arg *resp; + + if (arg->num_params != OPTEE_RPC_NUM_BUFS) { + 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 teesmc_param_memref *ph_mem, + struct teesmc_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_fd(shm); + if (res < 0) + return TEEC_ERROR_OUT_OF_MEMORY; + + *fd = res; + return TEEC_SUCCESS; +} + +static bool is_memref(struct teesmc_param *param) +{ + switch (param->attr & TEESMC_ATTR_TYPE_MASK) { + case TEESMC_ATTR_TYPE_MEMREF_INPUT: + case TEESMC_ATTR_TYPE_MEMREF_OUTPUT: + case TEESMC_ATTR_TYPE_MEMREF_INOUT: + return true; + default: + return false; + } +} + +static int optee_supp_copy_to_user(void __user *buf, + const struct teesmc_arg *arg, struct teesmc_arg *tmp) +{ + size_t s = TEESMC_GET_ARG_SIZE(OPTEE_RPC_NUM_BUFS); + struct teesmc_param *arg_params; + struct teesmc_param *tmp_params; + size_t n; + int ret; + + memcpy(tmp, arg, s); + + arg_params = TEESMC_GET_PARAMS(arg); + tmp_params = TEESMC_GET_PARAMS(tmp); + + for (n = 0; n < arg->num_params; n++) { + if (is_memref(arg_params + n)) + tmp_params[n].u.memref.shm_ref = -1; + } + + for (n = 0; n < arg->num_params; n++) { + if (is_memref(arg_params + n)) { + int fd = -1; + struct tee_shm *shm; + uint32_t res; + struct teesmc_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 out; + } + /* + * Let the caller of this function 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 out; + } + return 0; +out: + for (n = 0; n < arg->num_params; n++) { + int fd; + + if (!is_memref(arg_params + n)) + continue; + fd = tmp_params[n].u.memref.shm_ref; + if (fd >= 0) + __close_fd(current->files, fd); + } + return ret; + + +} + +int optee_supp_read(struct tee_filp *teefilp, void __user *buf, size_t len) +{ + struct tee_device *teedev = teefilp->teedev; + struct optee *optee = tee_get_drvdata(teedev); + struct optee_supp *supp = &optee->supp; + const size_t s = TEESMC_GET_ARG_SIZE(OPTEE_RPC_NUM_BUFS); + 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 protoct 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_filp *teefilp, void __user *buf, size_t len) +{ + struct tee_device *teedev = teefilp->teedev; + struct optee *optee = tee_get_drvdata(teedev); + struct optee_supp *supp = &optee->supp; + const size_t s = TEESMC_GET_ARG_SIZE(OPTEE_RPC_NUM_BUFS); + 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 fishy is going on, optee_supp_write() shouldn't + * be called 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/linux/sec-hw/optee/optee.h b/include/linux/sec-hw/optee/optee.h new file mode 100644 index 0000000..82525d7 --- /dev/null +++ b/include/linux/sec-hw/optee/optee.h @@ -0,0 +1,28 @@ +/* + * 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_OPTEE_H +#define OPTEE_OPTEE_H + +#include <linux/types.h> + +/* Number of buffers used in RPC communication */ +#define OPTEE_RPC_NUM_BUFS 2 + +struct optee_cmd_prefix { + __u32 smc_id; + __u32 pad; +}; + +#endif /* OPTEE_OPTEE_H */ diff --git a/include/linux/sec-hw/optee/teesmc.h b/include/linux/sec-hw/optee/teesmc.h new file mode 100644 index 0000000..259e466 --- /dev/null +++ b/include/linux/sec-hw/optee/teesmc.h @@ -0,0 +1,584 @@ +/* + * Copyright (c) 2014, 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 TEESMC_H +#define TEESMC_H + +#ifndef ASM +/* + * This section depends on uint64_t, uint32_t uint8_t already being + * defined. Since this file is used in several different environments + * (secure world OS and normal world Linux kernel to start with) where + * stdint.h may not be available it's the responsibility of the one + * including this file to provide those types. + */ + +/* + * Trusted OS SMC interface. + * + * The SMC interface follows SMC Calling Convention + * (ARM_DEN0028A_SMC_Calling_Convention). + * + * The primary objective of this API is to provide a transport layer on + * which a Global Platform compliant TEE interfaces can be deployed. But the + * interface can also be used for other implementations. + * + * This file is divided in two parts. + * Part 1 deals with passing parameters to Trusted Applications running in + * a trusted OS in secure world. + * Part 2 deals with the lower level handling of the SMC. + */ + +/* + ******************************************************************************* + * Part 1 - passing parameters to Trusted Applications + ******************************************************************************* + */ + +/* + * Same values as TEE_PARAM_* from TEE Internal API + */ +#define TEESMC_ATTR_TYPE_NONE 0 +#define TEESMC_ATTR_TYPE_VALUE_INPUT 1 +#define TEESMC_ATTR_TYPE_VALUE_OUTPUT 2 +#define TEESMC_ATTR_TYPE_VALUE_INOUT 3 +#define TEESMC_ATTR_TYPE_MEMREF_INPUT 5 +#define TEESMC_ATTR_TYPE_MEMREF_OUTPUT 6 +#define TEESMC_ATTR_TYPE_MEMREF_INOUT 7 + +#define TEESMC_ATTR_TYPE_MASK 0x7 + +/* + * Meta parameter to be absorbed by the Secure OS and not passed + * to the Trusted Application. + * + * One example of this is a struct teesmc_meta_open_session which + * is added to TEESMC{32,64}_CMD_OPEN_SESSION. + */ +#define TEESMC_ATTR_META 0x8 + +#define TEESMC_ATTR_CACHE_NONCACHE 0 +#define TEESMC_ATTR_CACHE_CACHED 1 +#define TEESMC_ATTR_CACHE_DEFAULT TEESMC_ATTR_CACHE_CACHED + +#define TEESMC_ATTR_CACHE_SHIFT 4 +#define TEESMC_ATTR_CACHE_MASK 0x7 + +#define TEESMC_CMD_OPEN_SESSION 0 +#define TEESMC_CMD_INVOKE_COMMAND 1 +#define TEESMC_CMD_CLOSE_SESSION 2 +#define TEESMC_CMD_CANCEL 3 + +/** + * struct teesmc_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 pointer via physical address instead of + * the virtual address with is usually used for pointers. This is because + * Secure and normal world has completely independent memory mapping. Normal + * world can even have a hypervisor which need to translate the guest + * physical address (AKA IPA in ARM lingo) to a real physical address + * before passing the structure to secure world. + */ +struct teesmc_param_memref { + uint64_t buf_ptr; + uint64_t size; + uint64_t shm_ref; +}; + +/** + * struct teesmc_param_value - values + * @a: first value + * @b: second value + * @c: third value + */ +struct teesmc_param_value { + uint64_t a; + uint64_t b; + uint64_t c; +}; + +/** + * struct teesmc_param - parameter + * @attr: attributes + * @memref: a memory reference + * @value: a value + * + * attr & TEESMC_ATTR_TYPE_MASK indicates if memref or value is used in the + * union. TEESMC_ATTR_TYPE_VALUE_* indicates value and + * TEESMC_ATTR_TYPE_MEMREF_* indicates memref. TEESMC_ATTR_TYPE_NONE + * indicates that none of the members are used. + */ +struct teesmc_param { + uint64_t attr; + union { + struct teesmc_param_memref memref; + struct teesmc_param_value value; + } u; +}; + +/** + * struct teesmc32_arg - SMC argument for Trusted OS + * @cmd: Command, one of TEESMC_CMD_* + * @ta_func: Trusted Application function, specific to the Trusted Application, + * used if cmd == TEESMC_CMD_INVOKE_COMMAND + * @session: In parameter for all TEESMC_CMD_* except + * TEESMC_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 SMC 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 TEESMC_ATTR_META bit in + * corresponding param_attrs). This is used for TEESMC_CMD_OPEN_SESSION + * to pass a struct teesmc32_meta_open_session which is needed find the + * Trusted Application and to indicate the credentials of the client. + */ +struct teesmc_arg { + uint32_t cmd; + uint32_t ta_func; + uint32_t session; + uint32_t ret; + uint32_t ret_origin; + uint32_t num_params __aligned(8); + + /* + * Note that num_params is 8 byte aligned since the 'struct + * teesmc_param' which follows requires 8 byte alignment. + * + * Commented out element used to visualize the layout dynamic part + * of the struct. Note that this field is not available at all + * if num_params == 0. + * + * params is accessed through the macro TEESMC_GET_PARAMS + * + * struct teesmc_param params[num_params]; + */ +}; + +/** + * TEESMC_GET_PARAMS - return pointer to struct teesmc_param * + * + * @x: Pointer to a struct teesmc_arg + * + * Returns a pointer to the params[] inside a struct teesmc_arg. + */ +#define TEESMC_GET_PARAMS(x) \ + (struct teesmc_param *)(((struct teesmc_arg *)(x)) + 1) + +/** + * TEESMC_GET_ARG_SIZE - return size of struct teesmc_arg + * + * @num_params: Number of parameters embedded in the struct teesmc_arg + * + * Returns the size of the struct teesmc_arg together with the number + * of embedded parameters. + */ +#define TEESMC_GET_ARG_SIZE(num_params) \ + (sizeof(struct teesmc_arg) + \ + sizeof(struct teesmc_param) * (num_params)) + +#define TEESMC_UUID_LEN 16 + +/** + * struct teesmc_meta_open_session - additional parameters for + * TEESMC32_CMD_OPEN_SESSION and + * TEESMC64_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 TEESMC{32,64}_CMD_OPEN_SESSION cmd. It's important + * that it really is the first parameter to make it easy for an eventual + * hypervisor to inspect and possibly update clnt_* values. + */ +struct teesmc_meta_open_session { + uint8_t uuid[TEESMC_UUID_LEN]; + uint8_t clnt_uuid[TEESMC_UUID_LEN]; + uint32_t clnt_login; +}; + + +#endif /*!ASM*/ + +/* + ******************************************************************************* + * Part 2 - low level SMC interaction + ******************************************************************************* + */ + +#define TEESMC_32 0 +#define TEESMC_64 0x40000000 +#define TEESMC_FAST_CALL 0x80000000 +#define TEESMC_STD_CALL 0 + +#define TEESMC_OWNER_MASK 0x3F +#define TEESMC_OWNER_SHIFT 24 + +#define TEESMC_FUNC_MASK 0xFFFF + +#define TEESMC_IS_FAST_CALL(smc_val) ((smc_val) & TEESMC_FAST_CALL) +#define TEESMC_IS_64(smc_val) ((smc_val) & TEESMC_64) +#define TEESMC_FUNC_NUM(smc_val) ((smc_val) & TEESMC_FUNC_MASK) +#define TEESMC_OWNER_NUM(smc_val) (((smc_val) >> TEESMC_OWNER_SHIFT) & \ + TEESMC_OWNER_MASK) + +#define TEESMC_CALL_VAL(type, calling_convention, owner, func_num) \ + ((type) | (calling_convention) | \ + (((owner) & TEESMC_OWNER_MASK) << TEESMC_OWNER_SHIFT) |\ + ((func_num) & TEESMC_FUNC_MASK)) + +#define TEESMC_OWNER_ARCH 0 +#define TEESMC_OWNER_CPU 1 +#define TEESMC_OWNER_SIP 2 +#define TEESMC_OWNER_OEM 3 +#define TEESMC_OWNER_STANDARD 4 +#define TEESMC_OWNER_TRUSTED_APP 48 +#define TEESMC_OWNER_TRUSTED_OS 50 + +#define TEESMC_OWNER_TRUSTED_OS_OPTEED 62 +#define TEESMC_OWNER_TRUSTED_OS_API 63 + +/* + * Function specified by SMC Calling convention. + */ +#define TEESMC32_FUNCID_CALLS_COUNT 0xFF00 +#define TEESMC32_CALLS_COUNT \ + TEESMC_CALL_VAL(TEESMC_32, TEESMC_FAST_CALL, \ + TEESMC_OWNER_TRUSTED_OS_API, \ + TEESMC32_FUNCID_CALLS_COUNT) + +/* + * 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 : Only 32 bit calls are supported + * 65cb6b93-af0c-4617-8ed6-644a8d1140f9 : Both 32 and 64 bit calls are supported + */ +#define TEESMC_UID_R0 0x65cb6b93 +#define TEESMC_UID_R1 0xaf0c4617 +#define TEESMC_UID_R2 0x8ed6644a +#define TEESMC_UID32_R3 0x8d1140f8 +#define TEESMC_UID64_R3 0x8d1140f9 +#define TEESMC32_FUNCID_CALLS_UID 0xFF01 +#define TEESMC32_CALLS_UID \ + TEESMC_CALL_VAL(TEESMC_32, TEESMC_FAST_CALL, \ + TEESMC_OWNER_TRUSTED_OS_API, \ + TEESMC32_FUNCID_CALLS_UID) + +/* + * Function specified by SMC Calling convention + * + * Returns 2.0 if using API specified in this file without further extentions. + */ +#define TEESMC_REVISION_MAJOR 2 +#define TEESMC_REVISION_MINOR 0 +#define TEESMC32_FUNCID_CALLS_REVISION 0xFF03 +#define TEESMC32_CALLS_REVISION \ + TEESMC_CALL_VAL(TEESMC_32, TEESMC_FAST_CALL, \ + TEESMC_OWNER_TRUSTED_OS_API, \ + TEESMC32_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 r0-4/w0-4 in the same way as TEESMC32_CALLS_UID + * described above. + */ +#define TEESMC_FUNCID_GET_OS_UUID 0 +#define TEESMC32_CALL_GET_OS_UUID \ + TEESMC_CALL_VAL(TEESMC_32, TEESMC_FAST_CALL, TEESMC_OWNER_TRUSTED_OS, \ + TEESMC_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 r0-1/w0-1 in the same way as TEESMC32_CALLS_REVISION + * described above. + */ +#define TEESMC_FUNCID_GET_OS_REVISION 1 +#define TEESMC32_CALL_GET_OS_REVISION \ + TEESMC_CALL_VAL(TEESMC_32, TEESMC_FAST_CALL, TEESMC_OWNER_TRUSTED_OS, \ + TEESMC_FUNCID_GET_OS_REVISION) + + + +/* + * Call with struct teesmc_arg as argument + * + * Call register usage: + * r0/x0 SMC Function ID, TEESMC*CALL_WITH_ARG + * r1/x1 Physical pointer to a struct teesmc_arg + * r2-6/x2-6 Not used + * r7/x7 Hypervisor Client ID register + * + * Normal return register usage: + * r0/x0 Return value, TEESMC_RETURN_* + * r1-3/x1-3 Not used + * r4-7/x4-7 Preserved + * + * Ebusy return register usage: + * r0/x0 Return value, TEESMC_RETURN_EBUSY + * r1-3/x1-3 Preserved + * r4-7/x4-7 Preserved + * + * RPC return register usage: + * r0/x0 Return value, TEESMC_RETURN_IS_RPC(val) + * r1-2/x1-2 RPC parameters + * r3-7/x3-7 Resume information, must be preserved + * + * Possible return values: + * TEESMC_RETURN_UNKNOWN_FUNCTION Trusted OS does not recognize this + * function. + * TEESMC_RETURN_OK Call completed, result updated in + * the previously supplied struct + * teesmc32_arg. + * TEESMC_RETURN_EBUSY Trusted OS busy, try again later. + * TEESMC_RETURN_EBADADDR Bad physcial pointer to struct + * teesmc32_arg. + * TEESMC_RETURN_EBADCMD Bad/unknown cmd in struct teesmc32_arg + * TEESMC_RETURN_IS_RPC() Call suspended by RPC call to normal + * world. + */ +/* Note: was 2 in 1.0 is now 4 in 2.0 since parameters has changed */ +#define TEESMC_FUNCID_CALL_WITH_ARG 4 +#define TEESMC32_CALL_WITH_ARG \ + TEESMC_CALL_VAL(TEESMC_32, TEESMC_STD_CALL, TEESMC_OWNER_TRUSTED_OS, \ + TEESMC_FUNCID_CALL_WITH_ARG) +/* Same as TEESMC32_CALL_WITH_ARG but a "fast call". */ +#define TEESMC32_FASTCALL_WITH_ARG \ + TEESMC_CALL_VAL(TEESMC_32, TEESMC_FAST_CALL, TEESMC_OWNER_TRUSTED_OS, \ + TEESMC_FUNCID_CALL_WITH_ARG) +/* Same as TEESMC32_CALL_WITH_ARG but with 64bit registers instead */ +#define TEESMC64_CALL_WITH_ARG \ + TEESMC_CALL_VAL(TEESMC_64, TEESMC_STD_CALL, TEESMC_OWNER_TRUSTED_OS, \ + TEESMC_FUNCID_CALL_WITH_ARG) +/* Same as TEESMC64_CALL_WITH_ARG but a "fast call". */ +#define TEESMC64_FASTCALL_WITH_ARG \ + TEESMC_CALL_VAL(TEESMC_64, TEESMC_FAST_CALL, TEESMC_OWNER_TRUSTED_OS, \ + TEESMC_FUNCID_CALL_WITH_ARG) + +/* + * Resume from RPC (for example after processing an IRQ) + * + * Call register usage: + * r0/x0 SMC Function ID, TEESMC*CALL_RETURN_FROM_RPC + * r1-3/x1-3 Value of r1-3/x1-3 when TEESMC*CALL_WITH_ARG returned + * TEESMC_RETURN_RPC in r0/x0 + * + * Return register usage is the same as for TEESMC*CALL_WITH_ARG above. + * + * Possible return values + * TEESMC_RETURN_UNKNOWN_FUNCTION Trusted OS does not recognize this + * function. + * TEESMC_RETURN_OK Original call completed, result + * updated in the previously supplied. + * struct teesmc32_arg + * TEESMC_RETURN_RPC Call suspended by RPC call to normal + * world. + * TEESMC_RETURN_EBUSY Trusted OS busy, try again later. + * TEESMC_RETURN_ERESUME Resume failed, the opaque resume + * information was corrupt. + */ +#define TEESMC_FUNCID_RETURN_FROM_RPC 3 +#define TEESMC32_CALL_RETURN_FROM_RPC \ + TEESMC_CALL_VAL(TEESMC_32, TEESMC_STD_CALL, TEESMC_OWNER_TRUSTED_OS, \ + TEESMC_FUNCID_RETURN_FROM_RPC) +/* Same as TEESMC32_CALL_RETURN_FROM_RPC but a "fast call". */ +#define TEESMC32_FASTCALL_RETURN_FROM_RPC \ + TEESMC_CALL_VAL(TEESMC_32, TEESMC_STD_CALL, TEESMC_OWNER_TRUSTED_OS, \ + TEESMC_FUNCID_RETURN_FROM_RPC) +/* + * Same as TEESMC32_CALL_RETURN_FROM_RPC, but used when it's a 64bit call + * that has returned. + */ +#define TEESMC64_CALL_RETURN_FROM_RPC \ + TEESMC_CALL_VAL(TEESMC_64, TEESMC_STD_CALL, TEESMC_OWNER_TRUSTED_OS, \ + TEESMC_FUNCID_RETURN_FROM_RPC) +/* Same as TEESMC64_CALL_RETURN_FROM_RPC but a "fast call". */ +#define TEESMC64_FASTCALL_RETURN_FROM_RPC \ + TEESMC_CALL_VAL(TEESMC_64, TEESMC_STD_CALL, TEESMC_OWNER_TRUSTED_OS, \ + TEESMC_FUNCID_RETURN_FROM_RPC) + +#define TEESMC_RETURN_RPC_PREFIX_MASK 0xFFFF0000 +#define TEESMC_RETURN_RPC_PREFIX 0xFFFF0000 +#define TEESMC_RETURN_RPC_FUNC_MASK 0x0000FFFF + +#define TEESMC_RETURN_GET_RPC_FUNC(ret) ((ret) & TEESMC_RETURN_RPC_FUNC_MASK) + +#define TEESMC_RPC_VAL(func) ((func) | TEESMC_RETURN_RPC_PREFIX) + +/* + * Allocate argument memory for RPC parameter passing. + * Argument memory is used to hold a struct teesmc_arg. + * + * "Call" register usage: + * r0/x0 This value, TEESMC_RETURN_RPC_ALLOC + * r1/x1 Size in bytes of required argument memory + * r2-7/x2-7 Resume information, must be preserved + * + * "Return" register usage: + * r0/x0 SMC Function ID, TEESMC32_CALL_RETURN_FROM_RPC if it was an + * AArch32 SMC return or TEESMC64_CALL_RETURN_FROM_RPC for + * AArch64 SMC return + * r1/x1 Physical pointer to allocated argument memory, 0 if size + * was 0 or if memory can't be allocated + * r2-7/x2-7 Preserved + */ +#define TEESMC_RPC_FUNC_ALLOC_ARG 0 +#define TEESMC_RETURN_RPC_ALLOC_ARG \ + TEESMC_RPC_VAL(TEESMC_RPC_FUNC_ALLOC_ARG) + +/* + * Allocate payload memory for RPC parameter passing. + * Payload memory is used to hold the memory referred to by struct + * teesmc_param_memref. + * + * "Call" register usage: + * r0/x0 This value, TEESMC_RETURN_RPC_ALLOC + * r1/x1 Size in bytes of required payload memory + * r2-7/x2-7 Resume information, must be preserved + * + * "Return" register usage: + * r0/x0 SMC Function ID, TEESMC32_CALL_RETURN_FROM_RPC if it was an + * AArch32 SMC return or TEESMC64_CALL_RETURN_FROM_RPC for + * AArch64 SMC return + * r1/x1 Physical pointer to allocated payload memory, 0 if size + * was 0 or if memory can't be allocated + * r2/x2 Shared memory cookie used when freeing the memory + * r3-7/x3-7 Preserved + */ +#define TEESMC_RPC_FUNC_ALLOC_PAYLOAD 1 +#define TEESMC_RETURN_RPC_ALLOC_PAYLOAD \ + TEESMC_RPC_VAL(TEESMC_RPC_FUNC_ALLOC_PAYLOAD) + +/* + * Free memory previously allocated by TEESMC_RETURN_RPC_ALLOC_ARG. + * + * "Call" register usage: + * r0/x0 This value, TEESMC_RETURN_RPC_FREE + * r1/x1 Shared memory cookie belonging to this argument memory + * r2-7/x2-7 Resume information, must be preserved + * + * "Return" register usage: + * r0/x0 SMC Function ID, TEESMC32_CALL_RETURN_FROM_RPC if it was an + * AArch32 SMC return or TEESMC64_CALL_RETURN_FROM_RPC for + * AArch64 SMC return + * r1/x1 Not used + * r2-7/x2-7 Preserved + */ +#define TEESMC_RPC_FUNC_FREE_ARG 2 +#define TEESMC_RETURN_RPC_FREE_ARG TEESMC_RPC_VAL(TEESMC_RPC_FUNC_FREE_ARG) + +/* + * Free memory previously allocated by TEESMC_RETURN_RPC_ALLOC_PAYLOAD. + * + * "Call" register usage: + * r0/x0 This value, TEESMC_RETURN_RPC_FREE + * r1/x1 Shared memory cookie belonging to this payload memory + * r2-7/x2-7 Resume information, must be preserved + * + * "Return" register usage: + * r0/x0 SMC Function ID, TEESMC32_CALL_RETURN_FROM_RPC if it was an + * AArch32 SMC return or TEESMC64_CALL_RETURN_FROM_RPC for + * AArch64 SMC return + * r1/x1 Not used + * r2-7/x2-7 Preserved + */ +#define TEESMC_RPC_FUNC_FREE_PAYLOAD 3 +#define TEESMC_RETURN_RPC_FREE_PAYLOAD \ + TEESMC_RPC_VAL(TEESMC_RPC_FUNC_FREE_PAYLOAD) + +/* + * Deliver an IRQ in normal world. + * + * "Call" register usage: + * r0/x0 TEESMC_RETURN_RPC_IRQ + * r1-7/x1-7 Resume information, must be preserved + * + * "Return" register usage: + * r0/x0 SMC Function ID, TEESMC32_CALL_RETURN_FROM_RPC if it was an + * AArch32 SMC return or TEESMC64_CALL_RETURN_FROM_RPC for + * AArch64 SMC return + * r1-7/x1-7 Preserved + */ +#define TEESMC_RPC_FUNC_IRQ 4 +#define TEESMC_RETURN_RPC_IRQ TEESMC_RPC_VAL(TEESMC_RPC_FUNC_IRQ) + +/* + * Do an RPC request. The supplied struct teesmc_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: + * r0/x0 TEESMC_RETURN_RPC_CMD + * r1/x1 Physical pointer to a struct teesmc_arg, must be preserved, + * only the data should be updated + * r2-7/x2-7 Resume information, must be preserved + * + * "Return" register usage: + * r0/x0 SMC Function ID, TEESMC32_CALL_RETURN_FROM_RPC if it was an + * AArch32 SMC return or TEESMC64_CALL_RETURN_FROM_RPC for + * AArch64 SMC return + * r1-7/x1-7 Preserved + */ +#define TEESMC_RPC_FUNC_CMD 5 +#define TEESMC_RETURN_RPC_CMD TEESMC_RPC_VAL(TEESMC_RPC_FUNC_CMD) + + +/* Returned in r0 */ +#define TEESMC_RETURN_UNKNOWN_FUNCTION 0xFFFFFFFF + +/* Returned in r0 only from Trusted OS functions */ +#define TEESMC_RETURN_OK 0x0 +#define TEESMC_RETURN_EBUSY 0x1 +#define TEESMC_RETURN_ERESUME 0x2 +#define TEESMC_RETURN_EBADADDR 0x3 +#define TEESMC_RETURN_EBADCMD 0x4 +#define TEESMC_RETURN_IS_RPC(ret) \ + (((ret) != TEESMC_RETURN_UNKNOWN_FUNCTION) && \ + ((((ret) & TEESMC_RETURN_RPC_PREFIX_MASK) == TEESMC_RETURN_RPC_PREFIX))) + +#endif /* TEESMC_H */ diff --git a/include/linux/sec-hw/optee/teesmc_optee.h b/include/linux/sec-hw/optee/teesmc_optee.h new file mode 100644 index 0000000..014beb8 --- /dev/null +++ b/include/linux/sec-hw/optee/teesmc_optee.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2014, STMicroelectronics International N.V. + * 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 TEESMC_OPTEE_H +#define TEESMC_OPTEE_H + +#define TEESMC_OPTEE_RETURN_NOTAVAIL 0x5700 + +/* + * Get Shared Memory Config + * + * Returns the Secure/Non-secure shared memory config. + * + * Call register usage: + * r0 SMC Function ID, TEESMC32_OPTEE_FASTCALL_GET_SHM_CONFIG + * r1-6 Not used + * r7 Hypervisor Client ID register + * + * Have config return register usage: + * r0 TEESMC_RETURN_OK + * r1 Physical address of start of SHM + * r2 Size of of SHM + * r3 1 if SHM is cached, 0 if uncached. + * r4-7 Preserved + * + * Not available register usage: + * r0 TEESMC_OPTEE_RETURN_NOTAVAIL + * r1-3 Not used + * r4-7 Preserved + */ +#define TEESMC_OPTEE_FUNCID_GET_SHM_CONFIG 0x5700 +#define TEESMC32_OPTEE_FASTCALL_GET_SHM_CONFIG \ + TEESMC_CALL_VAL(TEESMC_32, TEESMC_FAST_CALL, TEESMC_OWNER_TRUSTED_OS, \ + TEESMC_OPTEE_FUNCID_GET_SHM_CONFIG) + +/* + * Configures L2CC mutex + * + * Disables, enables usage of L2CC mutex. Returns or sets physical address + * of L2CC mutex. + * + * Call register usage: + * r0 SMC Function ID, TEESMC32_OPTEE_FASTCALL_L2CC_MUTEX + * r1 TEESMC_OPTEE_L2CC_MUTEX_GET_ADDR Get physical address of mutex + * TEESMC_OPTEE_L2CC_MUTEX_SET_ADDR Set physical address of mutex + * TEESMC_OPTEE_L2CC_MUTEX_ENABLE Enable usage of mutex + * TEESMC_OPTEE_L2CC_MUTEX_DISABLE Disable usage of mutex + * r2 if r1 == TEESMC_OPTEE_L2CC_MUTEX_SET_ADDR, physical address of mutex + * r3-6 Not used + * r7 Hypervisor Client ID register + * + * Have config return register usage: + * r0 TEESMC_RETURN_OK + * r1 Preserved + * r2 if r1 == 0, physical address of L2CC mutex + * r3-7 Preserved + * + * Error return register usage: + * r0 TEESMC_OPTEE_RETURN_NOTAVAIL Physical address not available + * TEESMC_RETURN_EBADADDR Bad supplied physical address + * TEESMC_RETURN_EBADCMD Unsupported value in r1 + * r1-7 Preserved + */ +#define TEESMC_OPTEE_L2CC_MUTEX_GET_ADDR 0 +#define TEESMC_OPTEE_L2CC_MUTEX_SET_ADDR 1 +#define TEESMC_OPTEE_L2CC_MUTEX_ENABLE 2 +#define TEESMC_OPTEE_L2CC_MUTEX_DISABLE 3 +#define TEESMC_OPTEE_FUNCID_L2CC_MUTEX 0x5701 +#define TEESMC32_OPTEE_FASTCALL_L2CC_MUTEX \ + TEESMC_CALL_VAL(TEESMC_32, TEESMC_FAST_CALL, TEESMC_OWNER_TRUSTED_OS, \ + TEESMC_OPTEE_FUNCID_L2CC_MUTEX) + +/* + * Overriding default UID of the API since the it has OP-TEE extensions + * 384fb3e0-e7f8-11e3-af63-0002a5d5c51b : Only 32 bit calls are supported + * 384fb3e0-e7f8-11e3-af63-0002a5d5c51c : Both 32 and 64 bit calls are supported + */ +#define TEESMC_OPTEE_UID_R0 0x384fb3e0 +#define TEESMC_OPTEE_UID_R1 0xe7f811e3 +#define TEESMC_OPTEE_UID_R2 0xaf630002 +#define TEESMC_OPTEE_UID32_R3 0xa5d5c51b +#define TEESMC_OPTEE_UID64_R3 0xa5d5c51c + +#define TEESMC_OPTEE_REVISION_MAJOR 2 +#define TEESMC_OPTEE_REVISION_MINOR 0 + +/* + * UUID for OP-TEE + * 486178e0-e7f8-11e3-bc5e-0002a5d5c51b + */ +#define TEESMC_OS_OPTEE_UUID_R0 0x486178e0 +#define TEESMC_OS_OPTEE_UUID_R1 0xe7f811e3 +#define TEESMC_OS_OPTEE_UUID_R2 0xbc5e0002 +#define TEESMC_OS_OPTEE_UUID_R3 0xa5d5c51b + +#define TEESMC_OS_OPTEE_REVISION_MAJOR 1 +#define TEESMC_OS_OPTEE_REVISION_MINOR 0 + +#endif /*TEESMC_OPTEE_H*/