blob: 48664543fcce24050fc5aefcdaf30ac84f1b345d [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__
Kyösti Mälkki8659e402014-12-21 08:55:47 +0200108 int flags = CBMEMC_RESET;
109
110 /* Do not clear output from bootblock. */
111 if (ENV_ROMSTAGE && !IS_ENABLED(CONFIG_CACHE_AS_RAM))
112 if (IS_ENABLED(CONFIG_BOOTBLOCK_CONSOLE))
113 flags = 0;
114
Julius Wernerec5e5e02014-08-20 15:29:56 -0700115 init_console_ptr(_preram_cbmem_console,
116 _preram_cbmem_console_size, flags);
Aaron Durbin2ad6bd22013-05-10 00:45:37 -0500117#else
118 /*
119 * Initializing before CBMEM is available, use static buffer to store
120 * the log.
121 */
Kyösti Mälkki8659e402014-12-21 08:55:47 +0200122 init_console_ptr(static_console, sizeof(static_console), CBMEMC_RESET);
Aaron Durbin2ad6bd22013-05-10 00:45:37 -0500123#endif
124}
125
126void cbmemc_tx_byte(unsigned char data)
127{
128 struct cbmem_console *cbm_cons_p = current_console();
129 u32 cursor;
130
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700131 if (!cbm_cons_p)
132 return;
133
134 cursor = cbm_cons_p->buffer_cursor++;
135 if (cursor < cbm_cons_p->buffer_size)
136 cbm_cons_p->buffer_body[cursor] = data;
137}
138
139/*
140 * Copy the current console buffer (either from the cache as RAM area, or from
141 * the static buffer, pointed at by cbmem_console_p) into the CBMEM console
142 * buffer space (pointed at by new_cons_p), concatenating the copied data with
143 * the CBMEM console buffer contents.
144 *
145 * If there is overflow - add to the destination area a string, reporting the
Martin Rothcbf2bd72013-07-09 21:51:14 -0600146 * overflow and the number of dropped characters.
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700147 */
Kyösti Mälkki8659e402014-12-21 08:55:47 +0200148static void copy_console_buffer(struct cbmem_console *old_cons_p,
149 struct cbmem_console *new_cons_p)
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700150{
Kyösti Mälkkia3c5ba32013-11-27 17:51:31 +0200151 u32 copy_size, dropped_chars;
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700152 u32 cursor = new_cons_p->buffer_cursor;
Aaron Durbin2ad6bd22013-05-10 00:45:37 -0500153
Kyösti Mälkkia3c5ba32013-11-27 17:51:31 +0200154 if (old_cons_p->buffer_cursor < old_cons_p->buffer_size)
155 copy_size = old_cons_p->buffer_cursor;
156 else
157 copy_size = old_cons_p->buffer_size;
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700158
Kyösti Mälkkia3c5ba32013-11-27 17:51:31 +0200159 if (cursor > new_cons_p->buffer_size)
160 copy_size = 0;
161 else if (cursor + copy_size > new_cons_p->buffer_size)
162 copy_size = new_cons_p->buffer_size - cursor;
163
164 dropped_chars = old_cons_p->buffer_cursor - copy_size;
165 if (dropped_chars) {
166 /* Reserve 80 chars to report overflow, if possible. */
167 if (copy_size < 80)
168 return;
169 copy_size -= 80;
170 dropped_chars += 80;
171 }
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700172
Aaron Durbin2ad6bd22013-05-10 00:45:37 -0500173 memcpy(new_cons_p->buffer_body + cursor, old_cons_p->buffer_body,
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700174 copy_size);
175
176 cursor += copy_size;
177
Kyösti Mälkkia3c5ba32013-11-27 17:51:31 +0200178 if (dropped_chars) {
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700179 const char loss_str1[] = "\n\n*** Log truncated, ";
180 const char loss_str2[] = " characters dropped. ***\n\n";
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700181
182 /*
183 * When running from ROM sprintf is not available, a simple
184 * itoa implementation is used instead.
185 */
186 int got_first_digit = 0;
187
188 /* Way more than possible number of dropped characters. */
189 u32 mult = 100000;
190
191 strcpy((char *)new_cons_p->buffer_body + cursor, loss_str1);
192 cursor += sizeof(loss_str1) - 1;
193
194 while (mult) {
195 int digit = dropped_chars / mult;
196 if (got_first_digit || digit) {
197 new_cons_p->buffer_body[cursor++] = digit + '0';
198 dropped_chars %= mult;
199 /* Excessive, but keeps it simple */
200 got_first_digit = 1;
201 }
202 mult /= 10;
203 }
204
205 strcpy((char *)new_cons_p->buffer_body + cursor, loss_str2);
206 cursor += sizeof(loss_str2) - 1;
207 }
208 new_cons_p->buffer_cursor = cursor;
209}
210
Kyösti Mälkki4fbac462015-01-07 04:48:43 +0200211static void cbmemc_reinit(void)
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700212{
Aaron Durbind70bf7c2015-04-20 15:24:19 -0500213 struct cbmem_console *cbm_cons_p;
214 const size_t size = CONFIG_CONSOLE_CBMEM_BUFFER_SIZE;
Kyösti Mälkki8659e402014-12-21 08:55:47 +0200215 int flags = CBMEMC_APPEND;
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700216
Aaron Durbind70bf7c2015-04-20 15:24:19 -0500217 /* No appending when no preram console available and adding for
218 * the first time. */
219 if (!ENV_RAMSTAGE && _preram_cbmem_console_size == 0)
220 flags = CBMEMC_RESET;
Kyösti Mälkki71b21452014-11-28 10:13:03 +0200221
Aaron Durbind70bf7c2015-04-20 15:24:19 -0500222 /* Need to reset the newly added cbmem console in romstage. */
223 if (ENV_ROMSTAGE)
Kyösti Mälkki8659e402014-12-21 08:55:47 +0200224 flags |= CBMEMC_RESET;
Kyösti Mälkki52a27222013-09-09 01:31:22 +0300225
Aaron Durbind70bf7c2015-04-20 15:24:19 -0500226 /* Need to reset the newly added cbmem console in ramstage
227 * when there was no console in preram environment. */
Kyösti Mälkki1de648e2015-05-26 06:46:41 +0300228 if (ENV_RAMSTAGE && IS_ENABLED(CONFIG_LATE_CBMEM_INIT))
229 flags |= CBMEMC_RESET;
Aaron Durbind70bf7c2015-04-20 15:24:19 -0500230
231 /* If CBMEM entry already existed, old contents is not altered. */
232 cbm_cons_p = cbmem_add(CBMEM_ID_CONSOLE, size);
233
234 init_console_ptr(cbm_cons_p, size, flags);
Vadim Bendebury32da8be2011-09-29 17:27:15 -0700235}
Kyösti Mälkki4fbac462015-01-07 04:48:43 +0200236ROMSTAGE_CBMEM_INIT_HOOK(cbmemc_reinit)
237RAMSTAGE_CBMEM_INIT_HOOK(cbmemc_reinit)
Aaron Durbind70bf7c2015-04-20 15:24:19 -0500238
Vadim Bendebury6e20e2f2015-04-10 18:04:04 -0700239#if IS_ENABLED(CONFIG_CONSOLE_CBMEM_DUMP_TO_UART)
240void cbmem_dump_console(void)
241{
242 struct cbmem_console *cbm_cons_p;
243 int cursor;
244
245 cbm_cons_p = current_console();
246 if (!cbm_cons_p)
247 return;
248
249 uart_init(0);
250 for (cursor = 0; cursor < cbm_cons_p->buffer_cursor; cursor++)
251 uart_tx_byte(0, cbm_cons_p->buffer_body[cursor]);
252}
253#endif