summaryrefslogtreecommitdiffstats
path: root/qemu/roms/seabios/src/output.c
diff options
context:
space:
mode:
Diffstat (limited to 'qemu/roms/seabios/src/output.c')
-rw-r--r--qemu/roms/seabios/src/output.c564
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)&regs[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);
+}