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 ******************************************************************************/ 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 ******************************************************************************/ 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 ******************************************************************************/ 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