grml...
[seabios.git] / src / output.c
1 // Raw screen writing and debug output code.
2 //
3 // Copyright (C) 2008,2009  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_TIMEOUT 100000
25
26 void
27 debug_serial_setup(void)
28 {
29     if (!CONFIG_DEBUG_SERIAL)
30         return;
31     // setup for serial logging: 8N1
32     u8 oldparam, newparam = 0x03;
33     oldparam = inb(CONFIG_DEBUG_SERIAL_PORT+SEROFF_LCR);
34     outb(newparam, CONFIG_DEBUG_SERIAL_PORT+SEROFF_LCR);
35     // Disable irqs
36     u8 oldier, newier = 0;
37     oldier = inb(CONFIG_DEBUG_SERIAL_PORT+SEROFF_IER);
38     outb(newier, CONFIG_DEBUG_SERIAL_PORT+SEROFF_IER);
39
40     if (oldparam != newparam || oldier != newier)
41         dprintf(1, "Changing serial settings was %x/%x now %x/%x\n"
42                 , oldparam, oldier, newparam, newier);
43 }
44
45 // Write a character to the serial port.
46 static void
47 debug_serial(char c)
48 {
49     if (!CONFIG_DEBUG_SERIAL)
50         return;
51     int timeout = DEBUG_TIMEOUT;
52     while ((inb(CONFIG_DEBUG_SERIAL_PORT+SEROFF_LSR) & 0x20) != 0x20)
53         if (!timeout--)
54             // Ran out of time.
55             return;
56     outb(c, CONFIG_DEBUG_SERIAL_PORT+SEROFF_DATA);
57 }
58
59 // Make sure all serial port writes have been completely sent.
60 static void
61 debug_serial_flush(void)
62 {
63     if (!CONFIG_DEBUG_SERIAL)
64         return;
65     int timeout = DEBUG_TIMEOUT;
66     while ((inb(CONFIG_DEBUG_SERIAL_PORT+SEROFF_LSR) & 0x60) != 0x60)
67         if (!timeout--)
68             // Ran out of time.
69             return;
70 }
71
72 // Write a character to debug port(s).
73 static void
74 putc_debug(struct putcinfo *action, char c)
75 {
76     if (! CONFIG_DEBUG_LEVEL)
77         return;
78     if (CONFIG_DEBUG_IO)
79         // Send character to debug port.
80         outb(c, CONFIG_DEBUG_IO_PORT);
81     if (c == '\n')
82         debug_serial('\r');
83     debug_serial(c);
84 }
85
86 // In segmented mode just need a dummy variable (putc_debug is always
87 // used anyway), and in 32bit flat mode need a pointer to the 32bit
88 // instance of putc_debug().
89 #if MODE16
90 static struct putcinfo debuginfo VAR16;
91 #elif MODESEGMENT
92 static struct putcinfo debuginfo VAR32SEG;
93 #else
94 static struct putcinfo debuginfo = { putc_debug };
95 #endif
96
97
98 /****************************************************************
99  * Screen writing
100  ****************************************************************/
101
102 // Show a character on the screen.
103 static void
104 screenc(char c)
105 {
106     struct bregs br;
107     memset(&br, 0, sizeof(br));
108     br.flags = F_IF;
109     br.ah = 0x0e;
110     br.al = c;
111     call16_int(0x10, &br);
112 }
113
114 // Handle a character from a printf request.
115 static void
116 putc_screen(struct putcinfo *action, char c)
117 {
118     if (ScreenAndDebug)
119         putc_debug(&debuginfo, c);
120     if (c == '\n')
121         screenc('\r');
122     screenc(c);
123 }
124
125 static struct putcinfo screeninfo = { putc_screen };
126
127
128 /****************************************************************
129  * Xprintf code
130  ****************************************************************/
131
132 // Output a character.
133 static void
134 putc(struct putcinfo *action, char c)
135 {
136     if (MODESEGMENT) {
137         // Only debugging output supported in segmented mode.
138         putc_debug(action, c);
139         return;
140     }
141
142     void (*func)(struct putcinfo *info, char c) = GET_GLOBAL(action->func);
143     func(action, c);
144 }
145
146 // Ouptut a string.
147 static void
148 puts(struct putcinfo *action, const char *s)
149 {
150     if (!MODESEGMENT && !s)
151         s = "(NULL)";
152     for (; *s; s++)
153         putc(action, *s);
154 }
155
156 // Output a string that is in the CS segment.
157 static void
158 puts_cs(struct putcinfo *action, const char *s)
159 {
160     char *vs = (char*)s;
161     for (;; vs++) {
162         char c = GET_GLOBAL(*vs);
163         if (!c)
164             break;
165         putc(action, c);
166     }
167 }
168
169 // Output an unsigned integer.
170 static void
171 putuint(struct putcinfo *action, u32 val)
172 {
173     char buf[12];
174     char *d = &buf[sizeof(buf) - 1];
175     *d-- = '\0';
176     for (;;) {
177         *d = (val % 10) + '0';
178         val /= 10;
179         if (!val)
180             break;
181         d--;
182     }
183     puts(action, d);
184 }
185
186 // Output a single digit hex character.
187 static inline void
188 putsinglehex(struct putcinfo *action, u32 val)
189 {
190     if (val <= 9)
191         val = '0' + val;
192     else
193         val = 'a' + val - 10;
194     putc(action, val);
195 }
196
197 // Output an integer in hexadecimal.
198 static void
199 puthex(struct putcinfo *action, u32 val, int width, int spacepad)
200 {
201     if (!width) {
202         u32 tmp = val;
203         width = 1;
204         while (tmp >>= 4)
205             width++;
206     } else if (spacepad)  {
207         u32 tmp = val;
208         u32 count = 1;
209         while (tmp >>= 4)
210             count++;
211         if (width > count) {
212             count = width - count;
213             width -= count;
214             while (count--)
215                 putc(action, ' ');
216         }
217     }
218
219     switch (width) {
220     default: putsinglehex(action, (val >> 28) & 0xf);
221     case 7:  putsinglehex(action, (val >> 24) & 0xf);
222     case 6:  putsinglehex(action, (val >> 20) & 0xf);
223     case 5:  putsinglehex(action, (val >> 16) & 0xf);
224     case 4:  putsinglehex(action, (val >> 12) & 0xf);
225     case 3:  putsinglehex(action, (val >> 8) & 0xf);
226     case 2:  putsinglehex(action, (val >> 4) & 0xf);
227     case 1:  putsinglehex(action, (val >> 0) & 0xf);
228     }
229 }
230
231 static inline int
232 isdigit(u8 c)
233 {
234     return ((u8)(c - '0')) < 10;
235 }
236
237 static void
238 bvprintf(struct putcinfo *action, const char *fmt, va_list args)
239 {
240     const char *s = fmt;
241     for (;; s++) {
242         char c = GET_GLOBAL(*(u8*)s);
243         if (!c)
244             break;
245         if (c != '%') {
246             putc(action, c);
247             continue;
248         }
249         const char *n = s+1;
250         int field_width = 0;
251         int spacepad = 1;
252         for (;;) {
253             c = GET_GLOBAL(*(u8*)n);
254             if (!isdigit(c))
255                 break;
256             if (!field_width && (c == '0'))
257                 spacepad = 0;
258             else
259                 field_width = field_width * 10 + c - '0';
260             n++;
261         }
262         if (c == 'l') {
263             // Ignore long format indicator
264             n++;
265             c = GET_GLOBAL(*(u8*)n);
266         }
267         s32 val;
268         const char *sarg;
269         switch (c) {
270         case '%':
271             putc(action, '%');
272             break;
273         case 'd':
274             val = va_arg(args, s32);
275             if (val < 0) {
276                 putc(action, '-');
277                 val = -val;
278             }
279             putuint(action, val);
280             break;
281         case 'u':
282             val = va_arg(args, s32);
283             putuint(action, val);
284             break;
285         case 'p':
286             /* %p always has 0x prepended */
287             putc(action, '0');
288             putc(action, 'x');
289             field_width = 8;
290             spacepad = 0;
291         case 'x':
292             val = va_arg(args, s32);
293             puthex(action, val, field_width, spacepad);
294             break;
295         case 'c':
296             val = va_arg(args, int);
297             putc(action, val);
298             break;
299         case '.':
300             // Hack to support "%.s" - meaning string on stack.
301             if (GET_GLOBAL(*(u8*)(n+1)) != 's')
302                 break;
303             n++;
304             sarg = va_arg(args, const char *);
305             puts(action, sarg);
306             break;
307         case 's':
308             sarg = va_arg(args, const char *);
309             puts_cs(action, sarg);
310             break;
311         default:
312             putc(action, '%');
313             n = s;
314         }
315         s = n;
316     }
317 }
318
319 void
320 panic(const char *fmt, ...)
321 {
322     if (CONFIG_DEBUG_LEVEL) {
323         va_list args;
324         va_start(args, fmt);
325         bvprintf(&debuginfo, fmt, args);
326         va_end(args);
327         debug_serial_flush();
328     }
329
330     // XXX - use PANIC PORT.
331     irq_disable();
332     for (;;)
333         hlt();
334 }
335
336 void
337 __dprintf(const char *fmt, ...)
338 {
339     if (!MODESEGMENT && CONFIG_THREADS && CONFIG_DEBUG_LEVEL >= DEBUG_thread
340         && *fmt != '\\' && *fmt != '/') {
341         struct thread_info *cur = getCurThread();
342         if (cur != &MainThread) {
343             // Show "thread id" for this debug message.
344             putc_debug(&debuginfo, '|');
345             puthex(&debuginfo, (u32)cur, 8, 0);
346             putc_debug(&debuginfo, '|');
347             putc_debug(&debuginfo, ' ');
348         }
349     }
350
351     va_list args;
352     va_start(args, fmt);
353     bvprintf(&debuginfo, fmt, args);
354     va_end(args);
355     debug_serial_flush();
356 }
357
358 void
359 printf(const char *fmt, ...)
360 {
361     ASSERT32FLAT();
362     va_list args;
363     va_start(args, fmt);
364     bvprintf(&screeninfo, fmt, args);
365     va_end(args);
366     if (ScreenAndDebug)
367         debug_serial_flush();
368 }
369
370
371 /****************************************************************
372  * snprintf
373  ****************************************************************/
374
375 struct snprintfinfo {
376     struct putcinfo info;
377     char *str, *end;
378 };
379
380 static void
381 putc_str(struct putcinfo *info, char c)
382 {
383     struct snprintfinfo *sinfo = container_of(info, struct snprintfinfo, info);
384     if (sinfo->str >= sinfo->end)
385         return;
386     *sinfo->str = c;
387     sinfo->str++;
388 }
389
390 // Build a formatted string.  Note, this function returns the actual
391 // number of bytes used (not including null) even in the overflow
392 // case.
393 int
394 snprintf(char *str, size_t size, const char *fmt, ...)
395 {
396     ASSERT32FLAT();
397     if (!size)
398         return 0;
399     struct snprintfinfo sinfo = { { putc_str }, str, str + size };
400     va_list args;
401     va_start(args, fmt);
402     bvprintf(&sinfo.info, fmt, args);
403     va_end(args);
404     char *end = sinfo.str;
405     if (end >= sinfo.end)
406         end = sinfo.end - 1;
407     *end = '\0';
408     return end - str;
409 }
410
411 // Build a formatted string - malloc'ing the memory.
412 char *
413 znprintf(size_t size, const char *fmt, ...)
414 {
415     ASSERT32FLAT();
416     if (!size)
417         return NULL;
418     char *str = malloc_tmp(size);
419     if (!str) {
420         warn_noalloc();
421         return NULL;
422     }
423     struct snprintfinfo sinfo = { { putc_str }, str, str + size };
424     va_list args;
425     va_start(args, fmt);
426     bvprintf(&sinfo.info, fmt, args);
427     va_end(args);
428     char *end = sinfo.str;
429     if (end >= sinfo.end)
430         end = sinfo.end - 1;
431     *end = '\0';
432     return str;
433 }
434
435
436 /****************************************************************
437  * Misc helpers
438  ****************************************************************/
439
440 void
441 hexdump(const void *d, int len)
442 {
443     int count=0;
444     while (len > 0) {
445         if (count % 8 == 0) {
446             putc(&debuginfo, '\n');
447             puthex(&debuginfo, count*4, 8, 0);
448             putc(&debuginfo, ':');
449         } else {
450             putc(&debuginfo, ' ');
451         }
452         puthex(&debuginfo, *(u32*)d, 8, 0);
453         count++;
454         len-=4;
455         d+=4;
456     }
457     putc(&debuginfo, '\n');
458     debug_serial_flush();
459 }
460
461 static void
462 dump_regs(struct bregs *regs)
463 {
464     if (!regs) {
465         dprintf(1, "  NULL\n");
466         return;
467     }
468     dprintf(1, "   a=%08x  b=%08x  c=%08x  d=%08x ds=%04x es=%04x ss=%04x\n"
469             , regs->eax, regs->ebx, regs->ecx, regs->edx
470             , regs->ds, regs->es, GET_SEG(SS));
471     dprintf(1, "  si=%08x di=%08x bp=%08x sp=%08x cs=%04x ip=%04x  f=%04x\n"
472             , regs->esi, regs->edi, regs->ebp, (u32)&regs[1]
473             , regs->code.seg, regs->code.offset, regs->flags);
474 }
475
476 // Report entry to an Interrupt Service Routine (ISR).
477 void
478 __debug_isr(const char *fname)
479 {
480     puts_cs(&debuginfo, fname);
481     putc(&debuginfo, '\n');
482     debug_serial_flush();
483 }
484
485 // Function called on handler startup.
486 void
487 __debug_enter(struct bregs *regs, const char *fname)
488 {
489     dprintf(1, "enter %s:\n", fname);
490     dump_regs(regs);
491 }
492
493 // Send debugging output info.
494 void
495 __debug_stub(struct bregs *regs, int lineno, const char *fname)
496 {
497     dprintf(1, "stub %s:%d:\n", fname, lineno);
498     dump_regs(regs);
499 }
500
501 // Report on an invalid parameter.
502 void
503 __warn_invalid(struct bregs *regs, int lineno, const char *fname)
504 {
505     if (CONFIG_DEBUG_LEVEL >= DEBUG_invalid) {
506         dprintf(1, "invalid %s:%d:\n", fname, lineno);
507         dump_regs(regs);
508     }
509 }
510
511 // Report on an unimplemented feature.
512 void
513 __warn_unimplemented(struct bregs *regs, int lineno, const char *fname)
514 {
515     if (CONFIG_DEBUG_LEVEL >= DEBUG_unimplemented) {
516         dprintf(1, "unimplemented %s:%d:\n", fname, lineno);
517         dump_regs(regs);
518     }
519 }
520
521 // Report a detected internal inconsistency.
522 void
523 __warn_internalerror(int lineno, const char *fname)
524 {
525     dprintf(1, "WARNING - internal error detected at %s:%d!\n"
526             , fname, lineno);
527 }
528
529 // Report on an allocation failure.
530 void
531 __warn_noalloc(int lineno, const char *fname)
532 {
533     dprintf(1, "WARNING - Unable to allocate resource at %s:%d!\n"
534             , fname, lineno);
535 }
536
537 // Report on a timeout exceeded.
538 void
539 __warn_timeout(int lineno, const char *fname)
540 {
541     dprintf(1, "WARNING - Timeout at %s:%d!\n", fname, lineno);
542 }
543
544 // Report a handler reporting an invalid parameter to the caller.
545 void
546 __set_invalid(struct bregs *regs, int lineno, const char *fname)
547 {
548     __warn_invalid(regs, lineno, fname);
549     set_invalid_silent(regs);
550 }
551
552 // Report a call of an unimplemented function.
553 void
554 __set_unimplemented(struct bregs *regs, int lineno, const char *fname)
555 {
556     __warn_unimplemented(regs, lineno, fname);
557     set_invalid_silent(regs);
558 }
559
560 // Report a handler reporting an invalid parameter code to the
561 // caller.  Note, the lineno and return code are encoded in the same
562 // parameter as gcc does a better job of scheduling function calls
563 // when there are 3 or less parameters.
564 void
565 __set_code_invalid(struct bregs *regs, u32 linecode, const char *fname)
566 {
567     u8 code = linecode;
568     u32 lineno = linecode >> 8;
569     __warn_invalid(regs, lineno, fname);
570     set_code_invalid_silent(regs, code);
571 }
572
573 // Report a call of an unimplemented function.
574 void
575 __set_code_unimplemented(struct bregs *regs, u32 linecode, const char *fname)
576 {
577     u8 code = linecode;
578     u32 lineno = linecode >> 8;
579     __warn_unimplemented(regs, lineno, fname);
580     set_code_invalid_silent(regs, code);
581 }