bootlog_module.c
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 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 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 */ 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 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 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 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