Change license from GPLv3 to LGPLv3.
[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 0x03f8
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+3);
26     outb(newparam, DEBUG_PORT+3);
27     // Disable irqs
28     u8 oldier, newier = 0;
29     oldier = inb(DEBUG_PORT+1);
30     outb(newier, DEBUG_PORT+1);
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+5) & 0x60) != 0x60)
43         if (!timeout--)
44             // Ran out of time.
45             return;
46     outb(c, DEBUG_PORT);
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)
138 {
139     putsinglehex(action, (val >> 28) & 0xf);
140     putsinglehex(action, (val >> 24) & 0xf);
141     putsinglehex(action, (val >> 20) & 0xf);
142     putsinglehex(action, (val >> 16) & 0xf);
143     putsinglehex(action, (val >> 12) & 0xf);
144     putsinglehex(action, (val >> 8) & 0xf);
145     putsinglehex(action, (val >> 4) & 0xf);
146     putsinglehex(action, (val >> 0) & 0xf);
147 }
148
149 static inline int
150 isdigit(u8 c)
151 {
152     return ((u8)(c - '0')) < 10;
153 }
154
155 static void
156 bvprintf(u16 action, const char *fmt, va_list args)
157 {
158     const char *s = fmt;
159     for (;; s++) {
160         char c = GET_GLOBAL(*(u8*)s);
161         if (!c)
162             break;
163         if (c != '%') {
164             putc(action, c);
165             continue;
166         }
167         const char *n = s+1;
168         for (;;) {
169             c = GET_GLOBAL(*(u8*)n);
170             if (!isdigit(c))
171                 break;
172             n++;
173         }
174         if (c == 'l') {
175             // Ignore long format indicator
176             n++;
177             c = GET_GLOBAL(*(u8*)n);
178         }
179         s32 val;
180         const char *sarg;
181         switch (c) {
182         case '%':
183             putc(action, '%');
184             break;
185         case 'd':
186             val = va_arg(args, s32);
187             if (val < 0) {
188                 putc(action, '-');
189                 val = -val;
190             }
191             putuint(action, val);
192             break;
193         case 'u':
194             val = va_arg(args, s32);
195             putuint(action, val);
196             break;
197         case 'p':
198         case 'x':
199             val = va_arg(args, s32);
200             puthex(action, val);
201             break;
202         case 'c':
203             val = va_arg(args, int);
204             putc(action, val);
205             break;
206         case '.':
207             // Hack to support "%.s" - meaning string on stack.
208             if (GET_GLOBAL(*(u8*)(n+1)) != 's')
209                 break;
210             n++;
211             sarg = va_arg(args, const char *);
212             puts(action, sarg);
213             break;
214         case 's':
215             sarg = va_arg(args, const char *);
216             puts_cs(action, sarg);
217             break;
218         default:
219             putc(action, '%');
220             n = s;
221         }
222         s = n;
223     }
224 }
225
226 void
227 BX_PANIC(const char *fmt, ...)
228 {
229     if (CONFIG_DEBUG_LEVEL) {
230         va_list args;
231         va_start(args, fmt);
232         bvprintf(0, fmt, args);
233         va_end(args);
234     }
235
236     // XXX - use PANIC PORT.
237     irq_disable();
238     for (;;)
239         hlt();
240 }
241
242 void
243 __dprintf(const char *fmt, ...)
244 {
245     va_list args;
246     va_start(args, fmt);
247     bvprintf(0, fmt, args);
248     va_end(args);
249 }
250
251 void
252 printf(const char *fmt, ...)
253 {
254     va_list args;
255     va_start(args, fmt);
256     bvprintf(1, fmt, args);
257     va_end(args);
258 }
259
260 static void
261 dump_regs(struct bregs *regs)
262 {
263     if (!regs) {
264         dprintf(1, "  NULL\n");
265         return;
266     }
267     dprintf(1, "  a=%x b=%x c=%x d=%x si=%x di=%x\n"
268             , regs->eax, regs->ebx, regs->ecx, regs->edx
269             , regs->esi, regs->edi);
270     dprintf(1, "  ds=%x es=%x ip=%x cs=%x f=%x r=%p\n"
271             , regs->ds, regs->es, regs->ip, regs->cs, regs->flags, regs);
272 }
273
274 // Report entry to an Interrupt Service Routine (ISR).
275 void
276 __debug_isr(const char *fname)
277 {
278     puts_cs(0, fname);
279     putc(0, '\n');
280 }
281
282 // Function called on handler startup.
283 void
284 __debug_enter(struct bregs *regs, const char *fname)
285 {
286     dprintf(1, "enter %s:\n", fname);
287     dump_regs(regs);
288 }
289
290 // Send debugging output info.
291 void
292 __debug_stub(struct bregs *regs, int lineno, const char *fname)
293 {
294     dprintf(1, "stub %s:%d:\n", fname, lineno);
295     dump_regs(regs);
296 }
297
298 // Report on a handler returning a failure notification to the caller.
299 void
300 __set_fail(struct bregs *regs, int lineno, const char *fname)
301 {
302     dprintf(1, "fail %s:%d:\n", fname, lineno);
303     dump_regs(regs);
304     set_fail_silent(regs);
305 }
306
307 // Report on a handler returning a failure code to the caller.  Note,
308 // the lineno and return code are encoded in the same parameter as gcc
309 // does a better job of scheduling function calls when there are 3 or
310 // less parameters.
311 void
312 __set_code_fail(struct bregs *regs, u32 linecode, const char *fname)
313 {
314     u8 code = linecode;
315     u32 lineno = linecode >> 8;
316     dprintf(1, "fail %s:%d(%x):\n", fname, lineno, code);
317     dump_regs(regs);
318     set_code_fail_silent(regs, code);
319 }