summaryrefslogtreecommitdiffstats
path: root/lib/console.c
blob: 4156feed51d9a4dd9d163c73b25fb39f397ec44e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
#include <types.h>
#include <cpu.h>
#include <console.h>
#include <globalvars.h>
#include <uart8250.h>
#include <stdarg.h>
#include <string.h>
#include <globalvars.h>
#include <build.h>

int vtxprintf(void (*)(unsigned char, void *arg), 
		void *arg, const char *, va_list);

/**
 * set the console log level
 * There are no invalid settings, although there are ones that 
 * do not make much sense. 
 *
 * @param level The new level
 */
void set_loglevel(unsigned int level) {
	if (level > BIOS_SPEW)
		printk(BIOS_ALWAYS, "Warning: ridiculous log level setting: %d (max %d)\n", 
			level, BIOS_SPEW);
	global_vars()->loglevel = level;
}

/**
 * get the console log level
 *
 * @return The level
 */
static unsigned int console_loglevel(void)
{
	return global_vars()->loglevel;
}

void console_loglevel_init(void)
{
	set_loglevel(CONFIG_DEFAULT_CONSOLE_LOGLEVEL);
}

#ifdef CONFIG_CONSOLE_BUFFER
struct printk_buffer *printk_buffer_addr(void)
{
	return global_vars()->printk_buffer;
}

void printk_buffer_move(void *newaddr, int newsize)
{
	struct printk_buffer *oldbuf, *newbuf;
	int copylen;
	oldbuf = printk_buffer_addr();
	newbuf = newaddr;
	newbuf->len = newsize;
	newbuf->readoffset = 0;
	/* Check for wraparound */
	if (oldbuf->writeoffset < oldbuf->readoffset) {
		/* Copy from readoffset to end of buffer. */
		copylen = oldbuf->len - oldbuf->readoffset;
	} else {
		/* Copy from readoffset to writeoffset (exclusive).*/
		copylen = oldbuf->writeoffset - oldbuf->readoffset;
	}
	if (copylen > newsize)
		copylen = newsize;
	/* If memcpy() ever uses printk we will see pretty explosions. */
	memcpy(&newbuf->buffer[0], &oldbuf->buffer[oldbuf->readoffset],
		copylen);
	newbuf->writeoffset = copylen;
	/* Check for wraparound */
	if (oldbuf->writeoffset < oldbuf->readoffset) {
		/* Copy from start of buffer to writeoffset (exclusive). */
		copylen = (copylen + oldbuf->writeoffset > newsize)
				? newsize - copylen : oldbuf->writeoffset;
		memcpy(&newbuf->buffer[newbuf->writeoffset],
			&oldbuf->buffer[0], copylen);
		newbuf->writeoffset += copylen;
	}
	global_vars()->printk_buffer = newbuf;
	return;
}

void printk_buffer_init(void)
{
	struct printk_buffer *buf = (struct printk_buffer *)PRINTK_BUF_ADDR_CAR;
	global_vars()->printk_buffer = buf;
	buf = printk_buffer_addr();
	buf->len = PRINTK_BUF_SIZE_CAR - sizeof(struct printk_buffer);
	buf->readoffset = 0;
	buf->writeoffset = 0;
	return;
}

void buffer_tx_byte(unsigned char byte, void *arg)
{
	struct printk_buffer *buf = printk_buffer_addr();
	buf->buffer[buf->writeoffset++] = byte;
	buf->writeoffset %= buf->len;
	/* Make sure writeoffset is always ahead of readoffset here. */
	if (buf->writeoffset == buf->readoffset) {
		buf->readoffset++;
		buf->readoffset %= buf->len;
	}
	return;
}
#endif

void console_tx_byte(unsigned char byte, void *arg)
{
#ifdef CONFIG_CONSOLE_BUFFER
	buffer_tx_byte(byte, arg);
#endif
#ifdef CONFIG_CONSOLE_SERIAL
	if (byte == '\n') {
		uart8250_tx_byte(TTYSx_BASE, '\r');
#ifdef CONFIG_CONSOLE_PREFIX
		uart8250_tx_byte(TTYSx_BASE, '\n');
		uart8250_tx_byte(TTYSx_BASE, '(');
		uart8250_tx_byte(TTYSx_BASE, 'C');
		uart8250_tx_byte(TTYSx_BASE, 'B');
		uart8250_tx_byte(TTYSx_BASE, ')');
		uart8250_tx_byte(TTYSx_BASE, ' ');
		return;
#endif
	}
	uart8250_tx_byte(TTYSx_BASE, byte);
#endif
}

int printk(int msg_level, const char *fmt, ...)
{
	va_list args;
	int i;

	if (msg_level > console_loglevel()) {
		return 0;
	}

#ifdef CONFIG_CONSOLE_PREPEND_LOG_LEVEL
	console_tx_byte('<', (void *)0);
	console_tx_byte(msg_level + '0', (void *)0);
	console_tx_byte('>', (void *)0);

	i = 3;
#else
	i = 0;
#endif

	va_start(args, fmt);
	i += vtxprintf(console_tx_byte, (void *)0, fmt, args);
	va_end(args);

#ifdef CONFIG_CHECK_STACK_USAGE
	check_stack();
#endif
	return i;
}

/**
 * Print a nice banner so we know what step we died on. 
 *
 * @param level The printk level (e.g. BIOS_EMERG)
 * @param s String to put in the middle of the banner
 */

void banner(int level, const char *s)
{
	int i;
	/* 10 = signs and a space. */
	printk(level, "========== ");
	for(i = 11; *s; i++, s++)
		printk(level, "%c", *s);
	/* trailing space */
	printk(level, " ");
	i++;
	/* fill it up to 80 columns */
	for(;  i < 80; i++)
		printk(level, "=");
	printk(level, "\n");
}

void console_init(void)
{
	static const char console_test[] =
		"\n\ncoreboot-"
		COREBOOT_VERSION
		COREBOOT_EXTRA_VERSION
		" "
		COREBOOT_BUILD
		" starting... (console_loglevel=%d)\n";

	printk(BIOS_ALWAYS, console_test, console_loglevel());
}

/**
 *  Halt and loop due to a fatal error.
 *  There have been several iterations of this function.
 *  The first simply did a hlt(). Doing a hlt() can make jtag debugging
 *  very difficult as one can not break into a hlt instruction on some CPUs.
 *  Second was to do a console_tx_byte of a NULL character.
 *  A number of concerns were raised about doing this idea.
 *  Third idea was to do an inb from port 0x80, the POST port. That design
 *  makes us very CPU-specific.
 *  The fourth idea was just POSTING the same
 *  code over and over. That would erase the most recent POST code,
 *  hindering diagnosis.
 *
 *  For now, for lack of a better alternative,
 *  we will call console_tx_byte ten times and then halt.
 *  Some CPU JTAG debbuggers might have problems but it is the right thing
 *  to do. We call with a NULL since it will clear any FIFOs in the path and
 *  won't clutter up the output, since NULL doesn't print a visible character
 *  on most terminal emulators.
 *
 *  @param str A string to print for the error
 *
 */
void die(const char *str)
{
	int i;

	printk(BIOS_EMERG, str);

	while (1) {
		for (i = 0; i < 10; i++)
			console_tx_byte(0, (void *)0);
		hlt();
	}
}