diff options
Diffstat (limited to 'plat/nvidia/tegra/lib/debug/profiler.c')
-rw-r--r-- | plat/nvidia/tegra/lib/debug/profiler.c | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/plat/nvidia/tegra/lib/debug/profiler.c b/plat/nvidia/tegra/lib/debug/profiler.c new file mode 100644 index 0000000..b5baf42 --- /dev/null +++ b/plat/nvidia/tegra/lib/debug/profiler.c @@ -0,0 +1,144 @@ +/* + * 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 <lib/mmio.h> +#include <lib/utils_def.h> +#include <lib/xlat_tables/xlat_tables_v2.h> +#include <profiler.h> +#include <stdbool.h> +#include <string.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); + } + } +} |