/* * * Copyright (C) 2008 Uwe Hermann * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "coreinfo.h" #if CONFIG(MODULE_BOOTLOG) #define LINES_SHOWN 19 #define TAB_WIDTH 2 /* Globals that are used for tracking screen state */ static char *g_buf = NULL; static s32 g_line = 0; static s32 g_lines_count = 0; static s32 g_max_cursor_line = 0; /* Copied from libpayload/drivers/cbmem_console.c */ struct cbmem_console { u32 size; u32 cursor; u8 body[0]; } __packed; #define CURSOR_MASK ((1 << 28) - 1) #define OVERFLOW (1 << 31) static u32 char_width(char c, u32 cursor, u32 screen_width) { if (c == '\n') { return screen_width - (cursor % screen_width); } else if (c == '\t') { return TAB_WIDTH; } else if (isprint(c)) { return 1; } return 0; } static u32 calculate_chars_count(char *str, u32 str_len, u32 screen_width, u32 screen_height) { u32 i, count = 0; for (i = 0; i < str_len; i++) { count += char_width(str[i], count, screen_width); } /* Ensure that 'count' can occupy at least the whole screen */ if (count < screen_width * screen_height) { count = screen_width * screen_height; } /* Pad to line end */ if (count % screen_width != 0) { count += screen_width - (count % screen_width); } return count; } /* * This method takes an input buffer and sanitizes it for display, which means: * - '\n' is converted to spaces until end of line * - Tabs are converted to spaces of size TAB_WIDTH * - Only printable characters are preserved */ static int sanitize_buffer_for_display(char *str, u32 str_len, char *out, u32 out_len, u32 screen_width) { u32 cursor = 0; u32 i; for (i = 0; i < str_len && cursor < out_len; i++) { u32 width = char_width(str[i], cursor, screen_width); if (width == 1) { out[cursor++] = str[i]; } else if (width > 1) { while (width-- && cursor < out_len) { out[cursor++] = ' '; } } } /* Fill the rest of the out buffer with spaces */ while (cursor < out_len) { out[cursor++] = ' '; } return 0; } static int bootlog_module_init(void) { /* Make sure that lib_sysinfo is initialized */ int ret = lib_get_sysinfo(); if (ret) { return -1; } struct cbmem_console *console = phys_to_virt(lib_sysinfo.cbmem_cons); if (console == NULL) { return -1; } /* Extract console information */ char *buffer = (char *)(&(console->body)); u32 size = console->size; u32 cursor = console->cursor & CURSOR_MASK; /* The cursor may be bigger than buffer size with older console code */ if (cursor >= size) { cursor = size - 1; } /* Calculate how much characters will be displayed on screen */ u32 chars_count = calculate_chars_count(buffer, cursor, SCREEN_X, LINES_SHOWN); u32 overflow_chars_count = 0; if (console->cursor & OVERFLOW) { overflow_chars_count = calculate_chars_count(buffer + cursor, size - cursor, SCREEN_X, LINES_SHOWN); } /* Sanity check, chars_count must be padded to full line */ if (chars_count % SCREEN_X || overflow_chars_count % SCREEN_X) { return -2; } g_lines_count = (chars_count + overflow_chars_count) / SCREEN_X; g_max_cursor_line = MAX(g_lines_count - 1 - LINES_SHOWN, 0); g_buf = malloc(chars_count); if (!g_buf) { return -3; } if (console->cursor & OVERFLOW) { if (sanitize_buffer_for_display(buffer + cursor, size - cursor, g_buf, overflow_chars_count, SCREEN_X) < 0) { goto err_free; } } if (sanitize_buffer_for_display(buffer, cursor, g_buf + overflow_chars_count, chars_count, SCREEN_X) < 0) { goto err_free; } /* TODO: Maybe a _cleanup hook where we call free()? */ return 0; err_free: free(g_buf); g_buf = NULL; return -4; } static int bootlog_module_redraw(WINDOW *win) { print_module_title(win, "coreboot Bootlog"); if (!g_buf) { return -1; } int x = 0, y = 0; char *tmp = g_buf + g_line * SCREEN_X; for (y = 0; y < LINES_SHOWN; y++) { for (x = 0; x < SCREEN_X; x++) { mvwaddch(win, y + 2, x, *tmp); tmp++; } } return 0; } static int bootlog_module_handle(int key) { if (!g_buf) { return 0; } switch (key) { case KEY_DOWN: g_line++; break; case KEY_UP: g_line--; break; case KEY_NPAGE: /* Page up */ g_line -= LINES_SHOWN; break; case KEY_PPAGE: /* Page down */ g_line += LINES_SHOWN; break; } if (g_line < 0) g_line = 0; if (g_line > g_max_cursor_line) g_line = g_max_cursor_line; return 1; } struct coreinfo_module bootlog_module = { .name = "Bootlog", .init = bootlog_module_init, .redraw = bootlog_module_redraw, .handle = bootlog_module_handle, }; #else struct coreinfo_module bootlog_module = { }; #endif