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