coreboot
coreboot is an Open Source project aimed at replacing the proprietary BIOS found in most computers.
cbmem_console.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
4 #include <console/console.h>
5 #include <console/uart.h>
6 #include <cbmem.h>
7 #include <symbols.h>
8 #include <types.h>
9 
10 /*
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.
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
27  */
28 struct cbmem_console {
31  u8 body[0];
33 
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!");
39 
41 
42 static bool console_paused;
43 
44 /*
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.
48  *
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.
53  */
54 
55 #define STATIC_CONSOLE_SIZE 1024
57 
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)
66 {
67  struct cbmem_console *cbm_cons_p = storage;
68 
69  if (!cbm_cons_p || total_space <= sizeof(struct cbmem_console)) {
71  return;
72  }
73 
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;
77  }
78 
79  current_console = cbm_cons_p;
80 }
81 
82 void cbmemc_init(void)
83 {
85  /* Pre-RAM environments use special buffer placed by linker script. */
86  init_console_ptr(_preram_cbmem_console, REGION_SIZE(preram_cbmem_console));
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);
94  } else {
95  /* Post-RAM uses static (BSS) buffer before CBMEM is reinitialized. */
97  }
98 }
99 
100 void cbmemc_tx_byte(unsigned char data)
101 {
103  return;
104 
107 
108  current_console->body[cursor++] = data;
109  if (cursor >= current_console->size) {
110  cursor = 0;
111  flags |= OVERFLOW;
112  }
113 
114  current_console->cursor = flags | cursor;
115 }
116 
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 
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 
159 static void cbmemc_reinit(int is_recovery)
160 {
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;
165 
166  init_console_ptr(cbmem_cons_p, size);
167  copy_console_buffer(previous_cons_p);
168 }
169 
170 /* Run the romstage 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). */
176 
177 #if CONFIG(CONSOLE_CBMEM_DUMP_TO_UART)
179 {
180  u32 cursor;
181  unsigned int console_index;
182 
183  if (!current_console)
184  return;
185 
186  console_index = get_uart_for_console();
187 
188  uart_init(console_index);
193  continue;
194  if (current_console->body[cursor] == '\n')
195  uart_tx_byte(console_index, '\r');
196  uart_tx_byte(console_index, current_console->body[cursor]);
197  }
198  }
199  for (cursor = 0; cursor < (current_console->cursor & CURSOR_MASK); cursor++) {
201  continue;
202  if (current_console->body[cursor] == '\n')
203  uart_tx_byte(console_index, '\r');
204  uart_tx_byte(console_index, current_console->body[cursor]);
205  }
206 }
207 #endif
208 
210 {
211  u32 cursor;
212  if (!current_console)
213  return;
214 
215  console_paused = true;
216 
225 
226  console_paused = false;
227 }
void * cbmem_add(u32 id, u64 size)
Definition: imd_cbmem.c:144
#define ROMSTAGE_CBMEM_INIT_HOOK_EARLY(init_fn_)
Definition: cbmem.h:149
#define MAX_SIZE
Definition: cbmem_console.c:34
#define OVERFLOW
Definition: cbmem_console.c:36
static struct cbmem_console * current_console
Definition: cbmem_console.c:40
static bool console_paused
Definition: cbmem_console.c:42
static void init_console_ptr(void *storage, u32 total_space)
Definition: cbmem_console.c:65
_Static_assert(CONFIG_CONSOLE_CBMEM_BUFFER_SIZE<=MAX_SIZE, "cbmem_console format cannot support buffers larger than 256MB!")
static void cbmemc_reinit(int is_recovery)
void cbmemc_init(void)
Definition: cbmem_console.c:82
#define CURSOR_MASK
Definition: cbmem_console.c:35
void cbmemc_tx_byte(unsigned char data)
static u8 static_console[STATIC_CONSOLE_SIZE]
Definition: cbmem_console.c:56
struct cbmem_console __packed
static void copy_console_buffer(struct cbmem_console *src_cons_p)
void cbmem_dump_console(void)
static int buffer_valid(struct cbmem_console *cbm_cons_p, u32 total_space)
Definition: cbmem_console.c:58
void cbmemc_copy_in(void *buffer, size_t size)
#define STATIC_CONSOLE_SIZE
Definition: cbmem_console.c:55
void cbmem_dump_console_to_uart(void)
#define CBMEM_ID_CONSOLE
Definition: cbmem_id.h:19
void do_putchar(unsigned char byte)
Definition: printk.c:55
POSTCAR_CBMEM_INIT_HOOK(migrate_ehci_debug)
RAMSTAGE_CBMEM_INIT_HOOK(migrate_ehci_debug)
#define REGION_SIZE(name)
Definition: symbols.h:10
#define BIOS_LOG_IS_MARKER(c)
Definition: loglevel.h:214
void uart_init(unsigned int idx)
Definition: pl011.c:8
void uart_tx_byte(unsigned int idx, unsigned char data)
Definition: pl011.c:12
u8 buffer[C2P_BUFFER_MAXSIZE]
Definition: psp_smm.c:18
#define ENV_SMM
Definition: rules.h:151
#define ENV_STRING
Definition: rules.h:156
#define ENV_ROMSTAGE_OR_BEFORE
Definition: rules.h:263
void smm_get_cbmemc_buffer(void **buffer_out, size_t *size_out)
#define NULL
Definition: stddef.h:19
uint32_t u32
Definition: stdint.h:51
uint8_t u8
Definition: stdint.h:45
Definition: nhlt.c:282
#define c(value, pmcreg, dst_bits)
unsigned int get_uart_for_console(void)
Definition: uartio_vpd.c:8