summaryrefslogtreecommitdiff
path: root/payloads/coreinfo/timestamps_module.c
blob: 3a24930748187e6790baf34d478064a0a2d5de50 (plain)
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
/* SPDX-License-Identifier: GPL-2.0-only */

#include "coreinfo.h"
#include <commonlib/timestamp_serialized.h>

#if CONFIG(MODULE_TIMESTAMPS)

#define LINES_SHOWN 19
#define TAB_WIDTH 2

/* Globals that are used for tracking screen state */
static char *g_buf;
static s32 g_line;
static s32 g_lines_count;
static s32 g_max_cursor_line;

static unsigned long tick_freq_mhz;

static const char *timestamp_name(uint32_t id)
{
	for (size_t i = 0; i < ARRAY_SIZE(timestamp_ids); i++) {
		if (timestamp_ids[i].id == id)
			return timestamp_ids[i].name;
	}

	return "<unknown>";
}

static void timestamp_set_tick_freq(unsigned long table_tick_freq_mhz)
{
	tick_freq_mhz = table_tick_freq_mhz;

	/* Honor table frequency. */
	if (tick_freq_mhz)
		return;

	tick_freq_mhz = lib_sysinfo.cpu_khz / 1000;

	if (!tick_freq_mhz) {
		fprintf(stderr, "Cannot determine timestamp tick frequency.\n");
		exit(1);
	}
}

static u64 arch_convert_raw_ts_entry(u64 ts)
{
	return ts / tick_freq_mhz;
}

static u32 char_width(char c, u32 cursor, u32 screen_width)
{
	if (c == '\n')
		return screen_width - (cursor % screen_width);
	else if (c == '\t')
		return TAB_WIDTH;
	else if (isprint(c))
		return 1;

	return 0;
}

static u32 calculate_chars_count(char *str, u32 str_len, u32 screen_width,
		u32 screen_height)
{
	u32 i, count = 0;

	for (i = 0; i < str_len; i++)
		count += char_width(str[i], count, screen_width);

	/* Ensure that 'count' can occupy at least the whole screen */
	if (count < screen_width * screen_height)
		count = screen_width * screen_height;

	/* Pad to line end */
	if (count % screen_width != 0)
		count += screen_width - (count % screen_width);

	return count;
}

/*
 * This method takes an input buffer and sanitizes it for display, which means:
 *  - '\n' is converted to spaces until end of line
 *  - Tabs are converted to spaces of size TAB_WIDTH
 *  - Only printable characters are preserved
 */
static int sanitize_buffer_for_display(char *str, u32 str_len, char *out,
		u32 out_len, u32 screen_width)
{
	u32 cursor = 0;
	u32 i;

	for (i = 0; i < str_len && cursor < out_len; i++) {
		u32 width = char_width(str[i], cursor, screen_width);

		if (width == 1)
			out[cursor++] = str[i];
		else if (width > 1)
			while (width-- && cursor < out_len)
				out[cursor++] = ' ';
	}

	/* Fill the rest of the out buffer with spaces */
	while (cursor < out_len)
		out[cursor++] = ' ';

	return 0;
}

static uint64_t timestamp_print_entry(char *buffer, size_t size, uint32_t *cur,
		uint32_t id, uint64_t stamp, uint64_t prev_stamp)
{
	const char *name;
	uint64_t step_time;

	name = timestamp_name(id);
	step_time = arch_convert_raw_ts_entry(stamp - prev_stamp);

	*cur += snprintf(buffer + *cur, size, "%4d: %-45s", id, name);
	*cur += snprintf(buffer + *cur, size, "%llu",
			arch_convert_raw_ts_entry(stamp));
	if (prev_stamp) {
		*cur += snprintf(buffer + *cur, size, " (");
		*cur += snprintf(buffer + *cur, size, "%llu", step_time);
		*cur += snprintf(buffer + *cur, size, ")");
	}
	*cur += snprintf(buffer + *cur, size, "\n");

	return step_time;
}

