Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 980af8d4 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "qseecom: fix race condition between load/unload app during reboot"

parents b39b9019 211e4f01
Loading
Loading
Loading
Loading
+231 −54
Original line number Diff line number Diff line
@@ -150,10 +150,16 @@ enum qseecom_listener_unregister_kthread_state {
	LSNR_UNREG_KT_WAKEUP,
};

enum qseecom_unload_app_kthread_state {
	UNLOAD_APP_KT_SLEEP = 0,
	UNLOAD_APP_KT_WAKEUP,
};

static DEFINE_MUTEX(qsee_bw_mutex);
static DEFINE_MUTEX(app_access_lock);
static DEFINE_MUTEX(clk_access_lock);
static DEFINE_MUTEX(listener_access_lock);
static DEFINE_MUTEX(unload_app_pending_list_lock);


struct sglist_info {
@@ -317,6 +323,16 @@ struct qseecom_control {
	struct task_struct *unregister_lsnr_kthread_task;
	wait_queue_head_t unregister_lsnr_kthread_wq;
	atomic_t unregister_lsnr_kthread_state;

	struct list_head  unload_app_pending_list_head;
	struct task_struct *unload_app_kthread_task;
	wait_queue_head_t unload_app_kthread_wq;
	atomic_t unload_app_kthread_state;
};

struct qseecom_unload_app_pending_list {
	struct list_head		list;
	struct qseecom_dev_handle	*data;
};

struct qseecom_sec_buf_fd_info {
@@ -346,6 +362,7 @@ struct qseecom_client_handle {
	struct qseecom_sec_buf_fd_info sec_buf_fd[MAX_ION_FD];
	bool from_smcinvoke;
	struct qtee_shm shm; /* kernel client's shm for req/rsp buf */
	bool unload_pending;
};

struct qseecom_listener_handle {
@@ -424,6 +441,8 @@ static int qseecom_free_ce_info(struct qseecom_dev_handle *data,
						void __user *argp);
static int qseecom_query_ce_info(struct qseecom_dev_handle *data,
						void __user *argp);
static int __qseecom_unload_app(struct qseecom_dev_handle *data,
				uint32_t app_id);

static int get_qseecom_keymaster_status(char *str)
{
@@ -2851,8 +2870,11 @@ static int qseecom_load_app(struct qseecom_dev_handle *data, void __user *argp)
		if (resp.result == QSEOS_RESULT_INCOMPLETE) {
			ret = __qseecom_process_incomplete_cmd(data, &resp);
			if (ret) {
				pr_err("process_incomplete_cmd failed err: %d\n",
					ret);
				/* TZ has created app_id, need to unload it */
				pr_err("incomp_cmd err %d, %d, unload %d %s\n",
					ret, resp.result, resp.data,
					load_img_req.img_name);
				__qseecom_unload_app(data, resp.data);
				ret = -EFAULT;
				goto loadapp_err;
			}
@@ -2953,18 +2975,61 @@ static int __qseecom_cleanup_app(struct qseecom_dev_handle *data)
	return ret;
}

static int __qseecom_unload_app(struct qseecom_dev_handle *data,
				uint32_t app_id)
{
	struct qseecom_unload_app_ireq req;
	struct qseecom_command_scm_resp resp;
	int ret = 0;

	/* Populate the structure for sending scm call to load image */
	req.qsee_cmd_id = QSEOS_APP_SHUTDOWN_COMMAND;
	req.app_id = app_id;

	/* SCM_CALL to unload the app */
	ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
			sizeof(struct qseecom_unload_app_ireq),
			&resp, sizeof(resp));
	if (ret) {
		pr_err("scm_call to unload app (id = %d) failed\n", app_id);
		return -EFAULT;
	}
	switch (resp.result) {
	case QSEOS_RESULT_SUCCESS:
		pr_warn("App (%d) is unloaded\n", app_id);
		break;
	case QSEOS_RESULT_INCOMPLETE:
		ret = __qseecom_process_incomplete_cmd(data, &resp);
		if (ret)
			pr_err("unload app %d fail proc incom cmd: %d,%d,%d\n",
				app_id, ret, resp.result, resp.data);
		else
			pr_warn("App (%d) is unloaded\n", app_id);
		break;
	case QSEOS_RESULT_FAILURE:
		pr_err("app (%d) unload_failed!!\n", app_id);
		ret = -EFAULT;
		break;
	default:
		pr_err("unload app %d get unknown resp.result %d\n",
				app_id, resp.result);
		ret = -EFAULT;
		break;
	}
	return ret;
}

static int qseecom_unload_app(struct qseecom_dev_handle *data,
				bool app_crash)
{
	unsigned long flags;
	unsigned long flags1;
	int ret = 0;
	struct qseecom_command_scm_resp resp;
	struct qseecom_registered_app_list *ptr_app = NULL;
	bool unload = false;
	bool found_app = false;
	bool found_dead_app = false;
	bool scm_called = false;
	bool doublecheck = false;

	if (!data) {
		pr_err("Invalid/uninitialized device handle\n");
@@ -3014,48 +3079,9 @@ static int qseecom_unload_app(struct qseecom_dev_handle *data,
			(char *)data->client.app_name);

	if (unload) {
		struct qseecom_unload_app_ireq req;
		/* Populate the structure for sending scm call to load image */
		req.qsee_cmd_id = QSEOS_APP_SHUTDOWN_COMMAND;
		req.app_id = data->client.app_id;

		/* SCM_CALL to unload the app */
		ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
				sizeof(struct qseecom_unload_app_ireq),
				&resp, sizeof(resp));
		scm_called = true;
		if (ret) {
			pr_err("scm_call to unload app (id = %d) failed\n",
								req.app_id);
			ret = -EFAULT;
			goto scm_exit;
		} else {
			pr_warn("App id %d now unloaded\n", req.app_id);
		}
		if (resp.result == QSEOS_RESULT_FAILURE) {
			pr_err("app (%d) unload_failed!!\n",
					data->client.app_id);
			ret = -EFAULT;
			goto scm_exit;
		}
		if (resp.result == QSEOS_RESULT_SUCCESS)
			pr_debug("App (%d) is unloaded!!\n",
					data->client.app_id);
		if (resp.result == QSEOS_RESULT_INCOMPLETE) {
			ret = __qseecom_process_incomplete_cmd(data, &resp);
			if (ret) {
				pr_err("process_incomplete_cmd fail err: %d\n",
									ret);
				goto scm_exit;
			}
		}
	}
		ret = __qseecom_unload_app(data, data->client.app_id);

scm_exit:
	if (scm_called) {
		/* double check if this app_entry still exists */
		bool doublecheck = false;

		spin_lock_irqsave(&qseecom.registered_app_list_lock, flags1);
		list_for_each_entry(ptr_app,
			&qseecom.registered_app_list_head, list) {
@@ -3075,6 +3101,7 @@ static int qseecom_unload_app(struct qseecom_dev_handle *data,
			found_app = false;
		}
	}

unload_exit:
	if (found_app) {
		spin_lock_irqsave(&qseecom.registered_app_list_lock, flags1);
@@ -3106,6 +3133,103 @@ static int qseecom_unload_app(struct qseecom_dev_handle *data,
	return ret;
}


static int qseecom_prepare_unload_app(struct qseecom_dev_handle *data)
{
	struct qseecom_unload_app_pending_list *entry = NULL;

	pr_debug("prepare to unload app(%d)(%s), pending %d\n",
		data->client.app_id, data->client.app_name,
		data->client.unload_pending);
	if (data->client.unload_pending)
		return 0;
	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
	if (!entry)
		return -ENOMEM;
	entry->data = data;
	mutex_lock(&unload_app_pending_list_lock);
	list_add_tail(&entry->list,
		&qseecom.unload_app_pending_list_head);
	mutex_unlock(&unload_app_pending_list_lock);
	data->client.unload_pending = true;
	pr_debug("unload ta %d pending\n", data->client.app_id);
	return 0;
}

static void __wakeup_unload_app_kthread(void)
{
	atomic_set(&qseecom.unload_app_kthread_state,
				UNLOAD_APP_KT_WAKEUP);
	wake_up_interruptible(&qseecom.unload_app_kthread_wq);
}

static bool __qseecom_find_pending_unload_app(uint32_t app_id, char *app_name)
{
	struct qseecom_unload_app_pending_list *entry = NULL;
	bool found = false;

	mutex_lock(&unload_app_pending_list_lock);
	list_for_each_entry(entry, &qseecom.unload_app_pending_list_head,
					list) {
		if ((entry->data->client.app_id == app_id) &&
			(!strcmp(entry->data->client.app_name, app_name))) {
			found = true;
			break;
		}
	}
	mutex_unlock(&unload_app_pending_list_lock);
	return found;
}

static void __qseecom_processing_pending_unload_app(void)
{
	struct qseecom_unload_app_pending_list *entry = NULL;
	struct list_head *pos;
	int ret = 0;

	mutex_lock(&unload_app_pending_list_lock);
	while (!list_empty(&qseecom.unload_app_pending_list_head)) {
		pos = qseecom.unload_app_pending_list_head.next;
		entry = list_entry(pos,
			struct qseecom_unload_app_pending_list, list);
		if (entry && entry->data) {
			pr_debug("process pending unload app %d (%s)\n",
				entry->data->client.app_id,
				entry->data->client.app_name);
			mutex_unlock(&unload_app_pending_list_lock);
			mutex_lock(&app_access_lock);
			ret = qseecom_unload_app(entry->data, true);
			if (ret)
				pr_err("unload app %d pending failed %d\n",
					entry->data->client.app_id, ret);
			mutex_unlock(&app_access_lock);
			mutex_lock(&unload_app_pending_list_lock);
			__qseecom_free_tzbuf(&entry->data->sglistinfo_shm);
			kzfree(entry->data);
		}
		list_del(pos);
		kzfree(entry);
	}
	mutex_unlock(&unload_app_pending_list_lock);
}

static int __qseecom_unload_app_kthread_func(void *data)
{
	while (!kthread_should_stop()) {
		wait_event_interruptible(
			qseecom.unload_app_kthread_wq,
			atomic_read(&qseecom.unload_app_kthread_state)
				== UNLOAD_APP_KT_WAKEUP);
		pr_debug("kthread to unload app is called, state %d\n",
			atomic_read(&qseecom.unload_app_kthread_state));
		__qseecom_processing_pending_unload_app();
		atomic_set(&qseecom.unload_app_kthread_state,
				UNLOAD_APP_KT_SLEEP);
	}
	pr_warn("kthread to unload app stopped\n");
	return 0;
}

static phys_addr_t __qseecom_uvirt_to_kphys(struct qseecom_dev_handle *data,
						unsigned long virt)
{
@@ -3552,6 +3676,13 @@ static int __qseecom_send_cmd(struct qseecom_dev_handle *data,
		return -ENOENT;
	}

	if (__qseecom_find_pending_unload_app(data->client.app_id,
						data->client.app_name)) {
		pr_err("app %d (%s) unload is pending\n",
			data->client.app_id, data->client.app_name);
		return -ENOENT;
	}

	if (qseecom.qsee_version < QSEE_VERSION_40) {
		send_data_req.app_id = data->client.app_id;
		send_data_req.req_ptr = (uint32_t)(__qseecom_uvirt_to_kphys(
@@ -4632,10 +4763,14 @@ static int __qseecom_load_fw(struct qseecom_dev_handle *data, char *appname,
		break;
	case QSEOS_RESULT_INCOMPLETE:
		ret = __qseecom_process_incomplete_cmd(data, &resp);
		if (ret)
			pr_err("process_incomplete_cmd FAILED\n");
		else
		if (ret) {
			pr_err("incomp_cmd err %d, %d, unload %d %s\n",
				ret, resp.result, resp.data, appname);
			__qseecom_unload_app(data, resp.data);
			ret = -EFAULT;
		} else {
			*app_id = resp.data;
		}
		break;
	case QSEOS_RESULT_FAILURE:
		pr_err("scm call failed with response QSEOS_RESULT FAILURE\n");
@@ -4824,6 +4959,7 @@ int qseecom_start_app(struct qseecom_handle **handle,
	uint32_t app_id = 0;

	__wakeup_unregister_listener_kthread();
	__wakeup_unload_app_kthread();

	if (atomic_read(&qseecom.qseecom_state) != QSEECOM_STATE_READY) {
		pr_err("Not allowed to be called in %d state\n",
@@ -4961,6 +5097,7 @@ int qseecom_start_app(struct qseecom_handle **handle,
	spin_unlock_irqrestore(&qseecom.registered_kclient_list_lock, flags);

	mutex_unlock(&app_access_lock);
	__wakeup_unload_app_kthread();
	return 0;

err:
@@ -4971,6 +5108,7 @@ int qseecom_start_app(struct qseecom_handle **handle,
	kfree(*handle);
	*handle = NULL;
	mutex_unlock(&app_access_lock);
	__wakeup_unload_app_kthread();
	return ret;
}
EXPORT_SYMBOL(qseecom_start_app);
@@ -4985,6 +5123,7 @@ int qseecom_shutdown_app(struct qseecom_handle **handle)
	bool found_handle = false;

	__wakeup_unregister_listener_kthread();
	__wakeup_unload_app_kthread();

	if (atomic_read(&qseecom.qseecom_state) != QSEECOM_STATE_READY) {
		pr_err("Not allowed to be called in %d state\n",
@@ -5025,7 +5164,7 @@ int qseecom_shutdown_app(struct qseecom_handle **handle)
		kzfree(kclient);
		*handle = NULL;
	}

	__wakeup_unload_app_kthread();
	return ret;
}
EXPORT_SYMBOL(qseecom_shutdown_app);
@@ -5039,6 +5178,7 @@ int qseecom_send_command(struct qseecom_handle *handle, void *send_buf,
	bool perf_enabled = false;

	__wakeup_unregister_listener_kthread();
	__wakeup_unload_app_kthread();

	if (atomic_read(&qseecom.qseecom_state) != QSEECOM_STATE_READY) {
		pr_err("Not allowed to be called in %d state\n",
@@ -7022,6 +7162,12 @@ static int __qseecom_qteec_issue_cmd(struct qseecom_dev_handle *data,
			(char *)data->client.app_name);
		return -ENOENT;
	}
	if (__qseecom_find_pending_unload_app(data->client.app_id,
						data->client.app_name)) {
		pr_err("app %d (%s) unload is pending\n",
			data->client.app_id, data->client.app_name);
		return -ENOENT;
	}

	req->req_ptr = (void *)__qseecom_uvirt_to_kvirt(data,
						(uintptr_t)req->req_ptr);
@@ -7221,6 +7367,12 @@ static int qseecom_qteec_invoke_modfd_cmd(struct qseecom_dev_handle *data,
			(char *)data->client.app_name);
		return -ENOENT;
	}
	if (__qseecom_find_pending_unload_app(data->client.app_id,
						data->client.app_name)) {
		pr_err("app %d (%s) unload is pending\n",
			data->client.app_id, data->client.app_name);
		return -ENOENT;
	}

	/* validate offsets */
	for (i = 0; i < MAX_ION_FD; i++) {
@@ -7370,6 +7522,7 @@ static long qseecom_ioctl(struct file *file,
		cmd != QSEECOM_IOCTL_SEND_MODFD_RESP &&
		cmd != QSEECOM_IOCTL_SEND_MODFD_RESP_64)
		__wakeup_unregister_listener_kthread();
	__wakeup_unload_app_kthread();

	switch (cmd) {
	case QSEECOM_IOCTL_REGISTER_LISTENER_REQ: {
@@ -7610,6 +7763,7 @@ static long qseecom_ioctl(struct file *file,
		mutex_unlock(&app_access_lock);
		if (ret)
			pr_err("failed load_app request: %d\n", ret);
		__wakeup_unload_app_kthread();
		break;
	}
	case QSEECOM_IOCTL_UNLOAD_APP_REQ: {
@@ -7628,6 +7782,7 @@ static long qseecom_ioctl(struct file *file,
		mutex_unlock(&app_access_lock);
		if (ret)
			pr_err("failed unload_app request: %d\n", ret);
		__wakeup_unload_app_kthread();
		break;
	}
	case QSEECOM_IOCTL_GET_QSEOS_VERSION_REQ: {
@@ -8122,9 +8277,12 @@ static int qseecom_release(struct inode *inode, struct file *file)
			mutex_unlock(&listener_access_lock);
			break;
		case QSEECOM_CLIENT_APP:
			mutex_lock(&app_access_lock);
			ret = qseecom_unload_app(data, true);
			mutex_unlock(&app_access_lock);
			pr_debug("release app %d (%s)\n",
				data->client.app_id, data->client.app_name);
			if (data->client.app_id) {
				free_private_data = false;
				ret = qseecom_prepare_unload_app(data);
			}
			break;
		case QSEECOM_SECURE_SERVICE:
		case QSEECOM_GENERIC:
@@ -9164,6 +9322,8 @@ static int qseecom_init_control(void)
	init_waitqueue_head(&qseecom.send_resp_wq);
	init_waitqueue_head(&qseecom.register_lsnr_pending_wq);
	init_waitqueue_head(&qseecom.unregister_lsnr_kthread_wq);
	INIT_LIST_HEAD(&qseecom.unload_app_pending_list_head);
	init_waitqueue_head(&qseecom.unload_app_kthread_wq);
	qseecom.send_resp_flag = 0;
	qseecom.qseos_version = QSEOS_VERSION_14;
	qseecom.commonlib_loaded = false;
@@ -9223,7 +9383,7 @@ static int qseecom_parse_dt(struct platform_device *pdev)
	return 0;
}

static int qseecom_create_kthread_unregister_lsnr(void)
static int qseecom_create_kthreads(void)
{
	int rc = 0;

@@ -9237,6 +9397,19 @@ static int qseecom_create_kthread_unregister_lsnr(void)
	}
	atomic_set(&qseecom.unregister_lsnr_kthread_state,
					LSNR_UNREG_KT_SLEEP);

	/*create a kthread to process pending ta unloading task */
	qseecom.unload_app_kthread_task = kthread_run(
			__qseecom_unload_app_kthread_func,
			NULL, "qseecom-unload-ta");
	if (IS_ERR(qseecom.unload_app_kthread_task)) {
		rc = PTR_ERR(qseecom.unload_app_kthread_task);
		pr_err("failed to create kthread to unload ta, rc = %x\n", rc);
		kthread_stop(qseecom.unregister_lsnr_kthread_task);
		return rc;
	}
	atomic_set(&qseecom.unload_app_kthread_state,
					UNLOAD_APP_KT_SLEEP);
	return 0;
}

@@ -9345,17 +9518,20 @@ static int qseecom_probe(struct platform_device *pdev)
	if (rc)
		goto exit_deinit_bus;

	rc = qseecom_create_kthread_unregister_lsnr();
	rc = qseecom_create_kthreads();
	if (rc)
		goto exit_deinit_bus;

	rc = qseecom_register_shmbridge();
	if (rc)
		goto exit_deinit_bus;
		goto exit_stop_kthreads;

	atomic_set(&qseecom.qseecom_state, QSEECOM_STATE_READY);
	return 0;

exit_stop_kthreads:
	kthread_stop(qseecom.unload_app_kthread_task);
	kthread_stop(qseecom.unregister_lsnr_kthread_task);
exit_deinit_bus:
	qseecom_deinit_bus();
exit_deinit_clock:
@@ -9405,6 +9581,7 @@ static int qseecom_remove(struct platform_device *pdev)
		qseecom_unload_commonlib_image();

	qseecom_deregister_shmbridge();
	kthread_stop(qseecom.unload_app_kthread_task);
	kthread_stop(qseecom.unregister_lsnr_kthread_task);
	qseecom_deinit_bus();
	qseecom_deinit_clk();