770 lines
15 KiB
C
770 lines
15 KiB
C
/*
|
|
* Motorola Background Debug Mode Remote Server
|
|
* Copyright (C) 1994, 95, 96, 1997 Free Software Foundation, Inc.
|
|
* Contributed by Chris Johns
|
|
*
|
|
* Pieces of code are taken from the gnatsd amd gdbserver.
|
|
*
|
|
* 31-11-1999 Chris Johns (ccj@acm.org)
|
|
*
|
|
* Chris Johns
|
|
* Objective Design Systems
|
|
* 35 Cairo Street
|
|
* Cammeray, Sydney, 2062, Australia
|
|
*
|
|
* ccj@acm.org
|
|
*
|
|
* 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 2 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, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include <BDMlib.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <netdb.h>
|
|
#include <signal.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/param.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
|
|
#include <syslog.h>
|
|
#include <unistd.h>
|
|
|
|
#ifndef __CYGWIN32__
|
|
#include <netinet/tcp.h>
|
|
#endif
|
|
|
|
char *xmalloc (unsigned n);
|
|
void xfree (char *p);
|
|
|
|
/*
|
|
* This should be the same as the client.
|
|
*/
|
|
|
|
#define BDM_SERVER_TIMEOUT (5)
|
|
#define BDM_SERVER_BUF_SIZE (4096)
|
|
|
|
/*
|
|
* Local data.
|
|
*/
|
|
static const char *version_string = "1.0.0";
|
|
static char myname[MAXHOSTNAMELEN];
|
|
static char *current_host;
|
|
static char *current_addr;
|
|
static char *program_name;
|
|
static int debug = 0;
|
|
|
|
/*
|
|
* Define the messages between the client and the server.
|
|
*/
|
|
|
|
enum bdm_message_id
|
|
{
|
|
HELO,
|
|
SRVCTL,
|
|
IOINT,
|
|
IOCMD,
|
|
IOIO,
|
|
READ,
|
|
WRITE,
|
|
QUIT,
|
|
INVALID
|
|
};
|
|
|
|
struct bdm_message
|
|
{
|
|
char *label;
|
|
enum bdm_message_id id;
|
|
};
|
|
|
|
struct bdm_message messages[] =
|
|
{
|
|
{ "HELO", HELO },
|
|
{ "SRVCTL", SRVCTL },
|
|
{ "IOINT", IOINT },
|
|
{ "IOCMD", IOCMD },
|
|
{ "IOIO", IOIO},
|
|
{ "READ", READ },
|
|
{ "WRITE", WRITE },
|
|
{ "QUIT", QUIT }
|
|
};
|
|
|
|
/*
|
|
* Ioctl code translation.
|
|
*/
|
|
static const int ioctl_code_table[] = {
|
|
BDM_INIT,
|
|
BDM_RESET_CHIP,
|
|
BDM_RESTART_CHIP,
|
|
BDM_STOP_CHIP,
|
|
BDM_STEP_CHIP,
|
|
BDM_GET_STATUS,
|
|
BDM_SPEED,
|
|
BDM_DEBUG,
|
|
BDM_RELEASE_CHIP,
|
|
BDM_GO,
|
|
BDM_READ_REG,
|
|
BDM_READ_SYSREG,
|
|
BDM_READ_LONGWORD,
|
|
BDM_READ_WORD,
|
|
BDM_READ_BYTE,
|
|
BDM_WRITE_REG,
|
|
BDM_WRITE_SYSREG,
|
|
BDM_WRITE_LONGWORD,
|
|
BDM_WRITE_WORD,
|
|
BDM_WRITE_BYTE,
|
|
BDM_GET_DRV_VER,
|
|
BDM_GET_CPU_TYPE,
|
|
BDM_GET_IF_TYPE,
|
|
BDM_GET_CF_PST, /* At the end to not break existing servers. */
|
|
BDM_SET_CF_PST,
|
|
BDM_READ_CTLREG,
|
|
BDM_WRITE_CTLREG,
|
|
BDM_READ_DBREG,
|
|
BDM_WRITE_DBREG
|
|
};
|
|
|
|
/*
|
|
* Get the name of the host.
|
|
*/
|
|
|
|
char *
|
|
get_name (struct in_addr *host)
|
|
{
|
|
char *buf;
|
|
int i;
|
|
struct hostent *hp;
|
|
#ifdef h_addr
|
|
char **pp;
|
|
#endif
|
|
|
|
hp = gethostbyaddr ((char *) host, sizeof (host), AF_INET);
|
|
|
|
if (hp == NULL)
|
|
return NULL;
|
|
|
|
i = strlen (hp->h_name);
|
|
buf = (char *) xmalloc (i + 1);
|
|
strcpy (buf, hp->h_name);
|
|
hp = gethostbyname (buf);
|
|
if (hp == NULL)
|
|
return NULL;
|
|
|
|
#ifdef h_addr
|
|
for (pp = hp->h_addr_list; *pp; pp++)
|
|
if (memcmp ((char *) &host->s_addr, (char *) *pp, hp->h_length) == 0)
|
|
break;
|
|
if (*pp == NULL)
|
|
return NULL;
|
|
#else
|
|
if (memcmp ((char *) &host->s_addr, (char *) hp->h_addr, hp->h_length) != 0)
|
|
return NULL;
|
|
#endif
|
|
|
|
return buf;
|
|
}
|
|
|
|
/*
|
|
* Start an inetd connection.
|
|
*/
|
|
|
|
void
|
|
start_connection ()
|
|
{
|
|
struct sockaddr_in s;
|
|
unsigned int len;
|
|
|
|
len = sizeof (s);
|
|
|
|
if (getpeername (0, (struct sockaddr *) &s, &len) < 0)
|
|
{
|
|
if (!isatty (0))
|
|
{
|
|
syslog (LOG_ERR, "%s: can't get peername %m", "?");
|
|
exit (1);
|
|
}
|
|
current_host = "stdin";
|
|
current_addr = current_host;
|
|
}
|
|
else
|
|
{
|
|
if (s.sin_family != AF_INET)
|
|
{
|
|
syslog (LOG_ERR, "%s: bad address family %ld",
|
|
"?", (long) s.sin_family);
|
|
exit (1);
|
|
}
|
|
|
|
current_addr = (char *) inet_ntoa (s.sin_addr);
|
|
current_host = get_name (&s.sin_addr);
|
|
|
|
if (!current_host) {
|
|
current_host = current_addr;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Message the message from the input handle.
|
|
*/
|
|
|
|
int
|
|
get_message (char *buf, int buf_len)
|
|
{
|
|
int numfds;
|
|
fd_set readfds;
|
|
|
|
while (1) {
|
|
FD_ZERO (&readfds);
|
|
FD_SET (0, &readfds);
|
|
|
|
numfds = select (0 + 1, &readfds, 0, 0, 0);
|
|
|
|
if (numfds > 0) {
|
|
int ret = read (0, buf, buf_len-1);
|
|
if (ret>=0) buf[ret]=0;
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Decode the id to an ioctl number.
|
|
*
|
|
* Note, watch checking the return value. Check for -1 not < 0.
|
|
*
|
|
* We should have version number checks between the client and server
|
|
* to avoid these numbers skewing.
|
|
*/
|
|
|
|
int
|
|
decode_id (unsigned long id)
|
|
{
|
|
if (id < (sizeof (ioctl_code_table) / sizeof (ioctl_code_table[0])))
|
|
return ioctl_code_table[id];
|
|
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Decode a message.
|
|
*/
|
|
|
|
enum bdm_message_id
|
|
decode_message (char *message)
|
|
{
|
|
unsigned id;
|
|
char *c;
|
|
char *s;
|
|
|
|
/*
|
|
* Make the first field (the message label) uppercase.
|
|
*/
|
|
|
|
s = strchr (message, ' ');
|
|
if (!s)
|
|
s = message + strlen (message);
|
|
for (c = message; c < s; c++)
|
|
*c = toupper (*c);
|
|
|
|
for (id = 0; id < (sizeof messages / sizeof (struct bdm_message)); id++)
|
|
if (strncmp (message, messages[id].label, strlen (messages[id].label)) == 0)
|
|
return messages[id].id;
|
|
|
|
syslog (LOG_INFO, "invalid message: %s", message);
|
|
|
|
return INVALID;
|
|
}
|
|
|
|
/*
|
|
* Open the port using the requested device.
|
|
*/
|
|
|
|
void
|
|
helo (char *device)
|
|
{
|
|
device += sizeof "HELO";
|
|
|
|
if (bdmOpen (device) < 0) {
|
|
syslog (LOG_INFO, "open error: %s (%d), opening `%s'",
|
|
bdmErrorString (), errno, device);
|
|
}
|
|
printf ("HELO %d %s BDM server %s ready.", errno, myname, version_string);
|
|
}
|
|
|
|
/*
|
|
* Server Control.
|
|
*/
|
|
|
|
void
|
|
server_control (char *message)
|
|
{
|
|
char *s = message + sizeof "SRVCTL";
|
|
|
|
if (strncmp (s, "DEBUG", sizeof "DEBUG" - 1) == 0) {
|
|
s = strchr (s, '=');
|
|
if (!s) {
|
|
syslog (LOG_INFO, "srv-ctl error: invalid debug syntax (%s)", message);
|
|
return;
|
|
}
|
|
s++;
|
|
debug = strtoul (s, NULL, 0);
|
|
syslog (LOG_INFO, "srv-ctl: debug level is %d", debug);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* IO Int read/write.
|
|
*/
|
|
|
|
void
|
|
ioint (char *message)
|
|
{
|
|
int id;
|
|
int code;
|
|
int var;
|
|
|
|
message += sizeof "IOINT";
|
|
id = strtoul (message, NULL, 0);
|
|
message = strchr (message, ',') + 1;
|
|
var = strtoul (message, NULL, 0);
|
|
|
|
code = decode_id (id);
|
|
if (code == -1) {
|
|
syslog (LOG_INFO, "ioint error: invalid ioctl id decode (%d)", errno);
|
|
}
|
|
|
|
if (bdmIoctlInt (code, &var) < 0) {
|
|
syslog (LOG_INFO, "ioint error: %s (%d)", bdmErrorString (), errno);
|
|
}
|
|
|
|
printf ("IOINT %d,0x%x.", errno, var);
|
|
}
|
|
|
|
/*
|
|
* IO Command.
|
|
*/
|
|
|
|
void
|
|
iocmd (char *message)
|
|
{
|
|
int id;
|
|
int code;
|
|
|
|
message += sizeof "IOCMD";
|
|
id = strtoul (message, NULL, 0);
|
|
|
|
code = decode_id (id);
|
|
if (code == -1) {
|
|
syslog (LOG_INFO, "iocmd error: invalid ioctl id decode (%d)", errno);
|
|
}
|
|
|
|
if (bdmIoctlCommand (code) < 0) {
|
|
syslog (LOG_INFO, "iocmd error: %s (%d)", bdmErrorString (), errno);
|
|
}
|
|
|
|
printf ("IOCMD %d.", errno);
|
|
}
|
|
|
|
/*
|
|
* IO ioctl addressed access.
|
|
*/
|
|
|
|
void
|
|
ioio (char *message)
|
|
{
|
|
int id;
|
|
int code;
|
|
struct BDMioctl ioc;
|
|
|
|
message += sizeof "IOIO";
|
|
id = strtoul (message, NULL, 0);
|
|
message = strchr (message, ',') + 1;
|
|
ioc.address = strtoul (message, NULL, 0);
|
|
message = strchr (message, ',') + 1;
|
|
ioc.value = strtoul (message, NULL, 0);
|
|
|
|
code = decode_id (id);
|
|
if (code == -1) {
|
|
syslog (LOG_INFO, "ioio error: invalid ioctl id decode (%d)", errno);
|
|
}
|
|
|
|
if (bdmIoctlIo (code, &ioc) < 0) {
|
|
syslog (LOG_INFO, "ioio error: %s (%d)", bdmErrorString (), errno);
|
|
}
|
|
|
|
printf ("IOIO %d,0x%x,0x%x.", errno, ioc.address, ioc.value);
|
|
}
|
|
|
|
/*
|
|
* Read a block of memory.
|
|
*/
|
|
|
|
void
|
|
bdm_read (char *message)
|
|
{
|
|
long read_nbytes;
|
|
unsigned char *buf;
|
|
unsigned long nbytes;
|
|
unsigned long byte;
|
|
|
|
message += sizeof "READ";
|
|
nbytes = strtoul (message, NULL, 0);
|
|
buf = (unsigned char*) xmalloc ((unsigned) nbytes);
|
|
|
|
read_nbytes = bdmRead (buf, nbytes);
|
|
|
|
if (debug)
|
|
syslog (LOG_INFO, "read: nbytes %ld (%ld), have read %ld",
|
|
nbytes, nbytes * 2, read_nbytes);
|
|
|
|
if (read_nbytes < 0) {
|
|
syslog (LOG_INFO, "read error: %s (%d)", bdmErrorString (), errno);
|
|
}
|
|
|
|
printf ("READ %d,%ld,", errno, read_nbytes);
|
|
|
|
if (read_nbytes > 0) {
|
|
for (byte = 0; byte < read_nbytes; byte++)
|
|
printf ("%02x", buf[byte]);
|
|
}
|
|
|
|
xfree ((char*) buf);
|
|
}
|
|
|
|
/*
|
|
* Write a block of memory.
|
|
*/
|
|
|
|
void
|
|
bdm_write (char *message, int msg_len, int msg_buf_len)
|
|
{
|
|
long written_nbytes;
|
|
unsigned char *buf;
|
|
unsigned long nbytes;
|
|
unsigned long byte;
|
|
unsigned long msg_bytes;
|
|
int msg_index;
|
|
unsigned char octet = 0;
|
|
char *msg;
|
|
|
|
nbytes = strtoul (message + sizeof "WRITE", &msg, 0);
|
|
if (*msg == ',')
|
|
msg++;
|
|
|
|
buf = (unsigned char*) xmalloc ((unsigned) nbytes);
|
|
byte = 0;
|
|
msg_bytes = 0;
|
|
|
|
msg_len -= msg - message;
|
|
msg_buf_len -= msg - message;
|
|
|
|
if (debug)
|
|
syslog (LOG_INFO, "write: nbytes %ld (%ld)", nbytes, nbytes * 2);
|
|
|
|
while (byte < nbytes) {
|
|
msg_index = 0;
|
|
if (msg_len < 2) {
|
|
int new_read;
|
|
new_read = get_message (msg + msg_len, msg_buf_len - msg_len);
|
|
if (new_read < 0) {
|
|
syslog (LOG_INFO, "write error: client timeout");
|
|
xfree ((char*) buf);
|
|
return;
|
|
}
|
|
msg_len += new_read;
|
|
}
|
|
|
|
if ((msg_len > 0) && !msg[msg_len - 1])
|
|
msg_len--;
|
|
|
|
if (debug) {
|
|
syslog (LOG_INFO, "write read: msg len is %d (%ld/%ld)",
|
|
msg_len, msg_bytes, msg_len + msg_bytes);
|
|
syslog (LOG_INFO, "write read msg: %s", msg + msg_index);
|
|
if (msg_len > 900)
|
|
syslog (LOG_INFO, "write read msg end: %s",
|
|
msg + msg_len - 100);
|
|
}
|
|
|
|
while ((byte < nbytes) && ((msg_len - msg_index) > 1)) {
|
|
if ((msg[msg_index] >= '0') && (msg[msg_index] <= '9'))
|
|
octet = msg[msg_index] - '0';
|
|
else if ((msg[msg_index] >= 'a') && (msg[msg_index] <= 'f')) {
|
|
msg[msg_index] = tolower (msg[msg_index]);
|
|
octet = msg[msg_index] - 'a' + 10;
|
|
}
|
|
else {
|
|
syslog (LOG_INFO,
|
|
"write read: hbyte %ld msg bytes %ld, len:%d index:%d,"
|
|
" invalid data %#2x",
|
|
byte, msg_bytes, msg_len, msg_index, msg[msg_index]);
|
|
}
|
|
|
|
msg_index++;
|
|
msg_bytes++;
|
|
|
|
octet <<= 4;
|
|
|
|
if ((msg[msg_index] >= '0') && (msg[msg_index] <= '9'))
|
|
octet |= msg[msg_index] - '0';
|
|
else if ((msg[msg_index] >= 'a') && (msg[msg_index] <= 'f')) {
|
|
msg[msg_index] = tolower (msg[msg_index]);
|
|
octet |= msg[msg_index] - 'a' + 10;
|
|
}
|
|
else {
|
|
syslog (LOG_INFO,
|
|
"write read: lbyte %ld msg bytes %ld, len:%d index:%d,"
|
|
" invalid data %#2x",
|
|
byte, msg_bytes, msg_len, msg_index, msg[msg_index]);
|
|
}
|
|
|
|
msg_index++;
|
|
msg_bytes++;
|
|
|
|
buf[byte] = octet;
|
|
|
|
byte++;
|
|
}
|
|
|
|
memcpy (msg, msg + msg_index, msg_len - msg_index);
|
|
msg_len -= msg_index;
|
|
}
|
|
|
|
written_nbytes = bdmWrite (buf, nbytes);
|
|
|
|
if (written_nbytes < 0) {
|
|
syslog (LOG_INFO, "write error: %s (%d)", bdmErrorString (), errno);
|
|
}
|
|
|
|
printf ("WRITE %d,%ld", errno, written_nbytes);
|
|
|
|
xfree ((char*) buf);
|
|
}
|
|
|
|
/*
|
|
* Clean up and exit.
|
|
*/
|
|
|
|
void
|
|
quit ()
|
|
{
|
|
bdmClose ();
|
|
if (debug)
|
|
syslog (LOG_INFO, "host finished: %s (%s)", current_host, current_addr);
|
|
exit (0);
|
|
}
|
|
|
|
/*
|
|
* Process a message.
|
|
*/
|
|
|
|
void
|
|
process_message (char *message, int msg_len, int msg_buf_len)
|
|
{
|
|
errno = 0;
|
|
|
|
switch (decode_message (message)) {
|
|
case HELO:
|
|
helo (message);
|
|
break;
|
|
|
|
case SRVCTL:
|
|
server_control (message);
|
|
break;
|
|
|
|
case IOINT:
|
|
ioint (message);
|
|
break;
|
|
|
|
case IOCMD:
|
|
iocmd (message);
|
|
break;
|
|
|
|
case IOIO:
|
|
ioio (message);
|
|
break;
|
|
|
|
case READ:
|
|
bdm_read (message);
|
|
break;
|
|
|
|
case WRITE:
|
|
bdm_write (message, msg_len, msg_buf_len);
|
|
break;
|
|
|
|
case QUIT:
|
|
quit ();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
usage ()
|
|
{
|
|
fprintf (stderr,
|
|
"Usage: %s [-h] [-n] [-V] [-d]\n",
|
|
program_name);
|
|
exit (1);
|
|
}
|
|
|
|
void
|
|
version ()
|
|
{
|
|
printf ("bdmd %s\n", version_string);
|
|
}
|
|
|
|
/*
|
|
* The server can be run from inetd or run from the
|
|
* a tty allow debug to be watched.
|
|
*
|
|
*/
|
|
|
|
int
|
|
main (int argc, char **argv)
|
|
{
|
|
struct hostent *hp;
|
|
int optc;
|
|
int not_inetd = 0;
|
|
char buf[BDM_SERVER_BUF_SIZE];
|
|
int cread;
|
|
|
|
program_name = argv[0];
|
|
|
|
while ((optc = getopt (argc, argv, "dnVh")) != EOF)
|
|
{
|
|
switch (optc)
|
|
{
|
|
case 'd':
|
|
debug++;
|
|
break;
|
|
|
|
case 'n':
|
|
not_inetd = 1;
|
|
break;
|
|
|
|
case 'V':
|
|
version ();
|
|
exit (0);
|
|
break;
|
|
|
|
case 'h':
|
|
default:
|
|
usage ();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Process options.
|
|
*/
|
|
|
|
umask (022);
|
|
|
|
/*
|
|
* chdir
|
|
*/
|
|
|
|
fflush (stderr);
|
|
fflush (stdout);
|
|
|
|
closelog ();
|
|
|
|
#ifdef LOG_DAEMON
|
|
openlog ("bdmd", (LOG_PID|LOG_CONS), LOG_DAEMON);
|
|
#else
|
|
openlog ("bdmd", LOG_PID);
|
|
#endif
|
|
|
|
if (gethostname (myname, MAXHOSTNAMELEN)) {
|
|
fprintf (stderr, "%s: cannot find out the name of this host\n",
|
|
program_name);
|
|
exit (1);
|
|
}
|
|
|
|
myname[MAXHOSTNAMELEN - 1] = 0;
|
|
|
|
/*
|
|
* Now see if we can get a FQDN for this host.
|
|
*/
|
|
|
|
hp = gethostbyname (myname);
|
|
if (hp && (strlen (hp->h_name) > strlen (myname)))
|
|
strncpy (myname, hp->h_name, MAXHOSTNAMELEN);
|
|
myname[MAXHOSTNAMELEN - 1] = 0;
|
|
|
|
bdmLogSyslog ();
|
|
|
|
/*
|
|
* Don't pay attention to SIGPIPE; read will give us the problem
|
|
* if it happens.
|
|
*/
|
|
|
|
signal (SIGPIPE, SIG_IGN);
|
|
|
|
if (!not_inetd)
|
|
start_connection ();
|
|
else
|
|
{
|
|
current_host = myname;
|
|
if (hp)
|
|
current_addr = (char *) inet_ntoa (*(struct in_addr *) hp->h_addr);
|
|
else
|
|
current_addr = myname;
|
|
}
|
|
|
|
if (debug)
|
|
syslog (LOG_INFO, "host connected: %s (%s)", current_host, current_addr);
|
|
|
|
/*
|
|
* Say hello to the remote end. It will be waiting.
|
|
*/
|
|
|
|
while (1) {
|
|
cread = get_message (buf, BDM_SERVER_BUF_SIZE);
|
|
|
|
if (cread == 0) {
|
|
break;
|
|
}
|
|
|
|
if (cread > 0) {
|
|
if (debug > 1) {
|
|
int len = strlen (buf);
|
|
syslog (LOG_INFO, "msg: %s", buf);
|
|
if (len>900)
|
|
syslog (LOG_INFO, "msg end: %s", buf + len - 100);
|
|
}
|
|
|
|
process_message (buf, cread, BDM_SERVER_BUF_SIZE);
|
|
|
|
fflush (stdout);
|
|
}
|
|
}
|
|
|
|
bdmClose ();
|
|
|
|
return 0;
|
|
}
|