static int timestamps_module_init(void)
{
	/* Make sure that lib_sysinfo is initialized */
	int ret = lib_get_sysinfo();

	if (ret)
		return -1;

	struct timestamp_table *timestamps = phys_to_virt(lib_sysinfo.tstamp_table);

	if (timestamps == NULL)
		return -1;

	/* Extract timestamps information */
	u64 base_time = timestamps->base_time;
	u16 max_entries = timestamps->max_entries;
	u32 n_entries = timestamps->num_entries;

	timestamp_set_tick_freq(timestamps->tick_freq_mhz);

	char *buffer;
	u32 buff_cur = 0;
	uint64_t prev_stamp;
	uint64_t total_time;

	/* Allocate a buffer big enough to contain all of the possible
	 * entries plus the other information (number entries, total time). */
	buffer = malloc((max_entries + 4) * SCREEN_X * sizeof(char));

	if (buffer == NULL)
		return -3;

	/* Write the content */
	buff_cur += snprintf(buffer, SCREEN_X, "%d entries total:\n\n",
			n_entries);

	prev_stamp = 0;
	timestamp_print_entry(buffer, SCREEN_X, &buff_cur, 0, base_time,
			prev_stamp);
	prev_stamp = base_time;

	total_time = 0;
	for (u32 i = 0; i < n_entries; i++) {
		uint64_t stamp;
		const struct timestamp_entry *tse = &timestamps->entries[i];

		stamp = tse->entry_stamp + base_time;
		total_time += timestamp_print_entry(buffer, SCREEN_X,
				&buff_cur, tse->entry_id, stamp, prev_stamp);
		prev_stamp = stamp;
	}

	buff_cur += snprintf(buffer + buff_cur, SCREEN_X, "\nTotal Time: ");
	buff_cur += snprintf(buffer + buff_cur, SCREEN_X, "%llu", total_time);
	buff_cur += snprintf(buffer + buff_cur, SCREEN_X, "\n");

	/* Calculate how many characters will be displayed on screen */
	u32 chars_count = calculate_chars_count(buffer, buff_cur + 1,
			SCREEN_X, LINES_SHOWN);

	/* Sanity check, chars_count must be padded to full line */
	if (chars_count % SCREEN_X != 0) {
		free(buffer);
		return -2;
	}

	g_lines_count = chars_count / SCREEN_X;
	g_max_cursor_line = MAX(g_lines_count - 1 - LINES_SHOWN, 0);

	g_buf = malloc(chars_count);
	if (!g_buf) {
		free(buffer);
		return -3;
	}

	if (sanitize_buffer_for_display(buffer, buff_cur + 1, g_buf,
				chars_count, SCREEN_X) < 0) {
		free(buffer);
		free(g_buf);
		g_buf = NULL;
		return -4;
	}

	free(buffer);

	return 0;
}

static int timestamps_module_redraw(WINDOW *win)
{
	print_module_title(win, "coreboot Timestamps");

	if (!g_buf)
		return -1;

	int x = 0, y = 0;
	char *tmp = g_buf + g_line * SCREEN_X;

	for (y = 0; y < LINES_SHOWN; y++) {
		for (x = 0; x < SCREEN_X; x++) {
			mvwaddch(win, y + 2, x, *tmp);
			tmp++;
		}
	}

	return 0;
}

static int timestamps_module_handle(int key)
{
	if (!g_buf)
		return 0;

	switch (key) {
	case KEY_DOWN:
		g_line++;
		break;
	case KEY_UP:
		g_line--;
		break;
	case KEY_NPAGE: /* Page up */
		g_line -= LINES_SHOWN;
		break;
	case KEY_PPAGE: /* Page down */
		g_line += LINES_SHOWN;
		break;
	}

	if (g_line < 0)
		g_line = 0;

	if (g_line > g_max_cursor_line)
		g_line = g_max_cursor_line;

	return 1;
}

struct coreinfo_module timestamps_module = {
	.name = "Timestamps",
	.init = timestamps_module_init,
	.redraw = timestamps_module_redraw,
	.handle = timestamps_module_handle,
};

#else

struct coreinfo_module timestamps_module = {
};

#endif