qseecom: Enhance secure app logging mechanism

The existing QSEE application logs are written from QSEE to HLOS
file system requiring a QSEE -> HLOS -> QSEE context switch for
every logged message. This change eliminates the context switch by
writing the log messages to a shared buffer.

In addition, the output from the debugfs log buffer can be stored
on a host PC rather than on a target with limited storage capacity.

Change-Id: I299370a29cf9ca583a9629c1336308bf3a42e926
Signed-off-by: Mona Hossain <mhossain@codeaurora.org>
diff --git a/arch/arm/mach-msm/include/mach/qseecomi.h b/arch/arm/mach-msm/include/mach/qseecomi.h
new file mode 100644
index 0000000..893645a
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/qseecomi.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __QSEECOMI_H_
+#define __QSEECOMI_H_
+
+#include <linux/qseecom.h>
+
+enum qseecom_command_scm_resp_type {
+	QSEOS_APP_ID = 0xEE01,
+	QSEOS_LISTENER_ID
+};
+
+enum qseecom_qceos_cmd_id {
+	QSEOS_APP_START_COMMAND      = 0x01,
+	QSEOS_APP_SHUTDOWN_COMMAND,
+	QSEOS_APP_LOOKUP_COMMAND,
+	QSEOS_REGISTER_LISTENER,
+	QSEOS_DEREGISTER_LISTENER,
+	QSEOS_CLIENT_SEND_DATA_COMMAND,
+	QSEOS_LISTENER_DATA_RSP_COMMAND,
+	QSEOS_LOAD_EXTERNAL_ELF_COMMAND,
+	QSEOS_UNLOAD_EXTERNAL_ELF_COMMAND,
+	QSEOS_GET_APP_STATE_COMMAND,
+	QSEOS_LOAD_SERV_IMAGE_COMMAND,
+	QSEOS_UNLOAD_SERV_IMAGE_COMMAND,
+	QSEOS_APP_REGION_NOTIFICATION,
+	QSEOS_REGISTER_LOG_BUF_COMMAND,
+	QSEOS_CMD_MAX     = 0xEFFFFFFF
+};
+
+enum qseecom_qceos_cmd_status {
+	QSEOS_RESULT_SUCCESS = 0,
+	QSEOS_RESULT_INCOMPLETE,
+	QSEOS_RESULT_FAILURE  = 0xFFFFFFFF
+};
+
+__packed  struct qsee_apps_region_info_ireq {
+	uint32_t qsee_cmd_id;
+	uint32_t addr;
+	uint32_t size;
+};
+
+__packed struct qseecom_check_app_ireq {
+	uint32_t qsee_cmd_id;
+	char     app_name[MAX_APP_NAME_SIZE];
+};
+
+__packed struct qseecom_load_app_ireq {
+	uint32_t qsee_cmd_id;
+	uint32_t mdt_len;		/* Length of the mdt file */
+	uint32_t img_len;		/* Length of .bxx and .mdt files */
+	uint32_t phy_addr;		/* phy addr of the start of image */
+	char     app_name[MAX_APP_NAME_SIZE];	/* application name*/
+};
+
+__packed struct qseecom_unload_app_ireq {
+	uint32_t qsee_cmd_id;
+	uint32_t  app_id;
+};
+
+__packed struct qseecom_load_lib_image_ireq {
+	uint32_t qsee_cmd_id;
+	uint32_t mdt_len;
+	uint32_t img_len;
+	uint32_t phy_addr;
+};
+
+__packed struct qseecom_unload_lib_image_ireq {
+	uint32_t qsee_cmd_id;
+};
+
+__packed struct qseecom_register_listener_ireq {
+	uint32_t qsee_cmd_id;
+	uint32_t listener_id;
+	void *sb_ptr;
+	uint32_t sb_len;
+};
+
+__packed struct qseecom_unregister_listener_ireq {
+	uint32_t qsee_cmd_id;
+	uint32_t  listener_id;
+};
+
+__packed struct qseecom_client_send_data_ireq {
+	uint32_t qsee_cmd_id;
+	uint32_t app_id;
+	void *req_ptr;
+	uint32_t req_len;
+	void *rsp_ptr;   /* First 4 bytes should always be the return status */
+	uint32_t rsp_len;
+};
+
+__packed struct qseecom_reg_log_buf_ireq {
+	uint32_t qsee_cmd_id;
+	unsigned long phy_addr;
+	uint32_t len;
+};
+
+/* send_data resp */
+__packed struct qseecom_client_listener_data_irsp {
+	uint32_t qsee_cmd_id;
+	uint32_t listener_id;
+};
+
+/*
+ * struct qseecom_command_scm_resp - qseecom response buffer
+ * @cmd_status: value from enum tz_sched_cmd_status
+ * @sb_in_rsp_addr: points to physical location of response
+ *                buffer
+ * @sb_in_rsp_len: length of command response
+ */
+__packed struct qseecom_command_scm_resp {
+	uint32_t result;
+	enum qseecom_command_scm_resp_type resp_type;
+	unsigned int data;
+};
+
+#endif /* __QSEECOMI_H_ */
diff --git a/arch/arm/mach-msm/tz_log.c b/arch/arm/mach-msm/tz_log.c
index da419fb..11dc436 100644
--- a/arch/arm/mach-msm/tz_log.c
+++ b/arch/arm/mach-msm/tz_log.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -12,16 +12,27 @@
  */
 #include <linux/debugfs.h>
 #include <linux/errno.h>
