1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #include <console/cbmem_console.h>
4 #include <console/console.h>
5 #include <console/uart.h>
11 * Structure describing console buffer. It is overlaid on a flat memory area,
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.
18 * NOTE: These are known implementations accessing this console that need to be
19 * updated in case of structure/API changes:
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
28 struct cbmem_console
{
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 */
36 #define OVERFLOW (1UL << 31) /* set if in ring-buffer mode */
37 _Static_assert(CONFIG_CONSOLE_CBMEM_BUFFER_SIZE
<= MAX_SIZE
,
38 "cbmem_console format cannot support buffers larger than 256MB!");
40 static struct cbmem_console
*current_console
;
42 static bool console_paused
;
45 * While running from ROM, before DRAM is initialized, some area in cache as
46 * RAM space is used for the console buffer storage. The size and location of
47 * the area are defined by the linker script with _(e)preram_cbmem_console.
49 * When running from RAM, some console output is generated before CBMEM is
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.
55 #define STATIC_CONSOLE_SIZE 1024
56 static u8 static_console
[STATIC_CONSOLE_SIZE
];
58 static int buffer_valid(struct cbmem_console
*cbm_cons_p
, u32 total_space
)
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
);
65 static void init_console_ptr(void *storage
, u32 total_space
)
67 struct cbmem_console
*cbm_cons_p
= storage
;
69 if (!cbm_cons_p
|| total_space
<= sizeof(struct cbmem_console
)) {
70 current_console
= NULL
;
74 if (!buffer_valid(cbm_cons_p
, total_space
)) {
75 cbm_cons_p
->size
= total_space
- sizeof(struct cbmem_console
);
76 cbm_cons_p
->cursor
= 0;
79 current_console
= cbm_cons_p
;
82 void cbmemc_init(void)
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
));
89 size_t cbmemc_size
= 0;
91 smm_get_cbmemc_buffer(&cbmemc
, &cbmemc_size
);
93 init_console_ptr(cbmemc
, cbmemc_size
);
95 /* Post-RAM uses static (BSS) buffer before CBMEM is reinitialized. */
96 init_console_ptr(static_console
, sizeof(static_console
));
100 void cbmemc_tx_byte(unsigned char data
)
102 if (!current_console
|| !current_console
->size
|| console_paused
)
105 u32 flags
= current_console
->cursor
& ~CURSOR_MASK
;
106 u32 cursor
= current_console
->cursor
& CURSOR_MASK
;
108 current_console
->body
[cursor
++] = data
;
109 if (cursor
>= current_console
->size
) {
114 current_console
->cursor
= flags
| cursor
;
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.
124 static void copy_console_buffer(struct cbmem_console
*src_cons_p
)
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
]);
141 for (c
= 0; c
< (src_cons_p
->cursor
& CURSOR_MASK
); c
++)
142 cbmemc_tx_byte(src_cons_p
->body
[c
]);
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;
149 void cbmemc_copy_in(void *buffer
, size_t size
)
151 struct cbmem_console
*previous
= (void *)buffer
;
153 if (!buffer_valid(previous
, size
))
156 copy_console_buffer(previous
);
159 static void cbmemc_reinit(int is_recovery
)
161 const size_t size
= CONFIG_CONSOLE_CBMEM_BUFFER_SIZE
;
162 /* If CBMEM entry already existed, old contents are not altered. */
163 struct cbmem_console
*cbmem_cons_p
= cbmem_add(CBMEM_ID_CONSOLE
, size
);
164 struct cbmem_console
*previous_cons_p
= current_console
;
166 init_console_ptr(cbmem_cons_p
, size
);
167 copy_console_buffer(previous_cons_p
);
170 /* Run this hook early so that the console region is one of the earliest created, and
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). */
173 CBMEM_READY_HOOK_EARLY(cbmemc_reinit
);
175 #if CONFIG(CONSOLE_CBMEM_DUMP_TO_UART)
176 void cbmem_dump_console_to_uart(void)
179 unsigned int console_index
;
181 if (!current_console
)
184 console_index
= get_uart_for_console();
186 uart_init(console_index
);
187 if (current_console
->cursor
& OVERFLOW
) {
188 for (cursor
= current_console
->cursor
& CURSOR_MASK
;
189 cursor
< current_console
->size
; cursor
++) {
190 if (BIOS_LOG_IS_MARKER(current_console
->body
[cursor
]))
192 if (current_console
->body
[cursor
] == '\n')
193 uart_tx_byte(console_index
, '\r');
194 uart_tx_byte(console_index
, current_console
->body
[cursor
]);
197 for (cursor
= 0; cursor
< (current_console
->cursor
& CURSOR_MASK
); cursor
++) {
198 if (BIOS_LOG_IS_MARKER(current_console
->body
[cursor
]))
200 if (current_console
->body
[cursor
] == '\n')
201 uart_tx_byte(console_index
, '\r');
202 uart_tx_byte(console_index
, current_console
->body
[cursor
]);
207 void cbmem_dump_console(void)
210 if (!current_console
)
213 console_paused
= true;
215 if (current_console
->cursor
& OVERFLOW
)
216 for (cursor
= current_console
->cursor
& CURSOR_MASK
;
217 cursor
< current_console
->size
; cursor
++)
218 if (!BIOS_LOG_IS_MARKER(current_console
->body
[cursor
]))
219 do_putchar(current_console
->body
[cursor
]);
220 for (cursor
= 0; cursor
< (current_console
->cursor
& CURSOR_MASK
); cursor
++)
221 if (!BIOS_LOG_IS_MARKER(current_console
->body
[cursor
]))
222 do_putchar(current_console
->body
[cursor
]);
224 console_paused
= false;