Loading drivers/soc/qcom/smcinvoke.c +80 −58 Original line number Diff line number Diff line Loading @@ -111,6 +111,9 @@ #define MEM_RGN_SRVR_ID 1 #define MEM_MAP_SRVR_ID 2 #define CBOBJ_SERVER_ID_START 0x10 #define CBOBJ_SERVER_ID_END ((1<<16) - 1) /* local obj id is represented by 15 bits */ #define MAX_LOCAL_OBJ_ID ((1<<15) - 1) /* CBOBJs will be served by server id 0x10 onwards */ #define TZHANDLE_GET_SERVER(h) ((uint16_t)((h) & 0xFFFF)) #define TZHANDLE_GET_OBJID(h) (((h) >> 16) & 0x7FFF) Loading Loading @@ -264,6 +267,9 @@ static struct smcinvoke_server_info *find_cb_server_locked(uint16_t server_id) static uint16_t next_cb_server_id_locked(void) { if (g_last_cb_server_id == CBOBJ_SERVER_ID_END) g_last_cb_server_id = CBOBJ_SERVER_ID_START; while (find_cb_server_locked(++g_last_cb_server_id)); return g_last_cb_server_id; Loading Loading @@ -301,6 +307,9 @@ static struct smcinvoke_mem_obj *find_mem_obj_locked(uint16_t mem_obj_id, static uint32_t next_mem_region_obj_id_locked(void) { if (g_last_mem_rgn_id == MAX_LOCAL_OBJ_ID) g_last_mem_rgn_id = 0; while (find_mem_obj_locked(++g_last_mem_rgn_id, SMCINVOKE_MEM_RGN_OBJ)); return g_last_mem_rgn_id; Loading @@ -308,6 +317,9 @@ static uint32_t next_mem_region_obj_id_locked(void) static uint32_t next_mem_map_obj_id_locked(void) { if (g_last_mem_map_obj_id == MAX_LOCAL_OBJ_ID) g_last_mem_map_obj_id = 0; while (find_mem_obj_locked(++g_last_mem_map_obj_id, SMCINVOKE_MEM_MAP_OBJ)); Loading Loading @@ -391,10 +403,10 @@ static void free_pending_cbobj_locked(struct kref *kref) static int get_pending_cbobj_locked(uint16_t srvr_id, int16_t obj_id) { struct smcinvoke_server_info *server = find_cb_server_locked(srvr_id); struct list_head *head = NULL; struct smcinvoke_cbobj *cbobj = NULL; struct smcinvoke_cbobj *obj = NULL; struct smcinvoke_server_info *server = find_cb_server_locked(srvr_id); if (!server) return OBJECT_ERROR_BADOBJ; Loading Loading @@ -462,7 +474,11 @@ static void delete_cb_txn(struct kref *kref) struct smcinvoke_cb_txn *cb_txn = container_of(kref, struct smcinvoke_cb_txn, ref_cnt); if (OBJECT_OP_METHODID(cb_txn->cb_req->hdr.op) == OBJECT_OP_RELEASE) release_tzhandle_locked(cb_txn->cb_req->hdr.tzhandle); kfree(cb_txn->cb_req); hash_del(&cb_txn->hash); kfree(cb_txn); } Loading Loading @@ -839,20 +855,13 @@ static void process_tzcb_req(void *buf, size_t buf_len, struct file **arr_filp) /* ret is going to TZ. Provide values from OBJECT_ERROR_<> */ int ret = OBJECT_ERROR_DEFUNCT; struct smcinvoke_cb_txn *cb_txn = NULL; struct smcinvoke_tzcb_req *cb_req = NULL; struct smcinvoke_tzcb_req *cb_req = NULL, *tmp_cb_req = NULL; struct smcinvoke_server_info *srvr_info = NULL; if (buf_len < sizeof(struct smcinvoke_tzcb_req)) return; cb_req = kzalloc(buf_len, GFP_KERNEL); if (!cb_req) { /* we need to return error to caller so fill up result */ cb_req = buf; cb_req->result = OBJECT_ERROR_KMEM; return; } memcpy(cb_req, buf, buf_len); /* check whether it is to be served by kernel or userspace */ if (TZHANDLE_IS_KERNEL_OBJ(cb_req->hdr.tzhandle)) { Loading @@ -864,11 +873,26 @@ static void process_tzcb_req(void *buf, size_t buf_len, struct file **arr_filp) return; } /* * We need a copy of req that could be sent to server. Otherwise, if * someone kills invoke caller, buf would go away and server would be * working on already freed buffer, causing a device crash. */ tmp_cb_req = kmemdup(buf, buf_len, GFP_KERNEL); if (!tmp_cb_req) { /* we need to return error to caller so fill up result */ cb_req->result = OBJECT_ERROR_KMEM; return; } cb_txn = kzalloc(sizeof(*cb_txn), GFP_KERNEL); if (!cb_txn) { ret = OBJECT_ERROR_KMEM; goto out; cb_req->result = OBJECT_ERROR_KMEM; kfree(tmp_cb_req); return; } /* no need for memcpy as we did kmemdup() above */ cb_req = tmp_cb_req; cb_txn->state = SMCINVOKE_REQ_PLACED; cb_txn->cb_req = cb_req; Loading @@ -880,6 +904,7 @@ static void process_tzcb_req(void *buf, size_t buf_len, struct file **arr_filp) srvr_info = find_cb_server_locked( TZHANDLE_GET_SERVER(cb_req->hdr.tzhandle)); if (!srvr_info || srvr_info->state == SMCINVOKE_SERVER_STATE_DEFUNCT) { /* ret equals Object_ERROR_DEFUNCT, at this point go to out */ mutex_unlock(&g_smcinvoke_lock); goto out; } Loading @@ -887,33 +912,36 @@ static void process_tzcb_req(void *buf, size_t buf_len, struct file **arr_filp) cb_txn->txn_id = ++srvr_info->txn_id; hash_add(srvr_info->reqs_table, &cb_txn->hash, cb_txn->txn_id); mutex_unlock(&g_smcinvoke_lock); /* * we need not worry that server_info will be deleted because as long * as this CBObj is served by this server, srvr_info will be valid. */ wake_up_interruptible(&srvr_info->req_wait_q); ret = wait_event_interruptible(srvr_info->rsp_wait_q, (cb_txn->state == SMCINVOKE_REQ_PROCESSED) || (srvr_info->state == SMCINVOKE_SERVER_STATE_DEFUNCT)); if (ret) pr_err("%s wait_event interrupted: ret = %d\n", __func__, ret); out: /* * If we are here, either req is processed or not * if processed, result would have been set by txn processor * if not processed, we should set result with ret which should have * correct value that TZ/TA can understand * we could be here because of either: a. Req is PROCESSED * b. Server was killed c. Invoke thread is killed * sometime invoke thread and server are part of same process. */ mutex_lock(&g_smcinvoke_lock); if (!cb_txn || (cb_txn->state != SMCINVOKE_REQ_PROCESSED)) { cb_req->result = ret; if (srvr_info && srvr_info->state == SMCINVOKE_SERVER_STATE_DEFUNCT && OBJECT_OP_METHODID(cb_req->hdr.op) == OBJECT_OP_RELEASE) { release_tzhandle_locked(cb_req->hdr.tzhandle); } } if (cb_txn) { hash_del(&cb_txn->hash); if (cb_txn->state == SMCINVOKE_REQ_PROCESSED) { /* * it is possible that server was killed immediately * after CB Req was processed but who cares now! */ } else if (!srvr_info || srvr_info->state == SMCINVOKE_SERVER_STATE_DEFUNCT) { cb_req->result = OBJECT_ERROR_DEFUNCT; } else { pr_debug("%s wait_event interrupted ret = %d\n", __func__, ret); cb_req->result = OBJECT_ERROR_ABORT; } memcpy(buf, cb_req, buf_len); kref_put(&cb_txn->ref_cnt, delete_cb_txn); } mutex_unlock(&g_smcinvoke_lock); } Loading Loading @@ -1439,24 +1467,26 @@ static long process_accept_req(struct file *filp, unsigned int cmd, cb_txn = find_cbtxn_locked(server_info, user_args.txn_id, SMCINVOKE_REQ_PROCESSING); mutex_unlock(&g_smcinvoke_lock); /* cb_txn can be null if userspace provides wrong txn id. */ /* * cb_txn can be null if userspace provides wrong txn id OR * invoke thread died while server was processing cb req. * if invoke thread dies, it would remove req from Q. So * no matching cb_txn would be on Q and hence NULL cb_txn. */ if (!cb_txn) { pr_err("%s: Invalid txn received = %d\n", pr_err("%s txn %d either invalid or removed from Q\n", __func__, user_args.txn_id); goto out; } ret = marshal_out_tzcb_req(&user_args, cb_txn, cb_txn->filp_to_release); /* * if client did not set error and we get error locally * if client did not set error and we get error locally, * we return local error to TA */ if (ret && cb_txn->cb_req->result == 0) cb_txn->cb_req->result = OBJECT_ERROR_UNAVAIL; if (OBJECT_OP_METHODID(user_args.op) == OBJECT_OP_RELEASE) release_tzhandles(&cb_txn->cb_req->hdr.tzhandle, 1); cb_txn->state = SMCINVOKE_REQ_PROCESSED; kref_put(&cb_txn->ref_cnt, delete_cb_txn); wake_up(&server_info->rsp_wait_q); Loading @@ -1475,7 +1505,7 @@ static long process_accept_req(struct file *filp, unsigned int cmd, ret = wait_event_interruptible(server_info->req_wait_q, !hash_empty(server_info->reqs_table)); if (ret) { pr_err("%s wait_event interrupted: ret = %d\n", pr_debug("%s wait_event interrupted: ret = %d\n", __func__, ret); goto out; } Loading Loading @@ -1530,34 +1560,26 @@ static long process_invoke_req(struct file *filp, unsigned int cmd, int32_t tzhandles_to_release[OBJECT_COUNTS_MAX_OO] = {0}; bool tz_acked = false; if (_IOC_SIZE(cmd) != sizeof(req)) { ret = -EINVAL; goto out; } if (tzobj->context_type != SMCINVOKE_OBJ_TYPE_TZ_OBJ) { ret = -EPERM; goto out; } if (_IOC_SIZE(cmd) != sizeof(req)) return -EINVAL; if (tzobj->context_type != SMCINVOKE_OBJ_TYPE_TZ_OBJ) return -EPERM; ret = copy_from_user(&req, (void __user *)arg, sizeof(req)); if (ret) { ret = -EFAULT; goto out; } if (ret) return -EFAULT; if (req.argsize != sizeof(union smcinvoke_arg)) return -EINVAL; nr_args = OBJECT_COUNTS_NUM_buffers(req.counts) + OBJECT_COUNTS_NUM_objects(req.counts); if (req.argsize != sizeof(union smcinvoke_arg)) { ret = -EINVAL; goto out; } if (nr_args) { args_buf = kcalloc(nr_args, req.argsize, GFP_KERNEL); if (!args_buf) { ret = -ENOMEM; goto out; } if (!args_buf) return -ENOMEM; ret = copy_from_user(args_buf, u64_to_user_ptr(req.args), nr_args * req.argsize); Loading Loading
drivers/soc/qcom/smcinvoke.c +80 −58 Original line number Diff line number Diff line Loading @@ -111,6 +111,9 @@ #define MEM_RGN_SRVR_ID 1 #define MEM_MAP_SRVR_ID 2 #define CBOBJ_SERVER_ID_START 0x10 #define CBOBJ_SERVER_ID_END ((1<<16) - 1) /* local obj id is represented by 15 bits */ #define MAX_LOCAL_OBJ_ID ((1<<15) - 1) /* CBOBJs will be served by server id 0x10 onwards */ #define TZHANDLE_GET_SERVER(h) ((uint16_t)((h) & 0xFFFF)) #define TZHANDLE_GET_OBJID(h) (((h) >> 16) & 0x7FFF) Loading Loading @@ -264,6 +267,9 @@ static struct smcinvoke_server_info *find_cb_server_locked(uint16_t server_id) static uint16_t next_cb_server_id_locked(void) { if (g_last_cb_server_id == CBOBJ_SERVER_ID_END) g_last_cb_server_id = CBOBJ_SERVER_ID_START; while (find_cb_server_locked(++g_last_cb_server_id)); return g_last_cb_server_id; Loading Loading @@ -301,6 +307,9 @@ static struct smcinvoke_mem_obj *find_mem_obj_locked(uint16_t mem_obj_id, static uint32_t next_mem_region_obj_id_locked(void) { if (g_last_mem_rgn_id == MAX_LOCAL_OBJ_ID) g_last_mem_rgn_id = 0; while (find_mem_obj_locked(++g_last_mem_rgn_id, SMCINVOKE_MEM_RGN_OBJ)); return g_last_mem_rgn_id; Loading @@ -308,6 +317,9 @@ static uint32_t next_mem_region_obj_id_locked(void) static uint32_t next_mem_map_obj_id_locked(void) { if (g_last_mem_map_obj_id == MAX_LOCAL_OBJ_ID) g_last_mem_map_obj_id = 0; while (find_mem_obj_locked(++g_last_mem_map_obj_id, SMCINVOKE_MEM_MAP_OBJ)); Loading Loading @@ -391,10 +403,10 @@ static void free_pending_cbobj_locked(struct kref *kref) static int get_pending_cbobj_locked(uint16_t srvr_id, int16_t obj_id) { struct smcinvoke_server_info *server = find_cb_server_locked(srvr_id); struct list_head *head = NULL; struct smcinvoke_cbobj *cbobj = NULL; struct smcinvoke_cbobj *obj = NULL; struct smcinvoke_server_info *server = find_cb_server_locked(srvr_id); if (!server) return OBJECT_ERROR_BADOBJ; Loading Loading @@ -462,7 +474,11 @@ static void delete_cb_txn(struct kref *kref) struct smcinvoke_cb_txn *cb_txn = container_of(kref, struct smcinvoke_cb_txn, ref_cnt); if (OBJECT_OP_METHODID(cb_txn->cb_req->hdr.op) == OBJECT_OP_RELEASE) release_tzhandle_locked(cb_txn->cb_req->hdr.tzhandle); kfree(cb_txn->cb_req); hash_del(&cb_txn->hash); kfree(cb_txn); } Loading Loading @@ -839,20 +855,13 @@ static void process_tzcb_req(void *buf, size_t buf_len, struct file **arr_filp) /* ret is going to TZ. Provide values from OBJECT_ERROR_<> */ int ret = OBJECT_ERROR_DEFUNCT; struct smcinvoke_cb_txn *cb_txn = NULL; struct smcinvoke_tzcb_req *cb_req = NULL; struct smcinvoke_tzcb_req *cb_req = NULL, *tmp_cb_req = NULL; struct smcinvoke_server_info *srvr_info = NULL; if (buf_len < sizeof(struct smcinvoke_tzcb_req)) return; cb_req = kzalloc(buf_len, GFP_KERNEL); if (!cb_req) { /* we need to return error to caller so fill up result */ cb_req = buf; cb_req->result = OBJECT_ERROR_KMEM; return; } memcpy(cb_req, buf, buf_len); /* check whether it is to be served by kernel or userspace */ if (TZHANDLE_IS_KERNEL_OBJ(cb_req->hdr.tzhandle)) { Loading @@ -864,11 +873,26 @@ static void process_tzcb_req(void *buf, size_t buf_len, struct file **arr_filp) return; } /* * We need a copy of req that could be sent to server. Otherwise, if * someone kills invoke caller, buf would go away and server would be * working on already freed buffer, causing a device crash. */ tmp_cb_req = kmemdup(buf, buf_len, GFP_KERNEL); if (!tmp_cb_req) { /* we need to return error to caller so fill up result */ cb_req->result = OBJECT_ERROR_KMEM; return; } cb_txn = kzalloc(sizeof(*cb_txn), GFP_KERNEL); if (!cb_txn) { ret = OBJECT_ERROR_KMEM; goto out; cb_req->result = OBJECT_ERROR_KMEM; kfree(tmp_cb_req); return; } /* no need for memcpy as we did kmemdup() above */ cb_req = tmp_cb_req; cb_txn->state = SMCINVOKE_REQ_PLACED; cb_txn->cb_req = cb_req; Loading @@ -880,6 +904,7 @@ static void process_tzcb_req(void *buf, size_t buf_len, struct file **arr_filp) srvr_info = find_cb_server_locked( TZHANDLE_GET_SERVER(cb_req->hdr.tzhandle)); if (!srvr_info || srvr_info->state == SMCINVOKE_SERVER_STATE_DEFUNCT) { /* ret equals Object_ERROR_DEFUNCT, at this point go to out */ mutex_unlock(&g_smcinvoke_lock); goto out; } Loading @@ -887,33 +912,36 @@ static void process_tzcb_req(void *buf, size_t buf_len, struct file **arr_filp) cb_txn->txn_id = ++srvr_info->txn_id; hash_add(srvr_info->reqs_table, &cb_txn->hash, cb_txn->txn_id); mutex_unlock(&g_smcinvoke_lock); /* * we need not worry that server_info will be deleted because as long * as this CBObj is served by this server, srvr_info will be valid. */ wake_up_interruptible(&srvr_info->req_wait_q); ret = wait_event_interruptible(srvr_info->rsp_wait_q, (cb_txn->state == SMCINVOKE_REQ_PROCESSED) || (srvr_info->state == SMCINVOKE_SERVER_STATE_DEFUNCT)); if (ret) pr_err("%s wait_event interrupted: ret = %d\n", __func__, ret); out: /* * If we are here, either req is processed or not * if processed, result would have been set by txn processor * if not processed, we should set result with ret which should have * correct value that TZ/TA can understand * we could be here because of either: a. Req is PROCESSED * b. Server was killed c. Invoke thread is killed * sometime invoke thread and server are part of same process. */ mutex_lock(&g_smcinvoke_lock); if (!cb_txn || (cb_txn->state != SMCINVOKE_REQ_PROCESSED)) { cb_req->result = ret; if (srvr_info && srvr_info->state == SMCINVOKE_SERVER_STATE_DEFUNCT && OBJECT_OP_METHODID(cb_req->hdr.op) == OBJECT_OP_RELEASE) { release_tzhandle_locked(cb_req->hdr.tzhandle); } } if (cb_txn) { hash_del(&cb_txn->hash); if (cb_txn->state == SMCINVOKE_REQ_PROCESSED) { /* * it is possible that server was killed immediately * after CB Req was processed but who cares now! */ } else if (!srvr_info || srvr_info->state == SMCINVOKE_SERVER_STATE_DEFUNCT) { cb_req->result = OBJECT_ERROR_DEFUNCT; } else { pr_debug("%s wait_event interrupted ret = %d\n", __func__, ret); cb_req->result = OBJECT_ERROR_ABORT; } memcpy(buf, cb_req, buf_len); kref_put(&cb_txn->ref_cnt, delete_cb_txn); } mutex_unlock(&g_smcinvoke_lock); } Loading Loading @@ -1439,24 +1467,26 @@ static long process_accept_req(struct file *filp, unsigned int cmd, cb_txn = find_cbtxn_locked(server_info, user_args.txn_id, SMCINVOKE_REQ_PROCESSING); mutex_unlock(&g_smcinvoke_lock); /* cb_txn can be null if userspace provides wrong txn id. */ /* * cb_txn can be null if userspace provides wrong txn id OR * invoke thread died while server was processing cb req. * if invoke thread dies, it would remove req from Q. So * no matching cb_txn would be on Q and hence NULL cb_txn. */ if (!cb_txn) { pr_err("%s: Invalid txn received = %d\n", pr_err("%s txn %d either invalid or removed from Q\n", __func__, user_args.txn_id); goto out; } ret = marshal_out_tzcb_req(&user_args, cb_txn, cb_txn->filp_to_release); /* * if client did not set error and we get error locally * if client did not set error and we get error locally, * we return local error to TA */ if (ret && cb_txn->cb_req->result == 0) cb_txn->cb_req->result = OBJECT_ERROR_UNAVAIL; if (OBJECT_OP_METHODID(user_args.op) == OBJECT_OP_RELEASE) release_tzhandles(&cb_txn->cb_req->hdr.tzhandle, 1); cb_txn->state = SMCINVOKE_REQ_PROCESSED; kref_put(&cb_txn->ref_cnt, delete_cb_txn); wake_up(&server_info->rsp_wait_q); Loading @@ -1475,7 +1505,7 @@ static long process_accept_req(struct file *filp, unsigned int cmd, ret = wait_event_interruptible(server_info->req_wait_q, !hash_empty(server_info->reqs_table)); if (ret) { pr_err("%s wait_event interrupted: ret = %d\n", pr_debug("%s wait_event interrupted: ret = %d\n", __func__, ret); goto out; } Loading Loading @@ -1530,34 +1560,26 @@ static long process_invoke_req(struct file *filp, unsigned int cmd, int32_t tzhandles_to_release[OBJECT_COUNTS_MAX_OO] = {0}; bool tz_acked = false; if (_IOC_SIZE(cmd) != sizeof(req)) { ret = -EINVAL; goto out; } if (tzobj->context_type != SMCINVOKE_OBJ_TYPE_TZ_OBJ) { ret = -EPERM; goto out; } if (_IOC_SIZE(cmd) != sizeof(req)) return -EINVAL; if (tzobj->context_type != SMCINVOKE_OBJ_TYPE_TZ_OBJ) return -EPERM; ret = copy_from_user(&req, (void __user *)arg, sizeof(req)); if (ret) { ret = -EFAULT; goto out; } if (ret) return -EFAULT; if (req.argsize != sizeof(union smcinvoke_arg)) return -EINVAL; nr_args = OBJECT_COUNTS_NUM_buffers(req.counts) + OBJECT_COUNTS_NUM_objects(req.counts); if (req.argsize != sizeof(union smcinvoke_arg)) { ret = -EINVAL; goto out; } if (nr_args) { args_buf = kcalloc(nr_args, req.argsize, GFP_KERNEL); if (!args_buf) { ret = -ENOMEM; goto out; } if (!args_buf) return -ENOMEM; ret = copy_from_user(args_buf, u64_to_user_ptr(req.args), nr_args * req.argsize); Loading