/* * 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 #include #include #include #include #include #include #include #include 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); } } }