/* Memory-access and commands for inferior process, 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!
*/

#include <stdio.h>
#include <sprite.h>
#include <signal.h>
#include <stddef.h>
#include "kernel/machTypes.h"

#include "defs.h"
#include "param.h"
#include "frame.h"
#include "inferior.h"

#include "wait.h"
#include "sprite.h"
#include "kernel/sun4.md/vmSunConst.h"
#include "kernel/sun4.md/machConst.h"
#include "kernel/sun4.md/dbg.h"


char *sys_siglist[] = DBG_EXECPTION_NAMES ;


#define	INDEX_OF_REGNUM(r)	(Regnum_to_index(r))

#define ERROR_NO_ATTACHED_HOST \
	if (!hostName) error("No machine attached.");

#define MARK_DISCONNECTED  {	 \
	initialized = 0;     	 \
	inferior_pid = 0;   	 \
	hostName = (char *) 0;	 \
	free(dataCache); 	 \
	free(cacheInfo); 	 \
	}


int kiodebug;
    static	int	initialized = 0;

int icache;


struct sig_mapping_struct {
	char 	*sig_name;	/* Print string for signal. */
	int	 dbgSig; 	/* Boolean - A signal used by the debugger. */
	int 	unixSignal; 	/* Unix signal equalient. */
} sig_mapping[] =  {
/* 0 */ { "Reset Trap", 0, SIGQUIT },
/* 1 */ { "Instruction Fault", 0, SIGSEGV },
/* 2 */ { "Illegal Instruction Fault", 0, SIGILL },
/* 3 */ { "Privilege Instruction Fault", 0, SIGILL },
/* 4 */ { "FPU Disabled Fault", 0, SIGFPE },
/* 5 */ { "Window Overflow Fault", 0, SIGBUS },
/* 6 */ { "Window Underflow Fault", 0, SIGBUS },
/* 7 */ { "Memory Address Fault", 0, SIGSEGV },
/* 8 */ { "FPU Exception Fault", 0, SIGFPE },
/* 9 */ { "Data Fault",  0, SIGSEGV },
/* 10 */ { "Tag Overflow Trap", 0, SIGSEGV },
/* 11 */ { "Unknown Trap 11",  0, SIGSEGV },
/* 12 */ { "Unknown Trap 12",  0, SIGSEGV },
/* 13 */ { "Unknown Trap 13",  0, SIGSEGV },
/* 14 */ { "Unknown Trap 14",  0, SIGSEGV },
/* 15 */ { "Unknown Trap 15",  0, SIGSEGV },
/* 16 */ { "Interrupt Trap",  0, SIGINT },
/* 17 */ { "Level 1 Interrupt", 0, SIGINT },
/* 18 */ { "Level 2 Interrupt", 0, SIGINT },
/* 19 */ { "Level 3 Interrupt", 0, SIGINT },
/* 20 */ { "Level 4 Interrupt", 0, SIGINT },
/* 21 */ { "Level 5 Interrupt", 0, SIGINT },
/* 22 */ { "Level 6 Interrupt", 0, SIGINT },
/* 23 */ { "Level 7 Interrupt", 0, SIGINT },
/* 24 */ { "Level 8 Interrupt", 0, SIGINT },
/* 25 */ { "Level 9 Interrupt", 0, SIGINT },
/* 26 */ { "Level 10 Interrupt", 0, SIGINT },
/* 27 */ { "Level 11 Interrupt", 0, SIGINT },
/* 28 */ { "Level 12 Interrupt", 0, SIGINT },
/* 29 */ { "Level 13 Interrupt", 0, SIGINT },
/* 30 */ { "Level 14 Interrupt", 0, SIGINT },
/* 31 */ { "Level 15 Interrupt", 0, SIGINT },
/* 32 */ { "Breakpoint Trap",  1, SIGTRAP },
/* 33 */ { "Unknown Trap",  0, SIGSEGV },
/* 34 */ { "UNKNOWN EXCEPTION", 0, SIGSEGV },
};

#define	NUM_SIG_MAPS	(sizeof(sig_mapping)/sizeof(sig_mapping[0]))


/* Descriptor for I/O to remote machine.  */
int remote_desc;

#define	PBUFSIZ	300

/* Maximum number of bytes to read/write at once.  The value here
   is chosen to fill up a packet (the headers account for the 32).  */
#define MAXBUFBYTES ((PBUFSIZ-32)/2)

static void remote_send ();
static void putpkt ();
static void getpkt ();
static void dcache_flush ();

#define	O(s, f)	((int) offsetof(s, f)/sizeof(int))

#if 0
((int)&(((Mach_RegState *) 0)->f)/sizeof(int))
#endif

static int Regnum_to_index(r) 

