/* This file is part of SIS (SPARC/RISCV instruction simulator)
Copyright (C) 2019 Free Software Foundation, Inc.
Contributed by Jiri Gaisler, European Space Agency
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
/* This code based on socket example at
* https://www.geeksforgeeks.org/socket-programming-cc/
* and on sparc-stub.c from gdb.
*/
#include
#include
#ifdef WIN32
#include
#else
#define WSAPOLLFD struct pollfd
#define WSAPoll poll
#include
#include
#include
#include
#include
#endif
#include
#include
#include
#include
#include "sis.h"
#define EBREAK 0x00100073
#define CEBREAK 0x90002
#ifndef SIGTRAP
#define SIGTRAP 5
#endif
#ifndef WIN32
#define closesocket close
#endif
int new_socket;
static char sendbuf[2048] = "$";
static const char hexchars[] = "0123456789abcdef";
static int detach = 0;
int
create_socket (int port)
{
int server_fd;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof (address);
struct protoent *proto;
#ifdef WIN32
WORD wver;
WSADATA wsaData;
wver = MAKEWORD (2, 0);
if (WSAStartup (wver, &wsaData))
return 0;
#endif
// Creating socket file descriptor
if ((server_fd = socket (AF_INET, SOCK_STREAM, 0)) == 0)
{
perror ("socket failed");
return 0;
}
// Forcefully attaching socket to the port
if (setsockopt
(server_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, sizeof (opt)))
{
perror ("setsockopt");
return 0;
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons (port);
// Forcefully attaching socket to the port
if (bind (server_fd, (struct sockaddr *) &address, sizeof (address)) < 0)
{
perror ("bind failed");
return 0;
}
if (listen (server_fd, 1) < 0)
{
perror ("listen");
return 0;
}
if ((new_socket = accept (server_fd, (struct sockaddr *) &address,
(int *) &addrlen)) < 0)
{
perror ("accept");
return 0;
}
closesocket (server_fd);
setsockopt (new_socket, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt,
sizeof (opt));
proto = getprotobyname ("tcp");
setsockopt (new_socket, proto->p_proto, TCP_NODELAY, (char *) &opt,
sizeof (opt));
#ifndef WIN32
fcntl (new_socket, F_SETOWN, getpid ());
#endif
return 1;
}
/* poll socket periodically to detect gdb break */
void
socket_poll ()
{
WSAPOLLFD fdarray = { 0 };
int ret;
fdarray.fd = new_socket;
fdarray.events = POLLRDNORM;
ret = WSAPoll (&fdarray, 1, 0);
if (ret)
ctrl_c = 1;
event (socket_poll, 0, 10000000);
}
static int
hex (unsigned char ch)
{
if (ch >= 'a' && ch <= 'f')
return ch - 'a' + 10;
if (ch >= '0' && ch <= '9')
return ch - '0';
if (ch >= 'A' && ch <= 'F')
return ch - 'A' + 10;
return -1;
}
void
checksum (char *buf)
{
unsigned char sum = 0;
while (*buf)
sum += *buf++;
*buf++ = '#';
*buf++ = hexchars[sum >> 4];
*buf++ = hexchars[sum & 0x0f];
*buf++ = 0;
}
int
check_pkg (unsigned char *buf, int len)
{
int i, start = 0;
unsigned char chksum = 0;
unsigned char rxsum = 0;
i = 0;
while ((i < len) && (buf[i] != '$'))
i++;
if (i == len)
return -1;
i++;
start = i;
while ((i < len) && (buf[i] != '#'))
{
chksum = chksum + buf[i++];
}
if (i == len)
return -1;
i++;
rxsum = (hex (buf[i]) << 4) | hex (buf[i + 1]);
if ((i < len) && (chksum == rxsum))
return start;
return -1;
}
static void
int2hex (char *hexbuf, char *intbuf, int len)
{
int i;
for (i = 0; i < len; i++)
{
hexbuf[i * 2] = hexchars[(intbuf[i] >> 4) & 0x0f];
hexbuf[i * 2 + 1] = hexchars[intbuf[i] & 0x0f];
}
hexbuf[len * 2] = 0;
}
static int
sim_stat ()
{
int i;
switch (simstat)
{
case OK:
i = 0;
break;
case NULL_HIT:
i = SIGSEGV;
break;
case ERROR_MODE:
i = SIGTERM;
break;
case CTRL_C:
i = SIGINT;
break;
default:
i = SIGTRAP;
}
return i;
}
int
gdb_remote_exec (char *buf)
{
char membuf[1024];
unsigned int i, j, len, addr;
int cont = 1;
char *cptr, *mptr;
char *txbuf = &sendbuf[1];
switch (buf[0])
{
case 'H':
if (buf[1] != 'c')
strcpy (txbuf, "OK");
break;
case '?': /* last signal */
if ((ebase.simtime == 0) && (sregs[cpu].pc == last_load_addr) &&
last_load_addr)
sprintf (txbuf, "W%02d", sim_stat ());
else
sprintf (txbuf, "S%02d", sim_stat ());
break;
case 'D': /* detach */
strcpy (txbuf, "OK");
detach = 1;
break;
case 'g': /* get registers */
len = arch->gdb_get_reg (membuf);
int2hex (txbuf, membuf, len);
break;
case 'm': /* read memory */
i = 1;
len = 0;
addr = 0;
while (buf[i] && (buf[i] != ','))
{
addr = (addr << 4) | hex (buf[i]);
i++;
}
i++;
while (buf[i] && (buf[i] != '#'))
{
len = (len << 4) | hex (buf[i]);
i++;
}
sim_read (addr, membuf, len);
int2hex (txbuf, membuf, len);
break;
case 'M': /* write memory */
i = 1;
len = 0;
addr = 0;
while (buf[i] && (buf[i] != ','))
{
addr = (addr << 4) | hex (buf[i]);
i++;
}
i++;
while (buf[i] && (buf[i] != ':'))
{
len = (len << 4) | hex (buf[i]);
i++;
}
i++;
j = 0;
while (buf[i] != '#')
{
membuf[j] = (hex (buf[i]) << 4) | hex (buf[i + 1]);
i += 2;
j += 1;
}
sim_write (addr, membuf, len);
strcpy (txbuf, "OK");
break;
case 'P': /* write register */
i = 1;
len = 0;
addr = 0;
while (buf[i] && (buf[i] != '='))
{
addr = (addr << 4) | hex (buf[i]);
i++;
}
i++;
while (buf[i] && (buf[i] != '#'))
{
if (cputype == CPU_RISCV)
{
j = hex (buf[i++]);
j <<= 4;
j |= hex (buf[i]);
len = (j << 24) | (len >> 8); /* value is in target order! */
}
else
len = (len << 4) | hex (buf[i]);
i++;
}
i++;
arch->set_register (&sregs[cpu], NULL, len, addr);
strcpy (txbuf, "OK");
break;
case 'C': /* continue execution */
sim_create_inferior ();
case 'c':
sim_resume (0);
i = sim_stat ();
sprintf (txbuf, "S%02x", i);
if ((i == SIGTRAP) && ebase.wphit)
{
if (ebase.wptype == 2)
sprintf (txbuf, "T%02xwatch:%x;", i, ebase.wpaddress);
else if (ebase.wptype == 3)
sprintf (txbuf, "T%02xrwatch:%x;", i, ebase.wpaddress);
}
break;
case 'k': /* kill */
case 'R': /* restart */
sim_create_inferior (0, 0, 0, 0);
strcpy (txbuf, "OK");
break;
case 'v':
if (strncmp (&buf[1], "Kill", 4) == 0)
{ /* restart */
sim_create_inferior (0, 0, 0, 0);
strcpy (txbuf, "OK");
}
else if (strncmp (&buf[1], "Run;", 4) == 0)
{ /* Restart */
sim_create_inferior (0, 0, 0, 0);
strcpy (txbuf, "S00");
}
else if (strncmp (&buf[1], "Cont", 4) == 0)
{ /* continue/step */
switch (buf[5])
{
case '?':
strcpy (txbuf, "vCont;c;s");
break;
case 'c':
sim_resume (0);
i = sim_stat ();
sprintf (txbuf, "S%02x", i);
break;
case 's':
sim_resume (1);
i = sim_stat ();
sprintf (txbuf, "S%02x", i);
break;
default:
strcpy (sendbuf, "$#");
}
}
break;
case 's':
case 'S':
sim_resume (1);
i = sim_stat ();
sprintf (txbuf, "S%02x", i);
break;
case 'Z': /* add break/watch point */
case 'z': /* remove break/watch point */
i = 3;
addr = 0;
while (buf[i] && (buf[i] != ','))
{
addr = (addr << 4) | hex (buf[i]);
i++;
}
i++;
len = hex (buf[i]);
if (buf[0] == 'Z')
j = sim_set_watchpoint (addr, len, hex (buf[1]));
else
j = sim_clear_watchpoint (addr, len, hex (buf[1]));
if (j)
strcpy (txbuf, "OK");
else
strcpy (txbuf, "E01");
break;
case 'q': /* query */
if (strncmp (&buf[1], "fThreadInfo", 11) == 0)
{
strcpy (txbuf, "l");
}
else if (strncmp (&buf[1], "Attached", 8) == 0)
{
strcpy (txbuf, "0");
}
else if (strncmp (&buf[1], "sThreadInfo", 11) == 0)
{
strcpy (txbuf, "l");
}
else if (strncmp (&buf[1], "Rcmd", 4) == 0)
{
cptr = &buf[6];
mptr = membuf;
while (*cptr != '#')
{
*mptr = hex (*cptr++) << 4;
*mptr++ |= hex (*cptr++);
}
*mptr = 0;
exec_cmd (membuf);
strcpy (txbuf, "OK");
}
break;
case '!': /* extended protocl */
strcpy (txbuf, "OK");
break;
default:
printf ("%s\n", buf);
}
checksum (txbuf);
return cont;
}
void
gdb_remote (int port)
{
unsigned char buffer[2048];
int cont = 1;
int res, len = 0;
char ack = '+';
char nok = '-';
sis_gdb_break = 1;
detach = 0;
printf ("gdb: listening on port %d ", port);
while (cont)
{
if ((cont = create_socket (port)))
{
send (new_socket, &ack, 1, 0);
printf ("connected\n");
}
while (cont)
{
do
{
#ifdef WIN32
len = recv (new_socket, buffer, 2048, 0);
if (len < 0)
len = 0;
#else
len = read (new_socket, buffer, 2048);
#endif
buffer[len] = 0;
if (sis_verbose > 1)
printf ("%s (%d)\n", buffer, len);
if (len == 1)
if (buffer[0] == '-')
{
if (sis_verbose > 1)
printf ("tx: %s\n", sendbuf);
send (new_socket, sendbuf, strlen (sendbuf), 0);
}
else if (buffer[0] == '+')
{
if (detach)
{
cont = 0;
break;
}
}
else if (buffer[0] == 3)
{
ctrl_c = 1;
}
if (len <= 0)
{
cont = 0;
break;
}
}
while (len < 2);
if (!cont)
break;
res = check_pkg (buffer, len);
if (res > 0)
{
if (sis_verbose > 1)
printf ("tx: +\n");
send (new_socket, &ack, 1, 0);
if (detach)
{
cont = 0;
}
else
{
strcpy (sendbuf, "$");
cont = gdb_remote_exec ((char *) &buffer[res]);
if (sis_verbose > 1)
printf ("tx: %s\n", sendbuf);
send (new_socket, sendbuf, strlen (sendbuf), 0);
}
}
else
{
if (sis_verbose > 1)
printf ("tx: -\n");
send (new_socket, &nok, 1, 0);
}
}
}
if (new_socket)
{
closesocket (new_socket);
}
new_socket = 0;
sis_gdb_break = 0;
}