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