{ 
  if (r == PC_REGNUM) return O(MachTrapEntry, trapEIP);
  if (r == FP_REGNUM) return O(Mach_RegState, userFPURegs);
  return -1;
};
#undef O

static int lastPid = -1;
char *hostName;

/* Open a connection to a remote debugger.
   NAME is the filename used for communication.  */

void
remote_open (name, from_tty)
     char *name;
     int from_tty;
{
  hostName = savestring(name,strlen(name));
  if (from_tty)
    printf ("Remote debugging using %s\n", name);
  remote_debugging = 1;
}

char *
remote_version()
{
  static char	version[1024];

  ERROR_NO_ATTACHED_HOST;
  Kdbx_Trace(DBG_GET_VERSION_STRING, 0, version, 1024);
  return version;
}

remote_load() { }



/* Tell the remote machine to resume.  */

int     step_addr, step_addr_contents[2];

int
remote_resume (step, signal)
     int step, signal;
{
  int cur_pc;

  ERROR_NO_ATTACHED_HOST;
  cur_pc = read_pc();
  if (Kdbx_Trace(step ? DBG_SINGLESTEP : DBG_CONTINUE, &cur_pc, 0, 
			sizeof(int)) < 0) {
        error("error trying to continue process\n");
  }
}

/* Wait until the remote machine stops, then return,
   storing status in STATUS just as `wait' would.  */

int
remote_wait (status)
     WAITTYPE *status;
{
    StopInfo    stopInfo;
    int		trap;
    int	text_size;
    extern CORE_ADDR text_start, text_end;
  ERROR_NO_ATTACHED_HOST;

  Kdbx_Trace(DBG_GET_STOP_INFO, (char *) 0, (char *)&stopInfo,
               sizeof(stopInfo));

    if (stopInfo.regs.pc == step_addr ||
	stopInfo.regs.pc == step_addr+4) {
         remote_write_bytes(step_addr,step_addr_contents, 8);
    }
    status->w_status = 0;
    status->w_stopval = WSTOPPED;

    trap = DBG_CVT_MACH_TRAP(stopInfo.trapType);
    status->w_stopsig = sig_mapping[trap].unixSignal;
    if (!sig_mapping[trap].dbgSig) { 
	printf("Kernel returns with signal (%d) %s\n",stopInfo.trapType,
		sig_mapping[trap].sig_name);
    }

    text_size = text_end - text_start;
    text_size &= ~(8*1024-1);
    text_start = stopInfo.codeStart - 8*1024;
    text_end = text_start+text_size;
    return status->w_stopsig;
}

/* Read the remote registers into the block REGS.  */
#define FIRST_LOCAL_REGNUM	16
void
remote_fetch_registers (regs)
     char *regs;
{
  StopInfo    stopInfo;
  ERROR_NO_ATTACHED_HOST;
  Kdbx_Trace(DBG_GET_STOP_INFO, (char *) 0, (char *)&stopInfo,
               sizeof(stopInfo));
  bcopy(stopInfo.regs.globals,regs,4*8);
  bcopy(stopInfo.regs.ins,regs+4*8,4*8);
  ((int *)regs)[Y_REGNUM] = stopInfo.regs.y; 
  ((int *)regs)[PS_REGNUM] = stopInfo.regs.curPsr; 
  ((int *)regs)[PC_REGNUM] = stopInfo.regs.pc; 
  ((int *)regs)[NPC_REGNUM] = stopInfo.regs.nextPc; 
  remote_read_bytes(stopInfo.regs.ins[6], 
		((int *) regs) + FIRST_LOCAL_REGNUM,4*16);

}


int
remote_attach(pid)
    int	pid;
{
    int	status;
    ERROR_NO_ATTACHED_HOST;
    if (pid != lastPid) {
	lastPid = pid;
	Kdbx_Trace(DBG_SET_PID, &pid, 0,sizeof(int));
    }
    start_remote();
    return 1;
}
int
remote_detach(sig)
    int	sig;
{
  int cur_pc;

  ERROR_NO_ATTACHED_HOST;
  cur_pc = read_pc();
  if (sig) 
      Kdbx_Trace(DBG_DETACH, &cur_pc, 0, sizeof(int));
   remote_clean_up();
   return 0;
}



/* Read a word from remote address ADDR and return it.
   This goes through the data cache.  */

int
remote_fetch_word (addr)
     CORE_ADDR addr;
{

  int buffer;
  extern CORE_ADDR text_start, text_end;

  ERROR_NO_ATTACHED_HOST;
  if (addr >= text_start && addr < text_end)
	{
	 Kdbx_Trace(DBG_INST_READ, addr, &buffer, sizeof(int));
	  return buffer;
	}
  Kdbx_Trace(DBG_DATA_READ, addr, &buffer, sizeof(int));
  return buffer;
}

