There are efforts to have bootflows that do not follow a traditional bootblock-romstage-postcar-ramstage model. As part of that CBMEM initialisation hooks will need to move from romstage to bootblock. The interface towards platforms and drivers will change to use one of CBMEM_CREATION_HOOK() or CBMEM_READY_HOOK(). Former will only be called in the first stage with CBMEM available. Change-Id: Ie24bf4e818ca69f539196c3a814f3c52d4103d7e Signed-off-by: Kyösti Mälkki <kyosti.malkki@gmail.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/63375 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Arthur Heymans <arthur@aheymans.xyz> Reviewed-by: Julius Werner <jwerner@chromium.org> Reviewed-by: Raul Rangel <rrangel@chromium.org>
		
			
				
	
	
		
			226 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			226 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* SPDX-License-Identifier: GPL-2.0-only */
 | 
						|
 | 
						|
#include <console/cbmem_console.h>
 | 
						|
#include <console/console.h>
 | 
						|
#include <console/uart.h>
 | 
						|
#include <cbmem.h>
 | 
						|
#include <symbols.h>
 | 
						|
#include <types.h>
 | 
						|
 | 
						|
/*
 | 
						|
 * Structure describing console buffer. It is overlaid on a flat memory area,
 | 
						|
 * with body covering the extent of the memory. Once the buffer is full,
 | 
						|
 * output will wrap back around to the start of the buffer. The high bit of the
 | 
						|
 * cursor field gets set to indicate that this happened. If the underlying
 | 
						|
 * storage allows this, the buffer will persist across multiple boots and append
 | 
						|
 * to the previous log.
 | 
						|
 *
 | 
						|
 * NOTE: These are known implementations accessing this console that need to be
 | 
						|
 * updated in case of structure/API changes:
 | 
						|
 *
 | 
						|
 * cbmem:	[coreboot]/src/util/cbmem/cbmem.c
 | 
						|
 * libpayload:	[coreboot]/payloads/libpayload/drivers/cbmem_console.c
 | 
						|
 * coreinfo:	[coreboot]/payloads/coreinfo/bootlog_module.c
 | 
						|
 * Linux:	drivers/firmware/google/memconsole-coreboot.c
 | 
						|
 * SeaBIOS:	src/firmware/coreboot.c
 | 
						|
 * GRUB:	grub-core/term/i386/coreboot/cbmemc.c
 | 
						|
 */
 | 
						|
struct cbmem_console {
 | 
						|
	u32 size;
 | 
						|
	u32 cursor;
 | 
						|
	u8  body[0];
 | 
						|
}  __packed;
 | 
						|
 | 
						|
#define MAX_SIZE (1 << 28)	/* can't be changed without breaking readers! */
 | 
						|
#define CURSOR_MASK (MAX_SIZE - 1)	/* bits 31-28 are reserved for flags */
 | 
						|
#define OVERFLOW (1UL << 31)		/* set if in ring-buffer mode */
 | 
						|
_Static_assert(CONFIG_CONSOLE_CBMEM_BUFFER_SIZE <= MAX_SIZE,
 | 
						|
	"cbmem_console format cannot support buffers larger than 256MB!");
 | 
						|
 | 
						|
static struct cbmem_console *current_console;
 | 
						|
 | 
						|
static bool console_paused;
 | 
						|
 | 
						|
/*
 | 
						|
 * While running from ROM, before DRAM is initialized, some area in cache as
 | 
						|
 * RAM space is used for the console buffer storage. The size and location of
 | 
						|
 * the area are defined by the linker script with _(e)preram_cbmem_console.
 | 
						|
 *
 | 
						|
 * When running from RAM, some console output is generated before CBMEM is
 | 
						|
 * reinitialized. This static buffer is used to store that output temporarily,
 | 
						|
 * to be concatenated with the CBMEM console buffer contents accumulated
 | 
						|
 * during the ROM stage, once CBMEM becomes available at RAM stage.
 | 
						|
 */
 | 
						|
 | 
						|
#define STATIC_CONSOLE_SIZE 1024
 | 
						|
static u8 static_console[STATIC_CONSOLE_SIZE];
 | 
						|
 | 
						|
