summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--payloads/coreinfo/bootlog_module.c45
-rw-r--r--payloads/libpayload/drivers/cbmem_console.c27
-rw-r--r--src/lib/cbmem_console.c210
-rw-r--r--util/cbmem/cbmem.c63
4 files changed, 163 insertions, 182 deletions
diff --git a/payloads/coreinfo/bootlog_module.c b/payloads/coreinfo/bootlog_module.c
index 1d990d1d0c..b3f0deeea5 100644
--- a/payloads/coreinfo/bootlog_module.c
+++ b/payloads/coreinfo/bootlog_module.c
@@ -35,6 +35,9 @@ struct cbmem_console {
u8 body[0];
} __attribute__ ((__packed__));
+#define CURSOR_MASK ((1 << 28) - 1)
+#define OVERFLOW (1 << 31)
+
static u32 char_width(char c, u32 cursor, u32 screen_width)
{
@@ -114,23 +117,28 @@ static int bootlog_module_init(void)
}
/* Extract console information */
char *buffer = (char *)(&(console->body));
- u32 buffer_size = console->size;
- u32 cursor = console->cursor;
+ u32 size = console->size;
+ u32 cursor = console->cursor & CURSOR_MASK;
- /* The cursor may be bigger than buffer size when the buffer is full */
- if (cursor >= buffer_size) {
- cursor = buffer_size - 1;
+ /* 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 + 1, SCREEN_X, LINES_SHOWN);
+ 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 != 0) {
+ if (chars_count % SCREEN_X || overflow_chars_count % SCREEN_X) {
return -2;
}
- g_lines_count = chars_count / SCREEN_X;
+ 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);
@@ -138,17 +146,26 @@ static int bootlog_module_init(void)
return -3;
}
- if (sanitize_buffer_for_display(buffer, cursor + 1,
- g_buf, chars_count,
- SCREEN_X) < 0) {
- free(g_buf);
- g_buf = NULL;
- return -4;
+ 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)
diff --git a/payloads/libpayload/drivers/cbmem_console.c b/payloads/libpayload/drivers/cbmem_console.c
index 687344400c..76c2abb74d 100644
--- a/payloads/libpayload/drivers/cbmem_console.c
+++ b/payloads/libpayload/drivers/cbmem_console.c
@@ -36,6 +36,9 @@ struct cbmem_console {
uint8_t body[0];
} __attribute__ ((__packed__));
+#define CURSOR_MASK ((1 << 28) - 1)
+#define OVERFLOW (1 << 31)
+
static struct cbmem_console *cbmem_console_p;
static struct console_output_driver cbmem_console_driver =
@@ -43,18 +46,32 @@ static struct console_output_driver cbmem_console_driver =
.write = &cbmem_console_write,
};
+static void do_write(const void *buffer, size_t count)
+{
+ memcpy(cbmem_console_p->body + (cbmem_console_p->cursor & CURSOR_MASK),
+ buffer, count);
+ cbmem_console_p->cursor += count;
+}
+
void cbmem_console_init(void)
{
cbmem_console_p = lib_sysinfo.cbmem_cons;
- if (cbmem_console_p)
+ if (cbmem_console_p && cbmem_console_p->size)
console_add_output_driver(&cbmem_console_driver);
}
void cbmem_console_write(const void *buffer, size_t count)
{
- if (cbmem_console_p->cursor + count >= cbmem_console_p->size)
- return;
+ while ((cbmem_console_p->cursor & CURSOR_MASK) + count >=
+ cbmem_console_p->size) {
+ size_t still_fits = cbmem_console_p->size -
+ (cbmem_console_p->cursor & CURSOR_MASK);
+ do_write(buffer, still_fits);
+ cbmem_console_p->cursor &= ~CURSOR_MASK;
+ cbmem_console_p->cursor |= OVERFLOW;
+ buffer += still_fits;
+ count -= still_fits;
+ }
- memcpy(cbmem_console_p->body + cbmem_console_p->cursor, buffer, count);
- cbmem_console_p->cursor += count;
+ do_write(buffer, count);
}
diff --git a/src/lib/cbmem_console.c b/src/lib/cbmem_console.c
index 284506619f..d570bbaa9a 100644
--- a/src/lib/cbmem_console.c
+++ b/src/lib/cbmem_console.c
@@ -23,9 +23,11 @@
/*
* 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, the cursor keeps going but the data is dropped on the floor. This
- * allows to tell how much data was lost in the process.
+ * 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.
*/
struct cbmem_console {
u32 size;
@@ -33,10 +35,13 @@ struct cbmem_console {
u8 body[0];
} __attribute__ ((__packed__));
-static struct cbmem_console *cbmem_console_p CAR_GLOBAL;
+#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 (1 << 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 void copy_console_buffer(struct cbmem_console *old_cons_p,
- struct cbmem_console *new_cons_p);
+static struct cbmem_console *cbmem_console_p CAR_GLOBAL;
#ifdef __PRE_RAM__
/*
@@ -62,38 +67,36 @@ static void copy_console_buffer(struct cbmem_console *old_cons_p,
static u8 static_console[STATIC_CONSOLE_SIZE];
#endif
-/* flags for init */
-#define CBMEMC_RESET (1<<0)
-#define CBMEMC_APPEND (1<<1)
-
-static inline struct cbmem_console *current_console(void)
+static struct cbmem_console *current_console(void)
{
return car_sync_var(cbmem_console_p);
}
-static inline void current_console_set(struct cbmem_console *new_console_p)
+static void current_console_set(struct cbmem_console *new_console_p)
{
car_set_var(cbmem_console_p, new_console_p);
}
-static inline void init_console_ptr(void *storage, u32 total_space, int flags)
+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 == 0) {
+ if (!cbm_cons_p || total_space <= sizeof(struct cbmem_console)) {
current_console_set(NULL);
return;
}
- if (flags & CBMEMC_RESET) {
+ if (!buffer_valid(cbm_cons_p, total_space)) {
cbm_cons_p->size = total_space - sizeof(struct cbmem_console);
cbm_cons_p->cursor = 0;
}
- if (flags & CBMEMC_APPEND) {
- struct cbmem_console *tmp_cons_p = current_console();
- if (tmp_cons_p)
- copy_console_buffer(tmp_cons_p, cbm_cons_p);
- }
current_console_set(cbm_cons_p);
}
@@ -101,149 +104,74 @@ static inline void init_console_ptr(void *storage, u32 total_space, int flags)
void cbmemc_init(void)
{
#ifdef __PRE_RAM__
- int flags = 0;
-
- /* If in bootblock always initialize the console first. */
- if (ENV_BOOTBLOCK)
- flags = CBMEMC_RESET;
- else if (ENV_ROMSTAGE) {
- /* Initialize console for the first time in romstage when
- * there's no prior stage that initialized it first. */
- if (!IS_ENABLED(CONFIG_VBOOT_STARTS_IN_BOOTBLOCK) &&
- !IS_ENABLED(CONFIG_BOOTBLOCK_CONSOLE))
- flags = CBMEMC_RESET;
- } else if (ENV_VERSTAGE) {
- /*
- * Initialize console for the first time in verstage when
- * there is no console in bootblock. Otherwise honor the
- * bootblock console when verstage comes right after
- * bootblock.
- */
- if (IS_ENABLED(CONFIG_VBOOT_STARTS_IN_BOOTBLOCK) &&
- !IS_ENABLED(CONFIG_BOOTBLOCK_CONSOLE))
- flags = CBMEMC_RESET;
- }
-
- init_console_ptr(_preram_cbmem_console,
- _preram_cbmem_console_size, flags);
+ /* Pre-RAM environments use special buffer placed by linker script. */
+ init_console_ptr(_preram_cbmem_console, _preram_cbmem_console_size);
#else
- /*
- * Initializing before CBMEM is available, use static buffer to store
- * the log.
- */
- init_console_ptr(static_console, sizeof(static_console), CBMEMC_RESET);
+ /* Post-RAM uses static (BSS) buffer before CBMEM is reinitialized. */
+ init_console_ptr(static_console, sizeof(static_console));
#endif
}
void cbmemc_tx_byte(unsigned char data)
{
struct cbmem_console *cbm_cons_p = current_console();
- u32 cursor;
- if (!cbm_cons_p)
+ if (!cbm_cons_p || !cbm_cons_p->size)
return;
- cursor = cbm_cons_p->cursor++;
- if (cursor < cbm_cons_p->size)
- cbm_cons_p->body[cursor] = data;
+ u32 flags = cbm_cons_p->cursor & ~CURSOR_MASK;
+ u32 cursor = cbm_cons_p->cursor & CURSOR_MASK;
+
+ cbm_cons_p->body[cursor++] = data;
+ if (cursor >= cbm_cons_p->size) {
+ cursor = 0;
+ flags |= OVERFLOW;
+ }
+
+ cbm_cons_p->cursor = flags | cursor;
}
/*
- * Copy the current console buffer (either from the cache as RAM area, or from
- * the static buffer, pointed at by cbmem_console_p) into the CBMEM console
- * buffer space (pointed at by new_cons_p), concatenating the copied data with
- * the CBMEM console buffer contents.
- *
- * If there is overflow - add to the destination area a string, reporting the
- * overflow and the number of dropped characters.
+ * 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 *old_cons_p,
- struct cbmem_console *new_cons_p)
+static void copy_console_buffer(struct cbmem_console *src_cons_p)
{
- u32 copy_size, dropped_chars;
- u32 cursor = new_cons_p->cursor;
+ u32 c;
- if (old_cons_p->cursor < old_cons_p->size)
- copy_size = old_cons_p->cursor;
- else
- copy_size = old_cons_p->size;
-
- if (cursor > new_cons_p->size)
- copy_size = 0;
- else if (cursor + copy_size > new_cons_p->size)
- copy_size = new_cons_p->size - cursor;
+ if (!src_cons_p)
+ return;
- dropped_chars = old_cons_p->cursor - copy_size;
- if (dropped_chars) {
- /* Reserve 80 chars to report overflow, if possible. */
- if (copy_size < 80)
- return;
- copy_size -= 80;
- dropped_chars += 80;
+ if (src_cons_p->cursor & OVERFLOW) {
+ const char overflow_warning[] = "\n*** Pre-CBMEM 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]);
}
- memcpy(new_cons_p->body + cursor, old_cons_p->body,
- copy_size);
-
- cursor += copy_size;
-
- if (dropped_chars) {
- const char loss_str1[] = "\n\n*** Log truncated, ";
- const char loss_str2[] = " characters dropped. ***\n\n";
-
- /*
- * When running from ROM sprintf is not available, a simple
- * itoa implementation is used instead.
- */
- int got_first_digit = 0;
-
- /* Way more than possible number of dropped characters. */
- u32 mult = 100000;
-
- strcpy((char *)new_cons_p->body + cursor, loss_str1);
- cursor += sizeof(loss_str1) - 1;
-
- while (mult) {
- int digit = dropped_chars / mult;
- if (got_first_digit || digit) {
- new_cons_p->body[cursor++] = digit + '0';
- dropped_chars %= mult;
- /* Excessive, but keeps it simple */
- got_first_digit = 1;
- }
- mult /= 10;
- }
+ for (c = 0; c < (src_cons_p->cursor & CURSOR_MASK); c++)
+ cbmemc_tx_byte(src_cons_p->body[c]);
- strcpy((char *)new_cons_p->body + cursor, loss_str2);
- cursor += sizeof(loss_str2) - 1;
- }
- new_cons_p->cursor = cursor;
+ /* 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;
}
static void cbmemc_reinit(int is_recovery)
{
- struct cbmem_console *cbm_cons_p;
const size_t size = CONFIG_CONSOLE_CBMEM_BUFFER_SIZE;
- int flags = CBMEMC_APPEND;
-
- /* No appending when no preram console available and adding for
- * the first time. */
- if (!ENV_RAMSTAGE && !ENV_POSTCAR && _preram_cbmem_console_size == 0)
- flags = CBMEMC_RESET;
-
- /* Need to reset the newly added cbmem console in romstage. */
- if (ENV_ROMSTAGE)
- flags |= CBMEMC_RESET;
+ /* 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();
- /* Need to reset the newly added cbmem console in ramstage
- * when there was no console in preram environment. */
- if (ENV_RAMSTAGE && IS_ENABLED(CONFIG_LATE_CBMEM_INIT))
- flags |= CBMEMC_RESET;
-
- /* If CBMEM entry already existed, old contents is not altered. */
- cbm_cons_p = cbmem_add(CBMEM_ID_CONSOLE, size);
-
- init_console_ptr(cbm_cons_p, size, flags);
+ init_console_ptr(cbmem_cons_p, size);
+ copy_console_buffer(previous_cons_p);
}
ROMSTAGE_CBMEM_INIT_HOOK(cbmemc_reinit)
RAMSTAGE_CBMEM_INIT_HOOK(cbmemc_reinit)
@@ -253,14 +181,18 @@ POSTCAR_CBMEM_INIT_HOOK(cbmemc_reinit)
void cbmem_dump_console(void)
{
struct cbmem_console *cbm_cons_p;
- int cursor;
+ u32 cursor;
cbm_cons_p = current_console();
if (!cbm_cons_p)
return;
uart_init(0);
- for (cursor = 0; cursor < cbm_cons_p->cursor; cursor++)
+ if (cbm_cons_p->cursor & OVERFLOW)
+ for (cursor = cbm_cons_p->cursor & CURSOR_MASK;
+ cursor < cbm_cons_p->size; cursor++)
+ uart_tx_byte(0, cbm_cons_p->body[cursor]);
+ for (cursor = 0; cursor < (cbm_cons_p->cursor & CURSOR_MASK); cursor++)
uart_tx_byte(0, cbm_cons_p->body[cursor]);
}
#endif
diff --git a/util/cbmem/cbmem.c b/util/cbmem/cbmem.c
index e60505222a..7b7f10d53f 100644
--- a/util/cbmem/cbmem.c
+++ b/util/cbmem/cbmem.c
@@ -601,52 +601,67 @@ static void dump_timestamps(int mach_readable)
unmap_memory();
}
+struct cbmem_console {
+ u32 size;
+ u32 cursor;
+ u8 body[0];
+} __attribute__ ((__packed__));
+
+#define CBMC_CURSOR_MASK ((1 << 28) - 1)
+#define CBMC_OVERFLOW (1 << 31)
+
/* dump the cbmem console */
static void dump_console(void)
{
- void *console_p;
+ struct cbmem_console *console_p;
char *console_c;
- uint32_t size;
- uint32_t cursor;
+ size_t size, cursor;
if (console.tag != LB_TAG_CBMEM_CONSOLE) {
fprintf(stderr, "No console found in coreboot table.\n");
return;
}
- console_p = map_memory_size((unsigned long)console.cbmem_addr,
- 2 * sizeof(uint32_t), 1);
- /* The in-memory format of the console area is:
- * u32 size
- * u32 cursor
- * char console[size]
- * Hence we have to add 8 to get to the actual console string.
- */
- size = ((uint32_t *)console_p)[0];
- cursor = ((uint32_t *)console_p)[1];
- /* Cursor continues to go on even after no more data fits in
- * the buffer but the data is dropped in this case.
- */
- if (size > cursor)
+ size = sizeof(*console_p);
+ console_p = map_memory_size((unsigned long)console.cbmem_addr, size, 1);
+ cursor = console_p->cursor & CBMC_CURSOR_MASK;
+ if (!(console_p->cursor & CBMC_OVERFLOW) && cursor < console_p->size)
size = cursor;
- console_c = calloc(1, size + 1);
+ else
+ size = console_p->size;
unmap_memory();
+
+ console_c = malloc(size + 1);
if (!console_c) {
fprintf(stderr, "Not enough memory for console.\n");
exit(1);
}
+ console_c[size] = '\0';
console_p = map_memory_size((unsigned long)console.cbmem_addr,
- size + sizeof(size) + sizeof(cursor), 1);
- aligned_memcpy(console_c, console_p + 8, size);
+ size + sizeof(*console_p), 1);
+ if (console_p->cursor & CBMC_OVERFLOW) {
+ if (cursor >= size) {
+ printf("cbmem: ERROR: CBMEM console struct is illegal, "
+ "output may be corrupt or out of order!\n\n");
+ cursor = 0;
+ }
+ aligned_memcpy(console_c, console_p->body + cursor,
+ size - cursor);
+ aligned_memcpy(console_c + size - cursor,
+ console_p->body, cursor);
+ } else {
+ aligned_memcpy(console_c, console_p->body, size);
+ }
+ /* Slight memory corruption may occur between reboots and give us a few
+ unprintable characters like '\0'. Replace them with '?' on output. */
+ for (cursor = 0; cursor < size; cursor++)
+ if (!isprint(console_c[cursor]) && !isspace(console_c[cursor]))
+ console_c[cursor] = '?';
printf("%s\n", console_c);
- if (size < cursor)
- printf("%d %s lost\n", cursor - size,
- (cursor - size) == 1 ? "byte":"bytes");
free(console_c);
-
unmap_memory();
}