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