1 /*
2  * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 /*******************************************************************************
8  * The profiler stores the timestamps captured during cold boot to the shared
9  * memory for the non-secure world. The non-secure world driver parses the
10  * shared memory block and writes the contents to a file on the device, which
11  * can be later extracted for analysis.
12  *
13  * Profiler memory map
14  *
15  * TOP     ---------------------------      ---
16  *            Trusted OS timestamps         3KB
17  *         ---------------------------      ---
18  *         Trusted Firmware timestamps      1KB
19  * BASE    ---------------------------      ---
20  *
21  ******************************************************************************/
22 
23 #include <arch.h>
24 #include <arch_helpers.h>
25 #include <assert.h>
26 #include <lib/mmio.h>
27 #include <lib/utils_def.h>
28 #include <lib/xlat_tables/xlat_tables_v2.h>
29 #include <profiler.h>
30 #include <stdbool.h>
31 #include <string.h>
32 
33 static uint64_t shmem_base_addr;
34 
35 #define MAX_PROFILER_RECORDS	U(16)
36 #define TAG_LEN_BYTES		U(56)
37 
38 /*******************************************************************************
39  * Profiler entry format
40  ******************************************************************************/
41 typedef struct {
42 	/* text explaining the timestamp location in code */
43 	uint8_t tag[TAG_LEN_BYTES];
44 	/* timestamp value */
45 	uint64_t timestamp;
46 } profiler_rec_t;
47 
48 static profiler_rec_t *head, *cur, *tail;
49 static uint32_t tmr;
50 static bool is_shmem_buf_mapped;
51 
52 /*******************************************************************************
53  * Initialise the profiling library
54  ******************************************************************************/
boot_profiler_init(uint64_t shmem_base,uint32_t tmr_base)55 void boot_profiler_init(uint64_t shmem_base, uint32_t tmr_base)
56 {
57 	uint64_t shmem_end_base;
58 
59 	assert(shmem_base != ULL(0));
60 	assert(tmr_base != U(0));
61 
62 	/* store the buffer address */
63 	shmem_base_addr = shmem_base;
64 
65 	/* calculate the base address of the last record */
66 	shmem_end_base = shmem_base + (sizeof(profiler_rec_t) *
67 			 (MAX_PROFILER_RECORDS - U(1)));
68 
69 	/* calculate the head, tail and cur values */
70 	head = (profiler_rec_t *)shmem_base;
71 	tail = (profiler_rec_t *)shmem_end_base;
72 	cur = head;
73 
74 	/* timer used to get the current timestamp */
75 	tmr = tmr_base;
76 }
77 
78 /*******************************************************************************
79  * Add tag and timestamp to profiler
80  ******************************************************************************/
boot_profiler_add_record(const char * str)81 void boot_profiler_add_record(const char *str)
82 {
83 	unsigned int len;
84 
85 	/* calculate the length of the tag */
86 	if (((unsigned int)strlen(str) + U(1)) > TAG_LEN_BYTES) {
87 		len = TAG_LEN_BYTES;
88 	} else {
89 		len = (unsigned int)strlen(str) + U(1);
90 	}
91 
92 	if (head != NULL) {
93 
94 		/*
95 		 * The profiler runs with/without MMU enabled. Check
96 		 * if MMU is enabled and memmap the shmem buffer, in
97 		 * case it is.
98 		 */
99 		if ((!is_shmem_buf_mapped) &&
100 		    ((read_sctlr_el3() & SCTLR_M_BIT) != U(0))) {
101 
102 			(void)mmap_add_dynamic_region(shmem_base_addr,
103 					shmem_base_addr,
104 					PROFILER_SIZE_BYTES,
105 					(MT_NS | MT_RW | MT_EXECUTE_NEVER));
106 
107 			is_shmem_buf_mapped = true;
108 		}
109 
110 		/* write the tag and timestamp to buffer */
111 		(void)snprintf((char *)cur->tag, len, "%s", str);
112 		cur->timestamp = mmio_read_32(tmr);
113 
114 		/* start from head if we reached the end */
115 		if (cur == tail) {
116 			cur = head;
117 		} else {
118 			cur++;
119 		}
120 	}
121 }
122 
123 /*******************************************************************************
124  * Deinint the profiler
125  ******************************************************************************/
boot_profiler_deinit(void)126 void boot_profiler_deinit(void)
127 {
128 	if (shmem_base_addr != ULL(0)) {
129 
130 		/* clean up resources */
131 		cur = NULL;
132 		head = NULL;
133 		tail = NULL;
134 
135 		/* flush the shmem for it to be visible to the NS world */
136 		flush_dcache_range(shmem_base_addr, PROFILER_SIZE_BYTES);
137 
138 		/* unmap the shmem buffer */
139 		if (is_shmem_buf_mapped) {
140 			(void)mmap_remove_dynamic_region(shmem_base_addr,
141 					PROFILER_SIZE_BYTES);
142 		}
143 	}
144 }
145