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