• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include "coreinfo.h"
4 #include <commonlib/timestamp_serialized.h>
5 
6 #if CONFIG(MODULE_TIMESTAMPS)
7 
8 #define LINES_SHOWN 19
9 #define TAB_WIDTH 2
10 
11 /* Globals that are used for tracking screen state */
12 static char *g_buf;
13 static s32 g_line;
14 static s32 g_lines_count;
15 static s32 g_max_cursor_line;
16 
17 static unsigned long tick_freq_mhz;
18 
timestamp_name(uint32_t id)19 static const char *timestamp_name(uint32_t id)
20 {
21 	for (size_t i = 0; i < ARRAY_SIZE(timestamp_ids); i++) {
22 		if (timestamp_ids[i].id == id)
23 			return timestamp_ids[i].name;
24 	}
25 
26 	return "<unknown>";
27 }
28 
timestamp_set_tick_freq(unsigned long table_tick_freq_mhz)29 static void timestamp_set_tick_freq(unsigned long table_tick_freq_mhz)
30 {
31 	tick_freq_mhz = table_tick_freq_mhz;
32 
33 	/* Honor table frequency. */
34 	if (tick_freq_mhz)
35 		return;
36 
37 	tick_freq_mhz = lib_sysinfo.cpu_khz / 1000;
38 
39 	if (!tick_freq_mhz) {
40 		fprintf(stderr, "Cannot determine timestamp tick frequency.\n");
41 		exit(1);
42 	}
43 }
44 
arch_convert_raw_ts_entry(u64 ts)45 static u64 arch_convert_raw_ts_entry(u64 ts)
46 {
47 	return ts / tick_freq_mhz;
48 }
49 
char_width(char c,u32 cursor,u32 screen_width)50 static u32 char_width(char c, u32 cursor, u32 screen_width)
51 {
52 	if (c == '\n')
53 		return screen_width - (cursor % screen_width);
54 	else if (c == '\t')
55 		return TAB_WIDTH;
56 	else if (isprint(c))
57 		return 1;
58 
59 	return 0;
60 }
61 
calculate_chars_count(char * str,u32 str_len,u32 screen_width,u32 screen_height)62 static u32 calculate_chars_count(char *str, u32 str_len, u32 screen_width,
63 		u32 screen_height)
64 {
65 	u32 i, count = 0;
66 
67 	for (i = 0; i < str_len; i++)
68 		count += char_width(str[i], count, screen_width);
69 
70 	/* Ensure that 'count' can occupy at least the whole screen */
71 	if (count < screen_width * screen_height)
72 		count = screen_width * screen_height;
73 
74 	/* Pad to line end */
75 	if (count % screen_width != 0)
76 		count += screen_width - (count % screen_width);
77 
78 	return count;
79 }
80 
81 /*
82  * This method takes an input buffer and sanitizes it for display, which means:
83  *  - '\n' is converted to spaces until end of line
84  *  - Tabs are converted to spaces of size TAB_WIDTH
85  *  - Only printable characters are preserved
86  */
sanitize_buffer_for_display(char * str,u32 str_len,char * out,u32 out_len,u32 screen_width)87 static int sanitize_buffer_for_display(char *str, u32 str_len, char *out,
88 		u32 out_len, u32 screen_width)
89 {
90 	u32 cursor = 0;
91 	u32 i;
92 
93 	for (i = 0; i < str_len && cursor < out_len; i++) {
94 		u32 width = char_width(str[i], cursor, screen_width);
95 
96 		if (width == 1)
97 			out[cursor++] = str[i];
98 		else if (width > 1)
99 			while (width-- && cursor < out_len)
100 				out[cursor++] = ' ';
101 	}
102 
103 	/* Fill the rest of the out buffer with spaces */
104 	while (cursor < out_len)
105 		out[cursor++] = ' ';
106 
107 	return 0;
108 }
109 
timestamp_print_entry(char * buffer,size_t size,uint32_t * cur,uint32_t id,uint64_t stamp,uint64_t prev_stamp)110 static uint64_t timestamp_print_entry(char *buffer, size_t size, uint32_t *cur,
111 		uint32_t id, uint64_t stamp, uint64_t prev_stamp)
112 {
113 	const char *name;
114 	uint64_t step_time;
115 
116 	name = timestamp_name(id);
117 	step_time = arch_convert_raw_ts_entry(stamp - prev_stamp);
118 
119 	*cur += snprintf(buffer + *cur, size, "%4d: %-45s", id, name);
120 	*cur += snprintf(buffer + *cur, size, "%llu",
121 			arch_convert_raw_ts_entry(stamp));
122 	if (prev_stamp) {
123 		*cur += snprintf(buffer + *cur, size, " (");
124 		*cur += snprintf(buffer + *cur, size, "%llu", step_time);
125 		*cur += snprintf(buffer + *cur, size, ")");
126 	}
127 	*cur += snprintf(buffer + *cur, size, "\n");
128 
129 	return step_time;
130 }
131 
timestamps_module_init(void)132 static int timestamps_module_init(void)
133 {
134 	/* Make sure that lib_sysinfo is initialized */
135 	int ret = lib_get_sysinfo();
136 
137 	if (ret)
138 		return -1;
139 
140 	struct timestamp_table *timestamps = phys_to_virt(lib_sysinfo.tstamp_table);
141 
142 	if (timestamps == NULL)
143 		return -1;
144 
145 	/* Extract timestamps information */
146 	u64 base_time = timestamps->base_time;
147 	u16 max_entries = timestamps->max_entries;
148 	u32 n_entries = timestamps->num_entries;
149 
150 	timestamp_set_tick_freq(timestamps->tick_freq_mhz);
151 
152 	char *buffer;
153 	u32 buff_cur = 0;
154 	uint64_t prev_stamp;
155 	uint64_t total_time;
156 
157 	/* Allocate a buffer big enough to contain all of the possible
158 	 * entries plus the other information (number entries, total time). */
159 	buffer = malloc((max_entries + 4) * SCREEN_X * sizeof(char));
160 
161 	if (buffer == NULL)
162 		return -3;
163 
164 	/* Write the content */
165 	buff_cur += snprintf(buffer, SCREEN_X, "%d entries total:\n\n",
166 			n_entries);
167 
168 	prev_stamp = 0;
169 	timestamp_print_entry(buffer, SCREEN_X, &buff_cur, 0, base_time,
170 			prev_stamp);
171 	prev_stamp = base_time;
172 
173 	total_time = 0;
174 	for (u32 i = 0; i < n_entries; i++) {
175 		uint64_t stamp;
176 		const struct timestamp_entry *tse = &timestamps->entries[i];
177 
178 		stamp = tse->entry_stamp + base_time;
179 		total_time += timestamp_print_entry(buffer, SCREEN_X,
180 				&buff_cur, tse->entry_id, stamp, prev_stamp);
181 		prev_stamp = stamp;
182 	}
183 
184 	buff_cur += snprintf(buffer + buff_cur, SCREEN_X, "\nTotal Time: ");
185 	buff_cur += snprintf(buffer + buff_cur, SCREEN_X, "%llu", total_time);
186 	buff_cur += snprintf(buffer + buff_cur, SCREEN_X, "\n");
187 
188 	/* Calculate how many characters will be displayed on screen */
189 	u32 chars_count = calculate_chars_count(buffer, buff_cur + 1,
190 			SCREEN_X, LINES_SHOWN);
191 
192 	/* Sanity check, chars_count must be padded to full line */
193 	if (chars_count % SCREEN_X != 0) {
194 		free(buffer);
195 		return -2;
196 	}
197 
198 	g_lines_count = chars_count / SCREEN_X;
199 	g_max_cursor_line = MAX(g_lines_count - 1 - LINES_SHOWN, 0);
200 
201 	g_buf = malloc(chars_count);
202 	if (!g_buf) {
203 		free(buffer);
204 		return -3;
205 	}
206 
207 	if (sanitize_buffer_for_display(buffer, buff_cur + 1, g_buf,
208 				chars_count, SCREEN_X) < 0) {
209 		free(buffer);
210 		free(g_buf);
211 		g_buf = NULL;
212 		return -4;
213 	}
214 
215 	free(buffer);
216 
217 	return 0;
218 }
219 
timestamps_module_redraw(WINDOW * win)220 static int timestamps_module_redraw(WINDOW *win)
221 {
222 	print_module_title(win, "coreboot Timestamps");
223 
224 	if (!g_buf)
225 		return -1;
226 
227 	int x = 0, y = 0;
228 	char *tmp = g_buf + g_line * SCREEN_X;
229 
230 	for (y = 0; y < LINES_SHOWN; y++) {
231 		for (x = 0; x < SCREEN_X; x++) {
232 			mvwaddch(win, y + 2, x, *tmp);
233 			tmp++;
234 		}
235 	}
236 
237 	return 0;
238 }
239 
timestamps_module_handle(int key)240 static int timestamps_module_handle(int key)
241 {
242 	if (!g_buf)
243 		return 0;
244 
245 	switch (key) {
246 	case KEY_DOWN:
247 		g_line++;
248 		break;
249 	case KEY_UP:
250 		g_line--;
251 		break;
252 	case KEY_NPAGE: /* Page up */
253 		g_line -= LINES_SHOWN;
254 		break;
255 	case KEY_PPAGE: /* Page down */
256 		g_line += LINES_SHOWN;
257 		break;
258 	}
259 
260 	if (g_line < 0)
261 		g_line = 0;
262 
263 	if (g_line > g_max_cursor_line)
264 		g_line = g_max_cursor_line;
265 
266 	return 1;
267 }
268 
269 struct coreinfo_module timestamps_module = {
270 	.name = "Timestamps",
271 	.init = timestamps_module_init,
272 	.redraw = timestamps_module_redraw,
273 	.handle = timestamps_module_handle,
274 };
275 
276 #else
277 
278 struct coreinfo_module timestamps_module = {
279 };
280 
281 #endif
282