vmware-svga/lib/metalkit/console.c
2009-04-13 07:05:42 +00:00

292 lines
7.1 KiB
C

/* -*- Mode: C; c-basic-offset: 3 -*-
*
* console.c - Abstract text console
*
* This file is part of Metalkit, a simple collection of modules for
* writing software that runs on the bare metal. Get the latest code
* at http://svn.navi.cx/misc/trunk/metalkit/
*
* Copyright (c) 2008-2009 Micah Dowty
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "types.h"
#include "console.h"
#include "intr.h"
ConsoleInterface gConsole;
/*
* Console_WriteString --
*
* Write a NUL-terminated string.
*/
fastcall void
Console_WriteString(const char *str)
{
char c;
while ((c = *(str++))) {
Console_WriteChar(c);
}
}
/*
* Console_WriteUInt32 --
*
* Write a positive 32-bit integer with arbitrary base from 2 to
* 16, up to 'digits' characters long. If 'padding' is non-NUL,
* this character is used for leading digits that would be zero.
* If padding is NUL, leading digits are suppressed entierly.
*/
fastcall void
Console_WriteUInt32(uint32 num, int digits, char padding, int base, Bool suppressZero)
{
if (digits == 0) {
return;
}
Console_WriteUInt32(num / base, digits - 1, padding, base, TRUE);
if (num == 0 && suppressZero) {
if (padding) {
Console_WriteChar(padding);
}
} else {
uint8 digit = num % base;
Console_WriteChar(digit >= 10 ? digit - 10 + 'A' : digit + '0');
}
}
/*
* Console_Format --
* Console_FormatV --
*
* Write a formatted string. This is for the most part a tiny
* subset of printf(). Supports the standard %c, %s, %d, %u,
* and %X specifiers.
*
* Deviates from a standard printf() in a few ways, in the interest
* of low-level utility and small code size:
*
* - Adds a nonstandard %b specifier, for binary numbers.
* - Width specifiers set an exact width, not a minimum width.
* - %x is treated as %X.
*/
void
Console_Format(const char *fmt, ...)
{
Console_FormatV(&fmt);
}
fastcall void
Console_FormatV(const char **args)
{
char c;
const char *fmt = *(args++);
while ((c = *(fmt++))) {
int width = 0;
Bool isSigned = FALSE;
char padding = '\0';
if (c != '%') {
Console_WriteChar(c);
continue;
}
while ((c = *(fmt++))) {
if (c == '0' && width == 0) {
/* If we get a leading 0 in the width specifier, turn on zero-padding */
padding = '0';
continue;
}
if (c >= '0' && c <= '9') {
/* Add another digit to the width specifier */
width = (width * 10) + (c - '0');
if (padding == '\0') {
padding = ' ';
}
continue;
}
/*
* Any other character means the width specifier has
* ended. If it's still zero, set the defaults.
*/
if (width == 0) {
width = 32;
}
/*
* Non-integer format specifiers
*/
if (c == 's') {
Console_WriteString((char*) *(args++));
break;
}
if (c == 'c') {
Console_WriteChar((char)(uint32) *(args++));
break;
}
/*
* Integers of different bases
*/
int base = 0;
if (c == 'X' || c == 'x') {
base = 16;
} else if (c == 'd') {
base = 10;
isSigned = TRUE;
} else if (c == 'u') {
base = 10;
} else if (c == 'b') {
base = 2;
}
if (base) {
uint32 value = (uint32)*(args++);
/*
* Print the sign for negative numbers.
*/
if (isSigned && 0 > (int32)value) {
Console_WriteChar('-');
width--;
value = -value;
}
Console_WriteUInt32(value, width, padding, base, FALSE);
break;
}
/* Unrecognized */
Console_WriteChar(c);
break;
}
}
}
/*
* Console_HexDump --
*
* Write a 32-bit hex dump to the console, labelling each
* line with addresses starting at 'startAddr'.
*/
fastcall void
Console_HexDump(uint32 *data, uint32 startAddr, uint32 numWords)
{
while (numWords) {
int32 lineWords = 4;
Console_Format("%08x:", startAddr);
while (numWords && lineWords) {
Console_Format(" %08x", *data);
data++;
startAddr += 4;
numWords--;
lineWords--;
}
Console_WriteChar('\n');
}
}
/*
* Console_UnhandledFault --
*
* Display a fatal error message with register and stack trace when
* an unhandled fault occurs. This fault handler must be installed
* using the Intr module.
*/
void
Console_UnhandledFault(int vector)
{
IntrContext *ctx = Intr_GetContext(vector);
/*
* Using a regular inline string constant, the linker can't
* optimize out this string when the function isn't used.
*/
static const char faultFmt[] =
"Fatal error:\n"
"Unhandled fault %d at %04x:%08x\n"
"\n"
"eax=%08x ebx=%08x ecx=%08x edx=%08x\n"
"esi=%08x edi=%08x esp=%08x ebp=%08x\n"
"eflags=%032b\n"
"\n";
Console_BeginPanic();
/*
* IntrContext's stack pointer includes the three values that were
* pushed by the hardware interrupt. Advance past these, so the
* stack trace shows the state of execution at the time of the
* fault rather than at the time our interrupt trampoline was
* invoked.
*/
ctx->esp += 3 * sizeof(int);
Console_Format(faultFmt,
vector, ctx->cs, ctx->eip,
ctx->eax, ctx->ebx, ctx->ecx, ctx->edx,
ctx->esi, ctx->edi, ctx->esp, ctx->ebp,
ctx->eflags);
Console_HexDump((void*)ctx->esp, ctx->esp, 64);
Console_Flush();
Intr_Disable();
Intr_Halt();
}
/*
* Console_Panic --
*
* Default panic handler. Prints a caller-defined message, and
* halts the machine.
*/
void
Console_Panic(const char *fmt, ...)
{
Console_BeginPanic();
Console_WriteString("Panic:\n");
Console_FormatV(&fmt);
Console_Flush();
Intr_Disable();
Intr_Halt();
}