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