xref: /aosp_15_r20/external/coreboot/payloads/coreinfo/bootlog_module.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include "coreinfo.h"
4 
5 #if CONFIG(MODULE_BOOTLOG)
6 
7 #define LINES_SHOWN 19
8 #define TAB_WIDTH 2
9 
10 /* Globals that are used for tracking screen state */
11 static char *g_buf = NULL;
12 static s32 g_line = 0;
13 static s32 g_lines_count = 0;
14 static s32 g_max_cursor_line = 0;
15 
16 /* Copied from libpayload/drivers/cbmem_console.c */
17 struct cbmem_console {
18 	u32 size;
19 	u32 cursor;
20 	u8 body[];
21 } __packed;
22 
23 #define CURSOR_MASK ((1 << 28) - 1)
24 #define OVERFLOW (1 << 31)
25 
char_width(char c,u32 cursor,u32 screen_width)26 static 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 
calculate_chars_count(char * str,u32 str_len,u32 screen_width,u32 screen_height)39 static 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  */
sanitize_buffer_for_display(char * str,u32 str_len,char * out,u32 out_len,u32 screen_width)66 static 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 }
89 
bootlog_module_init(void)90 static int bootlog_module_init(void)
91 {
92 	/* Make sure that lib_sysinfo is initialized */
93 	int ret = lib_get_sysinfo();
94 	if (ret) {
95 		return -1;
96 	}
97 
98 	struct cbmem_console *console = phys_to_virt(lib_sysinfo.cbmem_cons);
99 	if (console == NULL) {
100 		return -1;
101 	}
102 	/* Extract console information */
103 	char *buffer = (char *)(&(console->body));
104 	u32 size = console->size;
105 	u32 cursor = console->cursor & CURSOR_MASK;
106 
107 	/* The cursor may be bigger than buffer size with older console code */
108 	if (cursor >= size) {
109 		cursor = size - 1;
110 	}
111 
112 	/* Calculate how much characters will be displayed on screen */
113 	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 	}
119 
120 	/* Sanity check, chars_count must be padded to full line */
121 	if (chars_count % SCREEN_X || overflow_chars_count % SCREEN_X) {
122 		return -2;
123 	}
124 
125 	g_lines_count = (chars_count + overflow_chars_count) / SCREEN_X;
126 	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 
133 	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;
143 	}
144 
145 	/* TODO: Maybe a _cleanup hook where we call free()? */
146 
147 	return 0;
148 
149 err_free:
150 	free(g_buf);
151 	g_buf = NULL;
152 	return -4;
153 }
154 
bootlog_module_redraw(WINDOW * win)155 static int bootlog_module_redraw(WINDOW *win)
156 {
157 	print_module_title(win, "coreboot Bootlog");
158 
159 	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++;
170 		}
171 
172 	}
173 
174 	return 0;
175 }
176 
bootlog_module_handle(int key)177 static int bootlog_module_handle(int key)
178 {
179 	if (!g_buf) {
180 		return 0;
181 	}
182 
183 	switch (key) {
184 	case KEY_DOWN:
185 		g_line++;
186 		break;
187 	case KEY_UP:
188 		g_line--;
189 		break;
190 	case KEY_NPAGE: /* Page up */
191 		g_line -= LINES_SHOWN;
192 		break;
193 	case KEY_PPAGE: /* Page down */
194 		g_line += LINES_SHOWN;
195 		break;
196 	}
197 
198 	if (g_line < 0)
199 		g_line = 0;
200 
201 	if (g_line > g_max_cursor_line)
202 		g_line = g_max_cursor_line;
203 
204 	return 1;
205 }
206 
207 struct 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 
216 struct coreinfo_module bootlog_module = {
217 };
218 
219 #endif
220