+#include <linux/delay.h>
 #include <linux/io.h>
+#include <linux/msm_ion.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
+#include <linux/string.h>
 #include <linux/types.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
+#include <mach/scm.h>
+#include <mach/qseecomi.h>
 
 #define DEBUG_MAX_RW_BUF 4096
 
+/* QSEE_LOG_BUF_SIZE = 32K */
+#define QSEE_LOG_BUF_SIZE 0x8000
+
+
+/* TZ Diagnostic Area legacy version number */
+#define TZBSP_DIAG_MAJOR_VERSION_LEGACY	2
 /*
  * Preprocessor Definitions and Constants
  */
@@ -96,6 +107,24 @@
 	uint8_t int_desc[TZBSP_MAX_INT_DESC];
 	uint64_t int_count[TZBSP_CPU_COUNT]; /* # of times seen per CPU */
 };
+
+/*
+ * Log ring buffer position
+ */
+struct tzdbg_log_pos_t {
+	uint16_t wrap;
+	uint16_t offset;
+};
+
+ /*
+ * Log ring buffer
+ */
+struct tzdbg_log_t {
+	struct tzdbg_log_pos_t	log_pos;
+	/* open ended array to the end of the 4K IMEM buffer */
+	uint8_t					log_buf[];
+};
+
 /*
  * Diagnostic Table
  */
@@ -147,7 +176,7 @@
 	/*
 	 * We need at least 2K for the ring buffer
 	 */
