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