4.3BSD-Reno/src/sys/hp300/kgdb_stub.c

Compare this file to the similar file:
Show the results in this format:

/*
 * Copyright (c) 1988 University of Utah.
 * Copyright (c) 1990 The Regents of the University of California.
 * All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * the Systems Programming Group of the University of Utah Computer
 * Science Department.
 *
 * Redistribution is only permitted until one year after the first shipment
 * of 4.4BSD by the Regents.  Otherwise, redistribution and use in source and
 * binary forms are permitted provided that: (1) source distributions retain
 * this entire copyright notice and comment, and (2) distributions including
 * binaries display the following acknowledgement:  This product includes
 * software developed by the University of California, Berkeley and its
 * contributors'' in the documentation or other materials provided with the
 * distribution and in all advertising materials mentioning features or use
 * of this software.  Neither the name of the University nor the names of
 * its contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 *	@(#)kgdb_stub.c	7.3 (Berkeley) 6/22/90
 */

/*
 *
 *    The following gdb commands are supported:
 *
 * command          function                               Return value
 *
 *    g             return the value of the CPU registers  hex data or ENN
 *    G             set the value of the CPU registers     OK or ENN
 *
 *    mAA..AA,LLLL  Read LLLL bytes at address AA..AA      hex data or ENN
 *    MAA..AA,LLLL: Write LLLL bytes at address AA.AA      OK or ENN
 *
 *    c             Resume at current address              SNN   ( signal NN)
 *    cAA..AA       Continue at address AA..AA             SNN
 *
 *    s             Step one instruction                   SNN
 *    sAA..AA       Step one instruction from AA..AA       SNN
 *
 *    k             kill
 *
 *    ?             What was the last sigval ?             SNN   (signal NN)
 *
 * All commands and responses are sent with a packet which includes a
 * checksum.  A packet consists of
 *
 * $<packet info>#<checksum>.
 *
 * where
 * <packet info> :: <characters representing the command or response>
 * <checksum>    :: < two hex digits computed as modulo 256 sum of <packetinfo>>
 *
 * When a packet is received, it is first acknowledged with either '+' or '-'.
 * '+' indicates a successful transfer.  '-' indicates a failed transfer.
 *
 * Example:
 *
 * Host:                  Reply:
 * $m0,10#2a               +$00010203040506070809101112131415#42
 *
 ****************************************************************************/

#ifdef KGDB
#include "param.h"
#include "systm.h"
#include "trap.h"
#include "cpu.h"
#include "psl.h"
#include "reg.h"
#include "frame.h"
#include "buf.h"

extern void printf();
extern void bcopy();
extern int kernacc();
extern void chgkprot();

/* # of additional (beyond 4) bytes in 680x0 exception frame format n */
static int frame_bytes[16] = {
	4,	4,	8,	4,
	4,	4,	4,	4,
	54,	16,	28,	88,
	4,	4,	4,	4
};

#define USER    040             /* (XXX from trap.c) user-mode flag in type */

/*
 * BUFMAX defines the maximum number of characters in inbound/outbound
 * buffers.  At least NUMREGBYTES*2 are needed for register packets.
 */
#define BUFMAX 512

#ifndef KGDBDEV
#define KGDBDEV -1
#endif
#ifndef KGDBRATE
#define KGDBRATE 9600
#endif

int kgdb_dev = KGDBDEV;		/* remote debugging device (-1 if none) */
int kgdb_rate = KGDBRATE;	/* remote debugging baud rate */
int kgdb_debug_init = 0;	/* != 0 waits for remote at system init */
int kgdb_debug = 0;		/* > 0 prints command & checksum errors */

#include "../hp300/cons.h"

#define GETC	\
	(constab[major(kgdb_dev)].cn_getc ? \
		(*constab[major(kgdb_dev)].cn_getc)(kgdb_dev) : 0)
#define PUTC(c)	{ \
	if (constab[major(kgdb_dev)].cn_putc) \
		(*constab[major(kgdb_dev)].cn_putc)(kgdb_dev, c); \
}