/* Write a word WORD into remote address ADDR.
   This goes through the data cache.  */

void
remote_store_word (addr, word)
     CORE_ADDR addr;
     int word;
{
  extern CORE_ADDR text_start, text_end;
  ERROR_NO_ATTACHED_HOST;
 if (addr >= text_start && addr < text_end)
	{
	 Kdbx_Trace(DBG_INST_WRITE, &word, addr, sizeof(word));
	  return ;
	}
  Kdbx_Trace(DBG_DATA_WRITE,  &word, addr, sizeof(word));
  return ;
}

/* Write memory data directly to the remote machine.
   This does not inform the data cache; the data cache uses this.
   MEMADDR is the address in the remote memory space.
   MYADDR is the address of the buffer in our space.
   LEN is the number of bytes.  */

int
remote_write_bytes (memaddr, myaddr, len)
     CORE_ADDR memaddr;
     char *myaddr;
     int len;
{
  extern CORE_ADDR text_start, text_end;
  ERROR_NO_ATTACHED_HOST;
 if (memaddr >= text_start && memaddr < text_end)
	{
	 Kdbx_Trace(DBG_INST_WRITE, myaddr, memaddr, len);
	  return ;
	}
  Kdbx_Trace(DBG_DATA_WRITE,  myaddr, memaddr, len);
  return ;
}

/* Read memory data directly from the remote machine.
   This does not use the data cache; the data cache uses this.
   MEMADDR is the address in the remote memory space.
   MYADDR is the address of the buffer in our space.
   LEN is the number of bytes.  */

int
remote_read_bytes (memaddr, myaddr, len)
     CORE_ADDR memaddr;
     char *myaddr;
     int len;
{

  ERROR_NO_ATTACHED_HOST;
   Kdbx_Trace(DBG_DATA_READ, memaddr, myaddr, len);
}

/* Read LEN bytes from inferior memory at MEMADDR.  Put the result
   at debugger address MYADDR.  Returns errno value.  */
int
remote_read_inferior_memory(memaddr, myaddr, len)
     CORE_ADDR memaddr;
     char *myaddr;
     int len;
{
  int xfersize;
  while (len > 0)
    {
      if (len > MAXBUFBYTES)
	xfersize = MAXBUFBYTES;
      else
	xfersize = len;

      remote_read_bytes (memaddr, myaddr, xfersize);
      memaddr += xfersize;
      myaddr  += xfersize;
      len     -= xfersize;
    }
  return 0; /* no error */
}

/* Copy LEN bytes of data from debugger memory at MYADDR
   to inferior's memory at MEMADDR.  Returns errno value.  */
int
remote_write_inferior_memory (memaddr, myaddr, len)
     CORE_ADDR memaddr;
     char *myaddr;
     int len;
{
  int xfersize;
  while (len > 0)
    {
      if (len > MAXBUFBYTES)
	xfersize = MAXBUFBYTES;
      else
	xfersize = len;

      remote_write_bytes(memaddr, myaddr, xfersize);

      memaddr += xfersize;
      myaddr  += xfersize;
      len     -= xfersize;
    }
  return 0; /* no error */
}

/* Store the remote registers from the contents of the block REGS.  */

void
remote_store_registers (regs,regno)
     char *regs;
     int	regno;
{
	int	i;

	if (regno < 0) { 
	    remote_write_bytes(((int *) regs)[SP_REGNUM], 
				((int *) regs) + FIRST_LOCAL_REGNUM,4*16);
	    for (i = 0; i < 16; i++) {
		Kdbx_Trace(DBG_WRITE_REG, &(((int *)regs)[i]),
					INDEX_OF_REGNUM(i),sizeof(int));
	    }	
	   Kdbx_Trace(DBG_WRITE_REG, ((int *)regs)+Y_REGNUM,
			INDEX_OF_REGNUM(Y_REGNUM),sizeof(int));
	   Kdbx_Trace(DBG_WRITE_REG, ((int *)regs)+PC_REGNUM,
			INDEX_OF_REGNUM(PC_REGNUM),sizeof(int));
	   Kdbx_Trace(DBG_WRITE_REG, ((int *)regs)+NPC_REGNUM,
			    INDEX_OF_REGNUM(NPC_REGNUM),sizeof(int));
	   Kdbx_Trace(DBG_WRITE_REG, ((int *)regs)+PS_REGNUM,
			    INDEX_OF_REGNUM(PS_REGNUM),sizeof(int));

	} else {
	   int	ind = INDEX_OF_REGNUM(regno);
	   if (ind >= 0) 
	       Kdbx_Trace(DBG_WRITE_REG, ((int *)regs)+regno,ind,sizeof(int));
	   else
	        remote_write_bytes(((int *) regs)[SP_REGNUM], 
			((int *) regs) + regno,4);
	}
}

