diff options
author | 2015-08-28 09:58:54 +0800 | |
---|---|---|
committer | 2015-09-01 12:44:00 +0800 | |
commit | e44e3482bdb4d0ebde2d8b41830ac2cdb07948fb (patch) | |
tree | 66b09f592c55df2878107a468a91d21506104d3f /qemu/roms/seabios/src/output.c | |
parent | 9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 (diff) |
Add qemu 2.4.0
Change-Id: Ic99cbad4b61f8b127b7dc74d04576c0bcbaaf4f5
Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Diffstat (limited to 'qemu/roms/seabios/src/output.c')
-rw-r--r-- | qemu/roms/seabios/src/output.c | 564 |
1 files changed, 564 insertions, 0 deletions
diff --git a/qemu/roms/seabios/src/output.c b/qemu/roms/seabios/src/output.c new file mode 100644 index 000000000..45397b3f6 --- /dev/null +++ b/qemu/roms/seabios/src/output.c @@ -0,0 +1,564 @@ +// Raw screen writing and debug output code. +// +// Copyright (C) 2008-2013 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include <stdarg.h> // va_list + +#include "farptr.h" // GET_VAR +#include "bregs.h" // struct bregs +#include "config.h" // CONFIG_* +#include "biosvar.h" // GET_GLOBAL +#include "hw/serialio.h" // serial_debug_putc +#include "malloc.h" // malloc_tmp +#include "output.h" // dprintf +#include "stacks.h" // call16_int +#include "string.h" // memset +#include "util.h" // ScreenAndDebug + +struct putcinfo { + void (*func)(struct putcinfo *info, char c); +}; + + +/**************************************************************** + * Debug output + ****************************************************************/ + +void +debug_banner(void) +{ + dprintf(1, "SeaBIOS (version %s)\n", VERSION); +} + +// Write a character to debug port(s). +static void +debug_putc(struct putcinfo *action, char c) +{ + if (! CONFIG_DEBUG_LEVEL) + return; + qemu_debug_putc(c); + if (!MODESEGMENT) + coreboot_debug_putc(c); + serial_debug_putc(c); +} + +// Flush any pending output to debug port(s). +static void +debug_flush(void) +{ + serial_debug_flush(); +} + +// In segmented mode just need a dummy variable (debug_putc is always +// used anyway), and in 32bit flat mode need a pointer to the 32bit +// instance of debug_putc(). +#if MODE16 +static struct putcinfo debuginfo VAR16; +#elif MODESEGMENT +static struct putcinfo debuginfo VAR32SEG; +#else +static struct putcinfo debuginfo = { debug_putc }; +#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; + br.bl = 0x07; + call16_int(0x10, &br); +} + +// Handle a character from a printf request. +static void +screen_putc(struct putcinfo *action, char c) +{ + if (ScreenAndDebug) + debug_putc(&debuginfo, c); + if (c == '\n') + screenc('\r'); + screenc(c); +} + +static struct putcinfo screeninfo = { screen_putc }; + + +/**************************************************************** + * Xprintf code + ****************************************************************/ + +// Output a character. +static void +putc(struct putcinfo *action, char c) +{ + if (MODESEGMENT) { + // Only debugging output supported in segmented mode. + debug_putc(action, c); + return; + } + + void (*func)(struct putcinfo *info, char c) = GET_GLOBAL(action->func); + func(action, c); +} + +// Ouptut a string. +static void +puts(struct putcinfo *action, const char *s) +{ + if (!MODESEGMENT && !s) + s = "(NULL)"; + for (; *s; s++) + putc(action, *s); +} + +// Output a string that is in the CS segment. +static void +puts_cs(struct putcinfo *action, const char *s) +{ + char *vs = (char*)s; + for (;; vs++) { + char c = GET_GLOBAL(*vs); + if (!c) + break; + putc(action, c); + } +} + +// Output an unsigned integer. +static void +putuint(struct putcinfo *action, u32 val) +{ + char buf[12]; + char *d = &buf[sizeof(buf) - 1]; + *d-- = '\0'; + for (;;) { + *d = (val % 10) + '0'; + val /= 10; + if (!val) + break; + d--; + } + puts(action, d); +} + +// Output a single digit hex character. +static inline void +putsinglehex(struct putcinfo *action, u32 val) +{ + if (val <= 9) + val = '0' + val; + else + val = 'a' + val - 10; + putc(action, val); +} + +// Output an integer in hexadecimal with a specified width. +static void +puthex(struct putcinfo *action, u32 val, int width) +{ + 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); + } +} + +// Output an integer in hexadecimal with a minimum width. +static void +putprettyhex(struct putcinfo *action, u32 val, int width, char padchar) +{ + u32 tmp = val; + int count = 1; + while (tmp >>= 4) + count++; + width -= count; + while (width-- > 0) + putc(action, padchar); + puthex(action, val, count); +} + +static inline int +isdigit(u8 c) +{ + return ((u8)(c - '0')) < 10; +} + +static void +bvprintf(struct putcinfo *action, const char *fmt, va_list args) +{ + const char *s = fmt; + for (;; s++) { + char c = GET_GLOBAL(*(u8*)s); + if (!c) + break; + if (c != '%') { + putc(action, c); + continue; + } + const char *n = s+1; + int field_width = 0; + char padchar = ' '; + u8 is64 = 0; + for (;;) { + c = GET_GLOBAL(*(u8*)n); + if (!isdigit(c)) + break; + if (!field_width && (c == '0')) + padchar = '0'; + else + field_width = field_width * 10 + c - '0'; + n++; + } + if (c == 'l') { + // Ignore long format indicator + n++; + c = GET_GLOBAL(*(u8*)n); + } + if (c == 'l') { + is64 = 1; + n++; + c = GET_GLOBAL(*(u8*)n); + } + s32 val; + const char *sarg; + switch (c) { + case '%': + putc(action, '%'); + break; + case 'd': + val = va_arg(args, s32); + if (is64) + va_arg(args, s32); + if (val < 0) { + putc(action, '-'); + val = -val; + } + putuint(action, val); + break; + case 'u': + val = va_arg(args, s32); + if (is64) + va_arg(args, s32); + putuint(action, val); + break; + case 'p': + val = va_arg(args, s32); + putc(action, '0'); + putc(action, 'x'); + puthex(action, val, 8); + break; + case 'x': + val = va_arg(args, s32); + if (is64) { + u32 upper = va_arg(args, s32); + if (upper) { + putprettyhex(action, upper, field_width - 8, padchar); + puthex(action, val, 8); + break; + } + } + putprettyhex(action, val, field_width, padchar); + 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); + break; + default: + putc(action, '%'); + n = s; + } + 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_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. + debug_putc(&debuginfo, '|'); + puthex(&debuginfo, (u32)cur, 8); + debug_putc(&debuginfo, '|'); + debug_putc(&debuginfo, ' '); + } + } + + va_list args; + va_start(args, fmt); + bvprintf(&debuginfo, fmt, args); + va_end(args); + debug_flush(); +} + +void +printf(const char *fmt, ...) +{ + ASSERT32FLAT(); + va_list args; + va_start(args, fmt); + bvprintf(&screeninfo, fmt, args); + va_end(args); + if (ScreenAndDebug) + debug_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); + putc(&debuginfo, ':'); + } else { + putc(&debuginfo, ' '); + } + puthex(&debuginfo, *(u32*)d, 8); + count++; + len-=4; + d+=4; + } + putc(&debuginfo, '\n'); + debug_flush(); +} + +static void +dump_regs(struct bregs *regs) +{ + if (!regs) { + dprintf(1, " NULL\n"); + return; + } + 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(&debuginfo, fname); + putc(&debuginfo, '\n'); + debug_flush(); +} + +// Function called on handler startup. +void +__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) +{ + __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 +__set_code_invalid(struct bregs *regs, u32 linecode, const char *fname) +{ + 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 +__set_code_unimplemented(struct bregs *regs, u32 linecode, const char *fname) +{ + u8 code = linecode; + u32 lineno = linecode >> 8; + __warn_unimplemented(regs, lineno, fname); + set_code_invalid_silent(regs, code); +} |