metal-cos/sys/kern/debug.c

304 lines
6.4 KiB
C

/*
* Copyright (c) 2012-2014 Stanford University
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <sys/kassert.h>
#include <sys/kdebug.h>
#include "../dev/console.h"
#define DEBUG_MAX_LINE 128
#define DEBUG_MAX_ARGS 16
void
Debug_PrintHex(const char *data, size_t length, off_t off, size_t limit)
{
const size_t row_size = 16;
bool stop = false;
size_t row;
for (row = 0; !stop; row++) {
size_t ix = row * row_size;
if (ix >= limit || ix >= length)
return;
kprintf("%08lx ", row * row_size);
size_t col;
for (col = 0; col < row_size; col++) {
size_t ix = row * row_size + col;
if ((limit != 0 && ix >= limit) || ix >= length) {
stop = true;
for (; col < row_size; col++) {
kprintf(" ");
}
break;
}
ix += off;
kprintf("%02X ", (unsigned char)data[ix]);
}
kprintf(" |");
for (col = 0; col < row_size; col++) {
size_t ix = row * row_size + col;
if ((limit != 0 && ix >= limit) || ix >= length) {
stop = true;
for (; col < row_size; col++) {
kprintf(" ");
}
break;
}
ix += off;
unsigned char c = (unsigned char)data[ix];
if (c >= 0x20 && c < 0x7F)
kprintf("%c", c);
else
kprintf(".");
}
kprintf("|");
kprintf("\n");
}
}
uint64_t
Debug_GetValue(uintptr_t addr, int size, bool isSigned)
{
switch (size)
{
case 1: {
uint8_t *val = (uint8_t *)addr;
if (isSigned && ((*val & 0x80) == 0x80)) {
return (uint64_t)*val | 0xFFFFFFFFFFFFFF00ULL;
}
return *val;
}
case 2: {
uint16_t *val = (uint16_t *)addr;
if (isSigned && ((*val & 0x8000) == 0x8000)) {
return (uint64_t)*val | 0xFFFFFFFFFFFF0000ULL;
}
return *val;
}
case 4: {
uint32_t *val = (uint32_t *)addr;
if (isSigned && ((*val & 0x80000000) == 0x80000000)) {
return (uint64_t)*val | 0xFFFFFFFF00000000ULL;
}
return *val;
}
case 8: {
uint64_t *val = (uint64_t *)addr;
return *val;
}
default: {
kprintf("Debug_GetValue: Unknown size parameter '%d'\n", size);
return (uint64_t)-1;
}
}
}
void
Debug_PrintSymbol(uintptr_t off, int strategy)
{
kprintf("0x%llx", off);
}
uint64_t
Debug_StrToInt(const char *s)
{
int i = 0;
int base = 10;
uint64_t val = 0;
if (s[0] == '0' && s[1] == 'x')
{
base = 16;
i = 2;
}
while (s[i] != '\0') {
if (s[i] >= '0' && s[i] <= '9') {
val = val * base + (uint64_t)(s[i] - '0');
} else if (s[i] >= 'a' && s[i] <= 'f') {
if (base != 16)
kprintf("Not base 16!\n");
val = val * base + (uint64_t)(s[i] - 'a' + 10);
} else if (s[i] >= 'A' && s[i] <= 'F') {
if (base != 16)
kprintf("Not base 16!\n");
val = val * base + (uint64_t)(s[i] - 'A' + 10);
} else {
kprintf("Not a number!\n");
}
i++;
}
return val;
}
uint64_t
Debug_SymbolToInt(const char *s)
{
if (*s >= '0' || *s <= '9')
return Debug_StrToInt(s);
kprintf("Unknown symbol '%s'\n");
return 0;
}
#define PHELP(_cmd, _msg) kprintf("%-16s %s\n", _cmd, _msg)
extern DebugCommand __kdbgcmd_start[];
extern DebugCommand __kdbgcmd_end[];
void
Debug_Help(int argc, const char *argv[])
{
int i;
uintptr_t commands = (uintptr_t)&__kdbgcmd_end - (uintptr_t)&__kdbgcmd_start;
commands /= sizeof(DebugCommand);
DebugCommand *cmds = (DebugCommand *)&__kdbgcmd_start;
kprintf("Commands:\n");
for (i = 0; i < commands; i++)
{
kprintf("%-16s %s\n", cmds[i].name, cmds[i].description);
}
PHELP("continue", "Continue execution");
}
REGISTER_DBGCMD(help, "Display the list of commands", Debug_Help);
void
Debug_Echo(int argc, const char *argv[])
{
int i;
for (i = 1; i < argc; i++)
{
kprintf("%s ", argv[i]);
}
kprintf("\n");
}
REGISTER_DBGCMD(echo, "Echo arguments", Debug_Echo);
void
Debug_Dump(int argc, const char *argv[])
{
uint64_t off, len;
if (argc != 3)
{
kprintf("Dump requires 3 arguments\n");
return;
}
off = Debug_SymbolToInt(argv[1]);
len = Debug_SymbolToInt(argv[2]);
kprintf("Dump 0x%llx 0x%llx\n", off, len);
Debug_PrintHex((const char *)off, len, 0, len);
}
REGISTER_DBGCMD(dump, "Dump a region of memory", Debug_Dump);
void
Debug_Disasm(int argc, const char *argv[])
{
uintptr_t off;
int len = 1;
if (argc != 2 && argc != 3) {
kprintf("Disasm requires 2 or 3 arguments\n");
return;
}
off = Debug_SymbolToInt(argv[1]);
if (argc == 3)
len = Debug_SymbolToInt(argv[2]);
kprintf("Disassembly 0x%llx:\n", off);
for (; len > 0; len--)
{
off = db_disasm(off, false);
}
}
REGISTER_DBGCMD(disasm, "Disassemble", Debug_Disasm);
void
Debug_Prompt()
{
int argc;
char *argv[DEBUG_MAX_ARGS];
char *nextArg;
char buf[DEBUG_MAX_LINE];
kprintf("Entered Debugger!\n");
/*
* DebugCommand must be 128 bytes for the Section array to align properly
*/
ASSERT(sizeof(DebugCommand) == 128);
while (1) {
kprintf("> ");
// read input
Console_Gets((char *)&buf, DEBUG_MAX_LINE);
// parse input
argv[0] = buf;
for (argc = 1; argc < DEBUG_MAX_ARGS; argc++) {
nextArg = strchr(argv[argc - 1], ' ');
if (nextArg == NULL)
{
break;
}
*nextArg = '\0';
argv[argc] = nextArg + 1;
}
if (strcmp(argv[0], "continue") == 0) {
return; // Continue
} else {
// execute command
int i;
uintptr_t commands = (uintptr_t)&__kdbgcmd_end - (uintptr_t)&__kdbgcmd_start;
commands /= sizeof(DebugCommand);
DebugCommand *cmds = (DebugCommand *)&__kdbgcmd_start;
bool found = false;
for (i = 0; i < commands; i++)
{
if (strcmp(argv[0], cmds[i].name) == 0)
{
cmds[i].func(argc, (const char **)argv);
found = true;
}
}
if (!found)
kprintf("Unknown command '%s'\n", argv[0]);
}
}
}