Angel Pons | 118a9c7 | 2020-04-02 23:48:34 +0200 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
Vadim Bendebury | 32da8be | 2011-09-29 17:27:15 -0700 | [diff] [blame] | 2 | |
Kyösti Mälkki | 1d7541f | 2014-02-17 21:34:42 +0200 | [diff] [blame] | 3 | #include <console/cbmem_console.h> |
Vadim Bendebury | 6e20e2f | 2015-04-10 18:04:04 -0700 | [diff] [blame] | 4 | #include <console/uart.h> |
Vadim Bendebury | 32da8be | 2011-09-29 17:27:15 -0700 | [diff] [blame] | 5 | #include <cbmem.h> |
Julius Werner | ec5e5e0 | 2014-08-20 15:29:56 -0700 | [diff] [blame] | 6 | #include <symbols.h> |
Vadim Bendebury | 32da8be | 2011-09-29 17:27:15 -0700 | [diff] [blame] | 7 | |
| 8 | /* |
| 9 | * Structure describing console buffer. It is overlaid on a flat memory area, |
Julius Werner | d67c687 | 2017-02-02 17:32:00 -0800 | [diff] [blame] | 10 | * with body covering the extent of the memory. Once the buffer is full, |
| 11 | * output will wrap back around to the start of the buffer. The high bit of the |
| 12 | * cursor field gets set to indicate that this happened. If the underlying |
| 13 | * storage allows this, the buffer will persist across multiple boots and append |
| 14 | * to the previous log. |
Julius Werner | a915cea | 2017-04-24 17:08:06 -0700 | [diff] [blame] | 15 | * |
| 16 | * NOTE: These are known implementations accessing this console that need to be |
| 17 | * updated in case of structure/API changes: |
| 18 | * |
| 19 | * cbmem: [coreboot]/src/util/cbmem/cbmem.c |
| 20 | * libpayload: [coreboot]/payloads/libpayload/drivers/cbmem_console.c |
| 21 | * coreinfo: [coreboot]/payloads/coreinfo/bootlog_module.c |
| 22 | * Linux: drivers/firmware/google/memconsole-coreboot.c |
| 23 | * SeaBIOS: src/firmware/coreboot.c |
| 24 | * GRUB: grub-core/term/i386/coreboot/cbmemc.c |
Vadim Bendebury | 32da8be | 2011-09-29 17:27:15 -0700 | [diff] [blame] | 25 | */ |
| 26 | struct cbmem_console { |
Julius Werner | d09dc6b | 2017-02-03 12:45:21 -0800 | [diff] [blame] | 27 | u32 size; |
| 28 | u32 cursor; |
| 29 | u8 body[0]; |
Stefan Reinauer | 6a00113 | 2017-07-13 02:20:27 +0200 | [diff] [blame] | 30 | } __packed; |
Vadim Bendebury | 32da8be | 2011-09-29 17:27:15 -0700 | [diff] [blame] | 31 | |
Julius Werner | d67c687 | 2017-02-02 17:32:00 -0800 | [diff] [blame] | 32 | #define MAX_SIZE (1 << 28) /* can't be changed without breaking readers! */ |
| 33 | #define CURSOR_MASK (MAX_SIZE - 1) /* bits 31-28 are reserved for flags */ |
Ryan Salsamendi | fce582f | 2017-06-09 19:47:57 -0700 | [diff] [blame] | 34 | #define OVERFLOW (1UL << 31) /* set if in ring-buffer mode */ |
Julius Werner | d67c687 | 2017-02-02 17:32:00 -0800 | [diff] [blame] | 35 | _Static_assert(CONFIG_CONSOLE_CBMEM_BUFFER_SIZE <= MAX_SIZE, |
| 36 | "cbmem_console format cannot support buffers larger than 256MB!"); |
Aaron Durbin | 2ad6bd2 | 2013-05-10 00:45:37 -0500 | [diff] [blame] | 37 | |
Patrick Georgi | a8582c4 | 2019-11-30 10:49:17 +0100 | [diff] [blame] | 38 | static struct cbmem_console *current_console; |
Kyösti Mälkki | 8659e40 | 2014-12-21 08:55:47 +0200 | [diff] [blame] | 39 | |
Vadim Bendebury | 32da8be | 2011-09-29 17:27:15 -0700 | [diff] [blame] | 40 | /* |
| 41 | * While running from ROM, before DRAM is initialized, some area in cache as |
Elyes HAOUAS | 91e0e3c | 2016-07-30 15:51:13 +0200 | [diff] [blame] | 42 | * RAM space is used for the console buffer storage. The size and location of |
Julius Werner | ec5e5e0 | 2014-08-20 15:29:56 -0700 | [diff] [blame] | 43 | * the area are defined by the linker script with _(e)preram_cbmem_console. |
Kyösti Mälkki | e3acc8f | 2019-09-13 10:49:20 +0300 | [diff] [blame] | 44 | * |
Kyösti Mälkki | 513a1a8 | 2018-06-03 12:29:50 +0300 | [diff] [blame] | 45 | * When running from RAM, some console output is generated before CBMEM is |
Vadim Bendebury | 32da8be | 2011-09-29 17:27:15 -0700 | [diff] [blame] | 46 | * reinitialized. This static buffer is used to store that output temporarily, |
| 47 | * to be concatenated with the CBMEM console buffer contents accumulated |
| 48 | * during the ROM stage, once CBMEM becomes available at RAM stage. |
| 49 | */ |
Stefan Reinauer | 19d06b2 | 2013-09-09 11:22:18 -0700 | [diff] [blame] | 50 | |
Stefan Reinauer | 19d06b2 | 2013-09-09 11:22:18 -0700 | [diff] [blame] | 51 | #define STATIC_CONSOLE_SIZE 1024 |
Stefan Reinauer | 19d06b2 | 2013-09-09 11:22:18 -0700 | [diff] [blame] | 52 | static u8 static_console[STATIC_CONSOLE_SIZE]; |
Vadim Bendebury | 32da8be | 2011-09-29 17:27:15 -0700 | [diff] [blame] | 53 | |
Julius Werner | d67c687 | 2017-02-02 17:32:00 -0800 | [diff] [blame] | 54 | static int buffer_valid(struct cbmem_console *cbm_cons_p, u32 total_space) |
| 55 | { |
| 56 | return (cbm_cons_p->cursor & CURSOR_MASK) < cbm_cons_p->size && |
| 57 | cbm_cons_p->size <= MAX_SIZE && |
| 58 | cbm_cons_p->size == total_space - sizeof(struct cbmem_console); |
| 59 | } |
| 60 | |
| 61 | static void init_console_ptr(void *storage, u32 total_space) |
Aaron Durbin | 2ad6bd2 | 2013-05-10 00:45:37 -0500 | [diff] [blame] | 62 | { |
| 63 | struct cbmem_console *cbm_cons_p = storage; |
| 64 | |
Julius Werner | d67c687 | 2017-02-02 17:32:00 -0800 | [diff] [blame] | 65 | if (!cbm_cons_p || total_space <= sizeof(struct cbmem_console)) { |
Patrick Georgi | a8582c4 | 2019-11-30 10:49:17 +0100 | [diff] [blame] | 66 | current_console = NULL; |
Kyösti Mälkki | 8659e40 | 2014-12-21 08:55:47 +0200 | [diff] [blame] | 67 | return; |
| 68 | } |
| 69 | |
Julius Werner | d67c687 | 2017-02-02 17:32:00 -0800 | [diff] [blame] | 70 | if (!buffer_valid(cbm_cons_p, total_space)) { |
Julius Werner | d09dc6b | 2017-02-03 12:45:21 -0800 | [diff] [blame] | 71 | cbm_cons_p->size = total_space - sizeof(struct cbmem_console); |
| 72 | cbm_cons_p->cursor = 0; |
Kyösti Mälkki | 8659e40 | 2014-12-21 08:55:47 +0200 | [diff] [blame] | 73 | } |
Kyösti Mälkki | 8659e40 | 2014-12-21 08:55:47 +0200 | [diff] [blame] | 74 | |
Patrick Georgi | a8582c4 | 2019-11-30 10:49:17 +0100 | [diff] [blame] | 75 | current_console = cbm_cons_p; |
Aaron Durbin | 2ad6bd2 | 2013-05-10 00:45:37 -0500 | [diff] [blame] | 76 | } |
| 77 | |
| 78 | void cbmemc_init(void) |
| 79 | { |
Kyösti Mälkki | e3acc8f | 2019-09-13 10:49:20 +0300 | [diff] [blame] | 80 | if (ENV_ROMSTAGE_OR_BEFORE) { |
| 81 | /* Pre-RAM environments use special buffer placed by linker script. */ |
| 82 | init_console_ptr(_preram_cbmem_console, REGION_SIZE(preram_cbmem_console)); |
| 83 | } else { |
| 84 | /* Post-RAM uses static (BSS) buffer before CBMEM is reinitialized. */ |
| 85 | init_console_ptr(static_console, sizeof(static_console)); |
| 86 | } |
Aaron Durbin | 2ad6bd2 | 2013-05-10 00:45:37 -0500 | [diff] [blame] | 87 | } |
| 88 | |
| 89 | void cbmemc_tx_byte(unsigned char data) |
| 90 | { |
Patrick Georgi | a8582c4 | 2019-11-30 10:49:17 +0100 | [diff] [blame] | 91 | if (!current_console || !current_console->size) |
Vadim Bendebury | 32da8be | 2011-09-29 17:27:15 -0700 | [diff] [blame] | 92 | return; |
| 93 | |
Patrick Georgi | a8582c4 | 2019-11-30 10:49:17 +0100 | [diff] [blame] | 94 | u32 flags = current_console->cursor & ~CURSOR_MASK; |
| 95 | u32 cursor = current_console->cursor & CURSOR_MASK; |
Julius Werner | d67c687 | 2017-02-02 17:32:00 -0800 | [diff] [blame] | 96 | |
Patrick Georgi | a8582c4 | 2019-11-30 10:49:17 +0100 | [diff] [blame] | 97 | current_console->body[cursor++] = data; |
| 98 | if (cursor >= current_console->size) { |
Julius Werner | d67c687 | 2017-02-02 17:32:00 -0800 | [diff] [blame] | 99 | cursor = 0; |
| 100 | flags |= OVERFLOW; |
| 101 | } |
| 102 | |
Patrick Georgi | a8582c4 | 2019-11-30 10:49:17 +0100 | [diff] [blame] | 103 | current_console->cursor = flags | cursor; |
Vadim Bendebury | 32da8be | 2011-09-29 17:27:15 -0700 | [diff] [blame] | 104 | } |
| 105 | |
| 106 | /* |
Julius Werner | d67c687 | 2017-02-02 17:32:00 -0800 | [diff] [blame] | 107 | * Copy the current console buffer (either from the cache as RAM area or from |
| 108 | * the static buffer, pointed at by src_cons_p) into the newly initialized CBMEM |
| 109 | * console. The use of cbmemc_tx_byte() ensures that all special cases for the |
| 110 | * target console (e.g. overflow) will be handled. If there had been an |
| 111 | * overflow in the source console, log a message to that effect. |
Vadim Bendebury | 32da8be | 2011-09-29 17:27:15 -0700 | [diff] [blame] | 112 | */ |
Julius Werner | d67c687 | 2017-02-02 17:32:00 -0800 | [diff] [blame] | 113 | static void copy_console_buffer(struct cbmem_console *src_cons_p) |
Vadim Bendebury | 32da8be | 2011-09-29 17:27:15 -0700 | [diff] [blame] | 114 | { |
Julius Werner | d67c687 | 2017-02-02 17:32:00 -0800 | [diff] [blame] | 115 | u32 c; |
Aaron Durbin | 2ad6bd2 | 2013-05-10 00:45:37 -0500 | [diff] [blame] | 116 | |
Julius Werner | d67c687 | 2017-02-02 17:32:00 -0800 | [diff] [blame] | 117 | if (!src_cons_p) |
| 118 | return; |
Vadim Bendebury | 32da8be | 2011-09-29 17:27:15 -0700 | [diff] [blame] | 119 | |
Julius Werner | d67c687 | 2017-02-02 17:32:00 -0800 | [diff] [blame] | 120 | if (src_cons_p->cursor & OVERFLOW) { |
Julius Werner | d906bb6 | 2017-05-16 13:54:18 -0700 | [diff] [blame] | 121 | const char overflow_warning[] = "\n*** Pre-CBMEM " ENV_STRING |
| 122 | " console overflowed, log truncated! ***\n"; |
Julius Werner | d67c687 | 2017-02-02 17:32:00 -0800 | [diff] [blame] | 123 | for (c = 0; c < sizeof(overflow_warning) - 1; c++) |
| 124 | cbmemc_tx_byte(overflow_warning[c]); |
| 125 | for (c = src_cons_p->cursor & CURSOR_MASK; |
| 126 | c < src_cons_p->size; c++) |
| 127 | cbmemc_tx_byte(src_cons_p->body[c]); |
Kyösti Mälkki | a3c5ba3 | 2013-11-27 17:51:31 +0200 | [diff] [blame] | 128 | } |
Vadim Bendebury | 32da8be | 2011-09-29 17:27:15 -0700 | [diff] [blame] | 129 | |
Julius Werner | d67c687 | 2017-02-02 17:32:00 -0800 | [diff] [blame] | 130 | for (c = 0; c < (src_cons_p->cursor & CURSOR_MASK); c++) |
| 131 | cbmemc_tx_byte(src_cons_p->body[c]); |
Vadim Bendebury | 32da8be | 2011-09-29 17:27:15 -0700 | [diff] [blame] | 132 | |
Julius Werner | d67c687 | 2017-02-02 17:32:00 -0800 | [diff] [blame] | 133 | /* Invalidate the source console, so it will be reinitialized on the |
| 134 | next reboot. Otherwise, we might copy the same bytes again. */ |
| 135 | src_cons_p->size = 0; |
Vadim Bendebury | 32da8be | 2011-09-29 17:27:15 -0700 | [diff] [blame] | 136 | } |
| 137 | |
Aaron Durbin | 41607a4 | 2015-06-09 13:54:10 -0500 | [diff] [blame] | 138 | static void cbmemc_reinit(int is_recovery) |
Vadim Bendebury | 32da8be | 2011-09-29 17:27:15 -0700 | [diff] [blame] | 139 | { |
Aaron Durbin | d70bf7c | 2015-04-20 15:24:19 -0500 | [diff] [blame] | 140 | const size_t size = CONFIG_CONSOLE_CBMEM_BUFFER_SIZE; |
Julius Werner | d67c687 | 2017-02-02 17:32:00 -0800 | [diff] [blame] | 141 | /* If CBMEM entry already existed, old contents are not altered. */ |
| 142 | struct cbmem_console *cbmem_cons_p = cbmem_add(CBMEM_ID_CONSOLE, size); |
Patrick Georgi | a8582c4 | 2019-11-30 10:49:17 +0100 | [diff] [blame] | 143 | struct cbmem_console *previous_cons_p = current_console; |
Vadim Bendebury | 32da8be | 2011-09-29 17:27:15 -0700 | [diff] [blame] | 144 | |
Julius Werner | d67c687 | 2017-02-02 17:32:00 -0800 | [diff] [blame] | 145 | init_console_ptr(cbmem_cons_p, size); |
| 146 | copy_console_buffer(previous_cons_p); |
Vadim Bendebury | 32da8be | 2011-09-29 17:27:15 -0700 | [diff] [blame] | 147 | } |
Julius Werner | 913a47a | 2021-05-20 17:00:46 -0700 | [diff] [blame] | 148 | |
| 149 | /* Run the romstage hook early so that the console region is one of the earliest created, and |
| 150 | therefore more likely to stay in the same place even across different boot modes where some |
| 151 | other regions may sometimes not get created (e.g. RW_MCACHE in vboot recovery mode). */ |
| 152 | ROMSTAGE_CBMEM_INIT_HOOK_EARLY(cbmemc_reinit) |
Kyösti Mälkki | 4fbac46 | 2015-01-07 04:48:43 +0200 | [diff] [blame] | 153 | RAMSTAGE_CBMEM_INIT_HOOK(cbmemc_reinit) |
Aaron Durbin | 1e9a914 | 2016-09-16 16:23:21 -0500 | [diff] [blame] | 154 | POSTCAR_CBMEM_INIT_HOOK(cbmemc_reinit) |
Aaron Durbin | d70bf7c | 2015-04-20 15:24:19 -0500 | [diff] [blame] | 155 | |
Julius Werner | cd49cce | 2019-03-05 16:53:33 -0800 | [diff] [blame] | 156 | #if CONFIG(CONSOLE_CBMEM_DUMP_TO_UART) |
Vadim Bendebury | 6e20e2f | 2015-04-10 18:04:04 -0700 | [diff] [blame] | 157 | void cbmem_dump_console(void) |
| 158 | { |
Julius Werner | d67c687 | 2017-02-02 17:32:00 -0800 | [diff] [blame] | 159 | u32 cursor; |
Patrick Georgi | a8582c4 | 2019-11-30 10:49:17 +0100 | [diff] [blame] | 160 | if (!current_console) |
Vadim Bendebury | 6e20e2f | 2015-04-10 18:04:04 -0700 | [diff] [blame] | 161 | return; |
| 162 | |
| 163 | uart_init(0); |
Patrick Georgi | a8582c4 | 2019-11-30 10:49:17 +0100 | [diff] [blame] | 164 | if (current_console->cursor & OVERFLOW) |
| 165 | for (cursor = current_console->cursor & CURSOR_MASK; |
| 166 | cursor < current_console->size; cursor++) |
| 167 | uart_tx_byte(0, current_console->body[cursor]); |
| 168 | for (cursor = 0; cursor < (current_console->cursor & CURSOR_MASK); cursor++) |
| 169 | uart_tx_byte(0, current_console->body[cursor]); |
Vadim Bendebury | 6e20e2f | 2015-04-10 18:04:04 -0700 | [diff] [blame] | 170 | } |
| 171 | #endif |