/* 
 * Call a remote function.
 */
call_remote_function(funaddr,nargs,numBytes,argBuffer)
    CORE_ADDR funaddr;
    int		nargs;
    int		numBytes;
    char	*argBuffer;
{
  int	returnValue;
  ERROR_NO_ATTACHED_HOST;
  Kdbx_Trace(DBG_BEGIN_CALL, (char *)0, (char *)0, 0);  
  returnValue = Kdbx_Trace(DBG_CALL_FUNCTION,argBuffer,funaddr,numBytes);
  Kdbx_Trace(DBG_END_CALL, (char *)0, (char *)0, 0);       
  return returnValue;
}

void
remote_reboot (args)
     char *args;
{

  ERROR_NO_ATTACHED_HOST;
  if (!args)
	args = "";

  Kdbx_Trace(DBG_REBOOT, args, NULL, strlen(args));
}


#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sgtty.h>

/*
 * Direct mapped cache of data and code.   The cache is flushed after every
 * step and continue by incrementing the version number.  Flushing code
 * isn't necessary but since kdbx already has an internal code cache it
 * doesn't hurt and makes life easier.
 */
static	int	cacheBlockSize = -1;
static	int	cacheBlockShift = -1;
static	int	cacheSize = 128 * 1024;
#define	CACHE_BLOCK_MASK 	(cacheSize / cacheBlockSize - 1)
#define	CACHE_BLOCK_OFFSET_MASK	(cacheBlockSize - 1)
#define	NUM_CACHE_BLOCKS	(cacheSize >> cacheBlockShift)
#define	CACHE_OFFSET_MASK	(cacheSize - 1)
#define	GET_CACHE_BLOCK(address) (((unsigned int) address) >> cacheBlockShift)
/*
 * The data cache is just an array of characters.
 */
static	char	*dataCache;
/*
 * There is information about kept about each cache block.
 */
typedef	struct {
    int		version;	/* Version number of this cache block. */
    char	*realAddr;	/* Actual address of data stored in the block.*/
} CacheInfo;
static CacheInfo	*cacheInfo;
static int	currentVersion = 1;

/*
 * Stuff for the serial port.
 */
static	int	kernChannel = 0;


/*
 * Message buffers.
 */
static Dbg_Msg	msg;
static int	msgSize;
#define	REPLY_BUFFER_SIZE	16384
static	char	replyBuffer[REPLY_BUFFER_SIZE];
static	char	requestBuffer[DBG_MAX_REQUEST_SIZE];
static	int	msgNum = 0;

static void	RecvReply();

static	struct sockaddr_in	remote;
static	int			kdbxTimeout = 1;
static	int			netSocket;


/*
 *----------------------------------------------------------------------
 *
 * CreateSocket --
 *
 *	Creates a UDP socket connected to the Sprite host's kernel 
 *	debugger port.
 *
 * Results:
 *	The stream ID of the socket.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */
static int
CreateSocket(spriteHostName)
    char	*spriteHostName;
{
    int			socketID;
    struct hostent 	*hostPtr;

    hostPtr = gethostbyname(spriteHostName);
    if (hostPtr == (struct hostent *) NULL) {
	error("CreateSocket: unknown host %s\n", spriteHostName);
    }
    if (hostPtr->h_addrtype != AF_INET) {
	error("CreateSocket: bad address type for host %s\n", 
		spriteHostName);
    }

    socketID = socket(AF_INET, SOCK_DGRAM, 0);
    if (socketID < 0) {
	perror_with_name("CreateSocket: socket");
    }

    bzero((Address)&remote, sizeof(remote));
    bcopy(hostPtr->h_addr, (Address)&remote.sin_addr, hostPtr->h_length);
    remote.sin_port = htons(DBG_UDP_PORT);
    remote.sin_family = AF_INET;

    if (connect(socketID, (struct sockaddr *) &remote, sizeof(remote)) < 0) {
	perror_with_name("CreateSocket: connect");
    }

    return(socketID);
}


/*
 * ----------------------------------------------------------------------------
 *
 *  StartDebugger --
 *
 *     Start off a new conversation with the debugger.
 *
 * Results:
 *     None.
 *
 * Side effects:
 *     Setup r network socket.
 * ----------------------------------------------------------------------------
 */
static void
StartDebugger()
{
 {
        char	*host = hostName;
	hostName = (char *) 0;
	netSocket = CreateSocket(host);
	hostName = host;
    }
}


