Really fix linker issue with "debuginfo" variable.
[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 struct putcinfo {
16     void (*func)(struct putcinfo *info, char c);
17 };
18
19
20 /****************************************************************
21  * Debug output
22  ****************************************************************/
23
24 #define DEBUG_PORT PORT_SERIAL1
25 #define DEBUG_TIMEOUT 100000
26
27 void
28 debug_serial_setup()
29 {
30     if (!CONFIG_DEBUG_SERIAL)
31         return;
32     // setup for serial logging: 8N1
33     u8 oldparam, newparam = 0x03;
34     oldparam = inb(DEBUG_PORT+SEROFF_LCR);
35     outb(newparam, DEBUG_PORT+SEROFF_LCR);
36     // Disable irqs
37     u8 oldier, newier = 0;
38     oldier = inb(DEBUG_PORT+SEROFF_IER);
39     outb(newier, DEBUG_PORT+SEROFF_IER);
40
41     if (oldparam != newparam || oldier != newier)
42         dprintf(1, "Changing serial settings was %x/%x now %x/%x\n"
43                 , oldparam, oldier, newparam, newier);
44 }
45
46 // Write a character to the serial port.
47 static void
48 debug_serial(char c)
49 {
50     if (!CONFIG_DEBUG_SERIAL)
51         return;
52     int timeout = DEBUG_TIMEOUT;
53     while ((inb(DEBUG_PORT+SEROFF_LSR) & 0x60) != 0x60)
54         if (!timeout--)
55             // Ran out of time.
56             return;
57     outb(c, DEBUG_PORT+SEROFF_DATA);
58 }
59
60 // Make sure all serial port writes have been completely sent.
61 static void
62 debug_serial_flush()
63 {
64     if (!CONFIG_DEBUG_SERIAL)
65         return;
66     int timeout = DEBUG_TIMEOUT;
67     while ((inb(DEBUG_PORT+SEROFF_LSR) & 0x40) != 0x40)
68         if (!timeout--)
69             // Ran out of time.
70             return;
71 }
72
73 // Write a character to debug port(s).
74 static void
75 putc_debug(struct putcinfo *action, char c)
76 {
77     if (! CONFIG_DEBUG_LEVEL)
78         return;
79     if (! CONFIG_COREBOOT)
80         // Send character to debug port.
81         outb(c, PORT_BIOS_DEBUG);
82     if (c == '\n')
83         debug_serial('\r');
84     debug_serial(c);
85 }
86
87 #if MODE16
88 static struct putcinfo debuginfo VAR16 = { putc_debug };
89 #else
90 static struct putcinfo debuginfo = { putc_debug };
91 #endif
92
93
94 /****************************************************************
95  * Screen writing
96  ****************************************************************/
97
98 // Show a character on the screen.
99 static void
100 screenc(char c)
101 {
102     struct bregs br;
103     memset(&br, 0, sizeof(br));
104     br.flags = F_IF;
105     br.ah = 0x0e;
106     br.al = c;
107     call16_int(0x10, &br);
108 }
109
110 // Handle a character from a printf request.
111 static void
112 putc_screen(struct putcinfo *action, char c)
113 {
114     if (CONFIG_SCREEN_AND_DEBUG)
115         putc_debug(action, c);
116     if (c == '\n')
117         screenc('\r');
118     screenc(c);
119 }
120
121 static struct putcinfo screeninfo = { putc_screen };
122
123
124 /****************************************************************
125  * Xprintf code
126  ****************************************************************/
127
128 // Output a character.
129 static void
130 putc(struct putcinfo *action, char c)
131 {
132     if (MODE16) {
133         // Only debugging output supported in 16bit mode.
134         putc_debug(action, c);
135         return;
136     }
137
138     void (*func)(struct putcinfo *info, char c) = GET_GLOBAL(action->func);
139     func(action, c);
140 }
141
142 // Ouptut a string.
143 static void
144 puts(struct putcinfo *action, const char *s)
145 {
146     for (; *s; s++)
147         putc(action, *s);
148 }
149
150 // Output a string that is in the CS segment.
151 static void
152 puts_cs(struct putcinfo *action, const char *s)
153 {
154     char *vs = (char*)s;
155     for (;; vs++) {
156         char c = GET_GLOBAL(*vs);
157         if (!c)
158             break;
159         putc(action, c);
160     }
161 }
162
163 // Output an unsigned integer.
164 static void
165 putuint(struct putcinfo *action, u32 val)
166 {
167     char buf[12];
168     char *d = &buf[sizeof(buf) - 1];
169     *d-- = '\0';
170     for (;;) {
171         *d = (val % 10) + '0';
172         val /= 10;
173         if (!val)
174             break;
175         d--;
176     }
177     puts(action, d);
178 }
179
180 // Output a single digit hex character.
181 static inline void
182 putsinglehex(struct putcinfo *action, u32 val)
183 {
184     if (val <= 9)
185         val = '0' + val;
186     else
187         val = 'a' + val - 10;
188     putc(action, val);
189 }
190
191 // Output an integer in hexadecimal.
192 static void
193 puthex(struct putcinfo *action, u32 val, int width)
194 {
195     if (!width) {
196         u32 tmp = val;
197         width = 1;
198         if (tmp > 0xffff) {
199             width += 4;
200             tmp >>= 16;
201         }
202         if (tmp > 0xff) {
203             width += 2;
204             tmp >>= 8;
205         }
206         if (tmp > 0xf)
207             width += 1;
208     }
209
210     switch (width) {
211     default: putsinglehex(action, (val >> 28) & 0xf);
212     case 7:  putsinglehex(action, (val >> 24) & 0xf);
213     case 6:  putsinglehex(action, (val >> 20) & 0xf);
214     case 5:  putsinglehex(action, (val >> 16) & 0xf);
215     case 4:  putsinglehex(action, (val >> 12) & 0xf);
216     case 3:  putsinglehex(action, (val >> 8) & 0xf);
217     case 2:  putsinglehex(action, (val >> 4) & 0xf);
218     case 1:  putsinglehex(action, (val >> 0) & 0xf);
219     }
220 }
221
222 static inline int
223 isdigit(u8 c)
224 {
225     return ((u8)(c - '0')) < 10;
226 }
227
228 static void
229 bvprintf(struct putcinfo *action, const char *fmt, va_list args)
230 {
231     const char *s = fmt;
232     for (;; s++) {
233         char c = GET_GLOBAL(*(u8*)s);
234         if (!c)
235             break;
236         if (c != '%') {
237             putc(action, c);
238             continue;
239         }
240         const char *n = s+1;
241         int field_width = 0;
242         for (;;) {
243             c = GET_GLOBAL(*(u8*)n);
244             if (!isdigit(c))
245                 break;
246             field_width = field_width * 10 + c - '0';
247             n++;
248         }
249         if (c == 'l') {
250             // Ignore long format indicator
251             n++;
252             c = GET_GLOBAL(*(u8*)n);
253         }
254         s32 val;
255         const char *sarg;
256         switch (c) {
257         case '%':
258             putc(action, '%');
259             break;
260         case 'd':
261             val = va_arg(args, s32);
262             if (val < 0) {
263                 putc(action, '-');
264                 val = -val;
265             }
266             putuint(action, val);
267             break;
268         case 'u':
269             val = va_arg(args, s32);
270             putuint(action, val);
271             break;
272         case 'p':
273             /* %p always has 0x prepended */
274             putc(action, '0');
275             putc(action, 'x');
276             field_width = 8;
277         case 'x':
278             val = va_arg(args, s32);
279             puthex(action, val, field_width);
280             break;
281         case 'c':
282             val = va_arg(args, int);
283             putc(action, val);
284             break;
285         case '.':
286             // Hack to support "%.s" - meaning string on stack.
287             if (GET_GLOBAL(*(u8*)(n+1)) != 's')
288                 break;
289             n++;
290             sarg = va_arg(args, const char *);
291             puts(action, sarg);
292             break;
293         case 's':
294             sarg = va_arg(args, const char *);
295             puts_cs(action, sarg);
296             break;
297         default:
298             putc(action, '%');
299             n = s;
300         }
301         s = n;
302     }
303 }
304
305 void
306 panic(const char *fmt, ...)
307 {
308     if (CONFIG_DEBUG_LEVEL) {
309         va_list args;
310         va_start(args, fmt);
311         bvprintf(&debuginfo, fmt, args);
312         va_end(args);
313         debug_serial_flush();
314     }
315
316     // XXX - use PANIC PORT.
317     irq_disable();
318     for (;;)
319         hlt();
320 }
321
322 void
323 __dprintf(const char *fmt, ...)
324 {
325     va_list args;
326     va_start(args, fmt);
327     bvprintf(&debuginfo, fmt, args);
328     va_end(args);
329     debug_serial_flush();
330 }
331
332 void
333 printf(const char *fmt, ...)
334 {
335     ASSERT32();
336     va_list args;
337     va_start(args, fmt);
338     bvprintf(&screeninfo, fmt, args);
339     va_end(args);
340     if (CONFIG_SCREEN_AND_DEBUG)
341         debug_serial_flush();
342 }
343
344
345 /****************************************************************
346  * snprintf
347  ****************************************************************/
348
349 struct snprintfinfo {
350     struct putcinfo info;
351     char *str, *end;
352 };
353
354 static void
355 putc_str(struct putcinfo *info, char c)
356 {
357     struct snprintfinfo *sinfo = container_of(info, struct snprintfinfo, info);
358     if (sinfo->str >= sinfo->end)
359         return;
360     *sinfo->str = c;
361     sinfo->str++;
362 }
363
364 void
365 snprintf(char *str, size_t size, const char *fmt, ...)
366 {
367     ASSERT32();
368     if (!size)
369         return;
370     struct snprintfinfo sinfo = { { putc_str }, str, str + size };
371     va_list args;
372     va_start(args, fmt);
373     bvprintf(&sinfo.info, fmt, args);
374     va_end(args);
375     char *end = sinfo.str;
376     if (end >= sinfo.end)
377         end--;
378     *end = '\0';
379 }
380
381
382 /****************************************************************
383  * Misc helpers
384  ****************************************************************/
385
386 void
387 hexdump(const void *d, int len)
388 {
389     int count=0;
390     while (len > 0) {
391         if (count % 8 == 0) {
392             putc(&debuginfo, '\n');
393             puthex(&debuginfo, count*4, 8);
394             putc(&debuginfo, ':');
395         } else {
396             putc(&debuginfo, ' ');
397         }
398         puthex(&debuginfo, *(u32*)d, 8);
399         count++;
400         len-=4;
401         d+=4;
402     }
403     putc(&debuginfo, '\n');
404     debug_serial_flush();
405 }
406
407 static void
408 dump_regs(struct bregs *regs)
409 {
410     if (!regs) {
411         dprintf(1, "  NULL\n");
412         return;
413     }
414     dprintf(1, "   a=%08x  b=%08x  c=%08x  d=%08x ds=%04x es=%04x ss=%04x\n"
415             , regs->eax, regs->ebx, regs->ecx, regs->edx
416             , regs->ds, regs->es, GET_SEG(SS));
417     dprintf(1, "  si=%08x di=%08x bp=%08x sp=%08x cs=%04x ip=%04x  f=%04x\n"
418             , regs->esi, regs->edi, regs->ebp, (u32)&regs[1]
419             , regs->code.seg, regs->code.offset, regs->flags);
420 }
421
422 // Report entry to an Interrupt Service Routine (ISR).
423 void
424 __debug_isr(const char *fname)
425 {
426     puts_cs(&debuginfo, fname);
427     putc(&debuginfo, '\n');
428     debug_serial_flush();
429 }
430
431 // Function called on handler startup.
432 void
433 __debug_enter(struct bregs *regs, const char *fname)
434 {
435     dprintf(1, "enter %s:\n", fname);
436     dump_regs(regs);
437 }
438
439 // Send debugging output info.
440 void
441 __debug_stub(struct bregs *regs, int lineno, const char *fname)
442 {
443     dprintf(1, "stub %s:%d:\n", fname, lineno);
444     dump_regs(regs);
445 }
446
447 // Report on a handler returning a failure notification to the caller.
448 void
449 __set_fail(struct bregs *regs, int lineno, const char *fname)
450 {
451     dprintf(1, "fail %s:%d:\n", fname, lineno);
452     dump_regs(regs);
453     set_fail_silent(regs);
454 }
455
456 // Report on a handler returning a failure code to the caller.  Note,
457 // the lineno and return code are encoded in the same parameter as gcc
458 // does a better job of scheduling function calls when there are 3 or
459 // less parameters.
460 void
461 __set_code_fail(struct bregs *regs, u32 linecode, const char *fname)
462 {
463     u8 code = linecode;
464     u32 lineno = linecode >> 8;
465     dprintf(1, "fail %s:%d(%x):\n", fname, lineno, code);
466     dump_regs(regs);
467     set_code_fail_silent(regs, code);
468 }