static char hexchars[] = "0123456789abcdef";

/*
 * There are 180 bytes of registers on a 68020 w/68881.  Many of the fpa
 * registers are 12 byte (96 bit) registers.
 */
#define NUMREGBYTES 180

static char inbuffer[BUFMAX];
static char outbuffer[BUFMAX];

static inline int 
hex(ch)
	char ch;
{
	if ((ch >= '0') && (ch <= '9'))
		return (ch - '0');
	if ((ch >= 'a') && (ch <= 'f'))
		return (ch - ('a' - 10));
	return (0);
}

/* scan for the sequence $<data>#<checksum>     */
static void 
getpacket(char *buffer)
{
	unsigned char checksum;
	unsigned char xmitcsum;
	int i;
	int count;
	char ch;

	do {
		/*
		 * wait around for the start character, ignore all other
		 * characters
		 */
		while ((ch = GETC) != '$')
			;
		checksum = 0;
		count = 0;
		xmitcsum = 1;

		/* now, read until a # or end of buffer is found */
		while (count < BUFMAX) {
			ch = GETC;
			if (ch == '#')
				break;
			checksum = checksum + ch;
			buffer[count] = ch;
			count = count + 1;
		}
		buffer[count] = 0;

		if (ch == '#') {
			xmitcsum = hex(GETC) << 4;
			xmitcsum += hex(GETC);
			if (kgdb_debug && (checksum != xmitcsum)) {
				printf(
			"bad checksum.  My count = 0x%x, sent=0x%x. buf=%s\n",
					checksum, xmitcsum, buffer);
			}
			if (checksum != xmitcsum) {
				PUTC('-');	/* failed checksum */
			} else {
				PUTC('+');	/* successful transfer */
				/*
				 * if a sequence char is present, reply the
				 * sequence ID
				 */
				if (buffer[2] == ':') {
					PUTC(buffer[0]);
					PUTC(buffer[1]);
					/* remove sequence chars from buffer */
					for (i = 3; i <= count; ++i)
						buffer[i - 3] = buffer[i];
				}
			}
		}
	} while (checksum != xmitcsum);
}

/*
 * send the packet in buffer.  The host gets one chance to read it.  This
 * routine does not wait for a positive acknowledge.
 */
static void 
putpacket(char *buffer)
{
	unsigned char checksum;
	int count;
	char ch;

	/* $<packet info>#<checksum>. */
	do {
		PUTC('$');
		checksum = 0;
		count = 0;

		while (ch = buffer[count]) {
			PUTC(ch);
			checksum += ch;
			count += 1;
		}
		PUTC('#');
		PUTC(hexchars[checksum >> 4]);
		PUTC(hexchars[checksum & 15]);

	} while (0);	/* (GETC != '+'); */

}

static inline void 
debug_error(char *format, char *parm)
{
	if (kgdb_debug)
		printf(format, parm);
}

/*
 * Convert at most 'dig' digits of hex data in buf into a value.
 * Stop on non-hex char.  Return a pointer to next char in buf.
 */
static char *
hex2val(char *buf, int *val, int dig)
{
	int i, v;
	char ch;

	v = 0;
	for (i = dig; --i >= 0; ) {
		ch = *buf++;
		if ((ch >= '0') && (ch <= '9'))
			v = (v << 4) | (ch - '0');
		else if ((ch >= 'a') && (ch <= 'f'))
			v = (v << 4) | (ch - ('a' - 10));
		else {
			--buf;
			break;
		}
	}
	*val = v;
	return (buf);
}

/*
 * convert the integer value 'val' into 'dig' hex digits, placing
 * result in buf.  Return a pointer to the last char put in buf (null).
 */
static char *
val2hex(char *buf, int val, int dig)
{
	for (dig <<= 2; (dig -= 4) >= 0; )
		*buf++ = hexchars[(val >> dig) & 0xf];
	*buf = 0;
	return (buf);
}

/*
 * convert the memory pointed to by mem into hex, placing result in buf.
 * return a pointer to the last char put in buf (null).
 */
