Interrupts should be enabled when calling 16bit code.
[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     for (;; s++) {
114         char c = GET_GLOBAL(*(u8*)s);
115         if (!c)
116             break;
117         putc(action, c);
118     }
119 }
120
121 // Output an unsigned integer.
122 static void
123 putuint(u16 action, u32 val)
124 {
125     char buf[12];
126     char *d = &buf[sizeof(buf) - 1];
127     *d-- = '\0';
128     for (;;) {
129         *d = (val % 10) + '0';
130         val /= 10;
131         if (!val)
132             break;
133         d--;
134     }
135     puts(action, d);
136 }
137
138 // Output a single digit hex character.
139 static inline void
140 putsinglehex(u16 action, u32 val)
141 {
142     if (val <= 9)
143         val = '0' + val;
144     else
145         val = 'a' + val - 10;
146     putc(action, val);
147 }
148
149 // Output an integer in hexadecimal.
150 static void
151 puthex(u16 action, u32 val, int width)
152 {
153     if (!width) {
154         u32 tmp = val;
155         width = 1;
156         if (tmp > 0xffff) {
157             width += 4;
158             tmp >>= 16;
159         }
160         if (tmp > 0xff) {
161             width += 2;
162             tmp >>= 8;
163         }
164         if (tmp > 0xf)
165             width += 1;
166     }
167
168     switch (width) {
169     default: putsinglehex(action, (val >> 28) & 0xf);
170     case 7:  putsinglehex(action, (val >> 24) & 0xf);
171     case 6:  putsinglehex(action, (val >> 20) & 0xf);
172     case 5:  putsinglehex(action, (val >> 16) & 0xf);
173     case 4:  putsinglehex(action, (val >> 12) & 0xf);
174     case 3:  putsinglehex(action, (val >> 8) & 0xf);
175     case 2:  putsinglehex(action, (val >> 4) & 0xf);
176     case 1:  putsinglehex(action, (val >> 0) & 0xf);
177     }
178 }
179
180 static inline int
181 isdigit(u8 c)
182 {
183     return ((u8)(c - '0')) < 10;
184 }
185
186 static void
187 bvprintf(u16 action, const char *fmt, va_list args)
188 {
189     const char *s = fmt;
190     for (;; s++) {
191         char c = GET_GLOBAL(*(u8*)s);
192         if (!c)
193             break;
194         if (c != '%') {
195             putc(action, c);
196             continue;
197         }
198         const char *n = s+1;
199         int field_width = 0;
200         for (;;) {
201             c = GET_GLOBAL(*(u8*)n);
202             if (!isdigit(c))
203                 break;
204             field_width = field_width * 10 + c - '0';
205             n++;
206         }
207         if (c == 'l') {
208             // Ignore long format indicator
209             n++;
210             c = GET_GLOBAL(*(u8*)n);
211         }
212         s32 val;
213         const char *sarg;
214         switch (c) {
215         case '%':
216             putc(action, '%');
217             break;
218         case 'd':
219             val = va_arg(args, s32);
220             if (val < 0) {
221                 putc(action, '-');
222                 val = -val;
223             }
224             putuint(action, val);
225             break;
226         case 'u':
227             val = va_arg(args, s32);
228             putuint(action, val);
229             break;
230         case 'p':
231             /* %p always has 0x prepended */
232             putc(action, '0');
233             putc(action, 'x');
234             field_width = 8;
235         case 'x':
236             val = va_arg(args, s32);
237             puthex(action, val, field_width);
238             break;
239         case 'c':
240             val = va_arg(args, int);
241             putc(action, val);
242             break;
243         case '.':
244             // Hack to support "%.s" - meaning string on stack.
245             if (GET_GLOBAL(*(u8*)(n+1)) != 's')
246                 break;
247             n++;
248             sarg = va_arg(args, const char *);
249             puts(action, sarg);
250             break;
251         case 's':
252             sarg = va_arg(args, const char *);
253             puts_cs(action, sarg);
254             break;
255         default:
256             putc(action, '%');
257             n = s;
258         }
259         s = n;
260     }
261     debug_serial_flush();
262 }
263
264 void
265 panic(const char *fmt, ...)
266 {
267     if (CONFIG_DEBUG_LEVEL) {
268         va_list args;
269         va_start(args, fmt);
270         bvprintf(0, fmt, args);
271         va_end(args);
272     }
273
274     // XXX - use PANIC PORT.
275     irq_disable();
276     for (;;)
277         hlt();
278 }
279
280 void
281 __dprintf(const char *fmt, ...)
282 {
283     va_list args;
284     va_start(args, fmt);
285     bvprintf(0, fmt, args);
286     va_end(args);
287 }
288
289 void
290 printf(const char *fmt, ...)
291 {
292     va_list args;
293     va_start(args, fmt);
294     bvprintf(1, fmt, args);
295     va_end(args);
296 }
297
298 void
299 hexdump(void *d, int len)
300 {
301     int count=0;
302     while (len) {
303         if (count % 8 == 0) {
304             putc(0, '\n');
305             puthex(0, count*4, 8);
306             putc(0, ':');
307         } else {
308             putc(0, ' ');
309         }
310         puthex(0, *(u32*)d, 8);
311         count++;
312         len-=4;
313         d+=4;
314     }
315     putc(0, '\n');
316     debug_serial_flush();
317 }
318
319 static void
320 dump_regs(struct bregs *regs)
321 {
322     if (!regs) {
323         dprintf(1, "  NULL\n");
324         return;
325     }
326     dprintf(1, "   a=%08x  b=%08x  c=%08x  d=%08x ds=%04x es=%04x ss=%04x\n"
327             , regs->eax, regs->ebx, regs->ecx, regs->edx
328             , regs->ds, regs->es, GET_SEG(SS));
329     dprintf(1, "  si=%08x di=%08x bp=%08x sp=%08x cs=%04x ip=%04x  f=%04x\n"
330             , regs->esi, regs->edi, regs->ebp, (u32)&regs[1]
331             , regs->code.seg, regs->code.offset, regs->flags);
332 }
333
334 // Report entry to an Interrupt Service Routine (ISR).
335 void
336 __debug_isr(const char *fname)
337 {
338     puts_cs(0, fname);
339     putc(0, '\n');
340     debug_serial_flush();
341 }
342
343 // Function called on handler startup.
344 void
345 __debug_enter(struct bregs *regs, const char *fname)
346 {
347     dprintf(1, "enter %s:\n", fname);
348     dump_regs(regs);
349 }
350
351 // Send debugging output info.
352 void
353 __debug_stub(struct bregs *regs, int lineno, const char *fname)
354 {
355     dprintf(1, "stub %s:%d:\n", fname, lineno);
356     dump_regs(regs);
357 }
358
359 // Report on a handler returning a failure notification to the caller.
360 void
361 __set_fail(struct bregs *regs, int lineno, const char *fname)
362 {
363     dprintf(1, "fail %s:%d:\n", fname, lineno);
364     dump_regs(regs);
365     set_fail_silent(regs);
366 }
367
368 // Report on a handler returning a failure code to the caller.  Note,
369 // the lineno and return code are encoded in the same parameter as gcc
370 // does a better job of scheduling function calls when there are 3 or
371 // less parameters.
372 void
373 __set_code_fail(struct bregs *regs, u32 linecode, const char *fname)
374 {
375     u8 code = linecode;
376     u32 lineno = linecode >> 8;
377     dprintf(1, "fail %s:%d(%x):\n", fname, lineno, code);
378     dump_regs(regs);
379     set_code_fail_silent(regs, code);
380 }