-	uint8_t *ring_buffer;	/* TZ Ring Buffer */
+	struct tzdbg_log_t ring_buffer;	/* TZ Ring Buffer */
 };
 
 /*
@@ -160,7 +189,8 @@
 	TZDBG_VMID,
 	TZDBG_GENERAL,
 	TZDBG_LOG,
-	TZDBG_STATS_MAX,
+	TZDBG_QSEE_LOG,
+	TZDBG_STATS_MAX
 };
 
 struct tzdbg_stat {
@@ -184,8 +214,10 @@
 	.stat[TZDBG_VMID].name = "vmid",
 	.stat[TZDBG_GENERAL].name = "general",
 	.stat[TZDBG_LOG].name = "log",
+	.stat[TZDBG_QSEE_LOG].name = "qsee_log",
 };
 
+static struct tzdbg_log_t *g_qsee_log;
 
 /*
  * Debugfs data structure and functions
@@ -346,7 +378,7 @@
 	return len;
 }
 
-static int _disp_tz_log_stats(void)
+static int _disp_tz_log_stats_legacy(void)
 {
 	int len = 0;
 	unsigned char *ptr;
@@ -360,6 +392,97 @@
 	return len;
 }
 
+static int _disp_log_stats(struct tzdbg_log_t *log,
+			struct tzdbg_log_pos_t *log_start, uint32_t log_len,
+			size_t count, uint32_t buf_idx)
+{
+	uint32_t wrap_start;
+	uint32_t wrap_end;
+	uint32_t wrap_cnt;
+	int max_len;
+	int len = 0;
+	int i = 0;
+
+	wrap_start = log_start->wrap;
+	wrap_end = log->log_pos.wrap;
+
+	/* Calculate difference in # of buffer wrap-arounds */
+	if (wrap_end >= wrap_start) {
+		wrap_cnt = wrap_end - wrap_start;
+	} else {
+		/* wrap counter has wrapped around, invalidate start position */
+		wrap_cnt = 2;
+	}
+
+	if (wrap_cnt > 1) {
+		/* end position has wrapped around more than once, */
+		/* current start no longer valid                   */
+		log_start->wrap = log->log_pos.wrap - 1;
+		log_start->offset = (log->log_pos.offset + 1) % log_len;
+	} else if ((wrap_cnt == 1) &&
+		(log->log_pos.offset > log_start->offset)) {
+		/* end position has overwritten start */
+		log_start->offset = (log->log_pos.offset + 1) % log_len;
+	}
+
+	while (log_start->offset == log->log_pos.offset) {
+		/*
+		 * No data in ring buffer,
+		 * so we'll hang around until something happens
+		 */
+		unsigned long t = msleep_interruptible(50);
+		if (t != 0) {
+			/* Some event woke us up, so let's quit */
+			return 0;
+		}
+
+		if (buf_idx == TZDBG_LOG)
+			memcpy_fromio((void *)tzdbg.diag_buf, tzdbg.virt_iobase,
+						DEBUG_MAX_RW_BUF);
+
+	}
+
+	max_len = (count > DEBUG_MAX_RW_BUF) ? DEBUG_MAX_RW_BUF : count;
+
+	/*
+	 *  Read from ring buff while there is data and space in return buff
+	 */
+	while ((log_start->offset != log->log_pos.offset) && (len < max_len)) {
+		tzdbg.disp_buf[i++] = log->log_buf[log_start->offset];
+		log_start->offset = (log_start->offset + 1) % log_len;
+		if (0 == log_start->offset)
+			++log_start->wrap;
+		++len;
+	}
+
+	/*
+	 * return buffer to caller
+	 */
+	tzdbg.stat[buf_idx].data = tzdbg.disp_buf;
+	return len;
+}
+
+static int _disp_tz_log_stats(size_t count)
+{
+	static struct tzdbg_log_pos_t log_start = {0};
+	struct tzdbg_log_t *log_ptr;
+	log_ptr = (struct tzdbg_log_t *)((unsigned char *)tzdbg.diag_buf +
+				tzdbg.diag_buf->ring_off -
+				offsetof(struct tzdbg_log_t, log_buf));
+
+	return _disp_log_stats(log_ptr, &log_start,
+				tzdbg.diag_buf->ring_len, count, TZDBG_LOG);
+}
+
+static int _disp_qsee_log_stats(size_t count)
+{
+	static struct tzdbg_log_pos_t log_start = {0};
+
+	return _disp_log_stats(g_qsee_log, &log_start,
+			QSEE_LOG_BUF_SIZE - sizeof(struct tzdbg_log_pos_t),
+			count, TZDBG_QSEE_LOG);
+}
+
 static ssize_t tzdbgfs_read(struct file *file, char __user *buf,
 	size_t count, loff_t *offp)
 {
@@ -385,7 +508,17 @@
 		len = _disp_tz_vmid_stats();
 		break;
 	case TZDBG_LOG:
-		len = _disp_tz_log_stats();
+		if (TZBSP_DIAG_MAJOR_VERSION_LEGACY <
+				(tzdbg.diag_buf->version >> 16)) {
+			len = _disp_tz_log_stats(count);
+			*offp = 0;
+		} else {
+			len = _disp_tz_log_stats_legacy();
+		}
+		break;
+	case TZDBG_QSEE_LOG:
+		len = _disp_qsee_log_stats(count);
+		*offp = 0;
 		break;
 	default:
 		break;
@@ -410,6 +543,78 @@
 	.open    = tzdbgfs_open,
 };
 