static char *
mem2hex(char *buf, char *mem, int count)
{
	if ((count & 1) || ((int)mem & 1)) {
		char ch;

		while(--count >= 0) {
			ch = *mem++;
			*buf++ = hexchars[ch >> 4];
			*buf++ = hexchars[ch & 15];
		}
	} else {
		u_short s;
		u_short *mp = (u_short *)mem;

		for (count >>= 1; --count >= 0; ) {
			s = *mp++;
			*buf++ = hexchars[(s >> 12) & 15];
			*buf++ = hexchars[(s >> 8) & 15];
			*buf++ = hexchars[(s >> 4) & 15];
			*buf++ = hexchars[s & 15];
		}
	}
	*buf = 0;
	return (buf);
}

/*
 * Convert the hex array pointed to by buf into binary to be placed in mem.
 * Return a pointer to next char in buf.
 */
static char *
hex2mem(char *buf, char *mem, int count)
{
	int i;
	unsigned char ch;

	for (i = 0; i < count; ++i) {
		ch = hex(*buf++) << 4;
		ch = ch + hex(*buf++);
		*mem++ = ch;
	}
	return (buf);
}

/*
 * Translate a trap number into a unix compatible signal value.
 * (gdb only understands unix signal numbers).
 */
static int 
computeSignal(int type)
{
	int sigval;

	switch (type &~ USER) {
	case T_BUSERR:
		sigval = SIGBUS;
		break;		/* bus error           */
	case T_ADDRERR:
		sigval = SIGBUS;
		break;		/* address error       */
	case T_ILLINST:
		sigval = SIGILL;
		break;		/* illegal instruction */
	case T_ZERODIV:
		sigval = SIGFPE;
		break;		/* zero divide         */
	case T_CHKINST:
		sigval = SIGFPE;
		break;		/* chk instruction     */
	case T_TRAPVINST:
		sigval = SIGFPE;
		break;		/* trapv instruction   */
	case T_PRIVINST:
		sigval = SIGILL;
		break;		/* privilege violation */
	case T_TRACE:
		sigval = SIGTRAP;
		break;		/* trace trap          */
	case T_MMUFLT:
		sigval = SIGSEGV;
		break;
	case T_SSIR:
		sigval = SIGSEGV;
		break;
	case T_FMTERR:
		sigval = SIGILL;
		break;
	case T_FPERR:
		sigval = SIGFPE;
		break;
	case T_COPERR:
		sigval = SIGFPE;
		break;
	case T_ASTFLT:
		sigval = SIGINT;
		break;
	case T_TRAP15:
		sigval = SIGIOT;
		break;
	default:
		sigval = SIGEMT;
		break;
	}
	return (sigval);
}

#define RESPOND(str) (bcopy(str, outbuffer, sizeof(str)))

/*
 * This function does all command procesing for interfacing to 
 * a remote gdb.
 */
