aboutsummaryrefslogtreecommitdiff
path: root/src/lib/cbmem_console.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/cbmem_console.c')
-rw-r--r--src/lib/cbmem_console.c210
1 files changed, 71 insertions, 139 deletions
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