blob: f399f646396a249949bda9d0abbac834faf28bd7 [file] [log] [blame]
Vadim Bendebury32da8be2011-09-29 17:27:15 -07001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2011 The ChromiumOS Authors. All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
Vadim Bendebury32da8be2011-09-29 17:27:15 -070014 */
15
16#include <console/console.h>
Kyösti Mälkki1d7541f2014-02-17 21:34:42 +020017#include <console/cbmem_console.h>
Vadim Bendebury6e20e2f2015-04-10 18:04:04 -070018#include <console/uart.h>
Vadim Bendebury32da8be2011-09-29 17:27:15 -070019#include <cbmem.h>
Stefan Reinauerfd4f4132013-06-19 12:25:44 -070020#include <arch/early_variables.h>
Julius Wernerec5e5e02014-08-20 15:29:56 -070021#include <symbols.h>
Vadim Bendebury32da8be2011-09-29 17:27:15 -070022#include <string.h>
23
24/*
25 * Structure describing console buffer. It is overlaid on a flat memory area,
Martin Rothcbf2bd72013-07-09 21:51:14 -060026 * with buffer_body covering the extent of the memory. Once the buffer is
Vadim Bendebury32da8be2011-09-29 17:27:15 -070027 * full, the cursor keeps going but the data is dropped on the floor. This
28 * allows to tell how much data was lost in the process.
29 */
30struct cbmem_console {
31 u32 buffer_size;
32 u32 buffer_cursor;
33 u8 buffer_body[0];
34} __attribute__ ((__packed__));
35
Aaron Durbin2ad6bd22013-05-10 00:45:37 -050036static struct cbmem_console *cbmem_console_p CAR_GLOBAL;
37
Kyösti Mälkki8659e402014-12-21 08:55:47 +020038static void copy_console_buffer(struct cbmem_console *old_cons_p,
39 struct cbmem_console *new_cons_p);
40
Vadim Bendebury32da8be2011-09-29 17:27:15 -070041#ifdef __PRE_RAM__
42/*
43 * While running from ROM, before DRAM is initialized, some area in cache as
44 * ram space is used for the console buffer storage. The size and location of
Julius Wernerec5e5e02014-08-20 15:29:56 -070045 * the area are defined by the linker script with _(e)preram_cbmem_console.
Vadim Bendebury32da8be2011-09-29 17:27:15 -070046 */
Gabe Black19e7e7d2011-10-01 04:27:32 -070047
Vadim Bendebury32da8be2011-09-29 17:27:15 -070048#else
49
50/*
51 * When running from RAM, a lot of console output is generated before CBMEM is
52 * reinitialized. This static buffer is used to store that output temporarily,
53 * to be concatenated with the CBMEM console buffer contents accumulated
54 * during the ROM stage, once CBMEM becomes available at RAM stage.
55 */
Stefan Reinauer19d06b22013-09-09 11:22:18 -070056
Kyösti Mälkki2fb6b402014-12-19 08:20:45 +020057#if IS_ENABLED(CONFIG_EARLY_CBMEM_INIT)
Stefan Reinauer19d06b22013-09-09 11:22:18 -070058#define STATIC_CONSOLE_SIZE 1024
59#else
Kyösti Mälkki44c0e4e2013-11-27 22:54:11 +020060#define STATIC_CONSOLE_SIZE CONFIG_CONSOLE_CBMEM_BUFFER_SIZE
Stefan Reinauer19d06b22013-09-09 11:22:18 -070061#endif
62static u8 static_console[STATIC_CONSOLE_SIZE];
Vadim Bendebury32da8be2011-09-29 17:27:15 -070063#endif
64
Kyösti Mälkki8659e402014-12-21 08:55:47 +020065/* flags for init */
66#define CBMEMC_RESET (1<<0)
67#define CBMEMC_APPEND (1<<1)
68
Aaron Durbin2ad6bd22013-05-10 00:45:37 -050069static inline struct cbmem_console *current_console(void)
Vadim Bendebury32da8be2011-09-29 17:27:15 -070070{
Kyösti Mälkkie4554252014-12-31 18:34:59 +020071 return car_sync_var(cbmem_console_p);
Aaron Durbin2ad6bd22013-05-10 00:45:37 -050072}
73
74static inline void current_console_set(struct cbmem_console *new_console_p)
75{
Aaron Durbin2ad6bd22013-05-10 00:45:37 -050076 car_set_var(cbmem_console_p, new_console_p);
Aaron Durbin2ad6bd22013-05-10 00:45:37 -050077}
78
Kyösti Mälkki8659e402014-12-21 08:55:47 +020079static inline void init_console_ptr(void *storage, u32 total_space, int flags)
Aaron Durbin2ad6bd22013-05-10 00:45:37 -050080{
81 struct cbmem_console *cbm_cons_p = storage;
82
Aaron Durbind70bf7c2015-04-20 15:24:19 -050083 if (!cbm_cons_p || total_space == 0) {
Kyösti Mälkki8659e402014-12-21 08:55:47 +020084 current_console_set(NULL);
85 return;
86 }
87
88 if (flags & CBMEMC_RESET) {
89 cbm_cons_p->buffer_size = total_space - sizeof(struct cbmem_console);
90 cbm_cons_p->buffer_cursor = 0;
91 }
92 if (flags & CBMEMC_APPEND) {
93 struct cbmem_console *tmp_cons_p = current_console();
94 if (tmp_cons_p)
95 copy_console_buffer(tmp_cons_p, cbm_cons_p);
96 }
97
98 current_console_set(cbm_cons_p);
Aaron Durbin2ad6bd22013-05-10 00:45:37 -050099}
100
101void cbmemc_init(void)
102{
103#ifdef __PRE_RAM__
Aaron Durbin079fb3f2015-10-07 15:41:45 -0500104 int flags = 0;
Kyösti Mälkki8659e402014-12-21 08:55:47 +0200105
Aaron Durbin079fb3f2015-10-07 15:41:45 -0500106 /* If in bootblock always initialize the console first. */
107 if (ENV_BOOTBLOCK)
108 flags = CBMEMC_RESET;
109 else if (ENV_ROMSTAGE) {
110 /* Initialize console for the first time in romstage when
111 * there's no prior stage that initialized it first. */
112 if (!IS_ENABLED(CONFIG_VBOOT_STARTS_IN_BOOTBLOCK) &&
113 !IS_ENABLED(CONFIG_BOOTBLOCK_CONSOLE))
114 flags = CBMEMC_RESET;
115 } else if (ENV_VERSTAGE) {
116 /*
117 * Initialize console for the first time in verstage when
118 * there is no console in bootblock. Otherwise honor the
119 * bootblock console when verstage comes right after
120 * bootblock.
121 */
122 if (IS_ENABLED(CONFIG_VBOOT_STARTS_IN_BOOTBLOCK) &&
123 !IS_ENABLED(CONFIG_BOOTBLOCK_CONSOLE))
124 flags = CBMEMC_RESET;
125 }
Kyösti Mälkki8659e402014-12-21 08:55:47 +0200126
Julius Wernerec5e5e02014-08-20 15:29:56 -0700127 init_console_ptr(_preram_cbmem_console,
128 _preram_cbmem_console_size, flags);
Aaron Durbin2ad6bd22013-05-10 00:45:37 -0500129#else
130 /*
131 * Initializing before CBMEM is available, use static buffer to store
132 * the log.
133 */
Kyösti Mälkki8659e402014-12-21 08:55:47 +0200134 init_console_ptr(static_console, sizeof(static_console), CBMEMC_RESET);
Aaron Durbin2ad6bd22013-05-10 00:45:37 -0500135#endif
136}
137
138void cbmemc_tx_byte(unsigned char data)
139{
140 struct cbmem_console *cbm_cons_p = current_console();
141 u32 cursor;
142
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700143 if (!cbm_cons_p)
144 return;
145
146 cursor = cbm_cons_p->buffer_cursor++;
147 if (cursor < cbm_cons_p->buffer_size)
148 cbm_cons_p->buffer_body[cursor] = data;
149}
150
151/*
152 * Copy the current console buffer (either from the cache as RAM area, or from
153 * the static buffer, pointed at by cbmem_console_p) into the CBMEM console
154 * buffer space (pointed at by new_cons_p), concatenating the copied data with
155 * the CBMEM console buffer contents.
156 *
157 * If there is overflow - add to the destination area a string, reporting the
Martin Rothcbf2bd72013-07-09 21:51:14 -0600158 * overflow and the number of dropped characters.
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700159 */
Kyösti Mälkki8659e402014-12-21 08:55:47 +0200160static void copy_console_buffer(struct cbmem_console *old_cons_p,
161 struct cbmem_console *new_cons_p)
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700162{
Kyösti Mälkkia3c5ba32013-11-27 17:51:31 +0200163 u32 copy_size, dropped_chars;
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700164 u32 cursor = new_cons_p->buffer_cursor;
Aaron Durbin2ad6bd22013-05-10 00:45:37 -0500165
Kyösti Mälkkia3c5ba32013-11-27 17:51:31 +0200166 if (old_cons_p->buffer_cursor < old_cons_p->buffer_size)
167 copy_size = old_cons_p->buffer_cursor;
168 else
169 copy_size = old_cons_p->buffer_size;
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700170
Kyösti Mälkkia3c5ba32013-11-27 17:51:31 +0200171 if (cursor > new_cons_p->buffer_size)
172 copy_size = 0;
173 else if (cursor + copy_size > new_cons_p->buffer_size)
174 copy_size = new_cons_p->buffer_size - cursor;
175
176 dropped_chars = old_cons_p->buffer_cursor - copy_size;
177 if (dropped_chars) {
178 /* Reserve 80 chars to report overflow, if possible. */
179 if (copy_size < 80)
180 return;
181 copy_size -= 80;
182 dropped_chars += 80;
183 }
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700184
Aaron Durbin2ad6bd22013-05-10 00:45:37 -0500185 memcpy(new_cons_p->buffer_body + cursor, old_cons_p->buffer_body,
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700186 copy_size);
187
188 cursor += copy_size;
189
Kyösti Mälkkia3c5ba32013-11-27 17:51:31 +0200190 if (dropped_chars) {
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700191 const char loss_str1[] = "\n\n*** Log truncated, ";
192 const char loss_str2[] = " characters dropped. ***\n\n";
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700193
194 /*
195 * When running from ROM sprintf is not available, a simple
196 * itoa implementation is used instead.
197 */
198 int got_first_digit = 0;
199
200 /* Way more than possible number of dropped characters. */
201 u32 mult = 100000;
202
203 strcpy((char *)new_cons_p->buffer_body + cursor, loss_str1);
204 cursor += sizeof(loss_str1) - 1;
205
206 while (mult) {
207 int digit = dropped_chars / mult;
208 if (got_first_digit || digit) {
209 new_cons_p->buffer_body[cursor++] = digit + '0';
210 dropped_chars %= mult;
211 /* Excessive, but keeps it simple */
212 got_first_digit = 1;
213 }
214 mult /= 10;
215 }
216
217 strcpy((char *)new_cons_p->buffer_body + cursor, loss_str2);
218 cursor += sizeof(loss_str2) - 1;
219 }
220 new_cons_p->buffer_cursor = cursor;
221}
222
Aaron Durbin41607a42015-06-09 13:54:10 -0500223static void cbmemc_reinit(int is_recovery)
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700224{
Aaron Durbind70bf7c2015-04-20 15:24:19 -0500225 struct cbmem_console *cbm_cons_p;
226 const size_t size = CONFIG_CONSOLE_CBMEM_BUFFER_SIZE;
Kyösti Mälkki8659e402014-12-21 08:55:47 +0200227 int flags = CBMEMC_APPEND;
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700228
Aaron Durbind70bf7c2015-04-20 15:24:19 -0500229 /* No appending when no preram console available and adding for
230 * the first time. */
231 if (!ENV_RAMSTAGE && _preram_cbmem_console_size == 0)
232 flags = CBMEMC_RESET;
Kyösti Mälkki71b21452014-11-28 10:13:03 +0200233
Aaron Durbind70bf7c2015-04-20 15:24:19 -0500234 /* Need to reset the newly added cbmem console in romstage. */
235 if (ENV_ROMSTAGE)
Kyösti Mälkki8659e402014-12-21 08:55:47 +0200236 flags |= CBMEMC_RESET;
Kyösti Mälkki52a27222013-09-09 01:31:22 +0300237
Aaron Durbind70bf7c2015-04-20 15:24:19 -0500238 /* Need to reset the newly added cbmem console in ramstage
239 * when there was no console in preram environment. */
Kyösti Mälkki1de648e2015-05-26 06:46:41 +0300240 if (ENV_RAMSTAGE && IS_ENABLED(CONFIG_LATE_CBMEM_INIT))
241 flags |= CBMEMC_RESET;
Aaron Durbind70bf7c2015-04-20 15:24:19 -0500242
243 /* If CBMEM entry already existed, old contents is not altered. */
244 cbm_cons_p = cbmem_add(CBMEM_ID_CONSOLE, size);
245
246 init_console_ptr(cbm_cons_p, size, flags);
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700247}
Kyösti Mälkki4fbac462015-01-07 04:48:43 +0200248ROMSTAGE_CBMEM_INIT_HOOK(cbmemc_reinit)
249RAMSTAGE_CBMEM_INIT_HOOK(cbmemc_reinit)
Aaron Durbind70bf7c2015-04-20 15:24:19 -0500250
Vadim Bendebury6e20e2f2015-04-10 18:04:04 -0700251#if IS_ENABLED(CONFIG_CONSOLE_CBMEM_DUMP_TO_UART)
252void cbmem_dump_console(void)
253{
254 struct cbmem_console *cbm_cons_p;
255 int cursor;
256
257 cbm_cons_p = current_console();
258 if (!cbm_cons_p)
259 return;
260
261 uart_init(0);
262 for (cursor = 0; cursor < cbm_cons_p->buffer_cursor; cursor++)
263 uart_tx_byte(0, cbm_cons_p->buffer_body[cursor]);
264}
265#endif