Use C99 flexible arrays instead of older style of one-element or zero-length arrays. It allows the compiler to generate errors when the flexible array does not occur at the end in the structure. Change-Id: I17811256b04a17539d3ed77f406892ae77e97515 Signed-off-by: Elyes Haouas <ehaouas@noos.fr> Reviewed-on: https://review.coreboot.org/c/coreboot/+/76848 Reviewed-by: Felix Singer <service+coreboot-gerrit@felixsinger.de> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
		
			
				
	
	
		
			220 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			220 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* SPDX-License-Identifier: GPL-2.0-only */
 | |
| 
 | |
| #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[];
 | |
| } __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
 |