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