Files
retrobsd/tools/virtualmips/gdb_interface.c
2014-04-09 14:27:18 +01:00

724 lines
18 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* Copyright (C) 1996-1998 by the Board of Trustees
* of Leland Stanford Junior University.
*
* This file is part of the SimOS distribution.
* See LICENSE file for terms of the license.
*
*/
/*
* Copyright (C) yajin 2008 <yajinzhou@gmail.com >
*
* This file is part of the virtualmips distribution.
* See LICENSE file for terms of the license.
*
*/
/*GDB interface for virtualmips based on simos*/
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/signal.h>
#include <sys/types.h>
#include <assert.h>
#include "vm.h"
#include "mips.h"
#include "gdb_interface.h"
#include "utils.h"
#include "debug.h"
#define GDB_NUM_REGS 72 //general cpu register and cp0 reegister
#define VCONT "vCont"
#define QSYMBOL "qSymbol"
#define QC "qC"
#define QOFFSETS "qOffsets"
int delete_breakpoint (vm_instance_t * vm, m_uint32_t addr, int len)
{
virtual_breakpoint_t *tmp, *prev = 0;
tmp = vm->breakpoint_head;
// addr = SIGN_EXTEND(32,addr);
while (tmp) {
if (tmp->addr == addr) {
/*find it */
if (prev)
prev->next = tmp->next;
if (vm->breakpoint_head == tmp)
vm->breakpoint_head = tmp->next;
if (vm->breakpoint_tail == tmp)
vm->breakpoint_tail = prev;
if (tmp->save)
free (tmp->save);
free (tmp);
goto exit;
}
prev = tmp;
tmp = tmp->next;
}
exit:
return SUCCESS;
}
virtual_breakpoint_t *alloc_breakpoint (vm_instance_t * vm, m_uint32_t addr,
char *save, int len)
{
virtual_breakpoint_t *tmp;
tmp = malloc (sizeof (virtual_breakpoint_t));
if (!tmp)
return NULL;
memset (tmp, 0, sizeof (*tmp));
tmp->addr = (addr);
if (save) {
tmp->save = malloc (strlen (save) + 1);
strcpy (tmp->save, save);
}
//tmp->cpuno = cpuno;
tmp->len = len;
tmp->next = 0;
return tmp;
}
int insert_breakpoint (vm_instance_t * vm, virtual_breakpoint_t * new)
{
virtual_breakpoint_t *tmp;
if (vm->breakpoint_head == 0) {
vm->breakpoint_tail = vm->breakpoint_head = new;
} else {
tmp = vm->breakpoint_head;
while (tmp) {
if (tmp->addr == new->addr) {
/*the same one */
if (new->save)
free (new->save);
free (new);
goto exit;
}
tmp = tmp->next;
}
vm->breakpoint_tail->next = new;
vm->breakpoint_tail = new;
}
exit:
return SUCCESS;
}
static int fromhex (int a)
{
if (a >= '0' && a <= '9')
return a - '0';
else if (a >= 'a' && a <= 'f')
return a - 'a' + 10;
else {
fprintf (stderr, "Request contains invalid hex digit : %s %d\n",
__FILE__, __LINE__);
return 0;
}
}
/* Convert number NIB to a hex digit. */
static int tohex (int nib)
{
if (nib < 10)
return '0' + nib;
else
return 'a' + nib - 10;
}
#if 0
static int bin2hex (const char *bin, char *hex, int count)
{
int i;
/* May use a length, or a nul-terminated string as input. */
if (count == 0)
count = strlen (bin);
for (i = 0; i < count; i++) {
*hex++ = tohex ((*bin >> 4) & 0xf);
*hex++ = tohex (*bin++ & 0xf);
}
*hex = 0;
return i;
}
#endif
static void convert_int_to_ascii (char *from, char *to, int n)
{
int nib;
char ch;
while (n--) {
ch = *from++;
nib = ((ch & 0xf0) >> 4) & 0x0f;
*to++ = tohex (nib);
nib = ch & 0x0f;
*to++ = tohex (nib);
}
*to++ = 0;
}
/*****************************************************************
* readchar
* Returns next char from remote GDB. -1 if error.
*****************************************************************/
static int readchar (int fd)
{
static char buf[BUFSIZ];
static int bufcnt = 0;
static char *bufp;
if (bufcnt-- > 0)
return *bufp++ & 0x7f;
bufcnt = read (fd, buf, BUFSIZ);
if (bufcnt <= 0) {
printf ("Simdebug readchar");
return -1;
}
bufp = buf;
bufcnt--;
return *bufp++ & 0x7f;
}
/*****************************************************************
* getpkt
*
* Read a packet from the remote machine, with error checking,
* and store it in BUF. Returns length of packet, or negative if error.
*****************************************************************/
static int getpkt (int fd, char *buf)
{
char *bp;
unsigned char csum, c1, c2;
int c;
bp = buf;
while (1) {
csum = 0;
while (1) {
c = readchar (fd);
if (c == '$')
break;
if (c < 0)
goto errout;
}
bp = buf;
while (1) {
c = readchar (fd);
if (c < 0)
goto errout;
if (c == '#')
break;
*bp++ = c;
csum += c;
}
*bp = 0;
c = readchar (fd);
if (c < 0)
goto errout;
c1 = fromhex (c);
c = readchar (fd);
if (c < 0)
goto errout;
c2 = fromhex (c);
if (csum == (c1 << 4) + c2)
break;
if (write (fd, "-", 1) < 0)
return -1;
}
if (write (fd, "+", 1) < 0)
return -1;
return bp - buf;
errout:
return -1;
}
static void write_ok (char *buf)
{
buf[0] = 'O';
buf[1] = 'K';
buf[2] = '\0';
}
static void write_enn (char *buf)
{
buf[0] = 'E';
#if 0
buf[1] = 'N';
buf[2] = 'N';
#endif
buf[1] = '0';
buf[2] = '1';
buf[3] = '\0';
}
/* Send a packet to the remote machine, with error checking.
The data of the packet is in BUF. Returns >= 0 on success, -1 otherwise. */
static int putpkt (int fd, char *buf)
{
//printf("putpkt\n");
int i;
unsigned char csum = 0;
char buf2[2000];
char buf3[1];
int cnt = strlen (buf);
char *p;
/* Copy the packet into buffer BUF2, encapsulating it
* and giving it a checksum. */
p = buf2;
*p++ = '$';
for (i = 0; i < cnt; i++) {
csum += buf[i];
*p++ = buf[i];
}
*p++ = '#';
*p++ = tohex ((csum >> 4) & 0xf);
*p++ = tohex (csum & 0xf);
/* Send it over and over until we get a positive ack. */
do {
int cc;
if (write (fd, buf2, p - buf2) != p - buf2) {
printf ("putpkt(write)\n");
goto errout;
}
cc = read (fd, buf3, 1);
if (cc <= 0) {
printf ("putpkt(read)\n");
goto errout;
}
}
while (buf3[0] != '+');
return 1; /* Success! */
errout:
return -1;
}
static void attach_gdb (vm_instance_t * vm)
{
struct sockaddr_in sockaddr1;
unsigned int tmp;
int gdb_fd;
if (!vm->gdb_debug_from_poll)
printf ("Waiting for gdb on port %d.\n", vm->gdb_port);
tmp = sizeof (sockaddr1);
gdb_fd =
accept (vm->gdb_listen_sock, (struct sockaddr *) &sockaddr1, &tmp);
vm->gdb_interact_sock = gdb_fd;
if (gdb_fd == -1) {
printf ("Debugger accept failed\n");
return;
}
if (!vm->gdb_debug_from_poll)
printf ("GDB attached. Enjoy yourself!\n");
/* Tell TCP not to delay small packets. This greatly speeds up
* interactive response. */
tmp = 1;
setsockopt (gdb_fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &tmp,
sizeof (tmp));
tmp = 1;
setsockopt (gdb_fd, IPPROTO_TCP, TCP_NODELAY, (char *) &tmp,
sizeof (tmp));
// int save_fcntl_flags;
#if defined(F_SETFL) && defined (FASYNC)
save_fcntl_flags = fcntl (gdb_fd, F_GETFL, 0);
fcntl (gdb_fd, F_SETFL, save_fcntl_flags | FASYNC);
#if defined (F_SETOWN)
fcntl (gdb_fd, F_SETOWN, getpid ());
#endif
#endif
signal (SIGIO, SIG_IGN);
// return gdb_fd;
}
#define MAX_XFER (8*1024)
#define WORD_SZ 4
#define BYTE_SZ 1
int debug_read_memory (cpu_mips_t * cpu, m_uint32_t vaddr, unsigned int len,
char *buf)
{
//void *haddr=NULL;
//m_uint32_t exc;
//m_uint32_t data;
//m_uint8_t has_set_value=FALSE;
char readout_buf[MAX_XFER];
if (len > MAX_XFER) {
fprintf (stderr, "read_memory: too many bytes requested");
}
assert (len <= MAX_XFER);
int reallen = len;
char *cur = readout_buf;
int i = 0;
//We read one byte once to avoid align problem
for (i = 0; i < (reallen / BYTE_SZ); i++) {
cpu->mips_mts_gdb_lb (cpu, vaddr, cur);
cur += BYTE_SZ;
vaddr += BYTE_SZ;
}
convert_int_to_ascii (readout_buf, buf, len);
return SUCCESS;
}
int forced_inline cpu_hit_breakpoint (vm_instance_t * vm, m_uint32_t vaddr)
{
virtual_breakpoint_t *tmp = vm->breakpoint_head;
static char cmd_buf[BUFSIZ + 1];
while (tmp) {
if (tmp->addr == vaddr) {
sprintf (cmd_buf, "S%02x", SIGTRAP);
if (putpkt (vm->gdb_interact_sock, cmd_buf) < 0) {
/* the connection was terminated prematurely. Reset */
close (vm->gdb_interact_sock);
vm->gdb_interact_sock = -1;
vm->mipsy_debug_mode = 0; //we do not debug anymore
printf ("Remote debugger connection lost, continuing...\n");
return FAILURE;
}
return SUCCESS;
}
tmp = tmp->next;
}
return FAILURE;
}
/* Convert hex digit A to a number. */
Simdebug_result Simdebug_run (vm_instance_t * vm, int sig)
{
static char cmd_buf[BUFSIZ + 1];
char *b;
int i = 0;
Simdebug_result ret = SD_CONTINUE;
if (vm->gdb_interact_sock <= 0) {
attach_gdb (vm);
if (vm->gdb_interact_sock < 0) {
return SD_CONTINUE; /* could not get debugger */
}
vm->gdb_debug_from_poll = 0; /* whether or not we entered from poll doesn't
* matter this time, but we'd better reset it
* before next time this function runs
*/
} else {
/* reestablish an old connection. We might get here either
* for internal reasons (hitting a breakpoint) or for external
* reasons (gdb sent us a ^C). If external, we need to swallow
* the break packet. Then in either case, let GDB know why
* we stopped.
*/
if (vm->gdb_debug_from_poll) {
vm->gdb_debug_from_poll = 0;
int cc;
char c;
cc = read (vm->gdb_interact_sock, &c, 1);
if (cc != 1 || c != '\003') {
fprintf (stderr, "input_interrupt, cc = %d c = %d\n", cc, c);
goto errout;
}
sig = SIGINT;
/* Inform the remote debugger we have entered debugging mode. */
// sprintf (cmd_buf, "S%02x%02x", sig, debug_cpuno);
sprintf (cmd_buf, "S%02x", sig);
if (putpkt (vm->gdb_interact_sock, cmd_buf) < 0)
goto errout;
}
}
while (1) {
char ch;
i = 0;
int pkg_len = 0;
if ((pkg_len = getpkt (vm->gdb_interact_sock, cmd_buf)) <= 0)
goto errout;
ch = cmd_buf[i++];
SIM_DEBUG (('g', "gdb command %c \n", ch));
//printf("---\n gdb command %s\n",cmd_buf);
switch (ch) {
case 'c':
ret = SD_CONTINUE;
goto out;
case 'g': //read register
{
m_uint32_t r = 0;
b = cmd_buf;
for (i = 0; i < GDB_NUM_REGS; i++) {
//FIXME: find the mapping from register index to register of cpu and cpu0 of gdb
if (vm->boot_cpu->reg_get (vm->boot_cpu, i,
&r) == FAILURE)
r = 0;
/*
* flip the bytes around
*/
r = htovm32 (r); //gdb likes the data of target machine format
convert_int_to_ascii ((char *) &r, b, 4);
b += 8;
}
*b = 0;
break;
}
case 'H':
write_ok (cmd_buf);
break;
case 'm':
{
m_uint32_t memaddr;
unsigned int len;
if (sscanf (cmd_buf + 1, "%x,%x", &memaddr, &len) != 2) {
write_enn (cmd_buf);
} else {
if (debug_read_memory (vm->boot_cpu, memaddr, len,
cmd_buf) != SUCCESS)
write_enn (cmd_buf);
}
break;
}
case 'p':
{
m_uint32_t r, index = 0;
b = cmd_buf;
if (sscanf (cmd_buf + 1, "%x", &index) != 1)
write_enn (cmd_buf);
else {
// index=vmtoh32(index);
if (vm->boot_cpu->reg_get (vm->boot_cpu, index,
&r) == FAILURE)
r = 0;
/*
* flip the bytes around
*/
r = htovm32 (r); //gdb likes the data of target machine format
convert_int_to_ascii ((char *) &r, b, 4);
//*(r+4)=0;
//*(r+5)=0;
//*(r+6)=0;
//*(r+7)=0;
}
}
break;
case 'v':
{
char *cmd_tmp;
cmd_tmp = cmd_buf;
if (strncmp (cmd_buf, VCONT, strlen (VCONT)) != 0)
write_enn (cmd_buf);
else {
cmd_tmp += strlen (VCONT);
if (';' == cmd_tmp[0]) {
cmd_tmp++;
if ('c' == cmd_tmp[0]) {
ret = SD_CONTINUE;
goto out;
} else if ('s' == cmd_tmp[0]) {
/*single step */
int cpuno;
cpuno = 0;
ret = cpuno;
goto out;
} else
write_enn (cmd_buf);
} else if ('?' == (cmd_tmp)[0]) {
/*query */
strcpy (cmd_buf, VCONT ";c");
} else
write_enn (cmd_buf);
}
}
break;
case 'q':
{
if (strncmp (cmd_buf, QSYMBOL, strlen (QSYMBOL)) == 0)
write_ok (cmd_buf);
else if (strncmp (cmd_buf, QC, strlen (QC)) == 0) {
strcpy (cmd_buf, "QC0");
} else if (strncmp (cmd_buf, QOFFSETS,
strlen (QOFFSETS)) == 0) {
strcpy (cmd_buf, "Text=0;Data=0;Bss=0");
} else {
write_enn (cmd_buf);
}
}
break;
case 's':
case 'S':
ret = SD_NEXTI_ANYCPU;
goto out;
case 'z':
{
m_uint32_t addr;
int len;
if ('0' == (cmd_buf + 1)[0]) {
//memory breakpoint
if (sscanf (cmd_buf + 3, "%x,%x", &addr, &len) != 2) {
write_enn (cmd_buf);
}
else {
if (delete_breakpoint (vm, addr, len) != SUCCESS)
write_enn (cmd_buf);
else {
write_ok (cmd_buf);
}
}
}
else
write_enn (cmd_buf);
}
break;
case 'Z':
{
m_uint32_t addr;
int len;
if ('0' == (cmd_buf + 1)[0]) {
//memory breakpoint
if (sscanf (cmd_buf + 3, "%x,%x", &addr, &len) != 2) {
write_enn (cmd_buf);
}
else {
virtual_breakpoint_t *tmp =
alloc_breakpoint (vm, addr, 0, len);
insert_breakpoint (vm, tmp);
write_ok (cmd_buf);
}
} else
write_enn (cmd_buf);
}
break;
case '?':
sprintf (cmd_buf, "S%02x", sig);
break;
default:
write_enn (cmd_buf);
break;
}
//printf("send %s\n",cmd_buf);
if (putpkt (vm->gdb_interact_sock, cmd_buf) < 0)
goto errout;
}
out:
return ret;
errout:
/* the connection was terminated prematurely. Reset */
close (vm->gdb_interact_sock);
vm->gdb_interact_sock = -1;
printf ("Remote debugger connection lost, continuing...\n");
return SD_CONTINUE;
}
void bad_memory_access_gdb (vm_instance_t * vm)
{
static char cmd_buf[BUFSIZ + 1];
vm->mipsy_break_nexti = MIPS_BREAKANYCPU;
sprintf (cmd_buf, "S%02x", SIGTRAP);
if (putpkt (vm->gdb_interact_sock, cmd_buf) < 0) {
/* the connection was terminated prematurely. Reset */
close (vm->gdb_interact_sock);
vm->gdb_interact_sock = -1;
vm->mipsy_debug_mode = 0; //we do not debug anymore
printf ("Remote debugger connection lost, continuing...\n");
}
}