Files
m68k-bdm/m68k/server/bdmServer.c
Bernd Mueller adfd70813f initial push
2026-06-17 13:44:30 +02:00

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;
}