/*
 * ----------------------------------------------------------------------------
 *
 *  SendRequest --
 *
 *     Send a request message to the kernel.
 *
 * Results:
 *     None.
 *
 * Side effects:
 *     None.
 * ----------------------------------------------------------------------------
 */
static void
SendRequest(numBytes, newRequest)
    int		numBytes;
    Boolean	newRequest;
{
     {
	Dbg_Opcode	opcode;

	msgSize = numBytes;
	if (newRequest) {
	    msgNum++;
	}
	*(int *)requestBuffer = msgNum;
	bcopy(&msg, requestBuffer + 4, numBytes);
	if (write(netSocket, requestBuffer, numBytes + 4) < numBytes + 4) {
	     MARK_DISCONNECTED;
	    perror_with_name("SendRequest: Couldn't write to the kernel socket\n");
	    return;
	}
	if (newRequest) {
	    opcode = (Dbg_Opcode) msg.opcode;
	    if (opcode == DBG_DETACH || opcode == DBG_CONTINUE ||
		opcode == DBG_SINGLESTEP || opcode == DBG_DIVERT_SYSLOG || 
		opcode == DBG_BEGIN_CALL || 
		opcode == DBG_WRITE_REG || opcode == DBG_SET_PID) {
		int	dummy;
		/*
		 * Wait for explicit acknowledgments of these packets.
		 */
		RecvReply(opcode, 4, &dummy, NULL, 1);
	    }
	}
    }
}


/*
 * ----------------------------------------------------------------------------
 *
 *  RecvReply --
 *
 *     Receive a reply from the kernel.
 *
 * Results:
 *     None.
 *
 * Side effects:
 *     None.
 * ----------------------------------------------------------------------------
 */
static void
RecvReply(opcode, numBytes, destAddr, readStatusPtr, timeout)
    Dbg_Opcode	opcode;
    int		numBytes;
    char	*destAddr;
    int	*readStatusPtr;
    int	timeout;
{
    int		status;
    int 	resendRequest = 0;

    if (numBytes + 8 > REPLY_BUFFER_SIZE) {
	fprintf(stderr,"numBytes <%d> > REPLY_BUFFER_SIZE <%d>\n",
		    numBytes + 8, REPLY_BUFFER_SIZE);
	abort();
    }
     {
	int		readMask;
	struct	timeval	interval;
	int		bytesRead;

	interval.tv_sec = kdbxTimeout;
	interval.tv_usec = 0;
	do {
	    if (timeout) {
		int	numTimeouts;

		numTimeouts = 0;
		/*
		 * Loop timing out and sending packets until a new packet
		 * has arrived.
		 */
		do {
		    if (!resendRequest) { 
			readMask = 1 << netSocket;
			status = select(32, &readMask, NULL, NULL, &interval);
		    } else {
			status = 0;
			resendRequest = 0;
		    }
		    if (status == 1) {
			break;
		    } else if (status == -1) {
		        MARK_DISCONNECTED;
			perror_with_name("RecvReply: Couldn't select on socket.\n");
		    } else if (status == 0) {
			SendRequest(msgSize, 0);
			numTimeouts++;
			if (numTimeouts % 10 == 0) {
			    fprintf(stderr, 
				    "Timing out and resending to host %s\n",
				    hostName);
			    fflush(stderr);
			    QUIT;
			}
		    }
		} while (1);
	    }
	    if (opcode == DBG_DATA_READ || opcode == DBG_INST_READ ||
		opcode == DBG_GET_VERSION_STRING) {
		/*
		 * Data and instruction reads return variable size packets.
		 * The first two ints are message number and status.  If
		 * the status is OK then the data follows.
		 */
		immediate_quit++;
		bytesRead = read(netSocket, replyBuffer, numBytes + 8);
		immediate_quit--;
		if (bytesRead < 0) {
		    MARK_DISCONNECTED;
		    perror_with_name("RecvReply: Error reading socket.");
		}
		/*
		 * Check message number before the size because this could
		 * be an old packet.
		 */
		if (*(int *)replyBuffer != msgNum) {
		    printf("RecvReply: Old message number = %d, expecting %d\n",
			    *(int *)replyBuffer, msgNum);
		    fflush(stdout);
		    resendRequest = 1;
		    continue;
		}
		if (bytesRead == 8) {
		    /*
		     * Only 8 bytes so the read failed and there is no data.
		     */
		    *readStatusPtr = 0;
		    return;
		}
	        if (opcode == DBG_GET_VERSION_STRING) {
		     strncpy(destAddr, (char *)(replyBuffer + 4),numBytes);
		     return;
		}
		if (bytesRead != numBytes + 8) {
		    printf("RecvReply: Short read (1): op=%d exp=%d read=%d",
			    opcode, numBytes + 4, bytesRead);
		    continue;
		}
		*readStatusPtr = 1;
		bcopy(replyBuffer + 8, destAddr, numBytes);
		return;
	    } else if (opcode == DBG_END_CALL) {
		int	length;
		/*
		 * End call returns a variable size packet that contains
		 * the result of the call. The format of the message is 
		 * message number, length, data.
		 */
		immediate_quit++;
		bytesRead = read(netSocket, replyBuffer, REPLY_BUFFER_SIZE);
		immediate_quit--;
		if (bytesRead < 0) {
		    MARK_DISCONNECTED;
		    perror_with_name("RecvReply: Error reading socket.");
		}
		/*
		 * Check message number before the size because this could
		 * be an old packet.
		 */
		if (*(int *)replyBuffer != msgNum) {
		    printf("RecvReply: Old message number = %d, expecting %d\n",
			    *(int *)replyBuffer, msgNum);
		    fflush(stdout);
		    resendRequest = 1;
		    continue;
		}
		length = *( int *)(replyBuffer + 4);
		if (bytesRead - 8 != length) {
		    fprintf(stderr, "RecyReply: Short read for syslog data\n");
		    fflush(stderr);
		    length = bytesRead - 8;
		}
		if (length == 0) {
		    /*
		     * No data.
		     */
		    *readStatusPtr = 0;
		    return;
		}
		/*
		 * Dump out the buffer.
		 */
		write(1, replyBuffer + 8, length);
		*readStatusPtr = 1;
		return;
	    } else {
		/*
		 * Normal request so just read in the message which includes
		 * the message number.
		 */
		immediate_quit++;
		bytesRead = read(netSocket, replyBuffer, numBytes + 4);
		immediate_quit--;
		if (bytesRead < 0) {
		    MARK_DISCONNECTED;
		    perror_with_name("RecvReply: Error reading socket (2).");
		}
		/*
		 * Check message number before size because it could be
		 * an old packet.
		 */
		if (*(int *)replyBuffer != msgNum) {
		    printf("RecvReply: Old message number = %d, expecting %d\n",
			    *(int *)replyBuffer, msgNum);
		    fflush(stdout);
		    resendRequest = 1;
		    continue;
		}
		if (bytesRead != numBytes + 4) {
		    printf("RecvReply: Short read (2): op=%d exp=%d read=%d",
			    opcode, numBytes + 4, bytesRead);
		}
		if (*(int *)replyBuffer != msgNum) {
		    continue;
		}
		bcopy(replyBuffer + 4, destAddr, numBytes);
		return;
	    }
	} while (1);
    }
}


