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