145 lines
4.0 KiB
C
145 lines
4.0 KiB
C
/*
|
|
* 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);
|
|
}
|
|
}
|
|
}
|