4.4BSD/usr/src/contrib/gdb-4.7.lbl/gdb/remote-fp.c
/*
* Memory-access and commands for remote kbox (Kinetics ethernet/appletalk
* bridge), for GDB. Copyright (C) 1988 Free Software Foundation, Inc.
*
* GDB is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY. No author or distributor accepts responsibility to anyone for
* the consequences of using it or for whether it serves any particular
* purpose or works at all, unless he says so in writing. Refer to the GDB
* General Public License for full details.
*
* Everyone is granted permission to copy, modify and redistribute GDB, but only
* under the conditions described in the GDB General Public License. A copy
* of this license is supposed to have been given to you along with GDB so
* you can know your rights and responsibilities. It should be in a file
* named COPYING. Among other things, the copyright notice and this notice
* must be preserved on all copies.
*
* In other words, go ahead and share GDB, but don't try to stop anyone else
* from sharing it farther. Help stamp out software hoarding!
*/
/*
* Written by Van Jacobson (van@helios.ee.lbl.gov)
* Sat Jan 7 03:42:57 PST 1989:
*
* This is a first cut at remote debugging suport for a fastpath. All
* this version can do is read & write memory in the box. Support
* for writing regs or continue/resume really requires support in
* the prom ethernet driver or it's too easy to step on yourself.
*
* To use a version of gdb that contains this code (which we typically
* call kbdb for "kbox debugger") do the following:
*
* kbdb gw.sym (gw.sym is the a.out form of the .srec currently
* downloaded into the kbox)
* (gdb) attach @foo ("foo" is the host name or ip address of the
* kbox you want to debug)
*
* kbdb will print out a few lines including "Program received signal 5,
* Trace/BPT trap" and an address (usually ip4me in gw2.c). The "signal
* 5" has to do with fooling gdb into thinking that the attach worked.
* Everything else is legit (ie, the registers, stack, etc., should
* indicate that you're in the piece of code that processes incoming
* "debug" packets. You can now do all the normal gdb things (bt,
* print, set, etc.) except set breakpoints or change the execution
* path. If you type "return" it will appear to work (but doesn't
* really). If you type "continue", you'll get the "Program received
* signal 5," again. When you quit, you'll get the usual confirmer:
* "The program is running. Quit anyway? (y or n)". Just answer
* yes.
*
* This code will only work with the LBL modified KIP code (it
* requires some opcodes we added to the gateway debugging protocol).
*/
#include <stdio.h>
#include <signal.h>
#include "defs.h"
#include "frame.h"
#include "inferior.h"
#include "remote.h"
#include "wait.h"
#include <a.out.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <strings.h>
#include <netdb.h>
#include "kgdb_proto.h"
/*
* KIP fastpath gateway debug protocol
*/
struct gwdb {
u_long magic; /* magic number */
u_char op,seq; /* op code, sequence number */
u_short count; /* byte count */
u_long address; /* address of read/write */
u_char data[512];
};
/* Maximum number of bytes that can be read or written */
#define FP_MAXDATA sizeof(((struct gwdb *)0)->data)
/* errno byte + data */
#define FP_RPCSIZE (1 + FP_MAXDATA)
#define gwdbMagic ((u_long)0xFF068020)
#define gwdbPort 900 /* udp port number */
/* op codes */
#define gwdbRead 1
#define gwdbWrite 2
#define gwdbCall 3
#define gwdbStats 4
#define gwdbState 5
#define gwdbFrame 6
int kiodebug;
static struct sockaddr_in fsin = { AF_INET }; /* foreign sockaddr_in */
static int fsinlen;
/* Descriptor for I/O to remote machine. */
int fp_desc = -1;
static int fp_send(), fp_recv();
static void fp_close();
static int fp_put(), fp_get();
/*
* Open a connection to a remote debugger. NAME is the filename used for
* communication.
*/
void
fp_open(name, remote_fnp)
char *name;
struct remote_fn *remote_fnp;
{
register u_long iaddr;
struct hostent *hp;
if (*name >= '0' && *name <= '9' && (iaddr = inet_addr(name)) != -1) {
fsin.sin_family = AF_INET;
fsin.sin_addr.s_addr = iaddr;
} else if (hp = gethostbyname(name)) {
fsin.sin_family = hp->h_addrtype;
bcopy(hp->h_addr, (caddr_t)&fsin.sin_addr, hp->h_length);
} else {
error("Bad hostname/ip-address \"%s\"", name);
/* NOTREACHED */
}
fp_desc = socket(AF_INET, SOCK_DGRAM, 0);
if (fp_desc < 0) {
perror_with_name(name);
/* NOTREACHED */
}
fsin.sin_port = gwdbPort;
remote_fnp->send = fp_send;
remote_fnp->recv = fp_recv;
remote_fnp->close = fp_close;
remote_fnp->maxdata = FP_MAXDATA;
remote_fnp->rpcsize = FP_RPCSIZE;
}
/*
* Close down connection to remote debugger.
*/
static void
fp_close()
{
if (fp_desc < 0)
printf("fp_close(): fp_desc not open!\n");
else {
(void)close(fp_desc);
fp_desc = -1;
}
}
static int fp_lasttype;
static int
fp_send(type, bp, len)
register u_char type;
register u_char *bp;
register int len;
{
register int ret;
static struct gwdb dbpacket;
register struct gwdb *dp = &dbpacket;
fp_lasttype = type;
switch(KGDB_CMD(type)) {
case KGDB_MEM_R:
/* Read memory */
if (len != 5)
printf("fp_send: MEM_R: bad len (%d != 5)\n", len);
dp->magic = gwdbMagic;
dp->op = gwdbRead;
dp->seq = 1;
dp->count = *bp++;
bcopy((caddr_t)bp, (caddr_t)&dp->address, sizeof(dp->address));
if (ret = fp_put(dp))
return(ret);
break;
case KGDB_MEM_W:
/* Write memory */
if (len < 5)
printf("fp_send: MEM_W: bad len (%d < 5)\n", len);
dp->magic = gwdbMagic;
dp->op = gwdbWrite;
dp->seq = 1;
bcopy((caddr_t)bp, (caddr_t)&dp->address, sizeof(dp->address));
bp += sizeof(dp->address);
len -= sizeof(dp->address);
dp->count = len;
bcopy((caddr_t)bp, (caddr_t)dp->data, len);
if (ret = fp_put(dp))
return(ret);
break;
case KGDB_REG_R:
/* Read registers */
dp->magic = gwdbMagic;
dp->op = gwdbFrame;
dp->seq = 1;
dp->address = 0;
dp->count = REGISTER_BYTES;
if (ret = fp_put(dp))
return(ret);
break;
}
return(0);
}
static int
fp_recv(tp, ip, lenp, to)
int *tp;
register u_char *ip;
int *lenp;
int to;
{
register int type;
register int i, ret;
static struct gwdb dbpacket;
register struct gwdb *dp = &dbpacket;
char *fmt = "(fastpath %s not implemented)\n";
if (lenp)
*lenp = 0;
type = KGDB_CMD(fp_lasttype);
*tp = type | (fp_lasttype & KGDB_SEQ) | KGDB_ACK;
switch (type) {
case KGDB_MEM_R:
if (ret = fp_get(dp, to))
return(ret);
if (ip) {
/*
* Build a rpc KGDB_MEM_R reply. This is just
* an errno (which is always zero in the case
* of a fastpath) and the data.
*/
ip[0] = 0;
bcopy((caddr_t)dp->data, (caddr_t)&ip[1], dp->count);
if (lenp)
*lenp = dp->count + 1; /* rpc reply size */
}
break;
case KGDB_MEM_W:
if (ret = fp_get(dp, to))
return(ret);
break;
case KGDB_REG_R:
if (ret = fp_get(dp, to))
return(ret);
if (ip) {
register u_char *bp;
bp = dp->data;
for (i = 0; i < dp->count / sizeof(int); i++) {
*ip++ = i;
bcopy((caddr_t)bp, (caddr_t)ip, sizeof(int));
ip += sizeof(int);
bp += sizeof(int);
}
if (lenp)
*lenp = i * (sizeof(int) + 1);
}
break;
case KGDB_REG_W:
printf(fmt, "REG_W");
break;
case KGDB_CONT:
/* Next time, respond with a "signal" */
fp_lasttype = KGDB_SIGNAL;
printf(fmt, "CONT");
break;
case KGDB_STEP:
printf(fmt, "STEP");
break;
case KGDB_KILL:
printf(fmt, "KILL");
break;
case KGDB_SIGNAL:
printf(fmt, "SIGNAL");
if (ip) {
/* Build a fake rpc KGDB_SIGNAL reply */
ip[0] = SIGTRAP;
if (lenp)
*lenp = 1;
}
break;
case KGDB_EXEC:
printf(fmt, "EXEC");
break;
default:
printf("fp_send(): message type 0x%x unknown\n", fp_lasttype);
break;
}
return(0);
}
/*
* Send the command in BUF to the remote machine, and read the reply into
* BUF. Report an error if we get an error reply.
*/
static int
fp_put(dp)
struct gwdb *dp;
{
if (sendto(fp_desc, (caddr_t)dp, sizeof(*dp), 0,
(struct sockaddr *)&fsin, sizeof(fsin)) < 0) {
perror("fp_put(): sendto");
return(EKGDB_IO);
}
return(0);
}
#ifndef FD_SETSIZE
/* Gag. */
#define FD_SET(n, p) ((p)->fds_bits[0] |= (1<<(n)))
#define FD_CLR(n, p) ((p)->fds_bits[0] &= ~(1<<(n)))
#define FD_ISSET(n, p) ((p)->fds_bits[0] & (1<<(n)))
#define FD_ZERO(p) ((p)->fds_bits[0] = 0)
#endif
static int
fp_get(dp, to)
struct gwdb *dp;
int to;
{
register int n;
struct timeval timeout;
fd_set readfds;
/* Wait for data to show up */
timeout.tv_sec = to / 1000;
timeout.tv_usec = to % 1000;
FD_ZERO(&readfds);
FD_SET(fp_desc, &readfds);
n = select(fp_desc + 1, &readfds, (fd_set*)0, (fd_set*)0, &timeout);
/* XXX should probably check for EINTR */
if (n < 0) {
perror("fp_get(): select");
return(EKGDB_IO);
}
if (n == 0) {
fprintf(stderr, "fp_get(): timeout\n");
return(EKGDB_TIMEOUT);
}
fsinlen = sizeof(fsin);
bzero((caddr_t)dp, sizeof(*dp));
n = recvfrom(fp_desc, (caddr_t)dp, sizeof(*dp), 0,
(struct sockaddr *)&fsin, &fsinlen);
if (n < 0) {
perror("fp_get(): recvfrom");
return(EKGDB_IO);
}
/* Sanity checks */
if (n < sizeof(*dp))
return(EKGDB_RUNT);
if (n > sizeof(*dp))
return(EKGDB_2BIG);
if (dp->op == 0)
return(EKGDB_BADOP);
return(0);
}