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