blob: ad3b99bbba9be6d755fac1fc1aa50e56e180b4de [file] [log] [blame]
Angel Pons118a9c72020-04-02 23:48:34 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Vadim Bendebury32da8be2011-09-29 17:27:15 -07002
Kyösti Mälkki1d7541f2014-02-17 21:34:42 +02003#include <console/cbmem_console.h>
Raul E Rangelbf993112022-01-11 12:48:50 -07004#include <console/console.h>
Vadim Bendebury6e20e2f2015-04-10 18:04:04 -07005#include <console/uart.h>
Vadim Bendebury32da8be2011-09-29 17:27:15 -07006#include <cbmem.h>
Julius Wernerec5e5e02014-08-20 15:29:56 -07007#include <symbols.h>
Raul E Rangelbf993112022-01-11 12:48:50 -07008#include <types.h>
Vadim Bendebury32da8be2011-09-29 17:27:15 -07009
10/*
11 * Structure describing console buffer. It is overlaid on a flat memory area,
Julius Wernerd67c6872017-02-02 17:32:00 -080012 * 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 Wernera915cea2017-04-24 17:08:06 -070017 *
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 Bendebury32da8be2011-09-29 17:27:15 -070027 */
28struct cbmem_console {
Julius Wernerd09dc6b2017-02-03 12:45:21 -080029 u32 size;
30 u32 cursor;
31 u8 body[0];
Stefan Reinauer6a001132017-07-13 02:20:27 +020032} __packed;
Vadim Bendebury32da8be2011-09-29 17:27:15 -070033
Julius Wernerd67c6872017-02-02 17:32:00 -080034#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 Salsamendifce582f2017-06-09 19:47:57 -070036#define OVERFLOW (1UL << 31) /* set if in ring-buffer mode */
Julius Wernerd67c6872017-02-02 17:32:00 -080037_Static_assert(CONFIG_CONSOLE_CBMEM_BUFFER_SIZE <= MAX_SIZE,
38 "cbmem_console format cannot support buffers larger than 256MB!");
Aaron Durbin2ad6bd22013-05-10 00:45:37 -050039
Patrick Georgia8582c42019-11-30 10:49:17 +010040static struct cbmem_console *current_console;
Kyösti Mälkki8659e402014-12-21 08:55:47 +020041
Raul E Rangelbf993112022-01-11 12:48:50 -070042static bool console_paused;
43
Vadim Bendebury32da8be2011-09-29 17:27:15 -070044/*
45 * While running from ROM, before DRAM is initialized, some area in cache as
Elyes HAOUAS91e0e3c2016-07-30 15:51:13 +020046 * RAM space is used for the console buffer storage. The size and location of
Julius Wernerec5e5e02014-08-20 15:29:56 -070047 * the area are defined by the linker script with _(e)preram_cbmem_console.
Kyösti Mälkkie3acc8f2019-09-13 10:49:20 +030048 *
Kyösti Mälkki513a1a82018-06-03 12:29:50 +030049 * When running from RAM, some console output is generated before CBMEM is
Vadim Bendebury32da8be2011-09-29 17:27:15 -070050 * 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 Reinauer19d06b22013-09-09 11:22:18 -070054
Stefan Reinauer19d06b22013-09-09 11:22:18 -070055#define STATIC_CONSOLE_SIZE 1024
Stefan Reinauer19d06b22013-09-09 11:22:18 -070056static u8 static_console[STATIC_CONSOLE_SIZE];
Vadim Bendebury32da8be2011-09-29 17:27:15 -070057
Julius Wernerd67c6872017-02-02 17:32:00 -080058static 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
65static void init_console_ptr(void *storage, u32 total_space)
Aaron Durbin2ad6bd22013-05-10 00:45:37 -050066{
67 struct cbmem_console *cbm_cons_p = storage;
68
Julius Wernerd67c6872017-02-02 17:32:00 -080069 if (!cbm_cons_p || total_space <= sizeof(struct cbmem_console)) {
Patrick Georgia8582c42019-11-30 10:49:17 +010070 current_console = NULL;
Kyösti Mälkki8659e402014-12-21 08:55:47 +020071 return;
72 }
73
Julius Wernerd67c6872017-02-02 17:32:00 -080074 if (!buffer_valid(cbm_cons_p, total_space)) {
Julius Wernerd09dc6b2017-02-03 12:45:21 -080075 cbm_cons_p->size = total_space - sizeof(struct cbmem_console);
76 cbm_cons_p->cursor = 0;
Kyösti Mälkki8659e402014-12-21 08:55:47 +020077 }
Kyösti Mälkki8659e402014-12-21 08:55:47 +020078
Patrick Georgia8582c42019-11-30 10:49:17 +010079 current_console = cbm_cons_p;
Aaron Durbin2ad6bd22013-05-10 00:45:37 -050080}
81
82void cbmemc_init(void)
83{
Kyösti Mälkkie3acc8f2019-09-13 10:49:20 +030084 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 Rangelc5160982022-02-24 16:02:49 -070087 } 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älkkie3acc8f2019-09-13 10:49:20 +030094 } else {
95 /* Post-RAM uses static (BSS) buffer before CBMEM is reinitialized. */
96 init_console_ptr(static_console, sizeof(static_console));
97 }
Aaron Durbin2ad6bd22013-05-10 00:45:37 -050098}
99
100void cbmemc_tx_byte(unsigned char data)
101{
Raul E Rangelbf993112022-01-11 12:48:50 -0700102 if (!current_console || !current_console->size || console_paused)
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700103 return;
104
Patrick Georgia8582c42019-11-30 10:49:17 +0100105 u32 flags = current_console->cursor & ~CURSOR_MASK;
106 u32 cursor = current_console->cursor & CURSOR_MASK;
Julius Wernerd67c6872017-02-02 17:32:00 -0800107
Patrick Georgia8582c42019-11-30 10:49:17 +0100108 current_console->body[cursor++] = data;
109 if (cursor >= current_console->size) {
Julius Wernerd67c6872017-02-02 17:32:00 -0800110 cursor = 0;
111 flags |= OVERFLOW;
112 }
113
Patrick Georgia8582c42019-11-30 10:49:17 +0100114 current_console->cursor = flags | cursor;
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700115}
116
Raul Rangel03b1d3e2022-01-21 16:46:55 +0000117/*
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 */
124static 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 Rangel1e1aa0c2022-01-13 09:46:21 -0700149void 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 Durbin41607a42015-06-09 13:54:10 -0500159static void cbmemc_reinit(int is_recovery)
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700160{
Aaron Durbind70bf7c2015-04-20 15:24:19 -0500161 const size_t size = CONFIG_CONSOLE_CBMEM_BUFFER_SIZE;
Julius Wernerd67c6872017-02-02 17:32:00 -0800162 /* If CBMEM entry already existed, old contents are not altered. */
163 struct cbmem_console *cbmem_cons_p = cbmem_add(CBMEM_ID_CONSOLE, size);
Patrick Georgia8582c42019-11-30 10:49:17 +0100164 struct cbmem_console *previous_cons_p = current_console;
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700165
Julius Wernerd67c6872017-02-02 17:32:00 -0800166 init_console_ptr(cbmem_cons_p, size);
167 copy_console_buffer(previous_cons_p);
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700168}
Julius Werner913a47a2021-05-20 17:00:46 -0700169
Kyösti Mälkkifa3bc042022-03-31 07:40:10 +0300170/* Run this hook early so that the console region is one of the earliest created, and
Julius Werner913a47a2021-05-20 17:00:46 -0700171 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älkkifa3bc042022-03-31 07:40:10 +0300173CBMEM_READY_HOOK_EARLY(cbmemc_reinit);
Aaron Durbind70bf7c2015-04-20 15:24:19 -0500174
Julius Wernercd49cce2019-03-05 16:53:33 -0800175#if CONFIG(CONSOLE_CBMEM_DUMP_TO_UART)
Raul E Rangel41a1a9e2022-01-11 12:44:38 -0700176void cbmem_dump_console_to_uart(void)
Vadim Bendebury6e20e2f2015-04-10 18:04:04 -0700177{
Julius Wernerd67c6872017-02-02 17:32:00 -0800178 u32 cursor;
Raul E Rangel6ec3dd22022-01-21 13:34:21 -0700179 unsigned int console_index;
180
Patrick Georgia8582c42019-11-30 10:49:17 +0100181 if (!current_console)
Vadim Bendebury6e20e2f2015-04-10 18:04:04 -0700182 return;
183
Raul E Rangel6ec3dd22022-01-21 13:34:21 -0700184 console_index = get_uart_for_console();
185
186 uart_init(console_index);
187 if (current_console->cursor & OVERFLOW) {
Patrick Georgia8582c42019-11-30 10:49:17 +0100188 for (cursor = current_console->cursor & CURSOR_MASK;
Raul E Rangel6ec3dd22022-01-21 13:34:21 -0700189 cursor < current_console->size; cursor++) {
Julius Werner984d03c2022-01-21 15:33:47 -0800190 if (BIOS_LOG_IS_MARKER(current_console->body[cursor]))
191 continue;
Raul E Rangel6ec3dd22022-01-21 13:34:21 -0700192 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 Werner984d03c2022-01-21 15:33:47 -0800198 if (BIOS_LOG_IS_MARKER(current_console->body[cursor]))
199 continue;
Raul E Rangel6ec3dd22022-01-21 13:34:21 -0700200 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 Bendebury6e20e2f2015-04-10 18:04:04 -0700204}
205#endif
Raul E Rangelbf993112022-01-11 12:48:50 -0700206
207void 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 Werner984d03c2022-01-21 15:33:47 -0800218 if (!BIOS_LOG_IS_MARKER(current_console->body[cursor]))
219 do_putchar(current_console->body[cursor]);
Raul E Rangelbf993112022-01-11 12:48:50 -0700220 for (cursor = 0; cursor < (current_console->cursor & CURSOR_MASK); cursor++)
Julius Werner984d03c2022-01-21 15:33:47 -0800221 if (!BIOS_LOG_IS_MARKER(current_console->body[cursor]))
222 do_putchar(current_console->body[cursor]);
Raul E Rangelbf993112022-01-11 12:48:50 -0700223
224 console_paused = false;
225}