* Copyright 2005-2007, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*
* Copyright 2002, Manuel J. Petit. All rights reserved.
* Distributed under the terms of the NewOS License.
*/
#include "gdb.h"
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <ByteOrder.h>
#include <arch/debug.h>
#include <arch/debug_console.h>
#include <debug.h>
#include <elf.h>
#include <elf_priv.h>
#include <smp.h>
#include <vm/vm.h>
enum { INIT = 0, CMDREAD, CKSUM1, CKSUM2, WAITACK, QUIT, GDBSTATES };
static char sCommand[512];
static int sCommandIndex;
static int sCheckSum;
static char sReply[512];
static char sSafeMemory[512];
static int
parse_nibble(int input)
{
int nibble = 0xff;
if (input >= '0' && input <= '9')
nibble = input - '0';
if (input >= 'A' && input <= 'F')
nibble = 0x0a + input - 'A';
if (input >= 'a' && input <= 'f')
nibble = 0x0a + input - 'a';
return nibble;
}
static void
gdb_ack(void)
{
arch_debug_serial_putchar('+');
}
static void
gdb_nak(void)
{
arch_debug_serial_putchar('-');
}
static void
gdb_resend_reply(void)
{
arch_debug_serial_puts(sReply);
}
static void
gdb_reply(char const* format, ...)
{
int i;
int len;
int sum;
va_list args;
va_start(args, format);
sReply[0] = '$';
vsprintf(sReply + 1, format, args);
va_end(args);
len = strlen(sReply);
sum = 0;
for (i = 1; i < len; i++) {
sum += sReply[i];
}
sum %= 256;
sprintf(sReply + len, "#%02x", sum);
gdb_resend_reply();
}
static void
gdb_regreply()
{
sReply[0] = '$';
ssize_t bytesWritten = arch_debug_gdb_get_registers(sReply + 1,
sizeof(sReply) - 1);
if (bytesWritten < 0) {
gdb_reply("E01");
return;
}
bytesWritten++;
int sum = 0;
for (int32 i = 1; i < bytesWritten; i++)
sum += sReply[i];
sum %= 256;
int result = snprintf(sReply + bytesWritten, sizeof(sReply) - bytesWritten,
"#%02x", sum);
if (result >= (ssize_t)sizeof(sReply) - bytesWritten) {
gdb_reply("E01");
return;
}
gdb_resend_reply();
}
static void
gdb_memreply(char const* bytes, int numbytes)
{
int i;
int len;
int sum;
sReply[0] = '$';
for (i = 0; i < numbytes; i++)
sprintf(sReply + 1 + 2 * i, "%02x", (uint8)bytes[i]);
len = strlen(sReply);
sum = 0;
for (i = 1; i < len; i++)
sum += sReply[i];
sum %= 256;
sprintf(sReply + len, "#%02x", sum);
gdb_resend_reply();
}
static int
gdb_verify_checksum(void)
{
int i;
int len;
int sum;
len = strlen(sCommand);
sum = 0;
for (i = 0; i < len; i++)
sum += sCommand[i];
sum %= 256;
return (sum == sCheckSum) ? 1 : 0;
}
static int
gdb_parse_command(void)
{
if (!gdb_verify_checksum()) {
gdb_nak();
return INIT;
} else
gdb_ack();
switch (sCommand[0]) {
case '?':
gdb_reply("S09");
break;
case 'H':
gdb_reply("OK");
break;
case 'q':
{
if (strcmp(sCommand + 1, "Supported") == 0) {
gdb_reply("");
} else if (strcmp(sCommand + 1, "Offsets") == 0) {
elf_image_info* kernelImage = elf_get_kernel_image();
gdb_reply("Text=%lx;Data=%lx;Bss=%lx",
kernelImage->text_region.delta,
kernelImage->data_region.delta,
kernelImage->data_region.delta);
} else
gdb_reply("");
break;
}
case 'c':
return QUIT;
case 'g':
gdb_regreply();
break;
case 'G':
gdb_reply("E01");
break;
case 'm':
{
char* ptr;
addr_t address;
size_t len;
ptr = sCommand + 1;
address = 0;
len = 0;
while (ptr && *ptr && (*ptr != ',')) {
address <<= 4;
address += parse_nibble(*ptr);
ptr += 1;
}
if (*ptr == ',')
ptr += 1;
while (ptr && *ptr) {
len <<= 4;
len += parse_nibble(*ptr);
ptr += 1;
}
if (len > 128)
len = 128;
if (debug_memcpy(B_CURRENT_TEAM, sSafeMemory, (char*)address, len)
< 0) {
gdb_reply("E02");
} else
gdb_memreply(sSafeMemory, len);
break;
}
case 'D':
return QUIT;
case 'k':
return QUIT;
case 's':
gdb_reply("E01");
break;
default:
gdb_reply("");
break;
}
return WAITACK;
}
static int
gdb_init_handler(int input)
{
switch (input) {
case '$':
memset(sCommand, 0, sizeof(sCommand));
sCommandIndex = 0;
return CMDREAD;
default:
#if 0
gdb_nak();
#else
#endif
return INIT;
}
}
static int
gdb_cmdread_handler(int input)
{
switch (input) {
case '#':
return CKSUM1;
default:
sCommand[sCommandIndex] = input;
sCommandIndex += 1;
return CMDREAD;
}
}
static int
gdb_cksum1_handler(int input)
{
int nibble = parse_nibble(input);
if (nibble == 0xff) {
#if 0
gdb_nak();
return INIT;
#else
#endif
}
sCheckSum = nibble << 4;
return CKSUM2;
}
static int
gdb_cksum2_handler(int input)
{
int nibble = parse_nibble(input);
if (nibble == 0xff) {
#if 0
gdb_nak();
return INIT;
#else
#endif
}
sCheckSum += nibble;
return gdb_parse_command();
}
static int
gdb_waitack_handler(int input)
{
switch (input) {
case '+':
return INIT;
case '-':
gdb_resend_reply();
return WAITACK;
default:
gdb_nak();
return INIT;
}
}
static int
gdb_quit_handler(int input)
{
(void)(input);
return QUIT;
}
static int (*dispatch_table[GDBSTATES])(int) = {
&gdb_init_handler,
&gdb_cmdread_handler,
&gdb_cksum1_handler,
&gdb_cksum2_handler,
&gdb_waitack_handler,
&gdb_quit_handler
};
static int
gdb_state_dispatch(int curr, int input)
{
if (curr < INIT || curr >= GDBSTATES)
return QUIT;
return dispatch_table[curr](input);
}
static int
gdb_state_machine(void)
{
int state = INIT;
int c;
while (state != QUIT) {
c = arch_debug_serial_getchar();
state = gdb_state_dispatch(state, c);
}
return 0;
}
int
cmd_gdb(int argc, char** argv)
{
(void)(argc);
(void)(argv);
return gdb_state_machine();
}