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