/*
 * ----------------------------------------------------------------------------
 *
 *  WaitForKernel --
 *
 *      Wait for the kernel to send us a message to indicate that it is waiting
 *	to be debugged.
 *
 * Results:
 *     None.
 *
 * Side effects:
 *     None.
 * ----------------------------------------------------------------------------
 */
static void
WaitForKernel()
{
    {
	int	dummy;

	RecvReply(DBG_CONTINUE, 4, &dummy, NULL, 0);
    }
}


/*
 * ----------------------------------------------------------------------------
 *
 * BlockInCache --
 *
 *     See if the given block at the given address is in the cache.
 *
 * Results:
 *     1 if found block in cache, 0 if didn't.
 *
 * Side effects:
 *     None.
 */
static int
BlockInCache(blockNum, addr)
    int		blockNum;
    char	*addr;
{
    blockNum = blockNum & CACHE_BLOCK_MASK;
    return((int) (cacheInfo[blockNum].version == currentVersion &&
	   (unsigned int) cacheInfo[blockNum].realAddr == 
		    ((unsigned int) (addr) & ~CACHE_BLOCK_OFFSET_MASK)));
}


/*
 * ----------------------------------------------------------------------------
 *
 * FetchBlock --
 *
 *     Fetch the given data block from the cache or the kernel if necessary.
 *
 * Results:
 *     1 if could fetch block into cache, 0 if couldn't.
 *
 * Side effects:
 *     Data cache modified.
 */
static int
FetchBlock(blockNum, srcAddr, opcode)
    int		blockNum;
    char	*srcAddr;
    Dbg_Opcode	opcode;
{
    int	successfulRead;

    blockNum = blockNum & CACHE_BLOCK_MASK;
    srcAddr = (char *) ((unsigned int) (srcAddr) & ~CACHE_BLOCK_OFFSET_MASK);

    if (BlockInCache(blockNum, srcAddr)) {
	return(1);
    }
    msg.opcode = opcode;
    msg.data.readMem.address = (int) srcAddr;
    msg.data.readMem.numBytes = cacheBlockSize;
    SendRequest(sizeof(msg.opcode) + sizeof(Dbg_ReadMem), 1);
    RecvReply(opcode, cacheBlockSize, 
		&dataCache[(unsigned int) (srcAddr) & CACHE_OFFSET_MASK],
		&successfulRead, 1);
    if (successfulRead) {
	cacheInfo[blockNum].version = currentVersion;
	cacheInfo[blockNum].realAddr = srcAddr;
    }
    return(successfulRead);
}



