From 0a21e4cece61404bec7a93ac52fa9230963301a7 Mon Sep 17 00:00:00 2001
From: Biswajit Paul <biswajitpaul@codeaurora.org>
Date: Fri, 2 Sep 2016 22:09:23 -0700
Subject: [PATCH] qseecom: whitelist support for kernel client and listener

Add whitelist support for listener to send modified resp to TZ;
also add whitelist support for kernel client; and change the method
to check whitelist feature.

Bug: 31268796
CRs-Fixed: 1021945
Change-Id: I0030b0008d6224cda3fdc1f80308a7e9bcfe4405
Signed-off-by: Zhen Kong <zkong@codeaurora.org>
Signed-off-by: Biswajit Paul <biswajitpaul@codeaurora.org>
---
 drivers/misc/qseecom.c      | 219 +++++++++++++++++-------------------
 include/soc/qcom/qseecomi.h |  11 ++
 2 files changed, 112 insertions(+), 118 deletions(-)

diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index 799dc068693e..1c5e20e9d122 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -109,6 +109,35 @@ static DEFINE_MUTEX(qsee_bw_mutex);
 static DEFINE_MUTEX(app_access_lock);
 static DEFINE_MUTEX(clk_access_lock);
 
+struct sglist_info {
+	uint32_t indexAndFlags;
+	uint32_t sizeOrCount;
+};
+
+/*
+ * The 31th bit indicates only one or multiple physical address inside
+ * the request buffer. If it is set,  the index locates a single physical addr
+ * inside the request buffer, and `sizeOrCount` is the size of the memory being
+ * shared at that physical address.
+ * Otherwise, the index locates an array of {start, len} pairs (a
+ * "scatter/gather list"), and `sizeOrCount` gives the number of entries in
+ * that array.
+ *
+ * The 30th bit indicates 64 or 32bit address; when it is set, physical addr
+ * and scatter gather entry sizes are 64-bit values.  Otherwise, 32-bit values.
+ *
+ * The bits [0:29] of `indexAndFlags` hold an offset into the request buffer.
+ */
+#define SGLISTINFO_SET_INDEX_FLAG(c, s, i)	\
+	((uint32_t)(((c & 1) << 31) | ((s & 1) << 30) | (i & 0x3fffffff)))
+
+#define SGLISTINFO_TABLE_SIZE	(sizeof(struct sglist_info) * MAX_ION_FD)
+
+#define FEATURE_ID_WHITELIST	15	/*whitelist feature id*/
+
+#define MAKE_WHITELIST_VERSION(major, minor, patch) \
+	(((major & 0x3FF) << 22) | ((minor & 0x3FF) << 12) | (patch & 0xFFF))
+
 struct qseecom_registered_listener_list {
 	struct list_head                 list;
 	struct qseecom_register_listener_req svc;
@@ -120,6 +149,8 @@ struct qseecom_registered_listener_list {
 
 	wait_queue_head_t          rcv_req_wq;
 	int                        rcv_req_flag;
+	struct sglist_info sglistinfo_ptr[MAX_ION_FD];
+	uint32_t sglist_cnt;
 };
 
 struct qseecom_registered_app_list {
@@ -211,30 +242,6 @@ struct qseecom_listener_handle {
 
 static struct qseecom_control qseecom;
 
-struct sglist_info {
-	uint32_t indexAndFlags;
-	uint32_t sizeOrCount;
-};
-
-/*
- * The 31th bit indicates only one or multiple physical address inside
- * the request buffer. If it is set,  the index locates a single physical addr
- * inside the request buffer, and `sizeOrCount` is the size of the memory being
- * shared at that physical address.
- * Otherwise, the index locates an array of {start, len} pairs (a
- * "scatter/gather list"), and `sizeOrCount` gives the number of entries in
- * that array.
- *
- * The 30th bit indicates 64 or 32bit address; when it is set, physical addr
- * and scatter gather entry sizes are 64-bit values.  Otherwise, 32-bit values.
- *
- * The bits [0:29] of `indexAndFlags` hold an offset into the request buffer.
- */
-#define SGLISTINFO_SET_INDEX_FLAG(c, s, i)	\
-	((uint32_t)(((c & 1) << 31) | ((s & 1) << 30) | (i & 0x3fffffff)))
-
-#define SGLISTINFO_TABLE_SIZE	(sizeof(struct sglist_info) * MAX_ION_FD)
-
 struct qseecom_dev_handle {
 	enum qseecom_client_handle_type type;
 	union {
@@ -248,8 +255,9 @@ struct qseecom_dev_handle {
 	bool  perf_enabled;
 	bool  fast_load_enabled;
 	enum qseecom_bandwidth_request_mode mode;
-	struct sglist_info *sglistinfo_ptr;
+	struct sglist_info sglistinfo_ptr[MAX_ION_FD];
 	uint32_t sglist_cnt;
+	bool use_legacy_cmd;
 };
 
 struct qseecom_sg_entry {
@@ -451,6 +459,21 @@ static int qseecom_scm_call2(uint32_t svc_id, uint32_t tz_cmd_id,
 			ret = scm_call2(smc_id, &desc);
 			break;
 		}
+		case QSEOS_LISTENER_DATA_RSP_COMMAND_WHITELIST: {
+			struct qseecom_client_listener_data_irsp *req;
+			req = (struct qseecom_client_listener_data_irsp *)
+				req_buf;
+			smc_id =
+			TZ_OS_LISTENER_RESPONSE_HANDLER_WITH_WHITELIST_ID;
+			desc.arginfo =
+			TZ_OS_LISTENER_RESPONSE_HANDLER_WITH_WHITELIST_PARAM_ID;
+			desc.args[0] = req->listener_id;
+			desc.args[1] = req->status;
+			desc.args[2] = req->sglistinfo_ptr;
+			desc.args[3] = req->sglistinfo_len;
+			ret = scm_call2(smc_id, &desc);
+			break;
+		}
 		case QSEOS_LOAD_EXTERNAL_ELF_COMMAND: {
 			struct qseecom_load_app_ireq *req;
 			req = (struct qseecom_load_app_ireq *)req_buf;
@@ -833,7 +856,7 @@ static int qseecom_register_listener(struct qseecom_dev_handle *data,
 		return -EBUSY;
 	}
 
-	new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
+	new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL);
 	if (!new_entry) {
 		pr_err("kmalloc failed\n");
 		return -ENOMEM;
@@ -1241,6 +1264,16 @@ static int __qseecom_listener_has_sent_rsp(struct qseecom_dev_handle *data)
 	return ret || data->abort;
 }
 
+static void __qseecom_clean_listener_sglistinfo(
+			struct qseecom_registered_listener_list *ptr_svc)
+{
+	if (ptr_svc->sglist_cnt) {
+		memset(ptr_svc->sglistinfo_ptr, 0,
+			SGLISTINFO_TABLE_SIZE);
+		ptr_svc->sglist_cnt = 0;
+	}
+}
+
 static int __qseecom_process_incomplete_cmd(struct qseecom_dev_handle *data,
 					struct qseecom_command_scm_resp *resp)
 {
@@ -1252,6 +1285,7 @@ static int __qseecom_process_incomplete_cmd(struct qseecom_dev_handle *data,
 	struct qseecom_registered_listener_list *ptr_svc = NULL;
 	sigset_t new_sigset;
 	sigset_t old_sigset;
+	struct sglist_info *table = NULL;
 
 	while (resp->result == QSEOS_RESULT_INCOMPLETE) {
 		lstnr = resp->data;
@@ -1310,8 +1344,21 @@ static int __qseecom_process_incomplete_cmd(struct qseecom_dev_handle *data,
 		}
 
 		qseecom.send_resp_flag = 0;
-		send_data_rsp.qsee_cmd_id = QSEOS_LISTENER_DATA_RSP_COMMAND;
+		table = ptr_svc->sglistinfo_ptr;
+		if (qseecom.whitelist_support == false)
+			send_data_rsp.qsee_cmd_id =
+				QSEOS_LISTENER_DATA_RSP_COMMAND;
+		else
+			send_data_rsp.qsee_cmd_id =
+				QSEOS_LISTENER_DATA_RSP_COMMAND_WHITELIST;
+
 		send_data_rsp.listener_id  = lstnr;
+		send_data_rsp.sglistinfo_ptr =
+				(uint32_t)virt_to_phys(table);
+		send_data_rsp.sglistinfo_len =
+				SGLISTINFO_TABLE_SIZE;
+		dmac_flush_range((void *)table,
+				(void *)table + SGLISTINFO_TABLE_SIZE);
 		if (ptr_svc)
 			msm_ion_do_cache_op(qseecom.ion_clnt, ptr_svc->ihandle,
 					ptr_svc->sb_virt, ptr_svc->sb_length,
@@ -1324,6 +1371,7 @@ static int __qseecom_process_incomplete_cmd(struct qseecom_dev_handle *data,
 					(const void *)&send_data_rsp,
 					sizeof(send_data_rsp), resp,
 					sizeof(*resp));
+		__qseecom_clean_listener_sglistinfo(ptr_svc);
 		if (ret) {
 			pr_err("scm_call() failed with err: %d (app_id = %d)\n",
 				ret, data->client.app_id);
@@ -2080,11 +2128,11 @@ static int __qseecom_send_cmd(struct qseecom_dev_handle *data,
 		return -EINVAL;
 	}
 
-	if (qseecom.whitelist_support == false)
+	if (qseecom.whitelist_support == false || data->use_legacy_cmd == true)
 		send_data_req.qsee_cmd_id = QSEOS_CLIENT_SEND_DATA_COMMAND;
 	else
 		send_data_req.qsee_cmd_id =
-			QSEOS_CLIENT_SEND_DATA_COMMAND_WHITELIST;
+				QSEOS_CLIENT_SEND_DATA_COMMAND_WHITELIST;
 	send_data_req.app_id = data->client.app_id;
 	send_data_req.req_ptr = (uint32_t)(__qseecom_uvirt_to_kphys(data,
 					(uintptr_t)req->cmd_req_buf));
@@ -2192,6 +2240,8 @@ static int __qseecom_update_cmd_buf(void *msg, bool cleanup,
 	struct qseecom_send_modfd_cmd_req *req = NULL;
 	struct qseecom_send_modfd_listener_resp *lstnr_resp = NULL;
 	struct qseecom_registered_listener_list *this_lstnr = NULL;
+	uint32_t offset;
+	struct sg_table *sg_ptr;
 
 	if ((data->type != QSEECOM_LISTENER_SERVICE) &&
 			(data->type != QSEECOM_CLIENT_APP))
@@ -2213,7 +2263,6 @@ static int __qseecom_update_cmd_buf(void *msg, bool cleanup,
 	}
 
 	for (i = 0; i < MAX_ION_FD; i++) {
-		struct sg_table *sg_ptr = NULL;
 		if ((data->type != QSEECOM_LISTENER_SERVICE) &&
 						(req->ifd_data[i].fd > 0)) {
 			ihandle = ion_import_dma_buf(qseecom.ion_clnt,
@@ -2318,14 +2367,25 @@ static int __qseecom_update_cmd_buf(void *msg, bool cleanup,
 					ihandle, NULL, len,
 					ION_IOC_CLEAN_INV_CACHES);
 			if (data->type == QSEECOM_CLIENT_APP) {
+				offset = req->ifd_data[i].cmd_buf_offset;
 				data->sglistinfo_ptr[i].indexAndFlags =
 					SGLISTINFO_SET_INDEX_FLAG(
-					(sg_ptr->nents == 1), 0,
-					req->ifd_data[i].cmd_buf_offset);
+					(sg_ptr->nents == 1), 0, offset);
 				data->sglistinfo_ptr[i].sizeOrCount =
 					(sg_ptr->nents == 1) ?
 					sg->length : sg_ptr->nents;
 				data->sglist_cnt = i + 1;
+			} else {
+				offset = (lstnr_resp->ifd_data[i].cmd_buf_offset
+					+ (uintptr_t)lstnr_resp->resp_buf_ptr -
+					(uintptr_t)this_lstnr->sb_virt);
+				this_lstnr->sglistinfo_ptr[i].indexAndFlags =
+					SGLISTINFO_SET_INDEX_FLAG(
+					(sg_ptr->nents == 1), 0, offset);
+				this_lstnr->sglistinfo_ptr[i].sizeOrCount =
+					(sg_ptr->nents == 1) ?
+					sg->length : sg_ptr->nents;
+				this_lstnr->sglist_cnt = i + 1;
 			}
 		}
 		/* Deallocate the handle */
@@ -2866,14 +2926,6 @@ int qseecom_start_app(struct qseecom_handle **handle,
 	data->client.user_virt_sb_base = 0;
 	data->client.ihandle = NULL;
 
-	/* Allocate sglistinfo buffer for kernel client */
-	data->sglistinfo_ptr = kzalloc(SGLISTINFO_TABLE_SIZE, GFP_KERNEL);
-	if (!(data->sglistinfo_ptr)) {
-		kfree(data);
-		kfree(*handle);
-		*handle = NULL;
-		return -ENOMEM;
-	}
 	init_waitqueue_head(&data->abort_wq);
 	atomic_set(&data->ioctl_count, 0);
 
@@ -2881,7 +2933,6 @@ int qseecom_start_app(struct qseecom_handle **handle,
 				ION_HEAP(ION_QSECOM_HEAP_ID), 0);
 	if (IS_ERR_OR_NULL(data->client.ihandle)) {
 		pr_err("Ion client could not retrieve the handle\n");
-		kfree(data->sglistinfo_ptr);
 		kfree(data);
 		kfree(*handle);
 		*handle = NULL;
@@ -2986,7 +3037,6 @@ int qseecom_start_app(struct qseecom_handle **handle,
 	return 0;
 
 err:
-	kfree(data->sglistinfo_ptr);
 	kfree(data);
 	kfree(*handle);
 	*handle = NULL;
@@ -3048,7 +3098,6 @@ int qseecom_shutdown_app(struct qseecom_handle **handle)
 	atomic_dec(&data->ioctl_count);
 	mutex_unlock(&app_access_lock);
 	if (ret == 0) {
-		kzfree(data->sglistinfo_ptr);
 		kzfree(data);
 		kzfree(*handle);
 		kzfree(kclient);
@@ -3111,8 +3160,11 @@ int qseecom_send_command(struct qseecom_handle *handle, void *send_buf,
 		}
 		perf_enabled = true;
 	}
+	if (!strcmp(data->client.app_name, "securemm"))
+		data->use_legacy_cmd = true;
 
 	ret = __qseecom_send_cmd(data, &req);
+	data->use_legacy_cmd = false;
 	if (qseecom.support_bus_scaling)
 		__qseecom_add_bw_scale_down_timer(
 			QSEECOM_SEND_CMD_CRYPTO_TIMEOUT);
@@ -5336,6 +5388,7 @@ long qseecom_ioctl(struct file *file, unsigned cmd, unsigned long arg)
 		wake_up_all(&data->abort_wq);
 		if (ret)
 			pr_err("failed qseecom_send_mod_resp: %d\n", ret);
+		__qseecom_clean_data_sglistinfo(data);
 		break;
 	}
 	case QSEECOM_QTEEC_IOCTL_OPEN_SESSION_REQ: {
@@ -5461,12 +5514,6 @@ static int qseecom_open(struct inode *inode, struct file *file)
 	data->mode = INACTIVE;
 	init_waitqueue_head(&data->abort_wq);
 	atomic_set(&data->ioctl_count, 0);
-
-	data->sglistinfo_ptr = kzalloc(SGLISTINFO_TABLE_SIZE, GFP_KERNEL);
-	if (!(data->sglistinfo_ptr)) {
-		kzfree(data);
-		return -ENOMEM;
-	}
 	return ret;
 }
 
@@ -5517,7 +5564,6 @@ static int qseecom_release(struct inode *inode, struct file *file)
 		if (data->perf_enabled == true)
 			qsee_disable_clock_vote(data, CLK_DFAB);
 	}
-	kfree(data->sglistinfo_ptr);
 	kfree(data);
 
 	return ret;
@@ -5672,73 +5718,14 @@ static void __qseecom_deinit_clk(enum qseecom_ce_hw_instance ce)
 }
 
 /*
- * Check if whitelist feature is supported by making a test scm_call
- * to send a whitelist command to an invalid app ID 0
+ * Check whitelist feature, and if TZ feature version is < 1.0.0,
+ * then whitelist feature is not supported.
  */
 static int qseecom_check_whitelist_feature(void)
 {
-	struct qseecom_client_send_data_ireq send_data_req = {0};
-	struct qseecom_client_send_data_64bit_ireq send_data_req_64bit = {0};
-	struct qseecom_command_scm_resp resp;
-	uint32_t buf_size = 128;
-	void *buf = NULL;
-	void *cmd_buf = NULL;
-	size_t cmd_len;
-	int ret = 0;
-	phys_addr_t pa;
+	int version = scm_get_feat_version(FEATURE_ID_WHITELIST);
 
-	buf = kzalloc(buf_size, GFP_KERNEL);
-	if (!buf)
-		return -ENOMEM;
-	pa = virt_to_phys(buf);
-	if (qseecom.qsee_version < QSEE_VERSION_40) {
-		send_data_req.qsee_cmd_id =
-			QSEOS_CLIENT_SEND_DATA_COMMAND_WHITELIST;
-		send_data_req.app_id = 0;
-		send_data_req.req_ptr = (uint32_t)pa;
-		send_data_req.req_len = buf_size;
-		send_data_req.rsp_ptr = (uint32_t)pa;
-		send_data_req.rsp_len = buf_size;
-		send_data_req.sglistinfo_ptr = (uint32_t)pa;
-		send_data_req.sglistinfo_len = buf_size;
-		cmd_buf = (void *)&send_data_req;
-		cmd_len = sizeof(struct qseecom_client_send_data_ireq);
-	} else {
-		send_data_req_64bit.qsee_cmd_id =
-			QSEOS_CLIENT_SEND_DATA_COMMAND_WHITELIST;
-		send_data_req_64bit.app_id = 0;
-		send_data_req_64bit.req_ptr = (uint64_t)pa;
-		send_data_req_64bit.req_len = buf_size;
-		send_data_req_64bit.rsp_ptr = (uint64_t)pa;
-		send_data_req_64bit.rsp_len = buf_size;
-		send_data_req_64bit.sglistinfo_ptr = (uint64_t)pa;
-		send_data_req_64bit.sglistinfo_len = buf_size;
-		cmd_buf = (void *)&send_data_req_64bit;
-		cmd_len = sizeof(struct qseecom_client_send_data_64bit_ireq);
-	}
-	ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1,
-				cmd_buf, cmd_len,
-				&resp, sizeof(resp));
-/*
- * If this cmd exists and whitelist is supported, scm_call return -2 (scm
- * driver remap it to -EINVAL) and resp.result 0xFFFFFFED(-19); Otherwise,
- * scm_call return -1 (remap to -EIO).
- */
-	if (ret == -EIO) {
-		qseecom.whitelist_support = false;
-		ret = 0;
-	} else if (ret == -EINVAL &&
-		resp.result == QSEOS_RESULT_FAIL_SEND_CMD_NO_THREAD) {
-		qseecom.whitelist_support = true;
-		ret = 0;
-	} else {
-		pr_info("Check whitelist with ret = %d, result = 0x%x\n",
-			ret, resp.result);
-		qseecom.whitelist_support = false;
-		ret = 0;
-	}
-	kfree(buf);
-	return ret;
+	return version >= MAKE_WHITELIST_VERSION(1, 0, 0);
 }
 
 static int qseecom_probe(struct platform_device *pdev)
@@ -6052,11 +6039,7 @@ static int qseecom_probe(struct platform_device *pdev)
 	qseecom.qsee_perf_client = msm_bus_scale_register_client(
 					qseecom_platform_support);
 
-	rc = qseecom_check_whitelist_feature();
-	if (rc) {
-		rc = -EINVAL;
-		goto exit_destroy_ion_client;
-	}
+	qseecom.whitelist_support = qseecom_check_whitelist_feature();
 	pr_warn("qseecom.whitelist_support = %d\n",
 				qseecom.whitelist_support);
 
diff --git a/include/soc/qcom/qseecomi.h b/include/soc/qcom/qseecomi.h
index e0eb33c25041..9357e02529d5 100644
--- a/include/soc/qcom/qseecomi.h
+++ b/include/soc/qcom/qseecomi.h
@@ -65,6 +65,7 @@ enum qseecom_qceos_cmd_id {
 	QSEOS_CLIENT_SEND_DATA_COMMAND_WHITELIST = 0x1C,
 	QSEOS_TEE_OPEN_SESSION_WHITELIST = 0x1D,
 	QSEOS_TEE_INVOKE_COMMAND_WHITELIST = 0x1E,
+	QSEOS_LISTENER_DATA_RSP_COMMAND_WHITELIST = 0x1F,
 	QSEOS_FSM_LTE_INIT_DB = 0x100,
 	QSEOS_FSM_LTE_STORE_KENB = 0x101,
 	QSEOS_FSM_LTE_GEN_KEYS = 0x102,
@@ -164,6 +165,8 @@ __packed struct qseecom_client_listener_data_irsp {
 	uint32_t qsee_cmd_id;
 	uint32_t listener_id;
 	uint32_t status;
+	uint32_t sglistinfo_ptr;
+	uint32_t sglistinfo_len;
 };
 
 /*
@@ -597,4 +600,12 @@ __packed struct qseecom_client_send_fsm_key_req {
 	TZ_SYSCALL_PARAM_TYPE_VAL, TZ_SYSCALL_PARAM_TYPE_BUF_RW,	\
 	TZ_SYSCALL_PARAM_TYPE_VAL)
 
+#define TZ_OS_LISTENER_RESPONSE_HANDLER_WITH_WHITELIST_ID \
+	TZ_SYSCALL_CREATE_SMC_ID(TZ_OWNER_QSEE_OS, TZ_SVC_LISTENER, 0x05)
+
+#define TZ_OS_LISTENER_RESPONSE_HANDLER_WITH_WHITELIST_PARAM_ID \
+	TZ_SYSCALL_CREATE_PARAM_ID_4( \
+	TZ_SYSCALL_PARAM_TYPE_VAL, TZ_SYSCALL_PARAM_TYPE_VAL, \
+	TZ_SYSCALL_PARAM_TYPE_BUF_RW, TZ_SYSCALL_PARAM_TYPE_VAL)
+
 #endif /* __QSEECOMI_H_ */
-- 
GitLab