X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=src%2Foutput.c;h=bdde7ccaa4fbac2c5ec12700c152c0de9fc58203;hb=7123d9834d58287d43514d7799ed1a7b34eea243;hp=e22b15999a548b405329274171914f68122a9e37;hpb=c38e480c83e5090c622d3b42370134aff7d97632;p=seabios.git diff --git a/src/output.c b/src/output.c index e22b159..bdde7cc 100644 --- a/src/output.c +++ b/src/output.c @@ -1,63 +1,174 @@ -// Raw screen writing code. +// Raw screen writing and debug output code. // -// Copyright (C) 2008 Kevin O'Connor +// Copyright (C) 2008,2009 Kevin O'Connor // -// This file may be distributed under the terms of the GNU GPLv3 license. +// This file may be distributed under the terms of the GNU LGPLv3 license. #include // va_list #include "farptr.h" // GET_VAR -#include "util.h" // bprintf -#include "biosvar.h" // struct bregs +#include "util.h" // printf +#include "bregs.h" // struct bregs +#include "config.h" // CONFIG_* +#include "biosvar.h" // GET_GLOBAL +struct putcinfo { + void (*func)(struct putcinfo *info, char c); +}; + + +/**************************************************************** + * Debug output + ****************************************************************/ + +#define DEBUG_TIMEOUT 100000 + +void +debug_serial_setup(void) +{ + if (!CONFIG_DEBUG_SERIAL) + return; + // setup for serial logging: 8N1 + u8 oldparam, newparam = 0x03; + oldparam = inb(CONFIG_DEBUG_SERIAL_PORT+SEROFF_LCR); + outb(newparam, CONFIG_DEBUG_SERIAL_PORT+SEROFF_LCR); + // Disable irqs + u8 oldier, newier = 0; + oldier = inb(CONFIG_DEBUG_SERIAL_PORT+SEROFF_IER); + outb(newier, CONFIG_DEBUG_SERIAL_PORT+SEROFF_IER); + + if (oldparam != newparam || oldier != newier) + dprintf(1, "Changing serial settings was %x/%x now %x/%x\n" + , oldparam, oldier, newparam, newier); +} + +// Write a character to the serial port. +static void +debug_serial(char c) +{ + if (!CONFIG_DEBUG_SERIAL) + return; + int timeout = DEBUG_TIMEOUT; + while ((inb(CONFIG_DEBUG_SERIAL_PORT+SEROFF_LSR) & 0x20) != 0x20) + if (!timeout--) + // Ran out of time. + return; + outb(c, CONFIG_DEBUG_SERIAL_PORT+SEROFF_DATA); +} + +// Make sure all serial port writes have been completely sent. +static void +debug_serial_flush(void) +{ + if (!CONFIG_DEBUG_SERIAL) + return; + int timeout = DEBUG_TIMEOUT; + while ((inb(CONFIG_DEBUG_SERIAL_PORT+SEROFF_LSR) & 0x60) != 0x60) + if (!timeout--) + // Ran out of time. + return; +} + +// Write a character to debug port(s). static void -screenc(u8 c) +putc_debug(struct putcinfo *action, char c) +{ + if (! CONFIG_DEBUG_LEVEL) + return; + if (CONFIG_DEBUG_IO) + // Send character to debug port. + outb(c, CONFIG_DEBUG_IO_PORT); + if (c == '\n') + debug_serial('\r'); + debug_serial(c); +} + +// In segmented mode just need a dummy variable (putc_debug is always +// used anyway), and in 32bit flat mode need a pointer to the 32bit +// instance of putc_debug(). +#if MODE16 +static struct putcinfo debuginfo VAR16; +#elif MODESEGMENT +static struct putcinfo debuginfo VAR32SEG; +#else +static struct putcinfo debuginfo = { putc_debug }; +#endif + + +/**************************************************************** + * Screen writing + ****************************************************************/ + +// Show a character on the screen. +static void +screenc(char c) { struct bregs br; memset(&br, 0, sizeof(br)); + br.flags = F_IF; br.ah = 0x0e; br.al = c; call16_int(0x10, &br); } -// XXX - move PORT_DEBUG to standard place? -#define PORT_DEBUG 0x403 +// Handle a character from a printf request. +static void +putc_screen(struct putcinfo *action, char c) +{ + if (ScreenAndDebug) + putc_debug(&debuginfo, c); + if (c == '\n') + screenc('\r'); + screenc(c); +} + +static struct putcinfo screeninfo = { putc_screen }; + + +/**************************************************************** + * Xprintf code + ****************************************************************/ -// Write a charcter to the framebuffer. +// Output a character. static void -putc(u16 action, char c) +putc(struct putcinfo *action, char c) { - outb(c, PORT_DEBUG); - if (action) { - if (c == '\n') - screenc('\r'); - screenc(c); + if (MODESEGMENT) { + // Only debugging output supported in segmented mode. + putc_debug(action, c); + return; } + + void (*func)(struct putcinfo *info, char c) = GET_GLOBAL(action->func); + func(action, c); } -// Write a string to the framebuffer. +// Ouptut a string. static void -puts(u16 action, const char *s) +puts(struct putcinfo *action, const char *s) { + if (!MODESEGMENT && !s) + s = "(NULL)"; for (; *s; s++) putc(action, *s); } -// Write a string to the framebuffer. +// Output a string that is in the CS segment. static void -puts_cs(u16 action, const char *s) +puts_cs(struct putcinfo *action, const char *s) { - for (;; s++) { - char c = GET_VAR(CS, (u8)*s); + char *vs = (char*)s; + for (;; vs++) { + char c = GET_GLOBAL(*vs); if (!c) break; putc(action, c); } } -// Write an unsigned integer to the screen. +// Output an unsigned integer. static void -putuint(u16 action, u32 val) +putuint(struct putcinfo *action, u32 val) { char buf[12]; char *d = &buf[sizeof(buf) - 1]; @@ -72,9 +183,9 @@ putuint(u16 action, u32 val) puts(action, d); } -// Write a single digit hex character to the screen. +// Output a single digit hex character. static inline void -putsinglehex(u16 action, u32 val) +putsinglehex(struct putcinfo *action, u32 val) { if (val <= 9) val = '0' + val; @@ -83,34 +194,52 @@ putsinglehex(u16 action, u32 val) putc(action, val); } -// Write an integer in hexadecimal to the screen. +// Output an integer in hexadecimal. static void -puthex(u16 action, u32 val) +puthex(struct putcinfo *action, u32 val, int width, int spacepad) { - putsinglehex(action, (val >> 28) & 0xf); - putsinglehex(action, (val >> 24) & 0xf); - putsinglehex(action, (val >> 20) & 0xf); - putsinglehex(action, (val >> 16) & 0xf); - putsinglehex(action, (val >> 12) & 0xf); - putsinglehex(action, (val >> 8) & 0xf); - putsinglehex(action, (val >> 4) & 0xf); - putsinglehex(action, (val >> 0) & 0xf); + if (!width) { + u32 tmp = val; + width = 1; + while (tmp >>= 4) + width++; + } else if (spacepad) { + u32 tmp = val; + u32 count = 1; + while (tmp >>= 4) + count++; + if (width > count) { + count = width - count; + width -= count; + while (count--) + putc(action, ' '); + } + } + + switch (width) { + default: putsinglehex(action, (val >> 28) & 0xf); + case 7: putsinglehex(action, (val >> 24) & 0xf); + case 6: putsinglehex(action, (val >> 20) & 0xf); + case 5: putsinglehex(action, (val >> 16) & 0xf); + case 4: putsinglehex(action, (val >> 12) & 0xf); + case 3: putsinglehex(action, (val >> 8) & 0xf); + case 2: putsinglehex(action, (val >> 4) & 0xf); + case 1: putsinglehex(action, (val >> 0) & 0xf); + } } static inline int isdigit(u8 c) { - return c - '0' < 10; + return ((u8)(c - '0')) < 10; } -void -bprintf(u16 action, const char *fmt, ...) +static void +bvprintf(struct putcinfo *action, const char *fmt, va_list args) { - va_list args; - va_start(args, fmt); const char *s = fmt; for (;; s++) { - char c = GET_VAR(CS, (u8)*s); + char c = GET_GLOBAL(*(u8*)s); if (!c) break; if (c != '%') { @@ -118,16 +247,22 @@ bprintf(u16 action, const char *fmt, ...) continue; } const char *n = s+1; + int field_width = 0; + int spacepad = 1; for (;;) { - c = GET_VAR(CS, (u8)*n); + c = GET_GLOBAL(*(u8*)n); if (!isdigit(c)) break; + if (!field_width && (c == '0')) + spacepad = 0; + else + field_width = field_width * 10 + c - '0'; n++; } if (c == 'l') { // Ignore long format indicator n++; - c = GET_VAR(CS, (u8)*n); + c = GET_GLOBAL(*(u8*)n); } s32 val; const char *sarg; @@ -147,14 +282,28 @@ bprintf(u16 action, const char *fmt, ...) val = va_arg(args, s32); putuint(action, val); break; + case 'p': + /* %p always has 0x prepended */ + putc(action, '0'); + putc(action, 'x'); + field_width = 8; + spacepad = 0; case 'x': val = va_arg(args, s32); - puthex(action, val); + puthex(action, val, field_width, spacepad); break; case 'c': val = va_arg(args, int); putc(action, val); break; + case '.': + // Hack to support "%.s" - meaning string on stack. + if (GET_GLOBAL(*(u8*)(n+1)) != 's') + break; + n++; + sarg = va_arg(args, const char *); + puts(action, sarg); + break; case 's': sarg = va_arg(args, const char *); puts_cs(action, sarg); @@ -165,47 +314,268 @@ bprintf(u16 action, const char *fmt, ...) } s = n; } +} + +void +panic(const char *fmt, ...) +{ + if (CONFIG_DEBUG_LEVEL) { + va_list args; + va_start(args, fmt); + bvprintf(&debuginfo, fmt, args); + va_end(args); + debug_serial_flush(); + } + + // XXX - use PANIC PORT. + irq_disable(); + for (;;) + hlt(); +} + +void +__dprintf(const char *fmt, ...) +{ + if (!MODESEGMENT && CONFIG_THREADS && CONFIG_DEBUG_LEVEL >= DEBUG_thread + && *fmt != '\\' && *fmt != '/') { + struct thread_info *cur = getCurThread(); + if (cur != &MainThread) { + // Show "thread id" for this debug message. + putc_debug(&debuginfo, '|'); + puthex(&debuginfo, (u32)cur, 8, 0); + putc_debug(&debuginfo, '|'); + putc_debug(&debuginfo, ' '); + } + } + + va_list args; + va_start(args, fmt); + bvprintf(&debuginfo, fmt, args); + va_end(args); + debug_serial_flush(); +} + +void +printf(const char *fmt, ...) +{ + ASSERT32FLAT(); + va_list args; + va_start(args, fmt); + bvprintf(&screeninfo, fmt, args); + va_end(args); + if (ScreenAndDebug) + debug_serial_flush(); +} + + +/**************************************************************** + * snprintf + ****************************************************************/ + +struct snprintfinfo { + struct putcinfo info; + char *str, *end; +}; + +static void +putc_str(struct putcinfo *info, char c) +{ + struct snprintfinfo *sinfo = container_of(info, struct snprintfinfo, info); + if (sinfo->str >= sinfo->end) + return; + *sinfo->str = c; + sinfo->str++; +} + +// Build a formatted string. Note, this function returns the actual +// number of bytes used (not including null) even in the overflow +// case. +int +snprintf(char *str, size_t size, const char *fmt, ...) +{ + ASSERT32FLAT(); + if (!size) + return 0; + struct snprintfinfo sinfo = { { putc_str }, str, str + size }; + va_list args; + va_start(args, fmt); + bvprintf(&sinfo.info, fmt, args); + va_end(args); + char *end = sinfo.str; + if (end >= sinfo.end) + end = sinfo.end - 1; + *end = '\0'; + return end - str; +} + +// Build a formatted string - malloc'ing the memory. +char * +znprintf(size_t size, const char *fmt, ...) +{ + ASSERT32FLAT(); + if (!size) + return NULL; + char *str = malloc_tmp(size); + if (!str) { + warn_noalloc(); + return NULL; + } + struct snprintfinfo sinfo = { { putc_str }, str, str + size }; + va_list args; + va_start(args, fmt); + bvprintf(&sinfo.info, fmt, args); va_end(args); + char *end = sinfo.str; + if (end >= sinfo.end) + end = sinfo.end - 1; + *end = '\0'; + return str; +} + + +/**************************************************************** + * Misc helpers + ****************************************************************/ + +void +hexdump(const void *d, int len) +{ + int count=0; + while (len > 0) { + if (count % 8 == 0) { + putc(&debuginfo, '\n'); + puthex(&debuginfo, count*4, 8, 0); + putc(&debuginfo, ':'); + } else { + putc(&debuginfo, ' '); + } + puthex(&debuginfo, *(u32*)d, 8, 0); + count++; + len-=4; + d+=4; + } + putc(&debuginfo, '\n'); + debug_serial_flush(); } static void -dump_regs(const char *fname, const char *type, struct bregs *regs) +dump_regs(struct bregs *regs) { if (!regs) { - bprintf(0, "%s %s: NULL\n", type, fname); + dprintf(1, " NULL\n"); return; } - bprintf(0, "%s %s: a=%x b=%x c=%x d=%x si=%x di=%x\n" - , type, fname, regs->eax, regs->ebx, regs->ecx, regs->edx - , regs->esi, regs->edi); - bprintf(0, " ds=%x es=%x ip=%x cs=%x f=%x\n" - , regs->ds, regs->es - , regs->ip, regs->cs, regs->flags); + dprintf(1, " a=%08x b=%08x c=%08x d=%08x ds=%04x es=%04x ss=%04x\n" + , regs->eax, regs->ebx, regs->ecx, regs->edx + , regs->ds, regs->es, GET_SEG(SS)); + dprintf(1, " si=%08x di=%08x bp=%08x sp=%08x cs=%04x ip=%04x f=%04x\n" + , regs->esi, regs->edi, regs->ebp, (u32)®s[1] + , regs->code.seg, regs->code.offset, regs->flags); } +// Report entry to an Interrupt Service Routine (ISR). void __debug_isr(const char *fname) { - puts_cs(0, fname); - putc(0, '\n'); + puts_cs(&debuginfo, fname); + putc(&debuginfo, '\n'); + debug_serial_flush(); } // Function called on handler startup. void -__debug_enter(const char *fname, struct bregs *regs) +__debug_enter(struct bregs *regs, const char *fname) +{ + dprintf(1, "enter %s:\n", fname); + dump_regs(regs); +} + +// Send debugging output info. +void +__debug_stub(struct bregs *regs, int lineno, const char *fname) +{ + dprintf(1, "stub %s:%d:\n", fname, lineno); + dump_regs(regs); +} + +// Report on an invalid parameter. +void +__warn_invalid(struct bregs *regs, int lineno, const char *fname) +{ + if (CONFIG_DEBUG_LEVEL >= DEBUG_invalid) { + dprintf(1, "invalid %s:%d:\n", fname, lineno); + dump_regs(regs); + } +} + +// Report on an unimplemented feature. +void +__warn_unimplemented(struct bregs *regs, int lineno, const char *fname) +{ + if (CONFIG_DEBUG_LEVEL >= DEBUG_unimplemented) { + dprintf(1, "unimplemented %s:%d:\n", fname, lineno); + dump_regs(regs); + } +} + +// Report a detected internal inconsistency. +void +__warn_internalerror(int lineno, const char *fname) +{ + dprintf(1, "WARNING - internal error detected at %s:%d!\n" + , fname, lineno); +} + +// Report on an allocation failure. +void +__warn_noalloc(int lineno, const char *fname) +{ + dprintf(1, "WARNING - Unable to allocate resource at %s:%d!\n" + , fname, lineno); +} + +// Report on a timeout exceeded. +void +__warn_timeout(int lineno, const char *fname) +{ + dprintf(1, "WARNING - Timeout at %s:%d!\n", fname, lineno); +} + +// Report a handler reporting an invalid parameter to the caller. +void +__set_invalid(struct bregs *regs, int lineno, const char *fname) +{ + __warn_invalid(regs, lineno, fname); + set_invalid_silent(regs); +} + +// Report a call of an unimplemented function. +void +__set_unimplemented(struct bregs *regs, int lineno, const char *fname) { - // XXX - implement run time suppression test - dump_regs(fname, "enter", regs); + __warn_unimplemented(regs, lineno, fname); + set_invalid_silent(regs); } +// Report a handler reporting an invalid parameter code to the +// caller. Note, the lineno and return code are encoded in the same +// parameter as gcc does a better job of scheduling function calls +// when there are 3 or less parameters. void -__debug_fail(const char *fname, struct bregs *regs) +__set_code_invalid(struct bregs *regs, u32 linecode, const char *fname) { - dump_regs(fname, "fail", regs); + u8 code = linecode; + u32 lineno = linecode >> 8; + __warn_invalid(regs, lineno, fname); + set_code_invalid_silent(regs, code); } +// Report a call of an unimplemented function. void -__debug_stub(const char *fname, struct bregs *regs) +__set_code_unimplemented(struct bregs *regs, u32 linecode, const char *fname) { - dump_regs(fname, "stub", regs); + u8 code = linecode; + u32 lineno = linecode >> 8; + __warn_unimplemented(regs, lineno, fname); + set_code_invalid_silent(regs, code); }