timer: support TEE timer callback [2/2]
authorLiqiang Jin <liqiang.jin@amlogic.com>
Mon, 28 Oct 2019 10:16:32 +0000 (18:16 +0800)
committerWei Hong <wei.hong@amlogic.com>
Tue, 25 Feb 2020 06:10:56 +0000 (14:10 +0800)
PD#SWPL-1120

Problem:
TEE not support timer callback, but it is required by customer

Solution:
Implement TEE timer

Verify:
Android O + P212

Change-Id: Ic2fea99ce1570d6d6f83bbaa50e801fea4a64c87
Signed-off-by: Liqiang Jin <liqiang.jin@amlogic.com>
optee/call.c
optee/core.c
optee/optee_msg.h
optee/optee_private.h
optee/rpc.c

index 46831045b9037a1cfb935d0bea157d270134736d..96ea26a27c57c7948843efc54bdaa9d9453e82fb 100644 (file)
@@ -308,6 +308,8 @@ int optee_close_session(struct tee_context *ctx, u32 session)
        msg_arg->session = session;
        optee_do_call_with_arg(ctx, msg_parg);
 
+       optee_timer_missed_destroy(ctx, session);
+
        tee_shm_free(shm);
        return 0;
 }
index 7202489dbbd00ce675ea5ea015547aa2931bd32c..45028a1008fec14c5204143a05d0f4d940a8d48b 100644 (file)
@@ -246,6 +246,7 @@ static void optee_release(struct tee_context *ctx)
                        arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION;
                        arg->session = sess->session_id;
                        optee_do_call_with_arg(ctx, parg);
+                       optee_timer_missed_destroy(ctx, sess->session_id);
                }
                kfree(sess);
        }
@@ -511,6 +512,8 @@ static int optee_probe(struct platform_device *pdev)
 
        optee_log_init(optee->teedev, logger_shm_pa, LOGGER_SHM_SIZE);
 
+       optee_timer_init(&optee->timer);
+
        mutex_init(&optee->call_queue.mutex);
        INIT_LIST_HEAD(&optee->call_queue.waiters);
        optee_wait_queue_init(&optee->wait_queue);
