aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJens Wiklander <jens.wiklander@linaro.org>2016-04-27 21:55:29 +0200
committerJens Wiklander <jens.wiklander@linaro.org>2016-06-02 11:15:25 +0200
commitb5bccd5899793a17829ad03d88bbbb1a70e2a2f4 (patch)
treeba38feee001b237156721f32273558dec910ed6a
parent4f73fa5db9e10a361768fc25d9f1b2d1cef83a8b (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.c50
-rw-r--r--drivers/tee/optee/core.c51
-rw-r--r--drivers/tee/optee/optee_msg.h6
-rw-r--r--drivers/tee/optee/optee_private.h53
-rw-r--r--drivers/tee/optee/optee_smc.h8
-rw-r--r--drivers/tee/optee/rpc.c31
-rw-r--r--drivers/tee/optee/supp.c33
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, &param);
/*
* 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, &param);
}
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);