diff options
author | Jens Wiklander <jens.wiklander@linaro.org> | 2016-04-27 21:55:29 +0200 |
---|---|---|
committer | Jens Wiklander <jens.wiklander@linaro.org> | 2016-06-02 11:15:25 +0200 |
commit | b5bccd5899793a17829ad03d88bbbb1a70e2a2f4 (patch) | |
tree | ba38feee001b237156721f32273558dec910ed6a | |
parent | 4f73fa5db9e10a361768fc25d9f1b2d1cef83a8b (diff) |
[v10 delta] tee: add OP-TEE driver
V10 delta for "tee: add OP-TEE driver"
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
-rw-r--r-- | drivers/tee/optee/call.c | 50 | ||||
-rw-r--r-- | drivers/tee/optee/core.c | 51 | ||||
-rw-r--r-- | drivers/tee/optee/optee_msg.h | 6 | ||||
-rw-r--r-- | drivers/tee/optee/optee_private.h | 53 | ||||
-rw-r--r-- | drivers/tee/optee/optee_smc.h | 8 | ||||
-rw-r--r-- | drivers/tee/optee/rpc.c | 31 | ||||
-rw-r--r-- | drivers/tee/optee/supp.c | 33 |
7 files changed, 182 insertions, 50 deletions
diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c index c567f98ec4c7..8f9b12e8b915 100644 --- a/drivers/tee/optee/call.c +++ b/drivers/tee/optee/call.c @@ -11,14 +11,14 @@ * GNU General Public License for more details. * */ -#include <linux/types.h> +#include <linux/arm-smccc.h> +#include <linux/device.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 <linux/arm-smccc.h> +#include <linux/types.h> +#include <linux/uaccess.h> #include "optee_private.h" #include "optee_smc.h" @@ -29,7 +29,7 @@ struct optee_call_waiter { }; static void optee_cq_wait_init(struct optee_call_queue *cq, - struct optee_call_waiter *w) + struct optee_call_waiter *w) { mutex_lock(&cq->mutex); /* @@ -45,7 +45,7 @@ static void optee_cq_wait_init(struct optee_call_queue *cq, } static void optee_cq_wait_for_completion(struct optee_call_queue *cq, - struct optee_call_waiter *w) + struct optee_call_waiter *w) { wait_for_completion(&w->c); @@ -74,7 +74,7 @@ static void optee_cq_complete_one(struct optee_call_queue *cq) } static void optee_cq_wait_final(struct optee_call_queue *cq, - struct optee_call_waiter *w) + struct optee_call_waiter *w) { mutex_lock(&cq->mutex); @@ -94,7 +94,7 @@ static void optee_cq_wait_final(struct optee_call_queue *cq, /* Requires the filpstate mutex to be held */ static struct optee_session *find_session(struct optee_context_data *ctxdata, - u32 session_id) + u32 session_id) { struct optee_session *sess; @@ -104,6 +104,16 @@ static struct optee_session *find_session(struct optee_context_data *ctxdata, return NULL; } +/** + * optee_do_call_with_arg() - Do an SMC to OP-TEE in secure world + * @ctx: calling context + * @parg: physical address of message to pass to secure world + * + * Does and SMC to OP-TEE in secure world and handles eventual resulting + * Remote Procedure Calls (RPC) from OP-TEE. + * + * Returns return code from secure world, 0 is OK + */ u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg) { struct optee *optee = tee_get_drvdata(ctx->teedev); @@ -148,7 +158,8 @@ u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg) } static struct tee_shm *get_msg_arg(struct tee_context *ctx, size_t num_params, - struct optee_msg_arg **msg_arg, phys_addr_t *msg_parg) + struct optee_msg_arg **msg_arg, + phys_addr_t *msg_parg) { int rc; struct tee_shm *shm; @@ -179,8 +190,8 @@ out: } int optee_open_session(struct tee_context *ctx, - struct tee_ioctl_open_session_arg *arg, - struct tee_param *param) + struct tee_ioctl_open_session_arg *arg, + struct tee_param *param) { struct optee_context_data *ctxdata = ctx->data; int rc; @@ -215,7 +226,7 @@ int optee_open_session(struct tee_context *ctx, if (rc) goto out; - sess = kzalloc(sizeof(struct optee_session), GFP_KERNEL); + sess = kzalloc(sizeof(*sess), GFP_KERNEL); if (!sess) { rc = -ENOMEM; goto out; @@ -282,7 +293,7 @@ int optee_close_session(struct tee_context *ctx, u32 session) } int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, - struct tee_param *param) + struct tee_param *param) { struct optee_context_data *ctxdata = ctx->data; struct tee_shm *shm; @@ -357,6 +368,11 @@ int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session) return 0; } +/** + * optee_enable_shm_cache() - Enables caching of some shared memory allocation + * in OP-TEE + * @optee: main service struct + */ void optee_enable_shm_cache(struct optee *optee) { struct optee_call_waiter w; @@ -375,6 +391,11 @@ void optee_enable_shm_cache(struct optee *optee) optee_cq_wait_final(&optee->call_queue, &w); } +/** + * optee_enable_shm_cache() - Disables caching of some shared memory allocation + * in OP-TEE + * @optee: main service struct + */ void optee_disable_shm_cache(struct optee *optee) { struct optee_call_waiter w; @@ -393,8 +414,9 @@ void optee_disable_shm_cache(struct optee *optee) shm = reg_pair_to_ptr(res.a1, res.a2); tee_shm_free(shm); - } else + } else { optee_cq_wait_for_completion(&optee->call_queue, &w); + } } optee_cq_wait_final(&optee->call_queue, &w); } diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index 874550c84f30..dba3bfa7b894 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -11,17 +11,17 @@ * 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/io.h> +#include <linux/module.h> #include <linux/of.h> #include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/string.h> #include <linux/tee_drv.h> +#include <linux/types.h> +#include <linux/uaccess.h> #include "optee_private.h" #include "optee_smc.h" @@ -29,8 +29,16 @@ #define OPTEE_SHM_NUM_PRIV_PAGES 1 +/** + * optee_from_msg_param() - convert from OPTEE_MSG parameters to + * struct tee_param + * @params: subsystem internal parameter representation + * @num_params: number of elements in the parameter arrays + * @msg_params: OPTEE_MSG parameters + * Returns 0 on success or <0 on failure + */ int optee_from_msg_param(struct tee_param *params, size_t num_params, - const struct optee_msg_param *msg_params) + const struct optee_msg_param *msg_params) { int rc; size_t n; @@ -92,8 +100,15 @@ int optee_from_msg_param(struct tee_param *params, size_t num_params, return 0; } +/** + * optee_to_msg_param() - convert from struct tee_params to OPTEE_MSG parameters + * @msg_params: OPTEE_MSG parameters + * @num_params: number of elements in the parameter arrays + * @params: subsystem itnernal parameter representation + * Returns 0 on success or <0 on failure + */ int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params, - const struct tee_param *params) + const struct tee_param *params) { int rc; size_t n; @@ -145,7 +160,7 @@ int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params, } static void optee_get_version(struct tee_device *teedev, - struct tee_ioctl_version_data *vers) + struct tee_ioctl_version_data *vers) { struct tee_ioctl_version_data v = { .impl_id = TEE_IMPL_ID_OPTEE, @@ -158,11 +173,22 @@ static void optee_get_version(struct tee_device *teedev, static int optee_open(struct tee_context *ctx) { struct optee_context_data *ctxdata; + struct tee_device *teedev = ctx->teedev; + struct optee *optee = tee_get_drvdata(teedev); ctxdata = kzalloc(sizeof(*ctxdata), GFP_KERNEL); if (!ctxdata) return -ENOMEM; + if (teedev == optee->supp_teedev) { + if (!atomic_dec_and_test(&optee->supp.available)) { + /* Supplicant device is already open */ + atomic_inc(&optee->supp.available); + kfree(ctxdata); + return -EBUSY; + } + } + mutex_init(&ctxdata->mutex); INIT_LIST_HEAD(&ctxdata->sess_list); @@ -173,6 +199,8 @@ static int optee_open(struct tee_context *ctx) static void optee_release(struct tee_context *ctx) { struct optee_context_data *ctxdata = ctx->data; + struct tee_device *teedev = ctx->teedev; + struct optee *optee = tee_get_drvdata(teedev); struct tee_shm *shm; struct optee_msg_arg *arg = NULL; phys_addr_t parg; @@ -217,6 +245,9 @@ static void optee_release(struct tee_context *ctx) tee_shm_free(shm); ctx->data = NULL; + + if (teedev == optee->supp_teedev) + atomic_inc(&optee->supp.available); } static struct tee_driver_ops optee_ops = { @@ -275,7 +306,7 @@ static bool optee_msg_api_revision_is_compatible(optee_invoke_fn *invoke_fn) } static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn, - u32 *sec_caps) + u32 *sec_caps) { struct arm_smccc_res res; u32 a1 = 0; diff --git a/drivers/tee/optee/optee_msg.h b/drivers/tee/optee/optee_msg.h index ca2f8aa54f5e..19377c77ccda 100644 --- a/drivers/tee/optee/optee_msg.h +++ b/drivers/tee/optee/optee_msg.h @@ -27,6 +27,7 @@ #ifndef _OPTEE_MSG_H #define _OPTEE_MSG_H +#include <linux/bitops.h> #include <linux/types.h> /* @@ -63,14 +64,14 @@ * * Currently only used with OPTEE_MSG_CMD_OPEN_SESSION. */ -#define OPTEE_MSG_ATTR_META (1 << 8) +#define OPTEE_MSG_ATTR_META BIT(8) /* * The temporary shared memory object is not physically contigous and this * temp memref is followed by another fragment until the last temp memref * that doesn't have this bit set. */ -#define OPTEE_MSG_ATTR_FRAGMENT (1 << 9) +#define OPTEE_MSG_ATTR_FRAGMENT BIT(9) /* * Memory attributes for caching passed with temp memrefs. The actual value @@ -431,5 +432,4 @@ struct optee_msg_arg { */ #define OPTEE_MSG_RPC_CMD_SHM_FREE 7 - #endif /* _OPTEE_MSG_H */ diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index 7f13cb59da4a..2bd7dd87858d 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -15,11 +15,11 @@ #ifndef OPTEE_PRIVATE_H #define OPTEE_PRIVATE_H -#include <linux/types.h> +#include <linux/arm-smccc.h> #include <linux/semaphore.h> #include <linux/tee_drv.h> +#include <linux/types.h> #include "optee_msg.h" -#include <linux/arm-smccc.h> #define OPTEE_MAX_ARG_SIZE 1024 @@ -37,16 +37,35 @@ typedef void (optee_invoke_fn)(unsigned long, unsigned long, unsigned long, struct arm_smccc_res *); struct optee_call_queue { + /* Serializes access to this struct */ struct mutex mutex; struct list_head waiters; }; struct optee_wait_queue { + /* Serializes access to this struct */ struct mutex mu; struct list_head db; }; +/** + * struct optee_supp - supplicant synchronization struct + * @available: if 1 the supplicant device is available for use, else + * busy + * @func: supplicant function id to call + * @ret: call return value + * @num_params: number of elements in @param + * @param: parameters for @func + * @req_posted: if true, a request has been posted to the supplicant + * @supp_next_send: if true, next step is for supplicant to send response + * @thrd_mutex: held by the thread doing a request to supplicant + * @supp_mutex: held by supplicant while operating on this struct + * @data_to_supp: supplicant is waiting on this for next request + * @data_from_supp: requesting thread is waiting on this to get the result + */ struct optee_supp { + atomic_t available; + u32 func; u32 ret; size_t num_params; @@ -54,12 +73,27 @@ struct optee_supp { bool req_posted; bool supp_next_send; + /* Serializes access to this struct for requesting thread */ struct mutex thrd_mutex; + /* Serializes access to this struct for supplicant threads */ struct mutex supp_mutex; struct completion data_to_supp; struct completion data_from_supp; }; +/** + * struct optee - main service struct + * @supp_teedev: supplicant device + * @teedev: client device + * @dev: probed device + * @invoke_fn: function to issue smc or hvc + * @call_queue: queue of threads waiting to call @invoke_fn + * @wait_queue: queue of threads from secure world waiting for a + * secure world sync object + * @supp: supplicant synchronization struct for RPC to supplicant + * @pool: shared memory pool + * @ioremaped_shm virtual address of memory in shared memory pool + */ struct optee { struct tee_device *supp_teedev; struct tee_device *teedev; @@ -78,6 +112,7 @@ struct optee_session { }; struct optee_context_data { + /* Serializes access to this struct */ struct mutex mutex; struct list_head sess_list; }; @@ -107,26 +142,26 @@ void optee_supp_init(struct optee_supp *supp); void optee_supp_uninit(struct optee_supp *supp); int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params, - struct tee_param *param); + struct tee_param *param); int optee_supp_send(struct tee_context *ctx, u32 ret, u32 num_params, - struct tee_param *param); + struct tee_param *param); u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg); int optee_open_session(struct tee_context *ctx, - struct tee_ioctl_open_session_arg *arg, - struct tee_param *param); + struct tee_ioctl_open_session_arg *arg, + struct tee_param *param); int optee_close_session(struct tee_context *ctx, u32 session); int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, - struct tee_param *param); + struct tee_param *param); int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session); void optee_enable_shm_cache(struct optee *optee); void optee_disable_shm_cache(struct optee *optee); int optee_from_msg_param(struct tee_param *params, size_t num_params, - const struct optee_msg_param *msg_params); + const struct optee_msg_param *msg_params); int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params, - const struct tee_param *params); + const struct tee_param *params); /* * Small helpers diff --git a/drivers/tee/optee/optee_smc.h b/drivers/tee/optee/optee_smc.h index 60d75f1791cd..2a1722984a0b 100644 --- a/drivers/tee/optee/optee_smc.h +++ b/drivers/tee/optee/optee_smc.h @@ -28,6 +28,7 @@ #define OPTEE_SMC_H #include <linux/arm-smccc.h> +#include <linux/bitops.h> #define OPTEE_SMC_STD_CALL_VAL(func_num) \ ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL, ARM_SMCCC_SMC_32, \ @@ -202,11 +203,11 @@ * a2-7 Preserved */ /* Normal world works as a uniprocessor system */ -#define OPTEE_SMC_NSEC_CAP_UNIPROCESSOR (1 << 0) +#define OPTEE_SMC_NSEC_CAP_UNIPROCESSOR BIT(0) /* Secure world has reserved shared memory for normal world to use */ -#define OPTEE_SMC_SEC_CAP_HAVE_RESERVERED_SHM (1 << 0) +#define OPTEE_SMC_SEC_CAP_HAVE_RESERVERED_SHM BIT(0) /* Secure world can communicate via previously unregistered shared memory */ -#define OPTEE_SMC_SEC_CAP_UNREGISTERED_SHM (1 << 1) +#define OPTEE_SMC_SEC_CAP_UNREGISTERED_SHM BIT(1) #define OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES 9 #define OPTEE_SMC_EXCHANGE_CAPABILITIES \ OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES) @@ -243,7 +244,6 @@ #define OPTEE_SMC_DISABLE_SHM_CACHE \ OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_DISABLE_SHM_CACHE) - /* * Enable cache of shared memory objects * diff --git a/drivers/tee/optee/rpc.c b/drivers/tee/optee/rpc.c index 94b6ef8e00c7..ba8b5bbec203 100644 --- a/drivers/tee/optee/rpc.c +++ b/drivers/tee/optee/rpc.c @@ -12,8 +12,8 @@ * */ #include <linux/device.h> -#include <linux/slab.h> #include <linux/sched.h> +#include <linux/slab.h> #include <linux/tee_drv.h> #include "optee_private.h" #include "optee_smc.h" @@ -67,7 +67,7 @@ static struct wq_entry *wq_entry_get(struct optee_wait_queue *wq, u32 key) if (w->key == key) goto out; - w = kmalloc(sizeof(struct wq_entry), GFP_KERNEL); + w = kmalloc(sizeof(*w), GFP_KERNEL); if (w) { init_completion(&w->c); w->key = key; @@ -100,7 +100,7 @@ static void wq_wakeup(struct optee_wait_queue *wq, u32 key) } static void handle_rpc_func_cmd_wq(struct optee *optee, - struct optee_msg_arg *arg) + struct optee_msg_arg *arg) { struct optee_msg_param *params; @@ -157,7 +157,7 @@ bad: } static void handle_rpc_supp_cmd(struct tee_context *ctx, - struct optee_msg_arg *arg) + struct optee_msg_arg *arg) { struct tee_param *params; struct optee_msg_param *msg_params = OPTEE_MSG_GET_PARAMS(arg); @@ -204,7 +204,7 @@ static struct tee_shm *cmd_alloc_suppl(struct tee_context *ctx, size_t sz) } static void handle_rpc_func_cmd_shm_alloc(struct tee_context *ctx, - struct optee_msg_arg *arg) + struct optee_msg_arg *arg) { struct tee_device *teedev = ctx->teedev; struct optee_msg_param *params = OPTEE_MSG_GET_PARAMS(arg); @@ -270,16 +270,24 @@ static void cmd_free_suppl(struct tee_context *ctx, struct tee_shm *shm) param.u.value.b = tee_shm_get_id(shm); param.u.value.c = 0; - optee_supp_thrd_req(ctx, OPTEE_MSG_RPC_CMD_SHM_FREE, 1, ¶m); /* * Match the tee_shm_get_from_id() in cmd_alloc_suppl() as secure * world has released its reference. + * + * It's better to do this before sending the request to supplicant + * as we'd like to let the process doing the initial allocation to + * do release the last reference too in order to avoid stacking + * many pending fput() on the client process. This could otherwise + * happen if secure world does many allocate and free in a single + * invoke. */ tee_shm_put(shm); + + optee_supp_thrd_req(ctx, OPTEE_MSG_RPC_CMD_SHM_FREE, 1, ¶m); } static void handle_rpc_func_cmd_shm_free(struct tee_context *ctx, - struct optee_msg_arg *arg) + struct optee_msg_arg *arg) { struct optee_msg_param *params = OPTEE_MSG_GET_PARAMS(arg); struct tee_shm *shm; @@ -307,7 +315,7 @@ static void handle_rpc_func_cmd_shm_free(struct tee_context *ctx, } static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee, - struct tee_shm *shm) + struct tee_shm *shm) { struct optee_msg_arg *arg; @@ -339,6 +347,13 @@ static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee, } } +/** + * optee_handle_rpc() - handle RPC from secure world + * @ctx: context doing the RPC + * @param: value of registers for the RPC + * + * Result of RPC is written back into @param. + */ void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param) { struct tee_device *teedev = ctx->teedev; diff --git a/drivers/tee/optee/supp.c b/drivers/tee/optee/supp.c index b6478b63d379..c64650aadcf7 100644 --- a/drivers/tee/optee/supp.c +++ b/drivers/tee/optee/supp.c @@ -23,6 +23,7 @@ void optee_supp_init(struct optee_supp *supp) mutex_init(&supp->supp_mutex); init_completion(&supp->data_to_supp); init_completion(&supp->data_from_supp); + atomic_set(&supp->available, 1); } void optee_supp_uninit(struct optee_supp *supp) @@ -31,6 +32,15 @@ void optee_supp_uninit(struct optee_supp *supp) mutex_destroy(&supp->supp_mutex); } +/** + * optee_supp_thrd_req() - request service from supplicant + * @ctx: context doing the request + * @func: function requested + * @num_params: number of elements in @param array + * @param: parameters for function + * + * Returns result of operation to be passed to secure world + */ u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params, struct tee_param *param) { @@ -77,8 +87,18 @@ u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params, return ret; } +/** + * optee_supp_recv() - receive request for supplicant + * @ctx: context receiving the request + * @func: requested function in supplicant + * @num_params: number of elements allocated in @param, updated with number + * used elements + * @param: space for parameters for @func + * + * Returns 0 on success or <0 on failure + */ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params, - struct tee_param *param) + struct tee_param *param) { struct tee_device *teedev = ctx->teedev; struct optee *optee = tee_get_drvdata(teedev); @@ -144,8 +164,17 @@ out: return rc; } +/** + * optee_supp_send() - send result of request from supplicant + * @ctx: context sending result + * @ret: return value of request + * @num_params: number of parameters returned + * @param: returned parameters + * + * Returns 0 on success or <0 on failure. + */ int optee_supp_send(struct tee_context *ctx, u32 ret, u32 num_params, - struct tee_param *param) + struct tee_param *param) { struct tee_device *teedev = ctx->teedev; struct optee *optee = tee_get_drvdata(teedev); |