1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #include <console/cbmem_console.h>
4 #include <console/uart.h>
9 * Structure describing console buffer. It is overlaid on a flat memory area,
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.
16 * NOTE: These are known implementations accessing this console that need to be
17 * updated in case of structure/API changes:
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
26 struct cbmem_console
{
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 */
34 #define OVERFLOW (1UL << 31) /* set if in ring-buffer mode */
35 _Static_assert(CONFIG_CONSOLE_CBMEM_BUFFER_SIZE
<= MAX_SIZE
,
36 "cbmem_console format cannot support buffers larger than 256MB!");
38 static struct cbmem_console
*current_console
;
41 * While running from ROM, before DRAM is initialized, some area in cache as
42 * RAM space is used for the console buffer storage. The size and location of
43 * the area are defined by the linker script with _(e)preram_cbmem_console.
45 * When running from RAM, some console output is generated before CBMEM is
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.
51 #define STATIC_CONSOLE_SIZE 1024
52 static u8 static_console
[STATIC_CONSOLE_SIZE
];
54 static int buffer_valid(struct cbmem_console
*cbm_cons_p
, u32 total_space
)
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
);
61 static void init_console_ptr(void *storage
, u32 total_space
)
63 struct cbmem_console
*cbm_cons_p
= storage
;
65 if (!cbm_cons_p
|| total_space
<= sizeof(struct cbmem_console
)) {
66 current_console
= NULL
;
70 if (!buffer_valid(cbm_cons_p
, total_space
)) {
71 cbm_cons_p
->size
= total_space
- sizeof(struct cbmem_console
);
72 cbm_cons_p
->cursor
= 0;
75 current_console
= cbm_cons_p
;
78 void cbmemc_init(void)
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
));
84 /* Post-RAM uses static (BSS) buffer before CBMEM is reinitialized. */
85 init_console_ptr(static_console
, sizeof(static_console
));
89 void cbmemc_tx_byte(unsigned char data
)
91 if (!current_console
|| !current_console
->size
)
94 u32 flags
= current_console
->cursor
& ~CURSOR_MASK
;
95 u32 cursor
= current_console
->cursor
& CURSOR_MASK
;
97 current_console
->body
[cursor
++] = data
;
98 if (cursor
>= current_console
->size
) {
103 current_console
->cursor
= flags
| cursor
;
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.
113 static void copy_console_buffer(struct cbmem_console
*src_cons_p
)
120 if (src_cons_p
->cursor
& OVERFLOW
) {
121 const char overflow_warning
[] = "\n*** Pre-CBMEM " ENV_STRING
122 " console overflowed, log truncated! ***\n";
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
]);
130 for (c
= 0; c
< (src_cons_p
->cursor
& CURSOR_MASK
); c
++)
131 cbmemc_tx_byte(src_cons_p
->body
[c
]);
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;
138 static void cbmemc_reinit(int is_recovery
)
140 const size_t size
= CONFIG_CONSOLE_CBMEM_BUFFER_SIZE
;
141 /* If CBMEM entry already existed, old contents are not altered. */
142 struct cbmem_console
*cbmem_cons_p
= cbmem_add(CBMEM_ID_CONSOLE
, size
);
143 struct cbmem_console
*previous_cons_p
= current_console
;
145 init_console_ptr(cbmem_cons_p
, size
);
146 copy_console_buffer(previous_cons_p
);
148 ROMSTAGE_CBMEM_INIT_HOOK(cbmemc_reinit
)
149 RAMSTAGE_CBMEM_INIT_HOOK(cbmemc_reinit
)
150 POSTCAR_CBMEM_INIT_HOOK(cbmemc_reinit
)
152 #if CONFIG(CONSOLE_CBMEM_DUMP_TO_UART)
153 void cbmem_dump_console(void)
156 if (!current_console
)
160 if (current_console
->cursor
& OVERFLOW
)
161 for (cursor
= current_console
->cursor
& CURSOR_MASK
;
162 cursor
< current_console
->size
; cursor
++)
163 uart_tx_byte(0, current_console
->body
[cursor
]);
164 for (cursor
= 0; cursor
< (current_console
->cursor
& CURSOR_MASK
); cursor
++)
165 uart_tx_byte(0, current_console
->body
[cursor
]);