blob: 07fa814ed5d95683a091538e53b7604311091c36 [file] [log] [blame]
Jacob Garber07201d72020-09-08 12:25:44 -06001/* SPDX-License-Identifier: GPL-2.0-only */
Uwe Hermann0ab8cdd2008-04-22 20:19:53 +00002
3#include "coreinfo.h"
4
Julius Wernereab2a292019-03-05 16:55:15 -08005#if CONFIG(MODULE_BOOTLOG)
Uwe Hermann0ab8cdd2008-04-22 20:19:53 +00006
Yasha Cherikovsky37f45652015-11-14 19:21:14 +02007#define LINES_SHOWN 19
8#define TAB_WIDTH 2
Uwe Hermann0ab8cdd2008-04-22 20:19:53 +00009
Yasha Cherikovsky37f45652015-11-14 19:21:14 +020010/* Globals that are used for tracking screen state */
11static char *g_buf = NULL;
12static s32 g_line = 0;
13static s32 g_lines_count = 0;
14static s32 g_max_cursor_line = 0;
15
Yasha Cherikovsky37f45652015-11-14 19:21:14 +020016/* Copied from libpayload/drivers/cbmem_console.c */
17struct cbmem_console {
18 u32 size;
19 u32 cursor;
Elyes Haouasb66a5552023-07-30 17:29:36 +020020 u8 body[];
Stefan Reinauer6a001132017-07-13 02:20:27 +020021} __packed;
Yasha Cherikovsky37f45652015-11-14 19:21:14 +020022
Julius Wernerd67c6872017-02-02 17:32:00 -080023#define CURSOR_MASK ((1 << 28) - 1)
24#define OVERFLOW (1 << 31)
25
Yasha Cherikovsky37f45652015-11-14 19:21:14 +020026static u32 char_width(char c, u32 cursor, u32 screen_width)
27{
28 if (c == '\n') {
29 return screen_width - (cursor % screen_width);
30 } else if (c == '\t') {
31 return TAB_WIDTH;
32 } else if (isprint(c)) {
33 return 1;
34 }
35
36 return 0;
37}
38
39static u32 calculate_chars_count(char *str, u32 str_len, u32 screen_width, u32 screen_height)
40{
41 u32 i, count = 0;
42
43 for (i = 0; i < str_len; i++) {
44 count += char_width(str[i], count, screen_width);
45 }
46
47 /* Ensure that 'count' can occupy at least the whole screen */
48 if (count < screen_width * screen_height) {
49 count = screen_width * screen_height;
50 }
51
52 /* Pad to line end */
53 if (count % screen_width != 0) {
54 count += screen_width - (count % screen_width);
55 }
56
57 return count;
58}
59
60/*
61 * This method takes an input buffer and sanitizes it for display, which means:
62 * - '\n' is converted to spaces until end of line
63 * - Tabs are converted to spaces of size TAB_WIDTH
64 * - Only printable characters are preserved
65 */
66static int sanitize_buffer_for_display(char *str, u32 str_len, char *out, u32 out_len, u32 screen_width)
67{
68 u32 cursor = 0;
69 u32 i;
70
71 for (i = 0; i < str_len && cursor < out_len; i++) {
72 u32 width = char_width(str[i], cursor, screen_width);
73 if (width == 1) {
74 out[cursor++] = str[i];
75 } else if (width > 1) {
76 while (width-- && cursor < out_len) {
77 out[cursor++] = ' ';
78 }
79 }
80 }
81
82 /* Fill the rest of the out buffer with spaces */
83 while (cursor < out_len) {
84 out[cursor++] = ' ';
85 }
86
87 return 0;
88}
Uwe Hermann0ab8cdd2008-04-22 20:19:53 +000089
90static int bootlog_module_init(void)
91{
Yasha Cherikovsky37f45652015-11-14 19:21:14 +020092 /* Make sure that lib_sysinfo is initialized */
93 int ret = lib_get_sysinfo();
94 if (ret) {
95 return -1;
Uwe Hermann0ab8cdd2008-04-22 20:19:53 +000096 }
97
Nico Huber14adb7e2020-07-18 14:23:41 +020098 struct cbmem_console *console = phys_to_virt(lib_sysinfo.cbmem_cons);
Yasha Cherikovsky37f45652015-11-14 19:21:14 +020099 if (console == NULL) {
100 return -1;
Uwe Hermann0ab8cdd2008-04-22 20:19:53 +0000101 }
Yasha Cherikovsky37f45652015-11-14 19:21:14 +0200102 /* Extract console information */
103 char *buffer = (char *)(&(console->body));
Julius Wernerd67c6872017-02-02 17:32:00 -0800104 u32 size = console->size;
105 u32 cursor = console->cursor & CURSOR_MASK;
Yasha Cherikovsky37f45652015-11-14 19:21:14 +0200106
Julius Wernerd67c6872017-02-02 17:32:00 -0800107 /* The cursor may be bigger than buffer size with older console code */
108 if (cursor >= size) {
109 cursor = size - 1;
Yasha Cherikovsky37f45652015-11-14 19:21:14 +0200110 }
111
112 /* Calculate how much characters will be displayed on screen */
Julius Wernerd67c6872017-02-02 17:32:00 -0800113 u32 chars_count = calculate_chars_count(buffer, cursor, SCREEN_X, LINES_SHOWN);
114 u32 overflow_chars_count = 0;
115 if (console->cursor & OVERFLOW) {
116 overflow_chars_count = calculate_chars_count(buffer + cursor,
117 size - cursor, SCREEN_X, LINES_SHOWN);
118 }
Yasha Cherikovsky37f45652015-11-14 19:21:14 +0200119
120 /* Sanity check, chars_count must be padded to full line */
Julius Wernerd67c6872017-02-02 17:32:00 -0800121 if (chars_count % SCREEN_X || overflow_chars_count % SCREEN_X) {
Yasha Cherikovsky37f45652015-11-14 19:21:14 +0200122 return -2;
123 }
124
Julius Wernerd67c6872017-02-02 17:32:00 -0800125 g_lines_count = (chars_count + overflow_chars_count) / SCREEN_X;
Yasha Cherikovsky37f45652015-11-14 19:21:14 +0200126 g_max_cursor_line = MAX(g_lines_count - 1 - LINES_SHOWN, 0);
127
128 g_buf = malloc(chars_count);
129 if (!g_buf) {
130 return -3;
131 }
132
Julius Wernerd67c6872017-02-02 17:32:00 -0800133 if (console->cursor & OVERFLOW) {
134 if (sanitize_buffer_for_display(buffer + cursor, size - cursor,
135 g_buf, overflow_chars_count, SCREEN_X) < 0) {
136 goto err_free;
137 }
138 }
139 if (sanitize_buffer_for_display(buffer, cursor,
140 g_buf + overflow_chars_count,
141 chars_count, SCREEN_X) < 0) {
142 goto err_free;
Yasha Cherikovsky37f45652015-11-14 19:21:14 +0200143 }
Uwe Hermann0ab8cdd2008-04-22 20:19:53 +0000144
145 /* TODO: Maybe a _cleanup hook where we call free()? */
146
147 return 0;
Julius Wernerd67c6872017-02-02 17:32:00 -0800148
149err_free:
150 free(g_buf);
151 g_buf = NULL;
152 return -4;
Uwe Hermann0ab8cdd2008-04-22 20:19:53 +0000153}
154
155static int bootlog_module_redraw(WINDOW *win)
156{
Martin Rothe81ce042017-06-03 20:00:36 -0600157 print_module_title(win, "coreboot Bootlog");
Uwe Hermann0ab8cdd2008-04-22 20:19:53 +0000158
Yasha Cherikovsky37f45652015-11-14 19:21:14 +0200159 if (!g_buf) {
160 return -1;
161 }
162
163 int x = 0, y = 0;
164 char *tmp = g_buf + g_line * SCREEN_X;
165
166 for (y = 0; y < LINES_SHOWN; y++) {
167 for (x = 0; x < SCREEN_X; x++) {
168 mvwaddch(win, y + 2, x, *tmp);
169 tmp++;
Uwe Hermann0ab8cdd2008-04-22 20:19:53 +0000170 }
Yasha Cherikovsky37f45652015-11-14 19:21:14 +0200171
Uwe Hermann0ab8cdd2008-04-22 20:19:53 +0000172 }
173
174 return 0;
175}
176
Uwe Hermann0ab8cdd2008-04-22 20:19:53 +0000177static int bootlog_module_handle(int key)
178{
Yasha Cherikovsky37f45652015-11-14 19:21:14 +0200179 if (!g_buf) {
180 return 0;
181 }
Uwe Hermann0ab8cdd2008-04-22 20:19:53 +0000182
183 switch (key) {
184 case KEY_DOWN:
Yasha Cherikovsky37f45652015-11-14 19:21:14 +0200185 g_line++;
Uwe Hermann0ab8cdd2008-04-22 20:19:53 +0000186 break;
187 case KEY_UP:
Yasha Cherikovsky37f45652015-11-14 19:21:14 +0200188 g_line--;
Uwe Hermann0ab8cdd2008-04-22 20:19:53 +0000189 break;
Yasha Cherikovsky37f45652015-11-14 19:21:14 +0200190 case KEY_NPAGE: /* Page up */
191 g_line -= LINES_SHOWN;
Uwe Hermann0ab8cdd2008-04-22 20:19:53 +0000192 break;
Yasha Cherikovsky37f45652015-11-14 19:21:14 +0200193 case KEY_PPAGE: /* Page down */
194 g_line += LINES_SHOWN;
Uwe Hermann0ab8cdd2008-04-22 20:19:53 +0000195 break;
196 }
197
Yasha Cherikovsky37f45652015-11-14 19:21:14 +0200198 if (g_line < 0)
199 g_line = 0;
Uwe Hermann0ab8cdd2008-04-22 20:19:53 +0000200
Yasha Cherikovsky37f45652015-11-14 19:21:14 +0200201 if (g_line > g_max_cursor_line)
202 g_line = g_max_cursor_line;
Uwe Hermann0ab8cdd2008-04-22 20:19:53 +0000203
204 return 1;
205}
206
207struct coreinfo_module bootlog_module = {
208 .name = "Bootlog",
209 .init = bootlog_module_init,
210 .redraw = bootlog_module_redraw,
211 .handle = bootlog_module_handle,
212};
213
214#else
215
216struct coreinfo_module bootlog_module = {
217};
218
219#endif