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