Silence gcc asm deprectation warning in output.c.
[seabios.git] / src / output.c
1 // Raw screen writing and debug output code.
2 //
3 // Copyright (C) 2008  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 #define DEBUG_PORT PORT_SERIAL1
16 #define DEBUG_TIMEOUT 100000
17
18 void
19 debug_serial_setup()
20 {
21     if (!CONFIG_DEBUG_SERIAL)
22         return;
23     // setup for serial logging: 8N1
24     u8 oldparam, newparam = 0x03;
25     oldparam = inb(DEBUG_PORT+SEROFF_LCR);
26     outb(newparam, DEBUG_PORT+SEROFF_LCR);
27     // Disable irqs
28     u8 oldier, newier = 0;
29     oldier = inb(DEBUG_PORT+SEROFF_IER);
30     outb(newier, DEBUG_PORT+SEROFF_IER);
31
32     if (oldparam != newparam || oldier != newier)
33         dprintf(1, "Changing serial settings was %x/%x now %x/%x\n"
34                 , oldparam, oldier, newparam, newier);
35 }
36
37 // Write a character to the serial port.
38 static void
39 debug_serial(char c)
40 {
41     if (!CONFIG_DEBUG_SERIAL)
42         return;
43     int timeout = DEBUG_TIMEOUT;
44     while ((inb(DEBUG_PORT+SEROFF_LSR) & 0x60) != 0x60)
45         if (!timeout--)
46             // Ran out of time.
47             return;
48     outb(c, DEBUG_PORT+SEROFF_DATA);
49 }
50
51 // Make sure all serial port writes have been completely sent.
52 static void
53 debug_serial_flush()
54 {
55     if (!CONFIG_DEBUG_SERIAL)
56         return;
57     int timeout = DEBUG_TIMEOUT;
58     while ((inb(DEBUG_PORT+SEROFF_LSR) & 0x40) != 0x40)
59         if (!timeout--)
60             // Ran out of time.
61             return;
62 }
63
64 // Show a character on the screen.
65 static void
66 screenc(u8 c)
67 {
68     if (MODE16)
69         // printf is only used in 32bit code.
70         return;
71     struct bregs br;
72     memset(&br, 0, sizeof(br));
73     br.flags = F_IF;
74     br.ah = 0x0e;
75     br.al = c;
76     call16_int(0x10, &br);
77 }
78
79 // Output a character.
80 static void
81 putc(u16 action, char c)
82 {
83     if (CONFIG_DEBUG_LEVEL && (CONFIG_SCREEN_AND_DEBUG || !action)) {
84         if (! CONFIG_COREBOOT)
85             // Send character to debug port.
86             outb(c, PORT_BIOS_DEBUG);
87         // Send character to serial port.
88         if (c == '\n')
89             debug_serial('\r');
90         debug_serial(c);
91     }
92
93     if (action) {
94         // Send character to video screen.
95         if (c == '\n')
96             screenc('\r');
97         screenc(c);
98     }
99 }
100
101 // Ouptut a string.
102 static void
103 puts(u16 action, const char *s)
104 {
105     for (; *s; s++)
106         putc(action, *s);
107 }
108
109 // Output a string that is in the CS segment.
110 static void
111 puts_cs(u16 action, const char *s)
112 {
113     char *vs = (char*)s;
114     for (;; vs++) {
115         char c = GET_GLOBAL(*vs);
116         if (!c)
117             break;
118         putc(action, c);
119     }
120 }
121
122 // Output an unsigned integer.
123 static void
124 putuint(u16 action, u32 val)
125 {
126     char buf[12];
127     char *d = &buf[sizeof(buf) - 1];
128     *d-- = '\0';
129     for (;;) {
130         *d = (val % 10) + '0';
131         val /= 10;
132         if (!val)
133             break;
134         d--;
135     }
136     puts(action, d);
137 }
138
139 // Output a single digit hex character.
140 static inline void
141 putsinglehex(u16 action, u32 val)
142 {
143     if (val <= 9)
144         val = '0' + val;
145     else
146         val = 'a' + val - 10;
147     putc(action, val);
148 }
149
150 // Output an integer in hexadecimal.
151 static void
152 puthex(u16 action, u32 val, int width)
153 {
154     if (!width) {
155         u32 tmp = val;
156         width = 1;
157         if (tmp > 0xffff) {
158             width += 4;
159             tmp >>= 16;
160         }
161         if (tmp > 0xff) {
162             width += 2;
163             tmp >>= 8;
164         }
165         if (tmp > 0xf)
166             width += 1;
167     }
168
169     switch (width) {
170     default: putsinglehex(action, (val >> 28) & 0xf);
171     case 7:  putsinglehex(action, (val >> 24) & 0xf);
172     case 6:  putsinglehex(action, (val >> 20) & 0xf);
173     case 5:  putsinglehex(action, (val >> 16) & 0xf);
174     case 4:  putsinglehex(action, (val >> 12) & 0xf);
175     case 3:  putsinglehex(action, (val >> 8) & 0xf);
176     case 2:  putsinglehex(action, (val >> 4) & 0xf);
177     case 1:  putsinglehex(action, (val >> 0) & 0xf);
178     }
179 }
180
181 static inline int
182 isdigit(u8 c)
183 {
184     return ((u8)(c - '0')) < 10;
185 }
186
187 static void
188 bvprintf(u16 action, const char *fmt, va_list args)
189 {
190     const char *s = fmt;
191     for (;; s++) {
192         char c = GET_GLOBAL(*(u8*)s);
193         if (!c)
194             break;
195         if (c != '%') {
196             putc(action, c);
197             continue;
198         }
199         const char *n = s+1;
200         int field_width = 0;
201         for (;;) {
202             c = GET_GLOBAL(*(u8*)n);
203             if (!isdigit(c))
204                 break;
205             field_width = field_width * 10 + c - '0';
206             n++;
207         }
208         if (c == 'l') {
209             // Ignore long format indicator
210             n++;
211             c = GET_GLOBAL(*(u8*)n);
212         }
213         s32 val;
214         const char *sarg;
215         switch (c) {
216         case '%':
217             putc(action, '%');
218             break;
219         case 'd':
220             val = va_arg(args, s32);
221             if (val < 0) {
222                 putc(action, '-');
223                 val = -val;
224             }
225             putuint(action, val);
226             break;
227         case 'u':
228             val = va_arg(args, s32);
229             putuint(action, val);
230             break;
231         case 'p':
232             /* %p always has 0x prepended */
233             putc(action, '0');
234             putc(action, 'x');
235             field_width = 8;
236         case 'x':
237             val = va_arg(args, s32);
238             puthex(action, val, field_width);
239             break;
240         case 'c':
241             val = va_arg(args, int);
242             putc(action, val);
243             break;
244         case '.':
245             // Hack to support "%.s" - meaning string on stack.
246             if (GET_GLOBAL(*(u8*)(n+1)) != 's')
247                 break;
248             n++;
249             sarg = va_arg(args, const char *);
250             puts(action, sarg);
251             break;
252         case 's':
253             sarg = va_arg(args, const char *);
254             puts_cs(action, sarg);
255             break;
256         default:
257             putc(action, '%');
258             n = s;
259         }
260         s = n;
261     }
262     debug_serial_flush();
263 }
264
265 void
266 panic(const char *fmt, ...)
267 {
268     if (CONFIG_DEBUG_LEVEL) {
269         va_list args;
270         va_start(args, fmt);
271         bvprintf(0, fmt, args);
272         va_end(args);
273     }
274
275     // XXX - use PANIC PORT.
276     irq_disable();
277     for (;;)
278         hlt();
279 }
280
281 void
282 __dprintf(const char *fmt, ...)
283 {
284     va_list args;
285     va_start(args, fmt);
286     bvprintf(0, fmt, args);
287     va_end(args);
288 }
289
290 void
291 printf(const char *fmt, ...)
292 {
293     va_list args;
294     va_start(args, fmt);
295     bvprintf(1, fmt, args);
296     va_end(args);
297 }
298
299 void
300 hexdump(const void *d, int len)
301 {
302     int count=0;
303     while (len > 0) {
304         if (count % 8 == 0) {
305             putc(0, '\n');
306             puthex(0, count*4, 8);
307             putc(0, ':');
308         } else {
309             putc(0, ' ');
310         }
311         puthex(0, *(u32*)d, 8);
312         count++;
313         len-=4;
314         d+=4;
315     }
316     putc(0, '\n');
317     debug_serial_flush();
318 }
319
320 static void
321 dump_regs(struct bregs *regs)
322 {
323     if (!regs) {
324         dprintf(1, "  NULL\n");
325         return;
326     }
327     dprintf(1, "   a=%08x  b=%08x  c=%08x  d=%08x ds=%04x es=%04x ss=%04x\n"
328             , regs->eax, regs->ebx, regs->ecx, regs->edx
329             , regs->ds, regs->es, GET_SEG(SS));
330     dprintf(1, "  si=%08x di=%08x bp=%08x sp=%08x cs=%04x ip=%04x  f=%04x\n"
331             , regs->esi, regs->edi, regs->ebp, (u32)&regs[1]
332             , regs->code.seg, regs->code.offset, regs->flags);
333 }
334
335 // Report entry to an Interrupt Service Routine (ISR).
336 void
337 __debug_isr(const char *fname)
338 {
339     puts_cs(0, fname);
340     putc(0, '\n');
341     debug_serial_flush();
342 }
343
344 // Function called on handler startup.
345 void
346 __debug_enter(struct bregs *regs, const char *fname)
347 {
348     dprintf(1, "enter %s:\n", fname);
349     dump_regs(regs);
350 }
351
352 // Send debugging output info.
353 void
354 __debug_stub(struct bregs *regs, int lineno, const char *fname)
355 {
356     dprintf(1, "stub %s:%d:\n", fname, lineno);
357     dump_regs(regs);
358 }
359
360 // Report on a handler returning a failure notification to the caller.
361 void
362 __set_fail(struct bregs *regs, int lineno, const char *fname)
363 {
364     dprintf(1, "fail %s:%d:\n", fname, lineno);
365     dump_regs(regs);
366     set_fail_silent(regs);
367 }
368
369 // Report on a handler returning a failure code to the caller.  Note,
370 // the lineno and return code are encoded in the same parameter as gcc
371 // does a better job of scheduling function calls when there are 3 or
372 // less parameters.
373 void
374 __set_code_fail(struct bregs *regs, u32 linecode, const char *fname)
375 {
376     u8 code = linecode;
377     u32 lineno = linecode >> 8;
378     dprintf(1, "fail %s:%d(%x):\n", fname, lineno, code);
379     dump_regs(regs);
380     set_code_fail_silent(regs, code);
381 }