static int buffer_valid(struct cbmem_console *cbm_cons_p, u32 total_space)
 | 
						|
{
 | 
						|
	return (cbm_cons_p->cursor & CURSOR_MASK) < cbm_cons_p->size &&
 | 
						|
	       cbm_cons_p->size <= MAX_SIZE &&
 | 
						|
	       cbm_cons_p->size == total_space - sizeof(struct cbmem_console);
 | 
						|
}
 | 
						|
 | 
						|
static void init_console_ptr(void *storage, u32 total_space)
 | 
						|
{
 | 
						|
	struct cbmem_console *cbm_cons_p = storage;
 | 
						|
 | 
						|
	if (!cbm_cons_p || total_space <= sizeof(struct cbmem_console)) {
 | 
						|
		current_console = NULL;
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	if (!buffer_valid(cbm_cons_p, total_space)) {
 | 
						|
		cbm_cons_p->size = total_space - sizeof(struct cbmem_console);
 | 
						|
		cbm_cons_p->cursor = 0;
 | 
						|
	}
 | 
						|
 | 
						|
	current_console = cbm_cons_p;
 | 
						|
}
 | 
						|
 | 
						|
void cbmemc_init(void)
 | 
						|
{
 | 
						|
	if (ENV_ROMSTAGE_OR_BEFORE) {
 | 
						|
		/* Pre-RAM environments use special buffer placed by linker script. */
 | 
						|
		init_console_ptr(_preram_cbmem_console, REGION_SIZE(preram_cbmem_console));
 | 
						|
	} else if (ENV_SMM) {
 | 
						|
		void *cbmemc = NULL;
 | 
						|
		size_t cbmemc_size = 0;
 | 
						|
 | 
						|
		smm_get_cbmemc_buffer(&cbmemc, &cbmemc_size);
 | 
						|
 | 
						|
		init_console_ptr(cbmemc, cbmemc_size);
 | 
						|
	} else {
 | 
						|
		/* Post-RAM uses static (BSS) buffer before CBMEM is reinitialized. */
 | 
						|
		init_console_ptr(static_console, sizeof(static_console));
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void cbmemc_tx_byte(unsigned char data)
 | 
						|
{
 | 
						|
	if (!current_console || !current_console->size || console_paused)
 | 
						|
		return;
 | 
						|
 | 
						|
	u32 flags = current_console->cursor & ~CURSOR_MASK;
 | 
						|
	u32 cursor = current_console->cursor & CURSOR_MASK;
 | 
						|
 | 
						|
	current_console->body[cursor++] = data;
 | 
						|
	if (cursor >= current_console->size) {
 | 
						|
		cursor = 0;
 | 
						|
		flags |= OVERFLOW;
 | 
						|
	}
 | 
						|
 | 
						|
	current_console->cursor = flags | cursor;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Copy the current console buffer (either from the cache as RAM area or from
 | 
						|
 * the static buffer, pointed at by src_cons_p) into the newly initialized CBMEM
 | 
						|
 * console. The use of cbmemc_tx_byte() ensures that all special cases for the
 | 
						|
 * target console (e.g. overflow) will be handled. If there had been an
 | 
						|
 * overflow in the source console, log a message to that effect.
 | 
						|
 */
 | 
						|
static void copy_console_buffer(struct cbmem_console *src_cons_p)
 | 
						|
{
 | 
						|
	u32 c;
 | 
						|
 | 
						|
	if (!src_cons_p)
 | 
						|
		return;
 | 
						|
 | 
						|
	if (src_cons_p->cursor & OVERFLOW) {
 | 
						|
		const char overflow_warning[] = "\n*** Pre-CBMEM " ENV_STRING
 | 
						|
			" console overflowed, log truncated! ***\n";
 | 
						|
		for (c = 0; c < sizeof(overflow_warning) - 1; c++)
 | 
						|
			cbmemc_tx_byte(overflow_warning[c]);
 | 
						|
		for (c = src_cons_p->cursor & CURSOR_MASK;
 | 
						|
		     c < src_cons_p->size; c++)
 | 
						|
			cbmemc_tx_byte(src_cons_p->body[c]);
 | 
						|
	}
 | 
						|
 | 
						|
	for (c = 0; c < (src_cons_p->cursor & CURSOR_MASK); c++)
 | 
						|
		cbmemc_tx_byte(src_cons_p->body[c]);
 | 
						|
 | 
						|
	/* Invalidate the source console, so it will be reinitialized on the
 | 
						|
	   next reboot. Otherwise, we might copy the same bytes again. */
 | 
						|
	src_cons_p->size = 0;
 | 
						|
}
 | 
						|
 | 
						|
void cbmemc_copy_in(void *buffer, size_t size)
 | 
						|
{
 | 
						|
	struct cbmem_console *previous = (void *)buffer;
 | 
						|
 | 
						|
	if (!buffer_valid(previous, size))
 | 
						|
		return;
 | 
						|
 | 
						|
	copy_console_buffer(previous);
 | 
						|
}
 | 
						|
 | 
						|
static void cbmemc_reinit(int is_recovery)
 | 
						|
{
 | 
						|
	const size_t size = CONFIG_CONSOLE_CBMEM_BUFFER_SIZE;
 | 
						|
	/* If CBMEM entry already existed, old contents are not altered. */
 | 
						|
	struct cbmem_console *cbmem_cons_p = cbmem_add(CBMEM_ID_CONSOLE, size);
 | 
						|
	struct cbmem_console *previous_cons_p = current_console;
 | 
						|
 | 
						|
	init_console_ptr(cbmem_cons_p, size);
 | 
						|
	copy_console_buffer(previous_cons_p);
 | 
						|
}
 | 
						|
 | 
						|
/* Run this hook early so that the console region is one of the earliest created, and
 | 
						|
   therefore more likely to stay in the same place even across different boot modes where some
 | 
						|
   other regions may sometimes not get created (e.g. RW_MCACHE in vboot recovery mode). */
 | 
						|
CBMEM_READY_HOOK_EARLY(cbmemc_reinit);
 | 
						|
 | 
						|
#if CONFIG(CONSOLE_CBMEM_DUMP_TO_UART)
 | 
						|
void cbmem_dump_console_to_uart(void)
 | 
						|
{
 | 
						|
	u32 cursor;
 | 
						|
	unsigned int console_index;
 | 
						|
 | 
						|
	if (!current_console)
 | 
						|
		return;
 | 
						|
 | 
						|
	console_index = get_uart_for_console();
 | 
						|
 | 
						|
	uart_init(console_index);
 | 
						|
	if (current_console->cursor & OVERFLOW) {
 | 
						|
		for (cursor = current_console->cursor & CURSOR_MASK;
 | 
						|
		     cursor < current_console->size; cursor++) {
 | 
						|
			if (BIOS_LOG_IS_MARKER(current_console->body[cursor]))
 | 
						|
				continue;
 | 
						|
			if (current_console->body[cursor] == '\n')
 | 
						|
				uart_tx_byte(console_index, '\r');
 | 
						|
			uart_tx_byte(console_index, current_console->body[cursor]);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	for (cursor = 0; cursor < (current_console->cursor & CURSOR_MASK); cursor++) {
 | 
						|
		if (BIOS_LOG_IS_MARKER(current_console->body[cursor]))
 | 
						|
			continue;
 | 
						|
		if (current_console->body[cursor] == '\n')
 | 
						|
			uart_tx_byte(console_index, '\r');
 | 
						|
		uart_tx_byte(console_index, current_console->body[cursor]);
 | 
						|
	}
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
void cbmem_dump_console(void)
 | 
						|
{
 | 
						|
	u32 cursor;
 | 
						|
	if (!current_console)
 | 
						|
		return;
 | 
						|
 | 
						|
	console_paused = true;
 | 
						|
 | 
						|
	if (current_console->cursor & OVERFLOW)
 | 
						|
		for (cursor = current_console->cursor & CURSOR_MASK;
 | 
						|
		     cursor < current_console->size; cursor++)
 | 
						|
			if (!BIOS_LOG_IS_MARKER(current_console->body[cursor]))
 | 
						|
				do_putchar(current_console->body[cursor]);
 | 
						|
	for (cursor = 0; cursor < (current_console->cursor & CURSOR_MASK); cursor++)
 | 
						|
		if (!BIOS_LOG_IS_MARKER(current_console->body[cursor]))
 | 
						|
			do_putchar(current_console->body[cursor]);
 | 
						|
 | 
						|
	console_paused = false;
 | 
						|
}
 |