profiler.c 3.96 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
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 <mmio.h>
#include <profiler.h>
#include <stdbool.h>
#include <string.h>
#include <utils_def.h>
#include <xlat_tables_v2.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);
		}
	}
}