blob: da585b4f35b18f9266cb17ea93db4bf4831b0d15 [file] [log] [blame]
Kevin O'Connor65e63422008-07-19 14:12:32 -04001// Raw screen writing and debug output code.
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05002//
3// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
4//
Kevin O'Connorb1b7c2a2009-01-15 20:52:58 -05005// This file may be distributed under the terms of the GNU LGPLv3 license.
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05006
7#include <stdarg.h> // va_list
8
9#include "farptr.h" // GET_VAR
Kevin O'Connor567e4e32008-04-05 11:37:51 -040010#include "util.h" // printf
Kevin O'Connor9521e262008-07-04 13:04:29 -040011#include "bregs.h" // struct bregs
12#include "config.h" // CONFIG_*
Kevin O'Connor15157a32008-12-13 11:10:37 -050013#include "biosvar.h" // GET_GLOBAL
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050014
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040015#define DEBUG_PORT PORT_SERIAL1
Kevin O'Connor5c732402008-06-07 10:43:07 -040016#define DEBUG_TIMEOUT 100000
17
Kevin O'Connor61d6b062008-06-21 12:15:10 -040018void
19debug_serial_setup()
20{
21 if (!CONFIG_DEBUG_SERIAL)
22 return;
23 // setup for serial logging: 8N1
24 u8 oldparam, newparam = 0x03;
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040025 oldparam = inb(DEBUG_PORT+SEROFF_LCR);
26 outb(newparam, DEBUG_PORT+SEROFF_LCR);
Kevin O'Connor61d6b062008-06-21 12:15:10 -040027 // Disable irqs
28 u8 oldier, newier = 0;
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040029 oldier = inb(DEBUG_PORT+SEROFF_IER);
30 outb(newier, DEBUG_PORT+SEROFF_IER);
Kevin O'Connor61d6b062008-06-21 12:15:10 -040031
32 if (oldparam != newparam || oldier != newier)
33 dprintf(1, "Changing serial settings was %x/%x now %x/%x\n"
34 , oldparam, oldier, newparam, newier);
35}
36
Kevin O'Connor65e63422008-07-19 14:12:32 -040037// Write a character to the serial port.
Kevin O'Connor5c732402008-06-07 10:43:07 -040038static void
39debug_serial(char c)
40{
Kevin O'Connor3e832bb2009-05-15 22:22:12 -040041 if (!CONFIG_DEBUG_SERIAL)
42 return;
Kevin O'Connor5c732402008-06-07 10:43:07 -040043 int timeout = DEBUG_TIMEOUT;
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040044 while ((inb(DEBUG_PORT+SEROFF_LSR) & 0x60) != 0x60)
Kevin O'Connor5c732402008-06-07 10:43:07 -040045 if (!timeout--)
46 // Ran out of time.
47 return;
Kevin O'Connorc151b3b2009-05-12 22:59:41 -040048 outb(c, DEBUG_PORT+SEROFF_DATA);
Kevin O'Connor5c732402008-06-07 10:43:07 -040049}
50
Kevin O'Connorb9e62d22009-08-02 13:18:27 -040051// Make sure all serial port writes have been completely sent.
Kevin O'Connor3e832bb2009-05-15 22:22:12 -040052static void
53debug_serial_flush()
54{
55 if (!CONFIG_DEBUG_SERIAL)
56 return;
57 int timeout = DEBUG_TIMEOUT;
58 while ((inb(DEBUG_PORT+SEROFF_LSR) & 0x40) != 0x40)
59 if (!timeout--)
60 // Ran out of time.
61 return;
62}
63
Kevin O'Connor65e63422008-07-19 14:12:32 -040064// Show a character on the screen.
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050065static void
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050066screenc(u8 c)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050067{
Kevin O'Connor65e63422008-07-19 14:12:32 -040068 if (MODE16)
69 // printf is only used in 32bit code.
70 return;
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050071 struct bregs br;
72 memset(&br, 0, sizeof(br));
Kevin O'Connorf8e800d2009-09-24 21:01:16 -040073 br.flags = F_IF;
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050074 br.ah = 0x0e;
75 br.al = c;
76 call16_int(0x10, &br);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050077}
78
Kevin O'Connor65e63422008-07-19 14:12:32 -040079// Output a character.
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050080static void
81putc(u16 action, char c)
82{
Kevin O'Connorb9e62d22009-08-02 13:18:27 -040083 if (CONFIG_DEBUG_LEVEL && (CONFIG_SCREEN_AND_DEBUG || !action)) {
Kevin O'Connorac8df8c2008-05-24 23:46:33 -040084 if (! CONFIG_COREBOOT)
85 // Send character to debug port.
86 outb(c, PORT_BIOS_DEBUG);
Kevin O'Connor3e832bb2009-05-15 22:22:12 -040087 // Send character to serial port.
88 if (c == '\n')
89 debug_serial('\r');
90 debug_serial(c);
Kevin O'Connorac8df8c2008-05-24 23:46:33 -040091 }
Kevin O'Connor1812e202008-05-07 21:29:50 -040092
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050093 if (action) {
Kevin O'Connor1812e202008-05-07 21:29:50 -040094 // Send character to video screen.
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050095 if (c == '\n')
96 screenc('\r');
97 screenc(c);
98 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050099}
100
Kevin O'Connor65e63422008-07-19 14:12:32 -0400101// Ouptut a string.
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500102static void
103puts(u16 action, const char *s)
104{
105 for (; *s; s++)
106 putc(action, *s);
107}
108
Kevin O'Connor65e63422008-07-19 14:12:32 -0400109// Output a string that is in the CS segment.
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500110static void
111puts_cs(u16 action, const char *s)
112{
113 for (;; s++) {
Kevin O'Connor15157a32008-12-13 11:10:37 -0500114 char c = GET_GLOBAL(*(u8*)s);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500115 if (!c)
116 break;
117 putc(action, c);
118 }
119}
120
Kevin O'Connor65e63422008-07-19 14:12:32 -0400121// Output an unsigned integer.
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500122static void
123putuint(u16 action, u32 val)
124{
125 char buf[12];
126 char *d = &buf[sizeof(buf) - 1];
127 *d-- = '\0';
128 for (;;) {
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500129 *d = (val % 10) + '0';
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500130 val /= 10;
131 if (!val)
132 break;
133 d--;
134 }
135 puts(action, d);
136}
137
Kevin O'Connor65e63422008-07-19 14:12:32 -0400138// Output a single digit hex character.
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500139static inline void
140putsinglehex(u16 action, u32 val)
141{
142 if (val <= 9)
143 val = '0' + val;
144 else
145 val = 'a' + val - 10;
146 putc(action, val);
147}
148
Kevin O'Connor65e63422008-07-19 14:12:32 -0400149// Output an integer in hexadecimal.
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500150static void
Kevin O'Connor91b53a72009-05-05 22:52:09 -0400151puthex(u16 action, u32 val, int width)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500152{
Kevin O'Connor91b53a72009-05-05 22:52:09 -0400153 if (!width) {
154 u32 tmp = val;
155 width = 1;
156 if (tmp > 0xffff) {
157 width += 4;
158 tmp >>= 16;
159 }
160 if (tmp > 0xff) {
161 width += 2;
162 tmp >>= 8;
163 }
164 if (tmp > 0xf)
165 width += 1;
166 }
167
168 switch (width) {
169 default: putsinglehex(action, (val >> 28) & 0xf);
170 case 7: putsinglehex(action, (val >> 24) & 0xf);
171 case 6: putsinglehex(action, (val >> 20) & 0xf);
172 case 5: putsinglehex(action, (val >> 16) & 0xf);
173 case 4: putsinglehex(action, (val >> 12) & 0xf);
174 case 3: putsinglehex(action, (val >> 8) & 0xf);
175 case 2: putsinglehex(action, (val >> 4) & 0xf);
176 case 1: putsinglehex(action, (val >> 0) & 0xf);
177 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500178}
179
Kevin O'Connor7d0f08a2008-03-04 22:27:55 -0500180static inline int
181isdigit(u8 c)
182{
Kevin O'Connord6e4b4c2008-08-29 21:20:32 -0400183 return ((u8)(c - '0')) < 10;
Kevin O'Connor7d0f08a2008-03-04 22:27:55 -0500184}
185
Kevin O'Connor567e4e32008-04-05 11:37:51 -0400186static void
187bvprintf(u16 action, const char *fmt, va_list args)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500188{
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500189 const char *s = fmt;
190 for (;; s++) {
Kevin O'Connor15157a32008-12-13 11:10:37 -0500191 char c = GET_GLOBAL(*(u8*)s);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500192 if (!c)
193 break;
194 if (c != '%') {
195 putc(action, c);
196 continue;
197 }
198 const char *n = s+1;
Kevin O'Connor91b53a72009-05-05 22:52:09 -0400199 int field_width = 0;
Kevin O'Connor7d0f08a2008-03-04 22:27:55 -0500200 for (;;) {
Kevin O'Connor15157a32008-12-13 11:10:37 -0500201 c = GET_GLOBAL(*(u8*)n);
Kevin O'Connor7d0f08a2008-03-04 22:27:55 -0500202 if (!isdigit(c))
203 break;
Kevin O'Connor91b53a72009-05-05 22:52:09 -0400204 field_width = field_width * 10 + c - '0';
Kevin O'Connor7d0f08a2008-03-04 22:27:55 -0500205 n++;
206 }
Kevin O'Connora9096f42008-03-08 15:40:43 -0500207 if (c == 'l') {
208 // Ignore long format indicator
209 n++;
Kevin O'Connor15157a32008-12-13 11:10:37 -0500210 c = GET_GLOBAL(*(u8*)n);
Kevin O'Connora9096f42008-03-08 15:40:43 -0500211 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500212 s32 val;
213 const char *sarg;
214 switch (c) {
215 case '%':
216 putc(action, '%');
217 break;
218 case 'd':
219 val = va_arg(args, s32);
220 if (val < 0) {
221 putc(action, '-');
222 val = -val;
223 }
224 putuint(action, val);
225 break;
226 case 'u':
227 val = va_arg(args, s32);
228 putuint(action, val);
229 break;
Kevin O'Connorf06f03a2008-03-29 12:44:32 -0400230 case 'p':
Kevin O'Connor91b53a72009-05-05 22:52:09 -0400231 /* %p always has 0x prepended */
232 putc(action, '0');
233 putc(action, 'x');
234 field_width = 8;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500235 case 'x':
236 val = va_arg(args, s32);
Kevin O'Connor91b53a72009-05-05 22:52:09 -0400237 puthex(action, val, field_width);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500238 break;
Kevin O'Connor12dade52008-03-01 13:35:17 -0500239 case 'c':
240 val = va_arg(args, int);
241 putc(action, val);
242 break;
Kevin O'Connore0113c92008-04-05 15:51:12 -0400243 case '.':
244 // Hack to support "%.s" - meaning string on stack.
Kevin O'Connor15157a32008-12-13 11:10:37 -0500245 if (GET_GLOBAL(*(u8*)(n+1)) != 's')
Kevin O'Connore0113c92008-04-05 15:51:12 -0400246 break;
247 n++;
248 sarg = va_arg(args, const char *);
249 puts(action, sarg);
250 break;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500251 case 's':
252 sarg = va_arg(args, const char *);
253 puts_cs(action, sarg);
254 break;
255 default:
Kevin O'Connor4ce6a492008-02-29 00:21:27 -0500256 putc(action, '%');
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500257 n = s;
258 }
259 s = n;
260 }
Kevin O'Connor3e832bb2009-05-15 22:22:12 -0400261 debug_serial_flush();
Kevin O'Connor567e4e32008-04-05 11:37:51 -0400262}
263
264void
Kevin O'Connore07e18e2009-02-08 17:07:29 -0500265panic(const char *fmt, ...)
Kevin O'Connor567e4e32008-04-05 11:37:51 -0400266{
Kevin O'Connor79cb1192008-07-21 23:29:33 -0400267 if (CONFIG_DEBUG_LEVEL) {
268 va_list args;
269 va_start(args, fmt);
270 bvprintf(0, fmt, args);
271 va_end(args);
272 }
Kevin O'Connor567e4e32008-04-05 11:37:51 -0400273
274 // XXX - use PANIC PORT.
275 irq_disable();
276 for (;;)
277 hlt();
278}
279
280void
Kevin O'Connorac8df8c2008-05-24 23:46:33 -0400281__dprintf(const char *fmt, ...)
Kevin O'Connor567e4e32008-04-05 11:37:51 -0400282{
283 va_list args;
284 va_start(args, fmt);
285 bvprintf(0, fmt, args);
286 va_end(args);
287}
288
289void
290printf(const char *fmt, ...)
291{
292 va_list args;
293 va_start(args, fmt);
294 bvprintf(1, fmt, args);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500295 va_end(args);
296}
297
Kevin O'Connor1eba4292009-02-17 23:14:25 -0500298void
299hexdump(void *d, int len)
300{
301 int count=0;
302 while (len) {
303 if (count % 8 == 0) {
304 putc(0, '\n');
Kevin O'Connor91b53a72009-05-05 22:52:09 -0400305 puthex(0, count*4, 8);
Kevin O'Connor1eba4292009-02-17 23:14:25 -0500306 putc(0, ':');
307 } else {
308 putc(0, ' ');
309 }
Kevin O'Connor91b53a72009-05-05 22:52:09 -0400310 puthex(0, *(u32*)d, 8);
Kevin O'Connor1eba4292009-02-17 23:14:25 -0500311 count++;
312 len-=4;
313 d+=4;
314 }
315 putc(0, '\n');
Kevin O'Connor3e832bb2009-05-15 22:22:12 -0400316 debug_serial_flush();
Kevin O'Connor1eba4292009-02-17 23:14:25 -0500317}
318
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500319static void
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400320dump_regs(struct bregs *regs)
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500321{
322 if (!regs) {
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400323 dprintf(1, " NULL\n");
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500324 return;
325 }
Kevin O'Connor7da210c2009-05-16 23:57:08 -0400326 dprintf(1, " a=%08x b=%08x c=%08x d=%08x ds=%04x es=%04x ss=%04x\n"
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400327 , regs->eax, regs->ebx, regs->ecx, regs->edx
Kevin O'Connor7da210c2009-05-16 23:57:08 -0400328 , regs->ds, regs->es, GET_SEG(SS));
Kevin O'Connorf513c912009-05-27 22:27:10 -0400329 dprintf(1, " si=%08x di=%08x bp=%08x sp=%08x cs=%04x ip=%04x f=%04x\n"
330 , regs->esi, regs->edi, regs->ebp, (u32)&regs[1]
Kevin O'Connor9f985422009-09-09 11:34:39 -0400331 , regs->code.seg, regs->code.offset, regs->flags);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500332}
333
Kevin O'Connor05600342009-01-02 13:10:58 -0500334// Report entry to an Interrupt Service Routine (ISR).
Kevin O'Connorc65a3802008-03-02 13:58:23 -0500335void
Kevin O'Connored128492008-03-11 11:14:59 -0400336__debug_isr(const char *fname)
Kevin O'Connorc65a3802008-03-02 13:58:23 -0500337{
338 puts_cs(0, fname);
339 putc(0, '\n');
Kevin O'Connor3e832bb2009-05-15 22:22:12 -0400340 debug_serial_flush();
Kevin O'Connorc65a3802008-03-02 13:58:23 -0500341}
342
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500343// Function called on handler startup.
344void
Kevin O'Connor05600342009-01-02 13:10:58 -0500345__debug_enter(struct bregs *regs, const char *fname)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500346{
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400347 dprintf(1, "enter %s:\n", fname);
348 dump_regs(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500349}
350
Kevin O'Connor05600342009-01-02 13:10:58 -0500351// Send debugging output info.
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500352void
Kevin O'Connor05600342009-01-02 13:10:58 -0500353__debug_stub(struct bregs *regs, int lineno, const char *fname)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500354{
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400355 dprintf(1, "stub %s:%d:\n", fname, lineno);
356 dump_regs(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500357}
Kevin O'Connor4ce6a492008-02-29 00:21:27 -0500358
Kevin O'Connor05600342009-01-02 13:10:58 -0500359// Report on a handler returning a failure notification to the caller.
Kevin O'Connor4ce6a492008-02-29 00:21:27 -0500360void
Kevin O'Connor05600342009-01-02 13:10:58 -0500361__set_fail(struct bregs *regs, int lineno, const char *fname)
Kevin O'Connor4ce6a492008-02-29 00:21:27 -0500362{
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400363 dprintf(1, "fail %s:%d:\n", fname, lineno);
364 dump_regs(regs);
365 set_fail_silent(regs);
366}
367
Kevin O'Connor05600342009-01-02 13:10:58 -0500368// Report on a handler returning a failure code to the caller. Note,
369// the lineno and return code are encoded in the same parameter as gcc
370// does a better job of scheduling function calls when there are 3 or
371// less parameters.
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400372void
Kevin O'Connor05600342009-01-02 13:10:58 -0500373__set_code_fail(struct bregs *regs, u32 linecode, const char *fname)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400374{
Kevin O'Connor05600342009-01-02 13:10:58 -0500375 u8 code = linecode;
376 u32 lineno = linecode >> 8;
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400377 dprintf(1, "fail %s:%d(%x):\n", fname, lineno, code);
378 dump_regs(regs);
379 set_code_fail_silent(regs, code);
Kevin O'Connor4ce6a492008-02-29 00:21:27 -0500380}