1cd0b8cbfffa9e4b25be1f0cc1585310eff28c63
[seabios.git] / src / output.c
1 // Raw screen writing and debug output code.
2 //
3 // Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
4 //
5 // This file may be distributed under the terms of the GNU LGPLv3 license.
6
7 #include <stdarg.h> // va_list
8
9 #include "farptr.h" // GET_VAR
10 #include "util.h" // printf
11 #include "bregs.h" // struct bregs
12 #include "config.h" // CONFIG_*
13 #include "biosvar.h" // GET_GLOBAL
14
15 #define DEBUG_PORT PORT_SERIAL1
16 #define DEBUG_TIMEOUT 100000
17
18 void
19 debug_serial_setup()
20 {
21     if (!CONFIG_DEBUG_SERIAL)
22         return;
23     // setup for serial logging: 8N1
24     u8 oldparam, newparam = 0x03;
25     oldparam = inb(DEBUG_PORT+SEROFF_LCR);
26     outb(newparam, DEBUG_PORT+SEROFF_LCR);
27     // Disable irqs
28     u8 oldier, newier = 0;
29     oldier = inb(DEBUG_PORT+SEROFF_IER);
30     outb(newier, DEBUG_PORT+SEROFF_IER);
31
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
37 // Write a character to the serial port.
38 static void
39 debug_serial(char c)
40 {
41     if (!CONFIG_DEBUG_SERIAL)
42         return;
43     int timeout = DEBUG_TIMEOUT;
44     while ((inb(DEBUG_PORT+SEROFF_LSR) & 0x60) != 0x60)
45         if (!timeout--)
46             // Ran out of time.
47             return;
48     outb(c, DEBUG_PORT+SEROFF_DATA);
49 }
50
51 // Make sure all serial port writes have been completely sent.
52 static void
53 debug_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
64 // Show a character on the screen.
65 static void
66 screenc(u8 c)
67 {
68     if (MODE16)
69         // printf is only used in 32bit code.
70         return;
71     struct bregs br;
72     memset(&br, 0, sizeof(br));
73     br.ah = 0x0e;
74     br.al = c;
75     call16_int(0x10, &br);
76 }
77
78 // Output a character.
79 static void
80 putc(u16 action, char c)
81 {
82     if (CONFIG_DEBUG_LEVEL && (CONFIG_SCREEN_AND_DEBUG || !action)) {
83         if (! CONFIG_COREBOOT)
84             // Send character to debug port.
85             outb(c, PORT_BIOS_DEBUG);
86         // Send character to serial port.
87         if (c == '\n')
88             debug_serial('\r');
89         debug_serial(c);
90     }
91
92     if (action) {
93         // Send character to video screen.
94         if (c == '\n')
95             screenc('\r');
96         screenc(c);
97     }
98 }
99
100 // Ouptut a string.
101 static void
102 puts(u16 action, const char *s)
103 {
104     for (; *s; s++)
105         putc(action, *s);
106 }
107
108 // Output a string that is in the CS segment.
109 static void
110 puts_cs(u16 action, const char *s)
111 {
112     for (;; s++) {
113         char c = GET_GLOBAL(*(u8*)s);
114         if (!c)
115             break;
116         putc(action, c);
117     }
118 }
119
120 // Output an unsigned integer.
121 static void
122 putuint(u16 action, u32 val)
123 {
124     char buf[12];
125     char *d = &buf[sizeof(buf) - 1];
126     *d-- = '\0';
127     for (;;) {
128         *d = (val % 10) + '0';
129         val /= 10;
130         if (!val)
131             break;
132         d--;
133     }
134     puts(action, d);
135 }
136
137 // Output a single digit hex character.
138 static inline void
139 putsinglehex(u16 action, u32 val)
140 {
141     if (val <= 9)
142         val = '0' + val;
143     else
144         val = 'a' + val - 10;
145     putc(action, val);
146 }
147
148 // Output an integer in hexadecimal.
149 static void
150 puthex(u16 action, u32 val, int width)
151 {
152     if (!width) {
153         u32 tmp = val;
154         width = 1;
155         if (tmp > 0xffff) {
156             width += 4;
157             tmp >>= 16;
158         }
159         if (tmp > 0xff) {
160             width += 2;
161             tmp >>= 8;
162         }
163         if (tmp > 0xf)
164             width += 1;
165     }
166
167     switch (width) {
168     default: putsinglehex(action, (val >> 28) & 0xf);
169     case 7:  putsinglehex(action, (val >> 24) & 0xf);
170     case 6:  putsinglehex(action, (val >> 20) & 0xf);
171     case 5:  putsinglehex(action, (val >> 16) & 0xf);
172     case 4:  putsinglehex(action, (val >> 12) & 0xf);
173     case 3:  putsinglehex(action, (val >> 8) & 0xf);
174     case 2:  putsinglehex(action, (val >> 4) & 0xf);
175     case 1:  putsinglehex(action, (val >> 0) & 0xf);
176     }
177 }
178
179 static inline int
180 isdigit(u8 c)
181 {
182     return ((u8)(c - '0')) < 10;
183 }
184
185 static void
186 bvprintf(u16 action, const char *fmt, va_list args)
187 {
188     const char *s = fmt;
189     for (;; s++) {
190         char c = GET_GLOBAL(*(u8*)s);
191         if (!c)
192             break;
193         if (c != '%') {
194             putc(action, c);
195             continue;
196         }
197         const char *n = s+1;
198         int field_width = 0;
199         for (;;) {
200             c = GET_GLOBAL(*(u8*)n);
201             if (!isdigit(c))
202                 break;
203             field_width = field_width * 10 + c - '0';
204             n++;
205         }
206         if (c == 'l') {
207             // Ignore long format indicator
208             n++;
209             c = GET_GLOBAL(*(u8*)n);
210         }
211         s32 val;
212         const char *sarg;
213         switch (c) {
214         case '%':
215             putc(action, '%');
216             break;
217         case 'd':
218             val = va_arg(args, s32);
219             if (val < 0) {
220                 putc(action, '-');
221                 val = -val;
222             }
223             putuint(action, val);
224             break;
225         case 'u':
226             val = va_arg(args, s32);
227             putuint(action, val);
228             break;
229         case 'p':
230             /* %p always has 0x prepended */
231             putc(action, '0');
232             putc(action, 'x');
233             field_width = 8;
234         case 'x':
235             val = va_arg(args, s32);
236             puthex(action, val, field_width);
237             break;
238         case 'c':
239             val = va_arg(args, int);
240             putc(action, val);
241             break;
242         case '.':
243             // Hack to support "%.s" - meaning string on stack.
244             if (GET_GLOBAL(*(u8*)(n+1)) != 's')
245                 break;
246             n++;
247             sarg = va_arg(args, const char *);
248             puts(action, sarg);
249             break;
250         case 's':
251             sarg = va_arg(args, const char *);
252             puts_cs(action, sarg);
253             break;
254         default:
255             putc(action, '%');
256             n = s;
257         }
258         s = n;
259     }
260     debug_serial_flush();
261 }
262
263 void
264 panic(const char *fmt, ...)
265 {
266     if (CONFIG_DEBUG_LEVEL) {
267         va_list args;
268         va_start(args, fmt);
269         bvprintf(0, fmt, args);
270         va_end(args);
271     }
272
273     // XXX - use PANIC PORT.
274     irq_disable();
275     for (;;)
276         hlt();
277 }
278
279 void
280 __dprintf(const char *fmt, ...)
281 {
282     va_list args;
283     va_start(args, fmt);
284     bvprintf(0, fmt, args);
285     va_end(args);
286 }
287
288 void
289 printf(const char *fmt, ...)
290 {
291     va_list args;
292     va_start(args, fmt);
293     bvprintf(1, fmt, args);
294     va_end(args);
295 }
296
297 void
298 hexdump(void *d, int len)
299 {
300     int count=0;
301     while (len) {
302         if (count % 8 == 0) {
303             putc(0, '\n');
304             puthex(0, count*4, 8);
305             putc(0, ':');
306         } else {
307             putc(0, ' ');
308         }
309         puthex(0, *(u32*)d, 8);
310         count++;
311         len-=4;
312         d+=4;
313     }
314     putc(0, '\n');
315     debug_serial_flush();
316 }
317
318 static void
319 dump_regs(struct bregs *regs)
320 {
321     if (!regs) {
322         dprintf(1, "  NULL\n");
323         return;
324     }
325     dprintf(1, "   a=%08x  b=%08x  c=%08x  d=%08x ds=%04x es=%04x ss=%04x\n"
326             , regs->eax, regs->ebx, regs->ecx, regs->edx
327             , regs->ds, regs->es, GET_SEG(SS));
328     dprintf(1, "  si=%08x di=%08x bp=%08x sp=%08x cs=%04x ip=%04x  f=%04x\n"
329             , regs->esi, regs->edi, regs->ebp, (u32)&regs[1]
330             , regs->cs, regs->ip, regs->flags);
331 }
332
333 // Report entry to an Interrupt Service Routine (ISR).
334 void
335 __debug_isr(const char *fname)
336 {
337     puts_cs(0, fname);
338     putc(0, '\n');
339     debug_serial_flush();
340 }
341
342 // Function called on handler startup.
343 void
344 __debug_enter(struct bregs *regs, const char *fname)
345 {
346     dprintf(1, "enter %s:\n", fname);
347     dump_regs(regs);
348 }
349
350 // Send debugging output info.
351 void
352 __debug_stub(struct bregs *regs, int lineno, const char *fname)
353 {
354     dprintf(1, "stub %s:%d:\n", fname, lineno);
355     dump_regs(regs);
356 }
357
358 // Report on a handler returning a failure notification to the caller.
359 void
360 __set_fail(struct bregs *regs, int lineno, const char *fname)
361 {
362     dprintf(1, "fail %s:%d:\n", fname, lineno);
363     dump_regs(regs);
364     set_fail_silent(regs);
365 }
366
367 // Report on a handler returning a failure code to the caller.  Note,
368 // the lineno and return code are encoded in the same parameter as gcc
369 // does a better job of scheduling function calls when there are 3 or
370 // less parameters.
371 void
372 __set_code_fail(struct bregs *regs, u32 linecode, const char *fname)
373 {
374     u8 code = linecode;
375     u32 lineno = linecode >> 8;
376     dprintf(1, "fail %s:%d(%x):\n", fname, lineno, code);
377     dump_regs(regs);
378     set_code_fail_silent(regs, code);
379 }