Tegra: lib: library for profiling the cold boot path

The non secure world would like to profile the boot path for
the EL3 and S-EL1 firmwares. To allow it to do that, a non-secure
DRAM region (4K) is allocated and the base address is passed to
the EL3 firmware.

This patch adds a library to allow the platform code to store the
tag:timestamp pair to the shared memory. The tegra platform code
then uses the `record` method to add timestamps.

Original change by Akshay Sharan <asharan@nvidia.com>

Change-Id: Idbbef9c83ed84a508b04d85a6637775960dc94ba
Signed-off-by: Varun Wadekar <vwadekar@nvidia.com>
diff --git a/plat/nvidia/tegra/common/lib/debug/profiler.c b/plat/nvidia/tegra/common/lib/debug/profiler.c
new file mode 100644
index 0000000..f40244b
--- /dev/null
+++ b/plat/nvidia/tegra/common/lib/debug/profiler.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+/*******************************************************************************
+ * The profiler stores the timestamps captured during cold boot to the shared
+ * memory for the non-secure world. The non-secure world driver parses the
+ * shared memory block and writes the contents to a file on the device, which
+ * can be later extracted for analysis.
+ *
+ * Profiler memory map
+ *
+ * TOP     ---------------------------      ---
+ *            Trusted OS timestamps         3KB
+ *         ---------------------------      ---
+ *         Trusted Firmware timestamps      1KB
+ * BASE    ---------------------------      ---
+ *
+ ******************************************************************************/
+
+#include <arch.h>
+#include <arch_helpers.h>
+#include <assert.h>
+#include <mmio.h>
+#include <profiler.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <utils_def.h>
+#include <xlat_tables_v2.h>
+
+static uint64_t shmem_base_addr;
+
+#define MAX_PROFILER_RECORDS	U(16)
+#define TAG_LEN_BYTES		U(56)
+
+/*******************************************************************************
+ * Profiler entry format
+ ******************************************************************************/
+typedef struct {
+	/* text explaining the timestamp location in code */
+	uint8_t tag[TAG_LEN_BYTES];
+	/* timestamp value */
+	uint64_t timestamp;
+} profiler_rec_t;
+
+static profiler_rec_t *head, *cur, *tail;
+static uint32_t tmr;
+static bool is_shmem_buf_mapped;
+
+/*******************************************************************************
+ * Initialise the profiling library
+ ******************************************************************************/
+void boot_profiler_init(uint64_t shmem_base, uint32_t tmr_base)
+{
+	uint64_t shmem_end_base;
+
+	assert(shmem_base != ULL(0));
+	assert(tmr_base != U(0));
+
+	/* store the buffer address */
+	shmem_base_addr = shmem_base;
+
+	/* calculate the base address of the last record */
+	shmem_end_base = shmem_base + (sizeof(profiler_rec_t) *
+			 (MAX_PROFILER_RECORDS - U(1)));
+
+	/* calculate the head, tail and cur values */
+	head = (profiler_rec_t *)shmem_base;
+	tail = (profiler_rec_t *)shmem_end_base;
+	cur = head;
+
+	/* timer used to get the current timestamp */
+	tmr = tmr_base;
+}
+
+/*******************************************************************************
+ * Add tag and timestamp to profiler
+ ******************************************************************************/
+void boot_profiler_add_record(const char *str)
+{
+	unsigned int len;
+
+	/* calculate the length of the tag */
+	if (((unsigned int)strlen(str) + U(1)) > TAG_LEN_BYTES) {
+		len = TAG_LEN_BYTES;
+	} else {
+		len = (unsigned int)strlen(str) + U(1);
+	}
+
+	if (head != NULL) {
+
+		/*
+		 * The profiler runs with/without MMU enabled. Check
+		 * if MMU is enabled and memmap the shmem buffer, in
+		 * case it is.
+		 */
+		if ((!is_shmem_buf_mapped) &&
+		    ((read_sctlr_el3() & SCTLR_M_BIT) != U(0))) {
+
+			(void)mmap_add_dynamic_region(shmem_base_addr,
+					shmem_base_addr,
+					PROFILER_SIZE_BYTES,
+					(MT_NS | MT_RW | MT_EXECUTE_NEVER));
+
+			is_shmem_buf_mapped = true;
+		}
+
+		/* write the tag and timestamp to buffer */
+		(void)snprintf((char *)cur->tag, len, "%s", str);
+		cur->timestamp = mmio_read_32(tmr);
+
+		/* start from head if we reached the end */
+		if (cur == tail) {
+			cur = head;
+		} else {
+			cur++;
+		}
+	}
+}
+
+/*******************************************************************************
+ * Deinint the profiler
+ ******************************************************************************/
+void boot_profiler_deinit(void)
+{
+	if (shmem_base_addr != ULL(0)) {
+
+		/* clean up resources */
+		cur = NULL;
+		head = NULL;
+		tail = NULL;
+
+		/* flush the shmem for it to be visible to the NS world */
+		flush_dcache_range(shmem_base_addr, PROFILER_SIZE_BYTES);
+
+		/* unmap the shmem buffer */
+		if (is_shmem_buf_mapped) {
+			(void)mmap_remove_dynamic_region(shmem_base_addr,
+					PROFILER_SIZE_BYTES);
+		}
+	}
+}