int 
kgdb_trap(int type, unsigned code, unsigned v, struct frame *frame)
{
	int sigval;
	int addr, length;
	char *ptr;

	if (kgdb_dev < 0)
		/* not debugging */
		return (0);

	if (kgdb_debug)
		printf("type=%d, code=%d, vector=0x%x, pc=0x%x, sr=0x%x\n",
			type, code, frame->f_vector, frame->f_pc, frame->f_sr);

	/* reply to host that an exception has occurred */
	sigval = computeSignal(type);
	outbuffer[0] = 'S';
	(void)val2hex(&outbuffer[1], sigval, 2);
	putpacket(outbuffer);

	while (1) {
		outbuffer[0] = 0;
		getpacket(inbuffer);
		ptr = inbuffer;
		switch (*ptr++) {
		case '?':
			outbuffer[0] = 'S';
			(void)val2hex(&outbuffer[1], sigval, 2);
			break;
		case 'g':	/* return the value of the CPU registers */
			ptr = outbuffer;
			if (type & USER)
				ptr = mem2hex(ptr, (char *)frame->f_regs,
					      sizeof(frame->f_regs));
			else {
				ptr = mem2hex(ptr, (char *)frame->f_regs,
					      sizeof(frame->f_regs) - 4);
				addr = (int)&frame->f_pc -
					frame_bytes[frame->f_format];
				ptr = mem2hex(ptr, (char *)&addr, sizeof(addr));
			}
			addr = frame->f_sr;
			ptr = mem2hex(ptr, (char *)&addr, sizeof(addr));
			ptr = mem2hex(ptr, (char *)&frame->f_pc,
				      sizeof(frame->f_pc));
			break;
		case 'G':	/* set the value of the CPU registers */
			ptr = hex2mem(ptr, (char *)frame->f_regs,
				      sizeof(frame->f_regs) - 4);
			ptr = hex2mem(ptr, (char *)&addr, sizeof(addr));
			ptr = hex2mem(ptr, (char *)&addr, sizeof(addr));
			frame->f_sr = addr;
			ptr = hex2mem(ptr, (char *)&frame->f_pc,
				      sizeof(frame->f_pc));
			RESPOND("OK");
			break;

			/* mAA..AA,LLLL  Read LLLL bytes at address AA..AA */
		case 'm':
			ptr = hex2val(ptr, &addr, 8);
			if (*ptr++ != ',') {
				RESPOND("E01");
				debug_error("malformed read memory cmd: %s\n",
					    inbuffer);
				break;
			}
			ptr = hex2val(ptr, &length, 8);
			if (length <= 0 || length >= BUFMAX*2) {
				RESPOND("E02");
				if (kgdb_debug)
					printf("bad read memory length: %d\n",
					       length);
				break;
			}
			if (! kernacc(addr, length, B_READ)) {
				RESPOND("E03");
				if (kgdb_debug)
					printf("read access violation addr 0x%x len %d\n", addr, length);
				break;
			}
			(void)mem2hex(outbuffer, (char *)addr, length);
			break;

			/*
			 * MAA..AA,LLLL: Write LLLL bytes at address AA.AA
			 * return OK
			 */
		case 'M':
			ptr = hex2val(ptr, &addr, 8);
			if (*ptr++ != ',') {
				RESPOND("E01");
				debug_error("malformed write memory cmd: %s\n",
					    inbuffer);
				break;
			}
			ptr = hex2val(ptr, &length, 8);
			if (*ptr++ != ':') {
				RESPOND("E01");
				debug_error("malformed write memory cmd: %s\n",
					    inbuffer);
				break;
			}
			if (length <= 0 || length >= BUFMAX*2 - 32) {
				RESPOND("E02");
				if (kgdb_debug)
					printf("bad write memory length: %d\n",
					       length);
				break;
			}
			if (! kernacc(addr, length, B_READ)) {
				RESPOND("E03");
				if (kgdb_debug)
					printf("write access violation addr 0x%x len %d\n", addr, length);
				break;
			}
			if (! kernacc(addr, length, B_WRITE))
				chgkprot(addr, length, B_WRITE);
			(void)hex2mem(ptr, (char *)addr, length);
			RESPOND("OK");
			break;

			/*
			 * cAA..AA  Continue at address AA..AA
			 * sAA..AA  Step one instruction from AA..AA
			 * (addresses optional)
			 */
		case 'c':
		case 's':
			/*
			 * try to read optional start address.
			 */
			if (ptr != hex2val(ptr, &addr, 8)) {
				frame->f_pc = addr;
				if (kgdb_debug)
					printf("new pc = 0x%x\n", addr);
			}
			/* deal with the trace bit */
			if (inbuffer[0] == 's')
				frame->f_sr |= PSL_T;
			else
				frame->f_sr &=~ PSL_T;

			if (kgdb_debug)
				printf("restarting at 0x%x\n", frame->f_pc);

			return (1);

			/* kill the program (same as continue for now) */
		case 'k':
			return (1);
		}
		/* reply to the request */
		putpacket(outbuffer);
	}
}
#endif