aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Traina <pst@FreeBSD.org>1996-08-27 19:45:58 +0000
committerPaul Traina <pst@FreeBSD.org>1996-08-27 19:45:58 +0000
commitf8f0b4798e9c343483a95e2049912fbca993b581 (patch)
tree76982da048587d3d1d2cf24c80a398f8829d3224
parent8830dd31e3f8d85aea8bda32179f12239e3c5288 (diff)
downloadsrc-f8f0b4798e9c343483a95e2049912fbca993b581.tar.gz
src-f8f0b4798e9c343483a95e2049912fbca993b581.zip
Support for GDB remote debug protocol.
Sponsored by: Juniper Networks, Inc. <pst@jnx.com>
Notes
Notes: svn path=/head/; revision=17847
-rw-r--r--sys/amd64/amd64/amd64-gdbstub.c583
-rw-r--r--sys/amd64/amd64/db_interface.c12
-rw-r--r--sys/conf/files.i3863
-rw-r--r--sys/i386/boot/biosboot/boot.c4
-rw-r--r--sys/i386/boot/dosboot/fbsdboot.c1
-rw-r--r--sys/i386/boot/dosboot/reboot.h8
-rw-r--r--sys/i386/boot/netboot/bootmenu.c1
-rw-r--r--sys/i386/conf/files.i3863
-rw-r--r--sys/i386/i386/db_interface.c12
-rw-r--r--sys/i386/i386/i386-gdbstub.c583
-rw-r--r--sys/sys/reboot.h3
11 files changed, 1198 insertions, 15 deletions
diff --git a/sys/amd64/amd64/amd64-gdbstub.c b/sys/amd64/amd64/amd64-gdbstub.c
new file mode 100644
index 000000000000..41b72afaff3c
--- /dev/null
+++ b/sys/amd64/amd64/amd64-gdbstub.c
@@ -0,0 +1,583 @@
+/****************************************************************************
+
+ THIS SOFTWARE IS NOT COPYRIGHTED
+
+ HP offers the following for use in the public domain. HP makes no
+ warranty with regard to the software or it's performance and the
+ user accepts the software "AS IS" with all faults.
+
+ HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD
+ TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+****************************************************************************/
+
+/****************************************************************************
+ * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $
+ *
+ * Module name: remcom.c $
+ * Revision: 1.34 $
+ * Date: 91/03/09 12:29:49 $
+ * Contributor: Lake Stevens Instrument Division$
+ *
+ * Description: low level support for gdb debugger. $
+ *
+ * Considerations: only works on target hardware $
+ *
+ * Written by: Glenn Engel $
+ * ModuleState: Experimental $
+ *
+ * NOTES: See Below $
+ *
+ * Modified for FreeBSD by Stu Grossman.
+ *
+ * To enable debugger support, two things need to happen. One, a
+ * call to set_debug_traps() is necessary in order to allow any breakpoints
+ * or error conditions to be properly intercepted and reported to gdb.
+ * Two, a breakpoint needs to be generated to begin communication. This
+ * is most easily accomplished by a call to breakpoint(). Breakpoint()
+ * simulates a breakpoint by executing a trap #1.
+ *
+ * The external function exceptionHandler() is
+ * used to attach a specific handler to a specific 386 vector number.
+ * It should use the same privilege level it runs at. It should
+ * install it as an interrupt gate so that interrupts are masked
+ * while the handler runs.
+ * Also, need to assign exceptionHook and oldExceptionHook.
+ *
+ * Because gdb will sometimes write to the stack area to execute function
+ * calls, this program cannot rely on using the supervisor stack so it
+ * uses it's own stack area reserved in the int array remcomStack.
+ *
+ *************
+ *
+ * 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
+ *
+ ****************************************************************************/
+
+#include <sys/types.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <string.h>
+#include <machine/db_machdep.h>
+#include <machine/trap.h>
+#include <machine/psl.h>
+
+/************************************************************************/
+
+void gdb_handle_exception (db_regs_t *, int, int);
+
+extern jmp_buf db_jmpbuf;
+extern void db_read_bytes (vm_offset_t addr, int size, char *data);
+extern void db_write_bytes (vm_offset_t addr, int size, char *data);
+extern void siocnputc (dev_t, int c);
+extern int siocngetc (dev_t);
+
+/************************************************************************/
+/* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/
+/* at least NUMREGBYTES*2 are needed for register packets */
+#define BUFMAX 400
+
+/* Create private copies of common functions used by the stub. This prevents
+ nasty interactions between app code and the stub (for instance if user steps
+ into strlen, etc..) */
+
+static int
+strlen (const char *s)
+{
+ const char *s1 = s;
+
+ while (*s1++ != '\000');
+
+ return s1 - s;
+}
+
+static char *
+strcpy (char *dst, const char *src)
+{
+ char *retval = dst;
+
+ while ((*dst++ = *src++) != '\000');
+
+ return retval;
+}
+
+static int
+putDebugChar (int c) /* write a single character */
+{
+ siocnputc (NULL, c);
+ return 1;
+}
+
+static int
+getDebugChar (void) /* read and return a single char */
+{
+ return siocngetc (NULL);
+}
+
+static const char hexchars[]="0123456789abcdef";
+
+static int
+hex(char ch)
+{
+ if ((ch >= 'a') && (ch <= 'f')) return (ch-'a'+10);
+ if ((ch >= '0') && (ch <= '9')) return (ch-'0');
+ if ((ch >= 'A') && (ch <= 'F')) return (ch-'A'+10);
+ return (-1);
+}
+
+/* scan for the sequence $<data>#<checksum> */
+static void
+getpacket (char *buffer)
+{
+ unsigned char checksum;
+ unsigned char xmitcsum;
+ int i;
+ int count;
+ unsigned char ch;
+
+ do
+ {
+ /* wait around for the start character, ignore all other characters */
+
+ while ((ch = (getDebugChar () & 0x7f)) != '$');
+
+ checksum = 0;
+ xmitcsum = -1;
+
+ count = 0;
+
+ /* now, read until a # or end of buffer is found */
+
+ while (count < BUFMAX)
+ {
+ ch = getDebugChar () & 0x7f;
+ if (ch == '#')
+ break;
+ checksum = checksum + ch;
+ buffer[count] = ch;
+ count = count + 1;
+ }
+ buffer[count] = 0;
+
+ if (ch == '#')
+ {
+ xmitcsum = hex (getDebugChar () & 0x7f) << 4;
+ xmitcsum += hex (getDebugChar () & 0x7f);
+
+ if (checksum != xmitcsum)
+ putDebugChar ('-'); /* failed checksum */
+ else
+ {
+ putDebugChar ('+'); /* successful transfer */
+ /* if a sequence char is present, reply the sequence ID */
+ if (buffer[2] == ':')
+ {
+ putDebugChar (buffer[0]);
+ putDebugChar (buffer[1]);
+
+ /* remove sequence chars from buffer */
+
+ count = strlen (buffer);
+ for (i=3; i <= count; i++)
+ buffer[i-3] = buffer[i];
+ }
+ }
+ }
+ }
+ while (checksum != xmitcsum);
+}
+
+/* send the packet in buffer. */
+
+static void
+putpacket (char *buffer)
+{
+ unsigned char checksum;
+ int count;
+ unsigned char ch;
+
+ /* $<packet info>#<checksum>. */
+ do
+ {
+ putDebugChar ('$');
+ checksum = 0;
+ count = 0;
+
+ while (ch=buffer[count])
+ {
+ putDebugChar (ch);
+ checksum += ch;
+ count += 1;
+ }
+
+ putDebugChar ('#');
+ putDebugChar (hexchars[checksum >> 4]);
+ putDebugChar (hexchars[checksum & 0xf]);
+ }
+ while ((getDebugChar () & 0x7f) != '+');
+}
+
+static char remcomInBuffer[BUFMAX];
+static char remcomOutBuffer[BUFMAX];
+
+static int
+get_char (vm_offset_t addr)
+{
+ char data;
+
+ if (setjmp (db_jmpbuf))
+ return -1;
+
+ db_read_bytes (addr, 1, &data);
+
+ return data & 0xff;
+}
+
+static int
+set_char (vm_offset_t addr, int val)
+{
+ char data;
+
+ if (setjmp (db_jmpbuf))
+ return -1;
+
+ data = val;
+
+ db_write_bytes (addr, 1, &data);
+ return 0;
+}
+
+/* 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 (vm_offset_t mem, char *buf, int count)
+{
+ int i;
+ int ch;
+
+ for (i=0;i<count;i++) {
+ ch = get_char (mem++);
+ if (ch == -1)
+ return NULL;
+ *buf++ = hexchars[ch >> 4];
+ *buf++ = hexchars[ch % 16];
+ }
+ *buf = 0;
+ return(buf);
+}
+
+/* convert the hex array pointed to by buf into binary to be placed in mem */
+/* return a pointer to the character AFTER the last byte written */
+static char *
+hex2mem (char *buf, vm_offset_t mem, int count)
+{
+ int i;
+ int ch;
+ int rv;
+
+ for (i=0;i<count;i++) {
+ ch = hex(*buf++) << 4;
+ ch = ch + hex(*buf++);
+ rv = set_char (mem++, ch);
+ if (rv == -1)
+ return NULL;
+ }
+ return(buf);
+}
+
+/* this function takes the 386 exception vector and attempts to
+ translate this number into a unix compatible signal value */
+static int
+computeSignal (int exceptionVector)
+{
+ int sigval;
+ switch (exceptionVector & ~T_USER)
+ {
+ case 0: sigval = 8; break; /* divide by zero */
+ case 1: sigval = 5; break; /* debug exception */
+ case 3: sigval = 5; break; /* breakpoint */
+ case 4: sigval = 16; break; /* into instruction (overflow) */
+ case 5: sigval = 16; break; /* bound instruction */
+ case 6: sigval = 4; break; /* Invalid opcode */
+ case 7: sigval = 8; break; /* coprocessor not available */
+ case 8: sigval = 7; break; /* double fault */
+ case 9: sigval = 11; break; /* coprocessor segment overrun */
+ case 10: sigval = 5; break; /* Invalid TSS (also single-step) */
+ case 11: sigval = 11; break; /* Segment not present */
+ case 12: sigval = 11; break; /* stack exception */
+ case 13: sigval = 11; break; /* general protection */
+ case 14: sigval = 11; break; /* page fault */
+ case 16: sigval = 7; break; /* coprocessor error */
+ default:
+ sigval = 7; /* "software generated"*/
+ }
+ return (sigval);
+}
+
+/*
+ * While we find nice hex chars, build an int.
+ * Return number of chars processed.
+ */
+
+static int
+hexToInt(char **ptr, int *intValue)
+{
+ int numChars = 0;
+ int hexValue;
+
+ *intValue = 0;
+
+ while (**ptr)
+ {
+ hexValue = hex(**ptr);
+ if (hexValue >=0)
+ {
+ *intValue = (*intValue <<4) | hexValue;
+ numChars ++;
+ }
+ else
+ break;
+
+ (*ptr)++;
+ }
+
+ return (numChars);
+}
+
+#define NUMREGBYTES (sizeof registers)
+#define PC 8
+#define SP 4
+#define FP 5
+#define NUM_REGS 14
+
+/*
+ * This function does all command procesing for interfacing to gdb.
+ */
+void
+gdb_handle_exception (db_regs_t *raw_regs, int type, int code)
+{
+ int sigval;
+ int addr, length;
+ char * ptr;
+ struct i386regs {
+ unsigned int eax;
+ unsigned int ecx;
+ unsigned int edx;
+ unsigned int ebx;
+ unsigned int esp;
+ unsigned int ebp;
+ unsigned int esi;
+ unsigned int edi;
+ unsigned int eip;
+ unsigned int eflags;
+ unsigned int cs;
+ unsigned int ss;
+ unsigned int ds;
+ unsigned int es;
+ };
+ struct i386regs registers;
+
+ registers.eax = raw_regs->tf_eax;
+ registers.ebx = raw_regs->tf_ebx;
+ registers.ecx = raw_regs->tf_ecx;
+ registers.edx = raw_regs->tf_edx;
+
+ registers.esp = raw_regs->tf_esp;
+ registers.ebp = raw_regs->tf_ebp;
+ registers.esi = raw_regs->tf_esi;
+ registers.edi = raw_regs->tf_edi;
+
+ registers.eip = raw_regs->tf_eip;
+ registers.eflags = raw_regs->tf_eflags;
+
+ registers.cs = raw_regs->tf_cs;
+ registers.ss = raw_regs->tf_ss;
+ registers.ds = raw_regs->tf_ds;
+ registers.es = raw_regs->tf_es;
+
+ /* reply to host that an exception has occurred */
+ sigval = computeSignal (type);
+ ptr = remcomOutBuffer;
+
+ *ptr++ = 'T';
+ *ptr++ = hexchars[sigval >> 4];
+ *ptr++ = hexchars[sigval & 0xf];
+
+ *ptr++ = hexchars[PC >> 4];
+ *ptr++ = hexchars[PC & 0xf];
+ *ptr++ = ':';
+ ptr = mem2hex ((vm_offset_t)&registers.eip, ptr, 4);
+ *ptr++ = ';';
+
+ *ptr++ = hexchars[FP >> 4];
+ *ptr++ = hexchars[FP & 0xf];
+ *ptr++ = ':';
+ ptr = mem2hex ((vm_offset_t)&registers.ebp, ptr, 4);
+ *ptr++ = ';';
+
+ *ptr++ = hexchars[SP >> 4];
+ *ptr++ = hexchars[SP & 0xf];
+ *ptr++ = ':';
+ ptr = mem2hex ((vm_offset_t)&registers.esp, ptr, 4);
+ *ptr++ = ';';
+
+ *ptr++ = 0;
+
+ putpacket (remcomOutBuffer);
+
+ while (1)
+ {
+ remcomOutBuffer[0] = 0;
+
+ getpacket (remcomInBuffer);
+ switch (remcomInBuffer[0])
+ {
+ case '?':
+ remcomOutBuffer[0] = 'S';
+ remcomOutBuffer[1] = hexchars[sigval >> 4];
+ remcomOutBuffer[2] = hexchars[sigval % 16];
+ remcomOutBuffer[3] = 0;
+ break;
+
+ case 'g': /* return the value of the CPU registers */
+ mem2hex ((vm_offset_t)&registers, remcomOutBuffer, NUMREGBYTES);
+ break;
+
+ case 'G': /* set the value of the CPU registers - return OK */
+ hex2mem (&remcomInBuffer[1], (vm_offset_t)&registers, NUMREGBYTES);
+ strcpy (remcomOutBuffer, "OK");
+ break;
+
+ case 'P': /* Set the value of one register */
+ {
+ int regno;
+
+ ptr = &remcomInBuffer[1];
+
+ if (hexToInt (&ptr, &regno)
+ && *ptr++ == '='
+ && regno < NUM_REGS)
+ {
+ hex2mem (ptr, (vm_offset_t)&registers + regno * 4, 4);
+ strcpy(remcomOutBuffer,"OK");
+ }
+ else
+ strcpy (remcomOutBuffer, "P01");
+ break;
+ }
+ case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */
+ /* Try to read %x,%x. */
+
+ ptr = &remcomInBuffer[1];
+
+ if (hexToInt (&ptr, &addr)
+ && *(ptr++) == ','
+ && hexToInt (&ptr, &length))
+ {
+ if (mem2hex((vm_offset_t) addr, remcomOutBuffer, length) == NULL)
+ strcpy (remcomOutBuffer, "E03");
+ break;
+ }
+ else
+ strcpy (remcomOutBuffer, "E01");
+ break;
+
+ case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
+
+ /* Try to read '%x,%x:'. */
+
+ ptr = &remcomInBuffer[1];
+
+ if (hexToInt(&ptr,&addr)
+ && *(ptr++) == ','
+ && hexToInt(&ptr, &length)
+ && *(ptr++) == ':')
+ {
+ if (hex2mem(ptr, (vm_offset_t) addr, length) == NULL)
+ strcpy (remcomOutBuffer, "E03");
+ else
+ strcpy (remcomOutBuffer, "OK");
+ }
+ else
+ strcpy (remcomOutBuffer, "E02");
+ break;
+
+ /* cAA..AA Continue at address AA..AA(optional) */
+ /* sAA..AA Step one instruction from AA..AA(optional) */
+ case 'c' :
+ case 's' :
+ /* try to read optional parameter, pc unchanged if no parm */
+
+ ptr = &remcomInBuffer[1];
+ if (hexToInt(&ptr,&addr))
+ registers.eip = addr;
+
+
+ /* set the trace bit if we're stepping */
+ if (remcomInBuffer[0] == 's')
+ registers.eflags |= PSL_T;
+ else
+ registers.eflags &= ~PSL_T;
+
+ raw_regs->tf_eax = registers.eax;
+ raw_regs->tf_ebx = registers.ebx;
+ raw_regs->tf_ecx = registers.ecx;
+ raw_regs->tf_edx = registers.edx;
+
+ raw_regs->tf_esp = registers.esp;
+ raw_regs->tf_ebp = registers.ebp;
+ raw_regs->tf_esi = registers.esi;
+ raw_regs->tf_edi = registers.edi;
+
+ raw_regs->tf_eip = registers.eip;
+ raw_regs->tf_eflags = registers.eflags;
+
+ raw_regs->tf_cs = registers.cs;
+ raw_regs->tf_ss = registers.ss;
+ raw_regs->tf_ds = registers.ds;
+ raw_regs->tf_es = registers.es;
+ return;
+
+ } /* switch */
+
+ /* reply to the request */
+ putpacket (remcomOutBuffer);
+ }
+}
diff --git a/sys/amd64/amd64/db_interface.c b/sys/amd64/amd64/db_interface.c
index db7e06031524..159400c4dbae 100644
--- a/sys/amd64/amd64/db_interface.c
+++ b/sys/amd64/amd64/db_interface.c
@@ -23,7 +23,7 @@
* any improvements or extensions that they make and grant Carnegie the
* rights to redistribute these changes.
*
- * $Id: db_interface.c,v 1.18 1996/04/07 18:34:59 bde Exp $
+ * $Id: db_interface.c,v 1.19 1996/05/03 21:00:51 phk Exp $
*/
/*
@@ -48,6 +48,7 @@
#include <setjmp.h>
static int db_active = 0;
+extern void gdb_handle_exception __P((db_regs_t *, int, int));
db_regs_t ddb_regs;
@@ -93,7 +94,7 @@ kdb_trap(type, code, regs)
* our breakpoints by disarming our breakpoints and fixing up
* %eip.
*/
- if (cons_unavail) {
+ if (cons_unavail && !(boothowto & RB_GDB)) {
if (type == T_TRCTRAP) {
regs->tf_eflags &= ~PSL_T;
return (1);
@@ -133,7 +134,10 @@ kdb_trap(type, code, regs)
db_active++;
cnpollc(TRUE);
- db_trap(type, code);
+ if (boothowto & RB_GDB)
+ gdb_handle_exception(&ddb_regs, type, code);
+ else
+ db_trap(type, code);
cnpollc(FALSE);
db_active--;
@@ -267,7 +271,7 @@ Debugger(msg)
* OK if the call is for the debugger hotkey but not if the call
* is a weak form of panicing.
*/
- if (cons_unavail)
+ if (cons_unavail & !(boothowto & RB_GDB))
return;
if (!in_Debugger) {
diff --git a/sys/conf/files.i386 b/sys/conf/files.i386
index 9b217436cca4..090296cc41f7 100644
--- a/sys/conf/files.i386
+++ b/sys/conf/files.i386
@@ -1,7 +1,7 @@
# This file tells config what files go into building a kernel,
# files marked standard are always included.
#
-# $Id: files.i386,v 1.137 1996/07/05 18:49:01 jhay Exp $
+# $Id: files.i386,v 1.138 1996/07/08 19:44:30 wollman Exp $
#
aic7xxx_asm optional ahc device-driver \
dependency "$S/dev/aic7xxx/aic7xxx_asm.c" \
@@ -40,6 +40,7 @@ i386/i386/cons.c standard
i386/i386/db_disasm.c optional ddb
i386/i386/db_interface.c optional ddb
i386/i386/db_trace.c optional ddb
+i386/i386/i386-gdbstub.c optional ddb
i386/i386/exception.s standard
i386/i386/identcpu.c standard
i386/i386/in_cksum.c optional inet
diff --git a/sys/i386/boot/biosboot/boot.c b/sys/i386/boot/biosboot/boot.c
index 2decc57d0ea7..fc190003bfc3 100644
--- a/sys/i386/boot/biosboot/boot.c
+++ b/sys/i386/boot/biosboot/boot.c
@@ -24,7 +24,7 @@
* the rights to redistribute these changes.
*
* from: Mach, [92/04/03 16:51:14 rvb]
- * $Id: boot.c,v 1.52 1996/07/09 02:28:20 julian Exp $
+ * $Id: boot.c,v 1.53 1996/07/12 05:25:45 bde Exp $
*/
@@ -332,6 +332,8 @@ nextarg:
if (*howto & RB_SERIAL)
init_serial();
}
+ if (c == 'g')
+ *howto |= RB_GDB;
if (c == 'r')
*howto |= RB_DFLTROOT;
if (c == 's')
diff --git a/sys/i386/boot/dosboot/fbsdboot.c b/sys/i386/boot/dosboot/fbsdboot.c
index fd77aae814d1..4bb18e415b68 100644
--- a/sys/i386/boot/dosboot/fbsdboot.c
+++ b/sys/i386/boot/dosboot/fbsdboot.c
@@ -94,6 +94,7 @@ int main(int argc, char *argv[])
case 'a': howto |= RB_ASKNAME; break;
case 'c': howto |= RB_CONFIG; break;
case 'd': howto |= RB_KDB; break;
+ case 'g': howto |= RB_GDB; break;
case 'v': howto |= RB_VERBOSE; break;
case 'C': howto |= RB_CDROM; break;
case 'D': dos = 1; kernel = "c:\\kernel"; break;
diff --git a/sys/i386/boot/dosboot/reboot.h b/sys/i386/boot/dosboot/reboot.h
index 51b5f1f98f75..c21858a75cfc 100644
--- a/sys/i386/boot/dosboot/reboot.h
+++ b/sys/i386/boot/dosboot/reboot.h
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* @(#)reboot.h 8.1 (Berkeley) 6/2/93
- * $Id: reboot.h,v 1.7 1994/11/26 09:08:40 phk Exp $
+ * $Id: reboot.h,v 1.2 1995/04/27 18:22:32 phk Exp $
*/
#ifndef _SYS_REBOOT_H_
@@ -56,8 +56,10 @@
#define RB_MINIROOT 0x200 /* mini-root present in memory at boot time */
#define RB_CONFIG 0x400 /* invoke user configuration routing */
#define RB_VERBOSE 0x800 /* print all potentially useful info */
-#define RB_SERIAL 0x1000 /* user serial port as console */
-#define RB_CDROM 0x2000 /* use cdrom as root */
+#define RB_SERIAL 0x1000 /* user serial port as console */
+#define RB_CDROM 0x2000 /* use cdrom as root */
+#define RB_POWEROFF 0x4000 /* if you can, turn the power off */
+#define RB_GDB 0x8000 /* use GDB remote debugger instead of DDB */
#define RB_BOOTINFO 0x80000000 /* have `struct bootinfo *' arg */
diff --git a/sys/i386/boot/netboot/bootmenu.c b/sys/i386/boot/netboot/bootmenu.c
index d8f23306379b..7e5b0157001c 100644
--- a/sys/i386/boot/netboot/bootmenu.c
+++ b/sys/i386/boot/netboot/bootmenu.c
@@ -299,6 +299,7 @@ cmd_flags(buf)
case 'b': flags |= RB_HALT; break;
case 'c': flags |= RB_CONFIG; break;
case 'd': flags |= RB_KDB; break;
+ case 'g': flags |= RB_GDB; break;
case 'h': flags ^= RB_SERIAL; break;
case 's': flags |= RB_SINGLE; break;
case 'v': flags |= RB_VERBOSE; break;
diff --git a/sys/i386/conf/files.i386 b/sys/i386/conf/files.i386
index 9b217436cca4..090296cc41f7 100644
--- a/sys/i386/conf/files.i386
+++ b/sys/i386/conf/files.i386
@@ -1,7 +1,7 @@
# This file tells config what files go into building a kernel,
# files marked standard are always included.
#
-# $Id: files.i386,v 1.137 1996/07/05 18:49:01 jhay Exp $
+# $Id: files.i386,v 1.138 1996/07/08 19:44:30 wollman Exp $
#
aic7xxx_asm optional ahc device-driver \
dependency "$S/dev/aic7xxx/aic7xxx_asm.c" \
@@ -40,6 +40,7 @@ i386/i386/cons.c standard
i386/i386/db_disasm.c optional ddb
i386/i386/db_interface.c optional ddb
i386/i386/db_trace.c optional ddb
+i386/i386/i386-gdbstub.c optional ddb
i386/i386/exception.s standard
i386/i386/identcpu.c standard
i386/i386/in_cksum.c optional inet
diff --git a/sys/i386/i386/db_interface.c b/sys/i386/i386/db_interface.c
index db7e06031524..159400c4dbae 100644
--- a/sys/i386/i386/db_interface.c
+++ b/sys/i386/i386/db_interface.c
@@ -23,7 +23,7 @@
* any improvements or extensions that they make and grant Carnegie the
* rights to redistribute these changes.
*
- * $Id: db_interface.c,v 1.18 1996/04/07 18:34:59 bde Exp $
+ * $Id: db_interface.c,v 1.19 1996/05/03 21:00:51 phk Exp $
*/
/*
@@ -48,6 +48,7 @@
#include <setjmp.h>
static int db_active = 0;
+extern void gdb_handle_exception __P((db_regs_t *, int, int));
db_regs_t ddb_regs;
@@ -93,7 +94,7 @@ kdb_trap(type, code, regs)
* our breakpoints by disarming our breakpoints and fixing up
* %eip.
*/
- if (cons_unavail) {
+ if (cons_unavail && !(boothowto & RB_GDB)) {
if (type == T_TRCTRAP) {
regs->tf_eflags &= ~PSL_T;
return (1);
@@ -133,7 +134,10 @@ kdb_trap(type, code, regs)
db_active++;
cnpollc(TRUE);
- db_trap(type, code);
+ if (boothowto & RB_GDB)
+ gdb_handle_exception(&ddb_regs, type, code);
+ else
+ db_trap(type, code);
cnpollc(FALSE);
db_active--;
@@ -267,7 +271,7 @@ Debugger(msg)
* OK if the call is for the debugger hotkey but not if the call
* is a weak form of panicing.
*/
- if (cons_unavail)
+ if (cons_unavail & !(boothowto & RB_GDB))
return;
if (!in_Debugger) {
diff --git a/sys/i386/i386/i386-gdbstub.c b/sys/i386/i386/i386-gdbstub.c
new file mode 100644
index 000000000000..41b72afaff3c
--- /dev/null
+++ b/sys/i386/i386/i386-gdbstub.c
@@ -0,0 +1,583 @@
+/****************************************************************************
+
+ THIS SOFTWARE IS NOT COPYRIGHTED
+
+ HP offers the following for use in the public domain. HP makes no
+ warranty with regard to the software or it's performance and the
+ user accepts the software "AS IS" with all faults.
+
+ HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD
+ TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+****************************************************************************/
+
+/****************************************************************************
+ * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $
+ *
+ * Module name: remcom.c $
+ * Revision: 1.34 $
+ * Date: 91/03/09 12:29:49 $
+ * Contributor: Lake Stevens Instrument Division$
+ *
+ * Description: low level support for gdb debugger. $
+ *
+ * Considerations: only works on target hardware $
+ *
+ * Written by: Glenn Engel $
+ * ModuleState: Experimental $
+ *
+ * NOTES: See Below $
+ *
+ * Modified for FreeBSD by Stu Grossman.
+ *
+ * To enable debugger support, two things need to happen. One, a
+ * call to set_debug_traps() is necessary in order to allow any breakpoints
+ * or error conditions to be properly intercepted and reported to gdb.
+ * Two, a breakpoint needs to be generated to begin communication. This
+ * is most easily accomplished by a call to breakpoint(). Breakpoint()
+ * simulates a breakpoint by executing a trap #1.
+ *
+ * The external function exceptionHandler() is
+ * used to attach a specific handler to a specific 386 vector number.
+ * It should use the same privilege level it runs at. It should
+ * install it as an interrupt gate so that interrupts are masked
+ * while the handler runs.
+ * Also, need to assign exceptionHook and oldExceptionHook.
+ *
+ * Because gdb will sometimes write to the stack area to execute function
+ * calls, this program cannot rely on using the supervisor stack so it
+ * uses it's own stack area reserved in the int array remcomStack.
+ *
+ *************
+ *
+ * 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
+ *
+ ****************************************************************************/
+
+#include <sys/types.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <string.h>
+#include <machine/db_machdep.h>
+#include <machine/trap.h>
+#include <machine/psl.h>
+
+/************************************************************************/
+
+void gdb_handle_exception (db_regs_t *, int, int);
+
+extern jmp_buf db_jmpbuf;
+extern void db_read_bytes (vm_offset_t addr, int size, char *data);
+extern void db_write_bytes (vm_offset_t addr, int size, char *data);
+extern void siocnputc (dev_t, int c);
+extern int siocngetc (dev_t);
+
+/************************************************************************/
+/* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/
+/* at least NUMREGBYTES*2 are needed for register packets */
+#define BUFMAX 400
+
+/* Create private copies of common functions used by the stub. This prevents
+ nasty interactions between app code and the stub (for instance if user steps
+ into strlen, etc..) */
+
+static int
+strlen (const char *s)
+{
+ const char *s1 = s;
+
+ while (*s1++ != '\000');
+
+ return s1 - s;
+}
+
+static char *
+strcpy (char *dst, const char *src)
+{
+ char *retval = dst;
+
+ while ((*dst++ = *src++) != '\000');
+
+ return retval;
+}
+
+static int
+putDebugChar (int c) /* write a single character */
+{
+ siocnputc (NULL, c);
+ return 1;
+}
+
+static int
+getDebugChar (void) /* read and return a single char */
+{
+ return siocngetc (NULL);
+}
+
+static const char hexchars[]="0123456789abcdef";
+
+static int
+hex(char ch)
+{
+ if ((ch >= 'a') && (ch <= 'f')) return (ch-'a'+10);
+ if ((ch >= '0') && (ch <= '9')) return (ch-'0');
+ if ((ch >= 'A') && (ch <= 'F')) return (ch-'A'+10);
+ return (-1);
+}
+
+/* scan for the sequence $<data>#<checksum> */
+static void
+getpacket (char *buffer)
+{
+ unsigned char checksum;
+ unsigned char xmitcsum;
+ int i;
+ int count;
+ unsigned char ch;
+
+ do
+ {
+ /* wait around for the start character, ignore all other characters */
+
+ while ((ch = (getDebugChar () & 0x7f)) != '$');
+
+ checksum = 0;
+ xmitcsum = -1;
+
+ count = 0;
+
+ /* now, read until a # or end of buffer is found */
+
+ while (count < BUFMAX)
+ {
+ ch = getDebugChar () & 0x7f;
+ if (ch == '#')
+ break;
+ checksum = checksum + ch;
+ buffer[count] = ch;
+ count = count + 1;
+ }
+ buffer[count] = 0;
+
+ if (ch == '#')
+ {
+ xmitcsum = hex (getDebugChar () & 0x7f) << 4;
+ xmitcsum += hex (getDebugChar () & 0x7f);
+
+ if (checksum != xmitcsum)
+ putDebugChar ('-'); /* failed checksum */
+ else
+ {
+ putDebugChar ('+'); /* successful transfer */
+ /* if a sequence char is present, reply the sequence ID */
+ if (buffer[2] == ':')
+ {
+ putDebugChar (buffer[0]);
+ putDebugChar (buffer[1]);
+
+ /* remove sequence chars from buffer */
+
+ count = strlen (buffer);
+ for (i=3; i <= count; i++)
+ buffer[i-3] = buffer[i];
+ }
+ }
+ }
+ }
+ while (checksum != xmitcsum);
+}
+
+/* send the packet in buffer. */
+
+static void
+putpacket (char *buffer)
+{
+ unsigned char checksum;
+ int count;
+ unsigned char ch;
+
+ /* $<packet info>#<checksum>. */
+ do
+ {
+ putDebugChar ('$');
+ checksum = 0;
+ count = 0;
+
+ while (ch=buffer[count])
+ {
+ putDebugChar (ch);
+ checksum += ch;
+ count += 1;
+ }
+
+ putDebugChar ('#');
+ putDebugChar (hexchars[checksum >> 4]);
+ putDebugChar (hexchars[checksum & 0xf]);
+ }
+ while ((getDebugChar () & 0x7f) != '+');
+}
+
+static char remcomInBuffer[BUFMAX];
+static char remcomOutBuffer[BUFMAX];
+
+static int
+get_char (vm_offset_t addr)
+{
+ char data;
+
+ if (setjmp (db_jmpbuf))
+ return -1;
+
+ db_read_bytes (addr, 1, &data);
+
+ return data & 0xff;
+}
+
+static int
+set_char (vm_offset_t addr, int val)
+{
+ char data;
+
+ if (setjmp (db_jmpbuf))
+ return -1;
+
+ data = val;
+
+ db_write_bytes (addr, 1, &data);
+ return 0;
+}
+
+/* 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 (vm_offset_t mem, char *buf, int count)
+{
+ int i;
+ int ch;
+
+ for (i=0;i<count;i++) {
+ ch = get_char (mem++);
+ if (ch == -1)
+ return NULL;
+ *buf++ = hexchars[ch >> 4];
+ *buf++ = hexchars[ch % 16];
+ }
+ *buf = 0;
+ return(buf);
+}
+
+/* convert the hex array pointed to by buf into binary to be placed in mem */
+/* return a pointer to the character AFTER the last byte written */
+static char *
+hex2mem (char *buf, vm_offset_t mem, int count)
+{
+ int i;
+ int ch;
+ int rv;
+
+ for (i=0;i<count;i++) {
+ ch = hex(*buf++) << 4;
+ ch = ch + hex(*buf++);
+ rv = set_char (mem++, ch);
+ if (rv == -1)
+ return NULL;
+ }
+ return(buf);
+}
+
+/* this function takes the 386 exception vector and attempts to
+ translate this number into a unix compatible signal value */
+static int
+computeSignal (int exceptionVector)
+{
+ int sigval;
+ switch (exceptionVector & ~T_USER)
+ {
+ case 0: sigval = 8; break; /* divide by zero */
+ case 1: sigval = 5; break; /* debug exception */
+ case 3: sigval = 5; break; /* breakpoint */
+ case 4: sigval = 16; break; /* into instruction (overflow) */
+ case 5: sigval = 16; break; /* bound instruction */
+ case 6: sigval = 4; break; /* Invalid opcode */
+ case 7: sigval = 8; break; /* coprocessor not available */
+ case 8: sigval = 7; break; /* double fault */
+ case 9: sigval = 11; break; /* coprocessor segment overrun */
+ case 10: sigval = 5; break; /* Invalid TSS (also single-step) */
+ case 11: sigval = 11; break; /* Segment not present */
+ case 12: sigval = 11; break; /* stack exception */
+ case 13: sigval = 11; break; /* general protection */
+ case 14: sigval = 11; break; /* page fault */
+ case 16: sigval = 7; break; /* coprocessor error */
+ default:
+ sigval = 7; /* "software generated"*/
+ }
+ return (sigval);
+}
+
+/*
+ * While we find nice hex chars, build an int.
+ * Return number of chars processed.
+ */
+
+static int
+hexToInt(char **ptr, int *intValue)
+{
+ int numChars = 0;
+ int hexValue;
+
+ *intValue = 0;
+
+ while (**ptr)
+ {
+ hexValue = hex(**ptr);
+ if (hexValue >=0)
+ {
+ *intValue = (*intValue <<4) | hexValue;
+ numChars ++;
+ }
+ else
+ break;
+
+ (*ptr)++;
+ }
+
+ return (numChars);
+}
+
+#define NUMREGBYTES (sizeof registers)
+#define PC 8
+#define SP 4
+#define FP 5
+#define NUM_REGS 14
+
+/*
+ * This function does all command procesing for interfacing to gdb.
+ */
+void
+gdb_handle_exception (db_regs_t *raw_regs, int type, int code)
+{
+ int sigval;
+ int addr, length;
+ char * ptr;
+ struct i386regs {
+ unsigned int eax;
+ unsigned int ecx;
+ unsigned int edx;
+ unsigned int ebx;
+ unsigned int esp;
+ unsigned int ebp;
+ unsigned int esi;
+ unsigned int edi;
+ unsigned int eip;
+ unsigned int eflags;
+ unsigned int cs;
+ unsigned int ss;
+ unsigned int ds;
+ unsigned int es;
+ };
+ struct i386regs registers;
+
+ registers.eax = raw_regs->tf_eax;
+ registers.ebx = raw_regs->tf_ebx;
+ registers.ecx = raw_regs->tf_ecx;
+ registers.edx = raw_regs->tf_edx;
+
+ registers.esp = raw_regs->tf_esp;
+ registers.ebp = raw_regs->tf_ebp;
+ registers.esi = raw_regs->tf_esi;
+ registers.edi = raw_regs->tf_edi;
+
+ registers.eip = raw_regs->tf_eip;
+ registers.eflags = raw_regs->tf_eflags;
+
+ registers.cs = raw_regs->tf_cs;
+ registers.ss = raw_regs->tf_ss;
+ registers.ds = raw_regs->tf_ds;
+ registers.es = raw_regs->tf_es;
+
+ /* reply to host that an exception has occurred */
+ sigval = computeSignal (type);
+ ptr = remcomOutBuffer;
+
+ *ptr++ = 'T';
+ *ptr++ = hexchars[sigval >> 4];
+ *ptr++ = hexchars[sigval & 0xf];
+
+ *ptr++ = hexchars[PC >> 4];
+ *ptr++ = hexchars[PC & 0xf];
+ *ptr++ = ':';
+ ptr = mem2hex ((vm_offset_t)&registers.eip, ptr, 4);
+ *ptr++ = ';';
+
+ *ptr++ = hexchars[FP >> 4];
+ *ptr++ = hexchars[FP & 0xf];
+ *ptr++ = ':';
+ ptr = mem2hex ((vm_offset_t)&registers.ebp, ptr, 4);
+ *ptr++ = ';';
+
+ *ptr++ = hexchars[SP >> 4];
+ *ptr++ = hexchars[SP & 0xf];
+ *ptr++ = ':';
+ ptr = mem2hex ((vm_offset_t)&registers.esp, ptr, 4);
+ *ptr++ = ';';
+
+ *ptr++ = 0;
+
+ putpacket (remcomOutBuffer);
+
+ while (1)
+ {
+ remcomOutBuffer[0] = 0;
+
+ getpacket (remcomInBuffer);
+ switch (remcomInBuffer[0])
+ {
+ case '?':
+ remcomOutBuffer[0] = 'S';
+ remcomOutBuffer[1] = hexchars[sigval >> 4];
+ remcomOutBuffer[2] = hexchars[sigval % 16];
+ remcomOutBuffer[3] = 0;
+ break;
+
+ case 'g': /* return the value of the CPU registers */
+ mem2hex ((vm_offset_t)&registers, remcomOutBuffer, NUMREGBYTES);
+ break;
+
+ case 'G': /* set the value of the CPU registers - return OK */
+ hex2mem (&remcomInBuffer[1], (vm_offset_t)&registers, NUMREGBYTES);
+ strcpy (remcomOutBuffer, "OK");
+ break;
+
+ case 'P': /* Set the value of one register */
+ {
+ int regno;
+
+ ptr = &remcomInBuffer[1];
+
+ if (hexToInt (&ptr, &regno)
+ && *ptr++ == '='
+ && regno < NUM_REGS)
+ {
+ hex2mem (ptr, (vm_offset_t)&registers + regno * 4, 4);
+ strcpy(remcomOutBuffer,"OK");
+ }
+ else
+ strcpy (remcomOutBuffer, "P01");
+ break;
+ }
+ case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */
+ /* Try to read %x,%x. */
+
+ ptr = &remcomInBuffer[1];
+
+ if (hexToInt (&ptr, &addr)
+ && *(ptr++) == ','
+ && hexToInt (&ptr, &length))
+ {
+ if (mem2hex((vm_offset_t) addr, remcomOutBuffer, length) == NULL)
+ strcpy (remcomOutBuffer, "E03");
+ break;
+ }
+ else
+ strcpy (remcomOutBuffer, "E01");
+ break;
+
+ case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
+
+ /* Try to read '%x,%x:'. */
+
+ ptr = &remcomInBuffer[1];
+
+ if (hexToInt(&ptr,&addr)
+ && *(ptr++) == ','
+ && hexToInt(&ptr, &length)
+ && *(ptr++) == ':')
+ {
+ if (hex2mem(ptr, (vm_offset_t) addr, length) == NULL)
+ strcpy (remcomOutBuffer, "E03");
+ else
+ strcpy (remcomOutBuffer, "OK");
+ }
+ else
+ strcpy (remcomOutBuffer, "E02");
+ break;
+
+ /* cAA..AA Continue at address AA..AA(optional) */
+ /* sAA..AA Step one instruction from AA..AA(optional) */
+ case 'c' :
+ case 's' :
+ /* try to read optional parameter, pc unchanged if no parm */
+
+ ptr = &remcomInBuffer[1];
+ if (hexToInt(&ptr,&addr))
+ registers.eip = addr;
+
+
+ /* set the trace bit if we're stepping */
+ if (remcomInBuffer[0] == 's')
+ registers.eflags |= PSL_T;
+ else
+ registers.eflags &= ~PSL_T;
+
+ raw_regs->tf_eax = registers.eax;
+ raw_regs->tf_ebx = registers.ebx;
+ raw_regs->tf_ecx = registers.ecx;
+ raw_regs->tf_edx = registers.edx;
+
+ raw_regs->tf_esp = registers.esp;
+ raw_regs->tf_ebp = registers.ebp;
+ raw_regs->tf_esi = registers.esi;
+ raw_regs->tf_edi = registers.edi;
+
+ raw_regs->tf_eip = registers.eip;
+ raw_regs->tf_eflags = registers.eflags;
+
+ raw_regs->tf_cs = registers.cs;
+ raw_regs->tf_ss = registers.ss;
+ raw_regs->tf_ds = registers.ds;
+ raw_regs->tf_es = registers.es;
+ return;
+
+ } /* switch */
+
+ /* reply to the request */
+ putpacket (remcomOutBuffer);
+ }
+}
diff --git a/sys/sys/reboot.h b/sys/sys/reboot.h
index ecf6cb89bd71..d4b5ccbb0540 100644
--- a/sys/sys/reboot.h
+++ b/sys/sys/reboot.h
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* @(#)reboot.h 8.3 (Berkeley) 12/13/94
- * $Id: reboot.h,v 1.11 1996/03/11 02:09:55 hsu Exp $
+ * $Id: reboot.h,v 1.12 1996/08/22 03:50:32 julian Exp $
*/
#ifndef _SYS_REBOOT_H_
@@ -58,6 +58,7 @@
#define RB_SERIAL 0x1000 /* user serial port as console */
#define RB_CDROM 0x2000 /* use cdrom as root */
#define RB_POWEROFF 0x4000 /* if you can, turn the power off */
+#define RB_GDB 0x8000 /* use GDB remote debugger instead of DDB */
#define RB_BOOTINFO 0x80000000 /* have `struct bootinfo *' arg */