/*
 * ----------------------------------------------------------------------------
 *
 * Kdbx_Trace --
 *
 *     Write the trace command over to the kernel.  
 *
 * Results:
 *     None.
 *
 * Side effects:
 *     None.
 *
 * ----------------------------------------------------------------------------
 */
int Kdbx_Trace(opcode, srcAddr, destAddr, numBytes)
    Dbg_Opcode	opcode;		/* Which command */
    char	*srcAddr;	/* Where to read data from */
    char	*destAddr;	/* Where to write data to */
    int		numBytes;	/* The number of bytes to read or write */
{
    int			(*intrHandler)();
    int			i;

    if (!initialized) {
	int	moreData;
	/*
	 * Setup the cache and initiate a conversation with the other kernel.
	 */
	if (cacheBlockSize == -1) {
	     {
		cacheBlockSize = 256;
		cacheBlockShift = 8;
	    }
	}
	dataCache = (char *) malloc(cacheSize);
	cacheInfo = (CacheInfo *) malloc(NUM_CACHE_BLOCKS * sizeof(CacheInfo));
	for (i = 0; i < NUM_CACHE_BLOCKS; i++) {
	    cacheInfo[i].version = 0;
	}
	StartDebugger();
	/*
	 * Dump the system log by faking a call command.
	 */
	printf("Dumping system log ...\n");
	fflush(stdout);
	msg.opcode = (short)DBG_BEGIN_CALL;
	SendRequest(sizeof(msg.opcode), 1);
	msg.opcode = (short)DBG_END_CALL;
	do {
	    SendRequest(sizeof(msg.opcode), 1);
	    RecvReply(msg.opcode, 0, NULL, &moreData, 1);
	} while (moreData);
	initialized = 1;
    }


    if (opcode == DBG_DATA_READ || opcode == DBG_INST_READ) {
	int			firstBlock;
	int			lastBlock;
	unsigned	int	cacheOffset;
	int			toRead;

	/*
	 * Read using the cache.
	 */
	firstBlock = GET_CACHE_BLOCK(srcAddr); 
	lastBlock = GET_CACHE_BLOCK(srcAddr + numBytes - 1);
	for (i = firstBlock; i <= lastBlock; i++) {
	    cacheOffset = ((unsigned int) srcAddr) & CACHE_OFFSET_MASK;
	    if (i == lastBlock) {
		toRead = numBytes;
	    } else if (i == firstBlock) {
		toRead = cacheBlockSize - 
			    (cacheOffset & CACHE_BLOCK_OFFSET_MASK);
	    } else {
		toRead = cacheBlockSize;
	    }
	    if (!FetchBlock(i, srcAddr, opcode)) {
		error("ERROR: invalid read address 0x%x\n",srcAddr);
	    }
	    bcopy(&dataCache[cacheOffset], destAddr, toRead);
	    srcAddr += toRead;
	    destAddr += toRead;
	    numBytes -= toRead;
	}
	return(0);
    }

    if (opcode == DBG_DATA_WRITE || opcode == DBG_INST_WRITE) {
	int	firstBlock;
	int	lastBlock;
	int	cacheOffset;
	int	toWrite;
	char	*tSrcAddr;
	char	*tDestAddr;
	int	tNumBytes;

	/*
	 * If the block that is being fetched is in the cache then write the
	 * data there first before sending it over to the kernel.
	 */
	tSrcAddr = srcAddr;
	tDestAddr = destAddr;
	tNumBytes = numBytes;

	firstBlock = GET_CACHE_BLOCK(destAddr); 
	lastBlock = GET_CACHE_BLOCK(destAddr + numBytes - 1);
	for (i = firstBlock; i <= lastBlock; i++) {
	    cacheOffset = ((int) tDestAddr) & CACHE_OFFSET_MASK;
	    if (i == lastBlock) {
		toWrite = tNumBytes;
	    } else if (i == firstBlock) {
		toWrite = cacheBlockSize - 
			    (cacheOffset & CACHE_BLOCK_OFFSET_MASK);
	    } else {
		toWrite = cacheBlockSize;
	    }
	    if (BlockInCache(i, tDestAddr)) {
		bcopy(tSrcAddr, &dataCache[cacheOffset], tNumBytes);
	    }
	    tSrcAddr += toWrite;
	    tDestAddr += toWrite;
	    tNumBytes -= toWrite;
	}
    }

    msg.opcode = (short) opcode;

    /*
     * Do the rest of the work for the desired operation.
     */

    switch (opcode) {

	/*
	 * For these operations the desired data is read from the other
	 * kernel and stored at destAddr.
	 */
	case DBG_READ_ALL_REGS:
	case DBG_GET_STOP_INFO:
	    SendRequest(sizeof(msg.opcode), 1);
	    RecvReply(opcode, numBytes, destAddr, NULL, 1);
	    break;

	/*
	 * For this operation the desired data is read from srcAddr
	 * and written to the other kernel.
	 */
	case DBG_SET_PID:
	    msg.data.pid = *(int *)srcAddr;
	    SendRequest(sizeof(msg.opcode) + sizeof(msg.data.pid), 1);
	    break;

	/*
	 * When writing a general purpose register first the address to write
	 * that is stored in destAddr must be given to the other kernel.
	 * Then the data itself which is stored at srcAddr can be written over.
	 */
	case DBG_WRITE_REG:
	    msg.data.writeReg.regNum = (int) destAddr;
	    msg.data.writeReg.regVal = *(int *) srcAddr;
	    SendRequest(sizeof(msg.opcode) + sizeof(Dbg_WriteReg), 1);
	    break;

	/*
	 * When writing to the kernels instruction or data space first the
	 * address of where to write to (destAddr) and then the number of
	 * bytes to write (numBytes) must be sent over.  Finally all of
	 * the data is read from srcAddr and written over.
	 */

	case DBG_INST_WRITE:
	case DBG_DATA_WRITE: {
	    char	writeStatus;

	    msg.data.writeMem.address = (int) destAddr;
	    msg.data.writeMem.numBytes = numBytes;
	    bcopy(srcAddr, msg.data.writeMem.buffer, numBytes);
	    SendRequest(sizeof(msg.opcode) + 2 * sizeof(int) + numBytes, 1);
	    RecvReply(opcode, 1, &writeStatus, NULL, 1);
	    if (writeStatus == 0) {
		error("ERROR: invalid write address 0x%x\n",destAddr);
	    } 
	    break;
	}
	case DBG_DIVERT_SYSLOG:
	    msg.data.syslogCmd = (Dbg_SyslogCmd)srcAddr;
	    SendRequest(sizeof(msg.opcode) + sizeof(Dbg_SyslogCmd), 1);
	    break;
	case DBG_BEGIN_CALL:
	    SendRequest(sizeof(msg.opcode), 1);
	    break;
	case DBG_END_CALL: {
	    Boolean	moreData;
	    do {
		SendRequest(sizeof(msg.opcode), 1);
		RecvReply(opcode, 0, NULL, &moreData, 1);
	    } while (moreData);
	    break;
	}

	case DBG_DETACH: {
	    msg.opcode = (short) DBG_DIVERT_SYSLOG;
	    msg.data.syslogCmd = DBG_SYSLOG_TO_ORIG;
	    SendRequest(sizeof(msg.opcode) + sizeof(Dbg_SyslogCmd), 1);

	    msg.opcode = (short) DBG_DETACH;
	    msg.data.pc = *(int *) srcAddr;
	    SendRequest(sizeof(msg.opcode) + sizeof(msg.data.pc), 1);
	    break;
	}

	case DBG_CONTINUE:
	case DBG_SINGLESTEP:
	    SendRequest(sizeof(msg.opcode) + sizeof(msg.data.pc), 1);
	    currentVersion++;
	    WaitForKernel();
	    break;

	case DBG_CALL_FUNCTION: {
	    int		returnValue;

	    msg.data.callFunc.address = (int) destAddr;
	    msg.data.callFunc.numBytes = numBytes;
	    bcopy(srcAddr, msg.data.callFunc.buffer, numBytes);
	    SendRequest(sizeof(msg.opcode) + 2 * sizeof(int) + numBytes, 1);
	    RecvReply(opcode, sizeof(returnValue), &returnValue, NULL, 1);
	    return (returnValue);
	}
	case DBG_REBOOT: {
	    msg.data.reboot.stringLength = numBytes;
	    bcopy(srcAddr, msg.data.reboot.string, numBytes);
	    SendRequest(sizeof(msg.opcode) + sizeof(int) + numBytes, 1);
	    return (0);
	}
	case DBG_GET_VERSION_STRING: {
	    SendRequest(sizeof(msg.opcode), 1);
	    RecvReply(opcode,numBytes , destAddr, NULL, 1);
	    return (0);
	}
	default:
	    printf("Unknown opcode %d\n", opcode);
	    return(-1);
    }
    return(0);
}
remote_close()
{
}
remote_clean_up()
{
	MARK_DISCONNECTED;
}

