blob: d8fc7a952c735ceda10250bd95df43d251f5d59e [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.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
Patrick Georgib890a122015-03-26 15:17:45 +010017 * Foundation, Inc.
Vadim Bendebury32da8be2011-09-29 17:27:15 -070018 */
19
20#include <console/console.h>
Kyösti Mälkki1d7541f2014-02-17 21:34:42 +020021#include <console/cbmem_console.h>
Vadim Bendebury6e20e2f2015-04-10 18:04:04 -070022#include <console/uart.h>
Vadim Bendebury32da8be2011-09-29 17:27:15 -070023#include <cbmem.h>
Stefan Reinauerfd4f4132013-06-19 12:25:44 -070024#include <arch/early_variables.h>
Julius Wernerec5e5e02014-08-20 15:29:56 -070025#include <symbols.h>
Vadim Bendebury32da8be2011-09-29 17:27:15 -070026#include <string.h>
27
28/*
29 * Structure describing console buffer. It is overlaid on a flat memory area,
Martin Rothcbf2bd72013-07-09 21:51:14 -060030 * with buffer_body covering the extent of the memory. Once the buffer is
Vadim Bendebury32da8be2011-09-29 17:27:15 -070031 * full, the cursor keeps going but the data is dropped on the floor. This
32 * allows to tell how much data was lost in the process.
33 */
34struct cbmem_console {
35 u32 buffer_size;
36 u32 buffer_cursor;
37 u8 buffer_body[0];
38} __attribute__ ((__packed__));
39
Aaron Durbin2ad6bd22013-05-10 00:45:37 -050040static struct cbmem_console *cbmem_console_p CAR_GLOBAL;
41
Kyösti Mälkki8659e402014-12-21 08:55:47 +020042static void copy_console_buffer(struct cbmem_console *old_cons_p,
43 struct cbmem_console *new_cons_p);
44
Vadim Bendebury32da8be2011-09-29 17:27:15 -070045#ifdef __PRE_RAM__
46/*
47 * While running from ROM, before DRAM is initialized, some area in cache as
48 * ram space is used for the console buffer storage. The size and location of
Julius Wernerec5e5e02014-08-20 15:29:56 -070049 * the area are defined by the linker script with _(e)preram_cbmem_console.
Vadim Bendebury32da8be2011-09-29 17:27:15 -070050 */
Gabe Black19e7e7d2011-10-01 04:27:32 -070051
Vadim Bendebury32da8be2011-09-29 17:27:15 -070052#else
53
54/*
55 * When running from RAM, a lot of console output is generated before CBMEM is
56 * reinitialized. This static buffer is used to store that output temporarily,
57 * to be concatenated with the CBMEM console buffer contents accumulated
58 * during the ROM stage, once CBMEM becomes available at RAM stage.
59 */
Stefan Reinauer19d06b22013-09-09 11:22:18 -070060
Kyösti Mälkki2fb6b402014-12-19 08:20:45 +020061#if IS_ENABLED(CONFIG_EARLY_CBMEM_INIT)
Stefan Reinauer19d06b22013-09-09 11:22:18 -070062#define STATIC_CONSOLE_SIZE 1024
63#else
Kyösti Mälkki44c0e4e2013-11-27 22:54:11 +020064#define STATIC_CONSOLE_SIZE CONFIG_CONSOLE_CBMEM_BUFFER_SIZE
Stefan Reinauer19d06b22013-09-09 11:22:18 -070065#endif
66static u8 static_console[STATIC_CONSOLE_SIZE];
Vadim Bendebury32da8be2011-09-29 17:27:15 -070067#endif
68
Kyösti Mälkki8659e402014-12-21 08:55:47 +020069/* flags for init */
70#define CBMEMC_RESET (1<<0)
71#define CBMEMC_APPEND (1<<1)
72
Aaron Durbin2ad6bd22013-05-10 00:45:37 -050073static inline struct cbmem_console *current_console(void)
Vadim Bendebury32da8be2011-09-29 17:27:15 -070074{
Kyösti Mälkkie4554252014-12-31 18:34:59 +020075 return car_sync_var(cbmem_console_p);
Aaron Durbin2ad6bd22013-05-10 00:45:37 -050076}
77
78static inline void current_console_set(struct cbmem_console *new_console_p)
79{
Aaron Durbin2ad6bd22013-05-10 00:45:37 -050080 car_set_var(cbmem_console_p, new_console_p);
Aaron Durbin2ad6bd22013-05-10 00:45:37 -050081}
82
Kyösti Mälkki8659e402014-12-21 08:55:47 +020083static inline void init_console_ptr(void *storage, u32 total_space, int flags)
Aaron Durbin2ad6bd22013-05-10 00:45:37 -050084{
85 struct cbmem_console *cbm_cons_p = storage;
86
Aaron Durbind70bf7c2015-04-20 15:24:19 -050087 if (!cbm_cons_p || total_space == 0) {
Kyösti Mälkki8659e402014-12-21 08:55:47 +020088 current_console_set(NULL);
89 return;
90 }
91
92 if (flags & CBMEMC_RESET) {
93 cbm_cons_p->buffer_size = total_space - sizeof(struct cbmem_console);
94 cbm_cons_p->buffer_cursor = 0;
95 }
96 if (flags & CBMEMC_APPEND) {
97 struct cbmem_console *tmp_cons_p = current_console();
98 if (tmp_cons_p)
99 copy_console_buffer(tmp_cons_p, cbm_cons_p);
100 }
101
102 current_console_set(cbm_cons_p);
Aaron Durbin2ad6bd22013-05-10 00:45:37 -0500103}
104
105void cbmemc_init(void)
106{
107#ifdef __PRE_RAM__
Aaron Durbin079fb3f2015-10-07 15:41:45 -0500108 int flags = 0;
Kyösti Mälkki8659e402014-12-21 08:55:47 +0200109
Aaron Durbin079fb3f2015-10-07 15:41:45 -0500110 /* If in bootblock always initialize the console first. */
111 if (ENV_BOOTBLOCK)
112 flags = CBMEMC_RESET;
113 else if (ENV_ROMSTAGE) {
114 /* Initialize console for the first time in romstage when
115 * there's no prior stage that initialized it first. */
116 if (!IS_ENABLED(CONFIG_VBOOT_STARTS_IN_BOOTBLOCK) &&
117 !IS_ENABLED(CONFIG_BOOTBLOCK_CONSOLE))
118 flags = CBMEMC_RESET;
119 } else if (ENV_VERSTAGE) {
120 /*
121 * Initialize console for the first time in verstage when
122 * there is no console in bootblock. Otherwise honor the
123 * bootblock console when verstage comes right after
124 * bootblock.
125 */
126 if (IS_ENABLED(CONFIG_VBOOT_STARTS_IN_BOOTBLOCK) &&
127 !IS_ENABLED(CONFIG_BOOTBLOCK_CONSOLE))
128 flags = CBMEMC_RESET;
129 }
Kyösti Mälkki8659e402014-12-21 08:55:47 +0200130
Julius Wernerec5e5e02014-08-20 15:29:56 -0700131 init_console_ptr(_preram_cbmem_console,
132 _preram_cbmem_console_size, flags);
Aaron Durbin2ad6bd22013-05-10 00:45:37 -0500133#else
134 /*
135 * Initializing before CBMEM is available, use static buffer to store
136 * the log.
137 */
Kyösti Mälkki8659e402014-12-21 08:55:47 +0200138 init_console_ptr(static_console, sizeof(static_console), CBMEMC_RESET);
Aaron Durbin2ad6bd22013-05-10 00:45:37 -0500139#endif
140}
141
142void cbmemc_tx_byte(unsigned char data)
143{
144 struct cbmem_console *cbm_cons_p = current_console();
145 u32 cursor;
146
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700147 if (!cbm_cons_p)
148 return;
149
150 cursor = cbm_cons_p->buffer_cursor++;
151 if (cursor < cbm_cons_p->buffer_size)
152 cbm_cons_p->buffer_body[cursor] = data;
153}
154
155/*
156 * Copy the current console buffer (either from the cache as RAM area, or from
157 * the static buffer, pointed at by cbmem_console_p) into the CBMEM console
158 * buffer space (pointed at by new_cons_p), concatenating the copied data with
159 * the CBMEM console buffer contents.
160 *
161 * If there is overflow - add to the destination area a string, reporting the
Martin Rothcbf2bd72013-07-09 21:51:14 -0600162 * overflow and the number of dropped characters.
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700163 */
Kyösti Mälkki8659e402014-12-21 08:55:47 +0200164static void copy_console_buffer(struct cbmem_console *old_cons_p,
165 struct cbmem_console *new_cons_p)
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700166{
Kyösti Mälkkia3c5ba32013-11-27 17:51:31 +0200167 u32 copy_size, dropped_chars;
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700168 u32 cursor = new_cons_p->buffer_cursor;
Aaron Durbin2ad6bd22013-05-10 00:45:37 -0500169
Kyösti Mälkkia3c5ba32013-11-27 17:51:31 +0200170 if (old_cons_p->buffer_cursor < old_cons_p->buffer_size)
171 copy_size = old_cons_p->buffer_cursor;
172 else
173 copy_size = old_cons_p->buffer_size;
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700174
Kyösti Mälkkia3c5ba32013-11-27 17:51:31 +0200175 if (cursor > new_cons_p->buffer_size)
176 copy_size = 0;
177 else if (cursor + copy_size > new_cons_p->buffer_size)
178 copy_size = new_cons_p->buffer_size - cursor;
179
180 dropped_chars = old_cons_p->buffer_cursor - copy_size;
181 if (dropped_chars) {
182 /* Reserve 80 chars to report overflow, if possible. */
183 if (copy_size < 80)
184 return;
185 copy_size -= 80;
186 dropped_chars += 80;
187 }
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700188
Aaron Durbin2ad6bd22013-05-10 00:45:37 -0500189 memcpy(new_cons_p->buffer_body + cursor, old_cons_p->buffer_body,
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700190 copy_size);
191
192 cursor += copy_size;
193
Kyösti Mälkkia3c5ba32013-11-27 17:51:31 +0200194 if (dropped_chars) {
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700195 const char loss_str1[] = "\n\n*** Log truncated, ";
196 const char loss_str2[] = " characters dropped. ***\n\n";
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700197
198 /*
199 * When running from ROM sprintf is not available, a simple
200 * itoa implementation is used instead.
201 */
202 int got_first_digit = 0;
203
204 /* Way more than possible number of dropped characters. */
205 u32 mult = 100000;
206
207 strcpy((char *)new_cons_p->buffer_body + cursor, loss_str1);
208 cursor += sizeof(loss_str1) - 1;
209
210 while (mult) {
211 int digit = dropped_chars / mult;
212 if (got_first_digit || digit) {
213 new_cons_p->buffer_body[cursor++] = digit + '0';
214 dropped_chars %= mult;
215 /* Excessive, but keeps it simple */
216 got_first_digit = 1;
217 }
218 mult /= 10;
219 }
220
221 strcpy((char *)new_cons_p->buffer_body + cursor, loss_str2);
222 cursor += sizeof(loss_str2) - 1;
223 }
224 new_cons_p->buffer_cursor = cursor;
225}
226
Aaron Durbin41607a42015-06-09 13:54:10 -0500227static void cbmemc_reinit(int is_recovery)
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700228{
Aaron Durbind70bf7c2015-04-20 15:24:19 -0500229 struct cbmem_console *cbm_cons_p;
230 const size_t size = CONFIG_CONSOLE_CBMEM_BUFFER_SIZE;
Kyösti Mälkki8659e402014-12-21 08:55:47 +0200231 int flags = CBMEMC_APPEND;
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700232
Aaron Durbind70bf7c2015-04-20 15:24:19 -0500233 /* No appending when no preram console available and adding for
234 * the first time. */
235 if (!ENV_RAMSTAGE && _preram_cbmem_console_size == 0)
236 flags = CBMEMC_RESET;
Kyösti Mälkki71b21452014-11-28 10:13:03 +0200237
Aaron Durbind70bf7c2015-04-20 15:24:19 -0500238 /* Need to reset the newly added cbmem console in romstage. */
239 if (ENV_ROMSTAGE)
Kyösti Mälkki8659e402014-12-21 08:55:47 +0200240 flags |= CBMEMC_RESET;
Kyösti Mälkki52a27222013-09-09 01:31:22 +0300241
Aaron Durbind70bf7c2015-04-20 15:24:19 -0500242 /* Need to reset the newly added cbmem console in ramstage
243 * when there was no console in preram environment. */
Kyösti Mälkki1de648e2015-05-26 06:46:41 +0300244 if (ENV_RAMSTAGE && IS_ENABLED(CONFIG_LATE_CBMEM_INIT))
245 flags |= CBMEMC_RESET;
Aaron Durbind70bf7c2015-04-20 15:24:19 -0500246
247 /* If CBMEM entry already existed, old contents is not altered. */
248 cbm_cons_p = cbmem_add(CBMEM_ID_CONSOLE, size);
249
250 init_console_ptr(cbm_cons_p, size, flags);
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700251}
Kyösti Mälkki4fbac462015-01-07 04:48:43 +0200252ROMSTAGE_CBMEM_INIT_HOOK(cbmemc_reinit)
253RAMSTAGE_CBMEM_INIT_HOOK(cbmemc_reinit)
Aaron Durbind70bf7c2015-04-20 15:24:19 -0500254
Vadim Bendebury6e20e2f2015-04-10 18:04:04 -0700255#if IS_ENABLED(CONFIG_CONSOLE_CBMEM_DUMP_TO_UART)
256void cbmem_dump_console(void)
257{
258 struct cbmem_console *cbm_cons_p;
259 int cursor;
260
261 cbm_cons_p = current_console();
262 if (!cbm_cons_p)
263 return;
264
265 uart_init(0);
266 for (cursor = 0; cursor < cbm_cons_p->buffer_cursor; cursor++)
267 uart_tx_byte(0, cbm_cons_p->buffer_body[cursor]);
268}
269#endif