From: Liqiang Jin Date: Mon, 28 Oct 2019 10:16:32 +0000 (+0800) Subject: timer: support TEE timer callback [2/2] X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=00131a0ed9da01fcbd7b7c03a2af5aa2f9c3c991;p=GitHub%2FLineageOS%2FG12%2Fandroid_hardware_amlogic_kernel-modules_optee.git timer: support TEE timer callback [2/2] 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 --- diff --git a/optee/call.c b/optee/call.c index 4683104..96ea26a 100644 --- a/optee/call.c +++ b/optee/call.c @@ -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; } diff --git a/optee/core.c b/optee/core.c index 7202489..45028a1 100644 --- a/optee/core.c +++ b/optee/core.c @@ -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); /* diff --git a/optee/optee_msg.h b/optee/optee_msg.h index dd7a06e..77ed245 100644 --- a/optee/optee_msg.h +++ b/optee/optee_msg.h @@ -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 */ diff --git a/optee/optee_private.h b/optee/optee_private.h index 18a6f04..2bdf40e 100644 --- a/optee/optee_private.h +++ b/optee/optee_private.h @@ -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*/ diff --git a/optee/rpc.c b/optee/rpc.c index 9ed6311..12ba0cb 100644 --- a/optee/rpc.c +++ b/optee/rpc.c @@ -17,6 +17,7 @@ #include #include #include +#include #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); }