@@ -546,6 +549,8 @@ static int optee_remove(struct platform_device *pdev)
 {
        struct optee *optee = platform_get_drvdata(pdev);
 
+       optee_timer_destroy(&optee->timer);
+
        optee_log_exit(optee->teedev);
 
        /*
index dd7a06ee04629ed5e7daf9f14140ee21cc254670..77ed245fc5c4a5b7e70801fa3d659fcfebe57260 100644 (file)
@@ -415,4 +415,23 @@ struct optee_msg_arg {
  */
 #define OPTEE_MSG_RPC_CMD_SHM_FREE     7
 
+/*
+ * Create timer for TA
+ *
+ * [in]  param[0].u.value.a            TA session
+ * [in]  param[0].u.value.b            Timer handle
+ * [in]  param[1].u.value.a            Timeout
+ * [in]  param[2].u.value.b            Flags
+ */
+#define OPTEE_MSG_RPC_CMD_TIMER_CREATE  102
+
+/*
+ * Destroy timer previously created with OPTEE_MSG_RPC_CMD_TIMER_CREATE
+ *
+ * [in]  param[0].u.value.a            TA session
+ * [in]  param[0].u.value.b            Timer handle
+ */
+
+#define OPTEE_MSG_RPC_CMD_TIMER_DESTROY 103
+
 #endif /* _OPTEE_MSG_H */
index 18a6f047e5ea5add1c897f1d93870198376f3c3b..2bdf40ebde48d773ecf14979f61edeb33d82ebb8 100644 (file)
@@ -75,6 +75,19 @@ struct optee_supp {
        struct completion reqs_c;
 };
 
+/**
+ * struct optee_timer - timer struct
+ * @mutex:          held while accessing timer_list
+ * @timer_list:     list of timer data
+ * @wq:             work queue of the timer
+ */
+
+struct optee_timer {
+       struct mutex mutex;
+       struct list_head timer_list;
+       struct workqueue_struct *wq;
+};
+
 /**
  * struct optee - main service struct
  * @supp_teedev:       supplicant device
@@ -94,6 +107,7 @@ struct optee {
        struct optee_call_queue call_queue;
        struct optee_wait_queue wait_queue;
        struct optee_supp supp;
+       struct optee_timer timer;
        struct tee_shm_pool *pool;
        void *memremaped_shm;
 };
@@ -174,4 +188,8 @@ static inline void reg_pair_from_64(u32 *reg0, u32 *reg1, u64 val)
        *reg1 = val;
 }
 
+void optee_timer_init(struct optee_timer *timer);
+void optee_timer_destroy(struct optee_timer *timer);
+void optee_timer_missed_destroy(struct tee_context *ctx, u32 session);
+
 #endif /*OPTEE_PRIVATE_H*/
index 9ed6311c31a468471d737e588d280cb09c41cd69..12ba0cb3d54635eeca8469fa4316361a0323f5b2 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/delay.h>
 #include <linux/device.h>
 #include <linux/slab.h>
+#include <linux/workqueue.h>
 #include "tee_drv.h"
 #include "optee_private.h"
 #include "optee_smc.h"
@@ -310,6 +311,199 @@ static void handle_rpc_func_cmd_shm_free(struct tee_context *ctx,
        arg->ret = TEEC_SUCCESS;
 }
 
+#define TEE_SECURE_TIMER_FLAG_ONESHOT   0
+#define TEE_SECURE_TIMER_FLAG_PERIOD    1
+struct optee_timer_data {
+       struct tee_context *ctx;
+       uint32_t sess;
+       uint32_t handle;
+       uint32_t flags;
+       uint32_t timeout;
+       struct delayed_work work;
+       struct list_head list_node;
+       uint32_t delay_cancel;
+       uint32_t working;
+};
+
+static void timer_work_task(struct work_struct *work)
+{
+       struct tee_ioctl_invoke_arg arg;
+       struct tee_param params[4];
+       struct optee_timer_data *timer_data = container_of((struct delayed_work *)work,
+                       struct optee_timer_data, work);
+       struct tee_context *ctx = timer_data->ctx;
+       struct tee_device *teedev = ctx->teedev;
+       struct optee *optee = tee_get_drvdata(teedev);
+       struct optee_timer *timer = &optee->timer;
+       struct workqueue_struct *wq = timer->wq;
+       int ret = 0;
+
+       params[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
+       params[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
+       params[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
+       params[3].attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
+
+       params[0].u.value.a = timer_data->handle;
+
+       arg.session = timer_data->sess;
+       arg.num_params = 4;
+       arg.func = 0xFFFFFFFE;
+       mutex_lock(&timer->mutex);
+       timer_data->working = 1;
+       mutex_unlock(&timer->mutex);
+       ret = optee_invoke_func(ctx, &arg, params);
+       if (ret != 0)
+               pr_err(KERN_EMERG "%s: invoke cmd failed ret = 0x%x\n", __func__, ret);
+
+       mutex_lock(&timer->mutex);
+       if (timer_data->delay_cancel ||
+                       (!(timer_data->flags & TEE_SECURE_TIMER_FLAG_PERIOD))) {
+               list_del(&timer_data->list_node);
+               kfree(timer_data);
+               mutex_unlock(&timer->mutex);
+       } else {
+               timer_data->working = 0;
+               mutex_unlock(&timer->mutex);
+               queue_delayed_work(wq, &timer_data->work,
+                               msecs_to_jiffies(timer_data->timeout));
+       }
+}
+
+void optee_timer_init(struct optee_timer *timer)
+{
+       struct workqueue_struct *wq = NULL;
+
+       mutex_init(&timer->mutex);
+       INIT_LIST_HEAD(&timer->timer_list);
+
+       wq = create_workqueue("tee_timer");
+       if (!wq)
+               return;
+       timer->wq = wq;
+}
+
+void optee_timer_destroy(struct optee_timer *timer)
+{
+       struct optee_timer_data *timer_data = NULL;
+       struct optee_timer_data *temp = NULL;
+
+       mutex_lock(&timer->mutex);
+       list_for_each_entry_safe(timer_data, temp, &timer->timer_list, list_node) {
+               if (timer_data != NULL) {
+                       cancel_delayed_work_sync(&timer_data->work);
+                       list_del(&timer_data->list_node);
+                       kfree(timer_data);
+               }
+       }
+       mutex_unlock(&timer->mutex);
+
+       mutex_destroy(&timer->mutex);
+       destroy_workqueue(timer->wq);
+}
+
+void optee_timer_missed_destroy(struct tee_context *ctx, u32 session)
+{
+       struct optee_timer_data *timer_data = NULL;
+       struct optee_timer_data *temp = NULL;
+       struct tee_device *teedev = ctx->teedev;
+       struct optee *optee = tee_get_drvdata(teedev);
+       struct optee_timer *timer = &optee->timer;
+
+       mutex_lock(&timer->mutex);
+       list_for_each_entry_safe(timer_data, temp, &timer->timer_list, list_node) {
+               if (timer_data != NULL && timer_data->ctx == ctx
+                               && timer_data->sess == session) {
+                       if (timer_data->working) {
+                               timer_data->delay_cancel = 1;
+                               continue;
+                       }
+                       cancel_delayed_work_sync(&timer_data->work);
+                       list_del(&timer_data->list_node);
+                       kfree(timer_data);
+               }
+       }
+       mutex_unlock(&timer->mutex);
+}
+
+static void handle_rpc_func_cmd_timer_create(struct tee_context *ctx,
+                                        struct optee_msg_arg *arg)
+{
+       struct optee_timer_data *timer_data;
+       struct tee_device *teedev = ctx->teedev;
+       struct optee *optee = tee_get_drvdata(teedev);
+       struct optee_timer *timer = &optee->timer;
+       struct workqueue_struct *wq = timer->wq;
+
+       if (arg->num_params != 2 ||
+           arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT ||
+           arg->params[1].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) {
+               arg->ret = TEEC_ERROR_BAD_PARAMETERS;
+               return;
+       }
+
+       timer_data = kmalloc(sizeof(struct optee_timer_data), GFP_KERNEL);
+       if (!timer_data) {
+               arg->ret = TEEC_ERROR_OUT_OF_MEMORY;
+               return;
+       }
+
+       timer_data->ctx = ctx;
+       timer_data->sess = arg->params[0].u.value.a;
+       timer_data->handle = arg->params[0].u.value.b;
+       timer_data->timeout = arg->params[1].u.value.a;
+       timer_data->flags = arg->params[1].u.value.b;
+       timer_data->delay_cancel = 0;
+       timer_data->working= 0;
+       INIT_DELAYED_WORK(&timer_data->work, timer_work_task);
+
+       mutex_lock(&timer->mutex);
+       list_add_tail(&timer_data->list_node, &timer->timer_list);
+       mutex_unlock(&timer->mutex);
+
+       queue_delayed_work(wq, &timer_data->work, msecs_to_jiffies(timer_data->timeout));
+
+       arg->ret = TEEC_SUCCESS;
+}
+
+static void handle_rpc_func_cmd_timer_destroy(struct tee_context *ctx,
+                                        struct optee_msg_arg *arg)
+{
+       uint32_t handle;
+       struct tee_device *teedev = ctx->teedev;
+       struct optee *optee = tee_get_drvdata(teedev);
+       struct optee_timer *timer = &optee->timer;
+       struct optee_timer_data *timer_data;
+
+       if (arg->num_params != 1 ||
+           arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) {
+               arg->ret = TEEC_ERROR_BAD_PARAMETERS;
+               return;
+       }
+
+       handle = arg->params[0].u.value.b;
+
+       mutex_lock(&timer->mutex);
+       list_for_each_entry(timer_data, &timer->timer_list, list_node) {
+               if (timer_data->handle == handle) {
+                       if (timer_data->working) {
+                               timer_data->delay_cancel = 1;
+                               arg->ret = TEEC_SUCCESS;
+                               goto out;
+                       }
+                       cancel_delayed_work_sync(&timer_data->work);
+                       list_del(&timer_data->list_node);
+                       kfree(timer_data);
+
+                       arg->ret = TEEC_SUCCESS;
+                       goto out;
+               }
+       }
+
+       arg->ret = TEEC_ERROR_BAD_PARAMETERS;
+out:
+       mutex_unlock(&timer->mutex);
+}
+
 static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee,
                                struct tee_shm *shm)
 {
@@ -337,6 +531,12 @@ static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee,
        case OPTEE_MSG_RPC_CMD_SHM_FREE:
                handle_rpc_func_cmd_shm_free(ctx, arg);
                break;
+       case OPTEE_MSG_RPC_CMD_TIMER_CREATE:
+               handle_rpc_func_cmd_timer_create(ctx, arg);
+               break;
+       case OPTEE_MSG_RPC_CMD_TIMER_DESTROY:
+               handle_rpc_func_cmd_timer_destroy(ctx, arg);
+               break;
        default:
                handle_rpc_supp_cmd(ctx, arg);
        }