+static struct ion_client  *g_ion_clnt;
+static struct ion_handle *g_ihandle;
+
+/*
+ * Allocates log buffer from ION, registers the buffer at TZ
+ */
+static void tzdbg_register_qsee_log_buf(void)
+{
+	/* register log buffer scm request */
+	struct qseecom_reg_log_buf_ireq req;
+
+	/* scm response */
+	struct qseecom_command_scm_resp resp;
+	ion_phys_addr_t pa = 0;
+	uint32_t len;
+	int ret = 0;
+
+	/* Create ION msm client */
+	g_ion_clnt = msm_ion_client_create(ION_HEAP_CARVEOUT_MASK, "qsee_log");
+	if (g_ion_clnt == NULL) {
+		pr_err("%s: Ion client cannot be created\n", __func__);
+		return;
+	}
+
+	g_ihandle = ion_alloc(g_ion_clnt, QSEE_LOG_BUF_SIZE,
+			4096, ION_HEAP(ION_QSECOM_HEAP_ID), 0);
+	if (IS_ERR_OR_NULL(g_ihandle)) {
+		pr_err("%s: Ion client could not retrieve the handle\n",
+			__func__);
+		goto err1;
+	}
+
+	ret = ion_phys(g_ion_clnt, g_ihandle, &pa, &len);
+	if (ret) {
+		pr_err("%s: Ion conversion to physical address failed\n",
+			__func__);
+		goto err2;
+	}
+
+	req.qsee_cmd_id = QSEOS_REGISTER_LOG_BUF_COMMAND;
+	req.phy_addr = pa;
+	req.len = len;
+
+	/*  SCM_CALL  to register the log buffer */
+	ret = scm_call(SCM_SVC_TZSCHEDULER, 1,  &req, sizeof(req),
+		&resp, sizeof(resp));
+	if (ret) {
+		pr_err("%s: scm_call to register log buffer failed\n",
+			__func__);
+		goto err2;
+	}
+
+	if (resp.result != QSEOS_RESULT_SUCCESS) {
+		pr_err(
+		"%s: scm_call to register log buf failed, resp result =%d\n",
+		__func__, resp.result);
+		goto err2;
+	}
+
+	g_qsee_log =
+		(struct tzdbg_log_t *)ion_map_kernel(g_ion_clnt, g_ihandle);
+	g_qsee_log->log_pos.wrap = g_qsee_log->log_pos.offset = 0;
+	return;
+
+err2:
+	ion_free(g_ion_clnt, g_ihandle);
+	g_ihandle = NULL;
+err1:
+	ion_client_destroy(g_ion_clnt);
+	g_ion_clnt = NULL;
+}
+
 static int  tzdbgfs_init(struct platform_device *pdev)
 {
 	int rc = 0;
@@ -456,6 +661,13 @@
 	kzfree(tzdbg.disp_buf);
 	dent_dir = platform_get_drvdata(pdev);
 	debugfs_remove_recursive(dent_dir);
+	if (g_ion_clnt != NULL) {
+		if (!IS_ERR_OR_NULL(g_ihandle)) {
+			ion_unmap_kernel(g_ion_clnt, g_ihandle);
+			ion_free(g_ion_clnt, g_ihandle);
+		}
+	ion_client_destroy(g_ion_clnt);
+}
 }
 
 /*
@@ -527,6 +739,7 @@
 	if (tzdbgfs_init(pdev))
 		goto err;
 
+	tzdbg_register_qsee_log_buf();
 	return 0;
 err:
 	kfree(tzdbg.diag_buf);
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index b11e30e..fc49a1b 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -42,6 +42,7 @@
 #include <mach/scm.h>
 #include <mach/subsystem_restart.h>
 #include <mach/socinfo.h>
+#include <mach/qseecomi.h>
 #include "qseecom_legacy.h"
 #include "qseecom_kernel.h"
 
@@ -60,114 +61,11 @@
 
 #define QSEECOM_MAX_SG_ENTRY	512
 
-enum qseecom_command_scm_resp_type {
-	QSEOS_APP_ID = 0xEE01,
-	QSEOS_LISTENER_ID
-};
-
-enum qseecom_qceos_cmd_id {
-	QSEOS_APP_START_COMMAND      = 0x01,
-	QSEOS_APP_SHUTDOWN_COMMAND,
-	QSEOS_APP_LOOKUP_COMMAND,
-	QSEOS_REGISTER_LISTENER,
-	QSEOS_DEREGISTER_LISTENER,
-	QSEOS_CLIENT_SEND_DATA_COMMAND,
-	QSEOS_LISTENER_DATA_RSP_COMMAND,
-	QSEOS_LOAD_EXTERNAL_ELF_COMMAND,
-	QSEOS_UNLOAD_EXTERNAL_ELF_COMMAND,
-	QSEOS_GET_APP_STATE_COMMAND,
-	QSEOS_LOAD_SERV_IMAGE_COMMAND,
-	QSEOS_UNLOAD_SERV_IMAGE_COMMAND,
-	QSEOS_APP_REGION_NOTIFICATION,
-	QSEOS_CMD_MAX     = 0xEFFFFFFF
-};
-
-enum qseecom_qceos_cmd_status {
-	QSEOS_RESULT_SUCCESS = 0,
-	QSEOS_RESULT_INCOMPLETE,
-	QSEOS_RESULT_FAILURE  = 0xFFFFFFFF
-};
-
 enum qseecom_clk_definitions {
 	CLK_DFAB = 0,
 	CLK_SFPB,
 };
 
-__packed  struct qsee_apps_region_info_ireq {
-	uint32_t qsee_cmd_id;
-	uint32_t addr;
-	uint32_t size;
-};
-
-__packed struct qseecom_check_app_ireq {
-	uint32_t qsee_cmd_id;
-	char     app_name[MAX_APP_NAME_SIZE];
-};
-
-__packed struct qseecom_load_app_ireq {
-	uint32_t qsee_cmd_id;
-	uint32_t mdt_len;		/* Length of the mdt file */
-	uint32_t img_len;		/* Length of .bxx and .mdt files */
-	uint32_t phy_addr;		/* phy addr of the start of image */
-	char     app_name[MAX_APP_NAME_SIZE];	/* application name*/
-};
-
-__packed struct qseecom_unload_app_ireq {
-	uint32_t qsee_cmd_id;
-	uint32_t  app_id;
-};
-
-__packed struct qseecom_load_lib_image_ireq {
-	uint32_t qsee_cmd_id;
-	uint32_t mdt_len;
-	uint32_t img_len;
-	uint32_t phy_addr;
-};
-
-__packed struct qseecom_unload_lib_image_ireq {
-	uint32_t qsee_cmd_id;
-};
-
-__packed struct qseecom_register_listener_ireq {
-	uint32_t qsee_cmd_id;
-	uint32_t listener_id;
-	void *sb_ptr;
-	uint32_t sb_len;
-};
-
-__packed struct qseecom_unregister_listener_ireq {
-	uint32_t qsee_cmd_id;
-	uint32_t  listener_id;
-};
-
-__packed struct qseecom_client_send_data_ireq {
-	uint32_t qsee_cmd_id;
-	uint32_t app_id;
-	void *req_ptr;
-	uint32_t req_len;
-	void *rsp_ptr;   /* First 4 bytes should always be the return status */
-	uint32_t rsp_len;
-};
-
-/* send_data resp */
-__packed struct qseecom_client_listener_data_irsp {
-	uint32_t qsee_cmd_id;
-	uint32_t listener_id;
-};
-
-/*
- * struct qseecom_command_scm_resp - qseecom response buffer
- * @cmd_status: value from enum tz_sched_cmd_status
- * @sb_in_rsp_addr: points to physical location of response
- *                buffer
- * @sb_in_rsp_len: length of command response
- */
-__packed struct qseecom_command_scm_resp {
-	uint32_t result;
-	enum qseecom_command_scm_resp_type resp_type;
-	unsigned int data;
-};
-
 static struct class *driver_class;
 static dev_t qseecom_device_no;
 static struct cdev qseecom_cdev;