diff --git a/docs/plat/nvidia-tegra.rst b/docs/plat/nvidia-tegra.rst index e244c1c..90d2ae1 100644 --- a/docs/plat/nvidia-tegra.rst +++ b/docs/plat/nvidia-tegra.rst @@ -82,6 +82,8 @@ int uart\_id; /* L2 ECC parity protection disable flag \*/ int l2\_ecc\_parity\_prot\_dis; +/* SHMEM base address for storing the boot logs \*/ +uint64\_t boot\_profiler\_shmem\_base; } plat\_params\_from\_bl2\_t; Power Management diff --git a/plat/nvidia/tegra/common/lib/debug/profiler.c b/plat/nvidia/tegra/common/lib/debug/profiler.c new file mode 100644 index 0000000..f40244b --- /dev/null +++ b/plat/nvidia/tegra/common/lib/debug/profiler.c @@ -0,0 +1,146 @@ +/* + * 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 +#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); + } + } +} diff --git a/plat/nvidia/tegra/common/tegra_bl31_setup.c b/plat/nvidia/tegra/common/tegra_bl31_setup.c index e92960c..80cc712 100644 --- a/plat/nvidia/tegra/common/tegra_bl31_setup.c +++ b/plat/nvidia/tegra/common/tegra_bl31_setup.c @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -125,6 +126,7 @@ image_info_t bl32_img_info = { {0} }; uint64_t tzdram_start, tzdram_end, bl32_start, bl32_end; uint32_t console_clock; + int32_t ret; /* * For RESET_TO_BL31 systems, BL31 is the first bootloader to run so @@ -195,6 +197,32 @@ } /* + * The previous bootloader passes the base address of the shared memory + * location to store the boot profiler logs. Sanity check the + * address and initilise the profiler library, if it looks ok. + */ + if (plat_params->boot_profiler_shmem_base != 0ULL) { + + ret = bl31_check_ns_address(plat_params->boot_profiler_shmem_base, + PROFILER_SIZE_BYTES); + if (ret == (int32_t)0) { + + /* store the membase for the profiler lib */ + plat_bl31_params_from_bl2.boot_profiler_shmem_base = + plat_params->boot_profiler_shmem_base; + + /* initialise the profiler library */ + boot_profiler_init(plat_params->boot_profiler_shmem_base, + TEGRA_TMRUS_BASE); + } + } + + /* + * Add timestamp for platform early setup entry. + */ + boot_profiler_add_record("[TF] early setup entry"); + + /* * Initialize delay timer */ tegra_delay_timer_init(); @@ -244,6 +272,11 @@ /* Early platform setup for Tegra SoCs */ plat_early_platform_setup(); + /* + * Add timestamp for platform early setup exit. + */ + boot_profiler_add_record("[TF] early setup exit"); + INFO("BL3-1: Boot CPU: %s Processor [%lx]\n", (((read_midr() >> MIDR_IMPL_SHIFT) & MIDR_IMPL_MASK) == DENVER_IMPL) ? "Denver" : "ARM", read_mpidr()); @@ -268,6 +301,11 @@ ******************************************************************************/ void bl31_platform_setup(void) { + /* + * Add timestamp for platform setup entry. + */ + boot_profiler_add_record("[TF] plat setup entry"); + /* Initialize the gic cpu and distributor interfaces */ plat_gic_setup(); @@ -287,6 +325,11 @@ */ tegra_memctrl_tzram_setup(TEGRA_TZRAM_BASE, TEGRA_TZRAM_SIZE); + /* + * Add timestamp for platform setup exit. + */ + boot_profiler_add_record("[TF] plat setup exit"); + INFO("BL3-1: Tegra platform setup complete\n"); } @@ -305,6 +348,12 @@ * disabled before we jump to the non-secure world. */ tegra_memctrl_disable_ahb_redirection(); + + /* + * Add final timestamp before exiting BL31. + */ + boot_profiler_add_record("[TF] bl31 exit"); + boot_profiler_deinit(); } /******************************************************************************* @@ -325,6 +374,11 @@ #endif const plat_params_from_bl2_t *params_from_bl2 = bl31_get_plat_params(); + /* + * Add timestamp for arch setup entry. + */ + boot_profiler_add_record("[TF] arch setup entry"); + /* add memory regions */ mmap_add_region(rw_start, rw_start, rw_size, @@ -373,6 +427,11 @@ /* enable the MMU */ enable_mmu_el3(0); + /* + * Add timestamp for arch setup exit. + */ + boot_profiler_add_record("[TF] arch setup exit"); + INFO("BL3-1: Tegra: MMU enabled\n"); } diff --git a/plat/nvidia/tegra/common/tegra_common.mk b/plat/nvidia/tegra/common/tegra_common.mk index 6a0854e..9428f43 100644 --- a/plat/nvidia/tegra/common/tegra_common.mk +++ b/plat/nvidia/tegra/common/tegra_common.mk @@ -5,6 +5,7 @@ # PLAT_INCLUDES := -Iplat/nvidia/tegra/include/drivers \ + -Iplat/nvidia/tegra/include/lib \ -Iplat/nvidia/tegra/include \ -Iplat/nvidia/tegra/include/${TARGET_SOC} @@ -25,6 +26,7 @@ ${TEGRA_GICv2_SOURCES} \ ${COMMON_DIR}/aarch64/tegra_helpers.S \ ${COMMON_DIR}/drivers/pmc/pmc.c \ + ${COMMON_DIR}/lib/debug/profiler.c \ ${COMMON_DIR}/tegra_bl31_setup.c \ ${COMMON_DIR}/tegra_delay_timer.c \ ${COMMON_DIR}/tegra_fiq_glue.c \ diff --git a/plat/nvidia/tegra/include/lib/profiler.h b/plat/nvidia/tegra/include/lib/profiler.h new file mode 100644 index 0000000..60f8d80 --- /dev/null +++ b/plat/nvidia/tegra/include/lib/profiler.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __PROFILER_H__ +#define __PROFILER_H__ + +/******************************************************************************* + * Number of bytes of memory used by the profiler on Tegra + ******************************************************************************/ +#define PROFILER_SIZE_BYTES U(0x1000) + +void boot_profiler_init(uint64_t shmem_base, uint32_t tmr_base); +void boot_profiler_add_record(const char *str); +void boot_profiler_deinit(void); + +#endif /* __PROFILER_H__ */ diff --git a/plat/nvidia/tegra/include/tegra_private.h b/plat/nvidia/tegra/include/tegra_private.h index c7636f6..f55f5df 100644 --- a/plat/nvidia/tegra/include/tegra_private.h +++ b/plat/nvidia/tegra/include/tegra_private.h @@ -44,6 +44,8 @@ int32_t uart_id; /* L2 ECC parity protection disable flag */ int32_t l2_ecc_parity_prot_dis; + /* SHMEM base address for storing the boot logs */ + uint64_t boot_profiler_shmem_base; } plat_params_from_bl2_t; /*******************************************************************************