blob: 3a24930748187e6790baf34d478064a0a2d5de50 [file] [log] [blame]
Jacob Garber07201d72020-09-08 12:25:44 -06001/* SPDX-License-Identifier: GPL-2.0-only */
Antonello Dettori4b1668f2016-07-08 11:14:40 +02002
3#include "coreinfo.h"
4#include <commonlib/timestamp_serialized.h>
5
Julius Wernereab2a292019-03-05 16:55:15 -08006#if CONFIG(MODULE_TIMESTAMPS)
Antonello Dettori4b1668f2016-07-08 11:14:40 +02007
8#define LINES_SHOWN 19
9#define TAB_WIDTH 2
10
11/* Globals that are used for tracking screen state */
12static char *g_buf;
13static s32 g_line;
14static s32 g_lines_count;
15static s32 g_max_cursor_line;
16
17static unsigned long tick_freq_mhz;
18
19static const char *timestamp_name(uint32_t id)
20{
Jacob Garbera711e9c2019-06-28 10:58:56 -060021 for (size_t i = 0; i < ARRAY_SIZE(timestamp_ids); i++) {
Antonello Dettori4b1668f2016-07-08 11:14:40 +020022 if (timestamp_ids[i].id == id)
23 return timestamp_ids[i].name;
24 }
25
26 return "<unknown>";
27}
28
29static 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
45static u64 arch_convert_raw_ts_entry(u64 ts)
46{
47 return ts / tick_freq_mhz;
48}
49
50static 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
62static 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 */
87static 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
110static 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
132static 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
Nico Huberbea01e32020-07-18 16:15:42 +0200140 struct timestamp_table *timestamps = phys_to_virt(lib_sysinfo.tstamp_table);
Antonello Dettori4b1668f2016-07-08 11:14:40 +0200141
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;
Jacob Garbera711e9c2019-06-28 10:58:56 -0600174 for (u32 i = 0; i < n_entries; i++) {
Antonello Dettori4b1668f2016-07-08 11:14:40 +0200175 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 */
Jacob Garberb6d91752019-03-27 20:41:34 -0600193 if (chars_count % SCREEN_X != 0) {
194 free(buffer);
Antonello Dettori4b1668f2016-07-08 11:14:40 +0200195 return -2;
Jacob Garberb6d91752019-03-27 20:41:34 -0600196 }
Antonello Dettori4b1668f2016-07-08 11:14:40 +0200197
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);
Martin Roth2d1e0eb2017-08-03 10:55:01 -0600202 if (!g_buf) {
203 free(buffer);
Antonello Dettori4b1668f2016-07-08 11:14:40 +0200204 return -3;
Martin Roth2d1e0eb2017-08-03 10:55:01 -0600205 }
Antonello Dettori4b1668f2016-07-08 11:14:40 +0200206
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
220static int timestamps_module_redraw(WINDOW *win)
221{
Martin Rothe81ce042017-06-03 20:00:36 -0600222 print_module_title(win, "coreboot Timestamps");
Antonello Dettori4b1668f2016-07-08 11:14:40 +0200223
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
240static 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
269struct 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
278struct coreinfo_module timestamps_module = {
279};
280
281#endif