From: Robert Walsh <rjwalsh@durables.org>

Here's the patch.  It's against 2.6.0-test4-mm5, as I think I've already
mentioned.  Supported drivers are the e100 and 3c59x.  I've only done
this on x86 - I haven't tried with any other architecture.  I might try
it with Opteron next, as soon as I have a spare moment (which is rare
these days.)  I've put some documentation in
Documentation/i386/kgdb/kgdbeth.txt



 Documentation/i386/kgdb/kgdbeth.txt |  118 ++++++++
 arch/i386/kernel/kgdb_stub.c        |  206 +++++++++++++-
 arch/i386/lib/kgdb_serial.c         |   36 +-
 drivers/net/3c59x.c                 |   24 +
 drivers/net/Makefile                |    2 
 drivers/net/e100/e100_main.c        |   13 
 drivers/net/kgdb_eth.c              |  500 ++++++++++++++++++++++++++++++++++++
 include/asm-i386/kgdb.h             |   13 
 include/linux/netdevice.h           |   20 +
 net/core/dev.c                      |   20 +
 10 files changed, 921 insertions(+), 31 deletions(-)

diff -puN arch/i386/kernel/kgdb_stub.c~kgdb-over-ethernet arch/i386/kernel/kgdb_stub.c
--- 25/arch/i386/kernel/kgdb_stub.c~kgdb-over-ethernet	2003-11-04 21:59:06.000000000 -0800
+++ 25-akpm/arch/i386/kernel/kgdb_stub.c	2003-11-04 21:59:08.000000000 -0800
@@ -30,6 +30,8 @@
  *
  *  Written by:	     Glenn Engel $
  *  Updated by:	     David Grothe <dave@gcom.com>
+ *  Updated by:	     Robert Walsh <rjwalsh@durables.org>
+ *  Updated by:	     wangdi <wangdi@clusterfs.com>
  *  ModuleState:     Experimental $
  *
  *  NOTES:	     See Below $
@@ -49,6 +51,10 @@
  *	support for ia-32(x86) hardware debugging.
  *	Amit S. Kale ( akale@veritas.com )
  *
+ *  Modified to support debugging over ethernet by Robert Walsh
+ *  <rjwalsh@durables.org> and wangdi <wangdi@clusterfs.com>, based on
+ *  code by San Mehat.
+ *
  *
  *  To enable debugger support, two things need to happen.  One, a
  *  call to set_debug_traps() is necessary in order to allow any breakpoints
@@ -112,6 +118,7 @@
 #include <asm/processor.h>
 #include <linux/irq.h>
 #include <asm/desc.h>
+#include <linux/inet.h>
 
 /************************************************************************
  *
@@ -122,8 +129,16 @@ typedef void (*Function) (void);	/* poin
 /* Thread reference */
 typedef unsigned char threadref[8];
 
-extern void putDebugChar(int);	/* write a single character	 */
-extern int getDebugChar(void);	/* read and return a single char */
+extern int tty_putDebugChar(int);     /* write a single character      */
+extern int tty_getDebugChar(void);    /* read and return a single char */
+extern void tty_flushDebugChar(void); /* flush pending characters      */
+extern int eth_putDebugChar(int);     /* write a single character      */
+extern int eth_getDebugChar(void);    /* read and return a single char */
+extern void eth_flushDebugChar(void); /* flush pending characters      */
+extern void kgdb_eth_set_trapmode(int);
+extern void kgdb_eth_reply_arp(void);   /*send arp request */
+extern int kgdb_eth_is_initializing;
+
 
 /************************************************************************/
 /* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/
@@ -264,6 +279,41 @@ malloc(int size)
 }
 
 /*
+ * I/O dispatch functions...
+ * Based upon kgdb_eth, either call the ethernet
+ * handler or the serial one..
+ */
+void
+putDebugChar(int c)
+{
+	if (kgdb_eth == -1) {
+		tty_putDebugChar(c);
+	} else {
+		eth_putDebugChar(c);
+	}
+}
+
+int
+getDebugChar(void)
+{
+	if (kgdb_eth == -1) {
+		return tty_getDebugChar();
+	} else {
+		return eth_getDebugChar();
+	}
+}
+
+void
+flushDebugChar(void)
+{
+	if (kgdb_eth == -1) {
+		tty_flushDebugChar();
+	} else {
+		eth_flushDebugChar();
+	}
+}
+
+/*
  * Gdb calls functions by pushing agruments, including a return address
  * on the stack and the adjusting EIP to point to the function.	 The
  * whole assumption in GDB is that we are on a different stack than the
@@ -429,6 +479,7 @@ getpacket(char *buffer)
 
 	if (remote_debug)
 		printk("R:%s\n", buffer);
+	flushDebugChar();
 }
 
 /* send the packet in buffer.  */
@@ -441,25 +492,67 @@ putpacket(char *buffer)
 	char ch;
 
 	/*  $<packet info>#<checksum>. */
-	do {
-		if (remote_debug)
-			printk("T:%s\n", buffer);
-		putDebugChar('$');
-		checksum = 0;
-		count = 0;
 
-		while ((ch = buffer[count])) {
-			putDebugChar(ch);
-			checksum += ch;
-			count += 1;
-		}
+	if (kgdb_eth == -1) {
+		do {
+			if (remote_debug)
+				printk("T:%s\n", buffer);
+			putDebugChar('$');
+			checksum = 0;
+			count = 0;
+
+			while ((ch = buffer[count])) {
+				putDebugChar(ch);
+				checksum += ch;
+				count += 1;
+			}
+
+			putDebugChar('#');
+			putDebugChar(hexchars[checksum >> 4]);
+			putDebugChar(hexchars[checksum % 16]);
+			flushDebugChar();
 
-		putDebugChar('#');
-		putDebugChar(hexchars[checksum >> 4]);
-		putDebugChar(hexchars[checksum % 16]);
+		} while ((getDebugChar() & 0x7f) != '+');
+	} else {
+		/*
+		 * For udp, we can not transfer too much bytes once.
+		 * We only transfer MAX_SEND_COUNT size bytes each time
+		 */
 
-	} while ((getDebugChar() & 0x7f) != '+');
+#define MAX_SEND_COUNT 30
 
+		int send_count = 0, i = 0;
+		char send_buf[MAX_SEND_COUNT];
+
+		do {
+			if (remote_debug)
+				printk("T:%s\n", buffer);
+			putDebugChar('$');
+			checksum = 0;
+			count = 0;
+			send_count = 0;
+			while ((ch = buffer[count])) {
+				if (send_count >= MAX_SEND_COUNT) {
+					for(i = 0; i < MAX_SEND_COUNT; i++) {
+						putDebugChar(send_buf[i]);
+					}
+					flushDebugChar();
+					send_count = 0;
+				} else {
+					send_buf[send_count] = ch;
+					checksum += ch;
+					count ++;
+					send_count++;
+				}
+			}
+			for(i = 0; i < send_count; i++)
+				putDebugChar(send_buf[i]);
+			putDebugChar('#');
+			putDebugChar(hexchars[checksum >> 4]);
+			putDebugChar(hexchars[checksum % 16]);
+			flushDebugChar();
+		} while ((getDebugChar() & 0x7f) != '+');
+	}
 }
 
 static char remcomInBuffer[BUFMAX];
@@ -1180,6 +1273,13 @@ kgdb_handle_exception(int exceptionVecto
 		print_regs(&regs);
 		return (0);
 	}
+	/*
+	 * If we're using eth mode, set the 'mode' in the netdevice.
+	 */
+
+	if (kgdb_eth != -1) {
+		kgdb_eth_set_trapmode(1);
+	}
 
 	kgdb_local_irq_save(flags);
 
@@ -1437,8 +1537,13 @@ kgdb_handle_exception(int exceptionVecto
 	remcomOutBuffer[2] = hexchars[signo % 16];
 	remcomOutBuffer[3] = 0;
 
-	putpacket(remcomOutBuffer);
+	if (kgdb_eth_is_initializing) {
+		kgdb_eth_is_initializing = 0;
+	} else {
+		putpacket(remcomOutBuffer);
+	}
 
+	kgdb_eth_reply_arp();
 	while (1 == 1) {
 		error = 0;
 		remcomOutBuffer[0] = 0;
@@ -1595,6 +1700,10 @@ kgdb_handle_exception(int exceptionVecto
 
 			newPC = regs.eip;
 
+			if (kgdb_eth != -1) {
+				kgdb_eth_set_trapmode(0);
+			}
+
 			/* clear the trace bit */
 			regs.eflags &= 0xfffffeff;
 
@@ -2331,3 +2440,64 @@ kgdb_tstamp(int line, char *source, int 
 typedef int gdb_debug_hook(int exceptionVector,
 			   int signo, int err_code, struct pt_regs *linux_regs);
 gdb_debug_hook *linux_debug_hook = &kgdb_handle_exception;	/* histerical reasons... */
+
+static int __init kgdb_opt_kgdbeth(char *str)
+{
+	kgdb_eth = simple_strtoul(str, NULL, 10);
+	return 1;
+}
+
+static int __init kgdb_opt_kgdbeth_remoteip(char *str)
+{
+	kgdb_remoteip = in_aton(str);
+	return 1;
+}
+
+static int __init kgdb_opt_kgdbeth_listenport(char *str)
+{
+	kgdb_listenport = simple_strtoul(str, NULL, 10);
+	kgdb_sendport = kgdb_listenport - 1;
+	return 1;
+}
+
+static int __init parse_hw_addr(char *str, unsigned char *addr)
+{
+	int  i;
+	char *p;
+
+	p = str;
+	i = 0;
+	while(1)
+	{
+		unsigned int c;
+
+		sscanf(p, "%x:", &c);
+		addr[i++] = c;
+		while((*p != 0) && (*p != ':')) {
+			p++;
+		}
+		if (*p == 0) {
+			break;
+		}
+		p++;
+	}
+
+	return 1;
+}
+
+static int __init kgdb_opt_kgdbeth_remotemac(char *str)
+{
+	return parse_hw_addr(str, kgdb_remotemac);
+}
+static int __init kgdb_opt_kgdbeth_localmac(char *str)
+{
+	return parse_hw_addr(str, kgdb_localmac);
+}
+
+
+__setup("gdbeth=", kgdb_opt_kgdbeth);
+__setup("gdbeth_remoteip=", kgdb_opt_kgdbeth_remoteip);
+__setup("gdbeth_listenport=", kgdb_opt_kgdbeth_listenport);
+__setup("gdbeth_remotemac=", kgdb_opt_kgdbeth_remotemac);
+__setup("gdbeth_localmac=", kgdb_opt_kgdbeth_localmac);
+
diff -puN arch/i386/lib/kgdb_serial.c~kgdb-over-ethernet arch/i386/lib/kgdb_serial.c
--- 25/arch/i386/lib/kgdb_serial.c~kgdb-over-ethernet	2003-11-04 21:59:06.000000000 -0800
+++ 25-akpm/arch/i386/lib/kgdb_serial.c	2003-11-04 21:59:06.000000000 -0800
@@ -4,6 +4,9 @@
  * Written (hacked together) by David Grothe (dave@gcom.com)
  * Modified to allow invokation early in boot see also
  * kgdb.h for instructions by George Anzinger(george@mvista.com)
+ * Modified to handle debugging over ethernet by Robert Walsh
+ * <rjwalsh@durables.org> and wangdi <wangdi@clusterfs.com>, based on
+ * code by San Mehat.
  *
  */
 
@@ -155,12 +158,12 @@ write_char(struct async_struct *info, in
  * It will receive a limited number of characters of input
  * from the gdb  host machine and save them up in a buffer.
  *
- * When the gdb stub routine getDebugChar() is called it
+ * When the gdb stub routine tty_getDebugChar() is called it
  * draws characters out of the buffer until it is empty and
  * then reads directly from the serial port.
  *
  * We do not attempt to write chars from the interrupt routine
- * since the stubs do all of that via putDebugChar() which
+ * since the stubs do all of that via tty_putDebugChar() which
  * writes one byte after waiting for the interface to become
  * ready.
  *
@@ -226,7 +229,7 @@ extern char *kgdb_version;
 /*
  * Hook an IRQ for KGDB.
  *
- * This routine is called from putDebugChar, below.
+ * This routine is called from tty_putDebugChar, below.
  */
 static int ints_disabled = 1;
 int
@@ -331,7 +334,7 @@ program_uart(struct async_struct *info)
 }
 
 /*
- * getDebugChar
+ * tty_getDebugChar
  *
  * This is a GDB stub routine.	It waits for a character from the
  * serial interface and then returns it.  If there is no serial
@@ -345,11 +348,11 @@ extern spinlock_t kgdb_spinlock;
 /* Caller takes needed protections */
 
 int
-getDebugChar(void)
+tty_getDebugChar(void)
 {
 	volatile int chr, dum, time, end_time;
 
-	dbprintk(("getDebugChar(port %x): ", gdb_async_info->port));
+	dbprintk(("tty_getDebugChar(port %x): ", gdb_async_info->port));
 
 	if (gdb_async_info == NULL) {
 		gdb_hook_interrupt(&local_info, 0);
@@ -375,7 +378,7 @@ getDebugChar(void)
 	dbprintk(("%c\n", chr > ' ' && chr < 0x7F ? chr : ' '));
 	return (chr);
 
-}				/* getDebugChar */
+}				/* tty_getDebugChar */
 
 static int count = 3;
 static spinlock_t one_at_atime = SPIN_LOCK_UNLOCKED;
@@ -383,6 +386,9 @@ static spinlock_t one_at_atime = SPIN_LO
 static int __init
 kgdb_enable_ints(void)
 {
+	if (kgdb_eth != -1) {
+		return 0;
+	}
 	if (gdb_async_info == NULL) {
 		gdb_hook_interrupt(&local_info, 1);
 	}
@@ -444,7 +450,7 @@ kgdb_enable_ints_now(void)
 }
 
 /*
- * putDebugChar
+ * tty_putDebugChar
  *
  * This is a GDB stub routine.	It waits until the interface is ready
  * to transmit a char and then sends it.  If there is no serial
@@ -452,9 +458,9 @@ kgdb_enable_ints_now(void)
  * pretended to send the char.	Caller takes needed protections.
  */
 void
-putDebugChar(int chr)
+tty_putDebugChar(int chr)
 {
-	dbprintk(("putDebugChar(port %x): chr=%02x '%c', ints_on=%d\n",
+	dbprintk(("tty_putDebugChar(port %x): chr=%02x '%c', ints_on=%d\n",
 		  gdb_async_info->port,
 		  chr,
 		  chr > ' ' && chr < 0x7F ? chr : ' ', ints_disabled ? 0 : 1));
@@ -480,6 +486,14 @@ putDebugChar(int chr)
 		}
 	}
 
-}				/* putDebugChar */
+}				/* tty_putDebugChar */
+
+/*
+ * This does nothing for the serial port, since it doesn't buffer.
+ */
+
+void tty_flushDebugChar(void)
+{
+}
 
 module_init(kgdb_enable_ints);
diff -puN /dev/null Documentation/i386/kgdb/kgdbeth.txt
--- /dev/null	2002-08-30 16:31:37.000000000 -0700
+++ 25-akpm/Documentation/i386/kgdb/kgdbeth.txt	2003-11-04 21:59:06.000000000 -0800
@@ -0,0 +1,118 @@
+KGDB over ethernet
+==================
+
+Authors
+-------
+
+Robert Walsh <rjwalsh@durables.org>  (2.6 port)
+wangdi <wangdi@clusterfs.com>        (2.6 port)
+San Mehat                            (original 2.4 code)
+
+
+Introduction
+------------
+
+KGDB supports debugging over ethernet.  Only a limited set of ethernet
+devices are supported right now, but adding support for new devices
+should not be too complicated.  See "New Devices" below for details.
+
+
+Terminology
+-----------
+
+This document uses the following terms:
+
+  TARGET: the machine being debugged.
+  HOST:   the machine running gdb.
+
+
+Usage
+-----
+
+You need to use the following command-line options on the TARGET kernel:
+
+  gdbeth=DEVICENUM
+  gdbeth_remoteip=HOSTIPADDR
+  gdbeth_remotemac=REMOTEMAC
+  gdbeth_localmac=LOCALMAC
+
+kgdbeth=DEVICENUM sets the ethernet device number to listen on for
+debugging packets.  e.g. kgdbeth=0 listens on eth0.
+
+kgdbeth_remoteip=HOSTIPADDR sets the IP address of the HOST machine.
+Only packets originating from this IP address will be accepted by the
+debugger.  e.g. kgdbeth_remoteip=192.168.2.2
+
+kgdbeth_remotemac=REMOTEMAC sets the ethernet address of the HOST machine.
+e.g. kgdbeth_remotemac=00:07:70:12:4E:F5
+
+kgdbeth_localmac=LOCALMAC sets the ethernet address of the TARGET machine.
+e.g. kgdbeth_localmac=00:10:9F:18:21:3C
+
+You can also set the following command-line option on the TARGET kernel:
+
+  kgdbeth_listenport=PORT
+
+kgdbeth_listenport sets the UDP port to listen on for gdb debugging
+packets.  The default value is "6443".  e.g. kgdbeth_listenport=7654
+causes the kernel to listen on UDP port 7654 for debugging packets.
+
+On the HOST side, run gdb as normal and use a remote UDP host as the
+target:
+
+   % gdb ./vmlinux
+   GNU gdb Red Hat Linux (5.3post-0.20021129.18rh)
+   Copyright 2003 Free Software Foundation, Inc.
+   GDB is free software, covered by the GNU General Public License, and you are
+   welcome to change it and/or distribute copies of it under certain conditions.
+   Type "show copying" to see the conditions.
+   There is absolutely no warranty for GDB.  Type "show warranty" for details.
+   This GDB was configured as "i386-redhat-linux-gnu"...
+   (gdb) target remote udp:HOSTNAME:6443
+
+You can now continue as if you were debugging over a serial line.
+
+Observations
+------------
+
+I've used this with NFS and various other network applications (ssh,
+etc.) and it's doesn't appear to interfere with their operation in
+any way.  It doesn't seem to effect the NIC it uses - i.e. you don't
+need a dedicated NIC for this.
+
+Limitations
+-----------
+
+In the inital release of this code you _must_ break into the system with the
+debugger by hand, early after boot, as described above.
+
+Otherwise, the first time the kernel tries to enter the debugger (say, via an
+oops or a BUG), the kgdb stub will doublefault and die because things aren't
+fully set up yet.
+
+Supported devices
+-----------------
+
+Right now, the following drivers are supported:
+
+  e100 driver (drivers/net/e100/*)
+  3c59x driver (drivers/net/3c59x.c)
+
+
+New devices
+-----------
+
+Supporting a new device is straightforward.  Just add a "poll" routine to
+the driver and hook it into the kgdb_net_poll_rx field in the netdevice
+structure.  For an example, look in drivers/net/3c59x.c and search
+for CONFIG_KGDB (two places.)
+
+The poll routine is usually quite simple - it's usually enough to just
+disable interrupts, call the device's interrupt routine and re-enable
+interrupts again.
+
+
+Bug reports
+-----------
+
+Send bug reports to Robert Walsh <rjwalsh@durables.org>.
diff -puN drivers/net/3c59x.c~kgdb-over-ethernet drivers/net/3c59x.c
--- 25/drivers/net/3c59x.c~kgdb-over-ethernet	2003-11-04 21:59:06.000000000 -0800
+++ 25-akpm/drivers/net/3c59x.c	2003-11-04 21:59:06.000000000 -0800
@@ -1063,6 +1063,22 @@ static int __devinit vortex_init_one (st
 	return rc;
 }
 
+#ifdef CONFIG_KGDB
+static void vortex_rx_poll(struct net_device *dev)
+{
+	disable_irq(dev->irq);
+	vortex_interrupt(dev->irq, (void *)dev, 0);
+	enable_irq(dev->irq);
+}
+
+static void boomerang_rx_poll(struct net_device *dev)
+{
+	disable_irq(dev->irq);
+	boomerang_interrupt(dev->irq, (void *)dev, 0);
+	enable_irq(dev->irq);
+}
+#endif
+
 /*
  * Start up the PCI/EISA device which is described by *gendev.
  * Return 0 on success.
@@ -1450,6 +1466,14 @@ static int __devinit vortex_probe1(struc
 	dev->set_multicast_list = set_rx_mode;
 	dev->tx_timeout = vortex_tx_timeout;
 	dev->watchdog_timeo = (watchdog * HZ) / 1000;
+#ifdef CONFIG_KGDB
+	if (vp->full_bus_master_tx) {
+		dev->kgdb_net_poll_rx = boomerang_rx_poll;
+	} else {
+		dev->kgdb_net_poll_rx = vortex_rx_poll;
+	}
+#endif
+
 	if (pdev) {
 		vp->pm_state_valid = 1;
  		pci_save_state(VORTEX_PCI(vp), vp->power_state);
diff -puN drivers/net/e100/e100_main.c~kgdb-over-ethernet drivers/net/e100/e100_main.c
--- 25/drivers/net/e100/e100_main.c~kgdb-over-ethernet	2003-11-04 21:59:06.000000000 -0800
+++ 25-akpm/drivers/net/e100/e100_main.c	2003-11-04 21:59:06.000000000 -0800
@@ -539,6 +539,15 @@ e100_trigger_SWI(struct e100_private *bd
 	readw(&(bdp->scb->scb_status));	/* flushes last write, read-safe */
 }
 
+#ifdef CONFIG_KGDB
+static void e100_rx_poll(struct net_device *dev)
+{
+	disable_irq(dev->irq);
+	e100intr(dev->irq, (void *)dev, 0);
+	enable_irq(dev->irq);
+}
+#endif
+
 static int __devinit
 e100_found1(struct pci_dev *pcid, const struct pci_device_id *ent)
 {
@@ -631,7 +640,9 @@ e100_found1(struct pci_dev *pcid, const 
 	dev->set_multicast_list = &e100_set_multi;
 	dev->set_mac_address = &e100_set_mac;
 	dev->do_ioctl = &e100_ioctl;
-
+#ifdef CONFIG_KGDB
+	dev->kgdb_net_poll_rx = e100_rx_poll;
+#endif
 	if (bdp->flags & USE_IPCB)
 	dev->features = NETIF_F_SG | NETIF_F_IP_CSUM |
 			NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
diff -puN /dev/null drivers/net/kgdb_eth.c
--- /dev/null	2002-08-30 16:31:37.000000000 -0700
+++ 25-akpm/drivers/net/kgdb_eth.c	2003-11-04 21:59:08.000000000 -0800
@@ -0,0 +1,500 @@
+/*
+ * Network interface GDB stub
+ *
+ * Written by San Mehat (nettwerk@biodome.org)
+ * Based upon 'gdbserial' by David Grothe (dave@gcom.com)
+ * and Scott Foehner (sfoehner@engr.sgi.com)
+ *
+ * Twiddled for 2.6 by Robert Walsh <rjwalsh@durables.org>
+ * and wangdi <wangdi@clusterfs.com>.
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/config.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/termios.h>
+#include <asm/kgdb.h>
+#include <linux/if_ether.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/atomic.h>
+
+#define	GDB_BUF_SIZE	512		/* power of 2, please */
+
+static char	kgdb_buf[GDB_BUF_SIZE] ;
+static int	kgdb_buf_in_inx ;
+static atomic_t	kgdb_buf_in_cnt ;
+static int	kgdb_buf_out_inx ;
+
+extern void	set_debug_traps(void) ;		/* GDB routine */
+extern void	breakpoint(void);
+
+unsigned int	kgdb_remoteip = 0;
+unsigned short	kgdb_listenport = 6443;
+unsigned short	kgdb_sendport= 6442;
+int		kgdb_eth = -1; /* Default tty mode */
+unsigned char	kgdb_remotemac[6] = {0xff,0xff,0xff,0xff,0xff,0xff};
+unsigned char	kgdb_localmac[6] = {0xff,0xff,0xff,0xff,0xff,0xff};
+int		kgdb_eth_is_initializing = 0;
+
+struct net_device *kgdb_netdevice = NULL;
+
+/*
+ * Get a char if available, return -1 if nothing available.
+ * Empty the receive buffer first, then look at the interface hardware.
+ */
+static int
+read_char(void)
+{
+	/* intr routine has queued chars */
+	if (atomic_read(&kgdb_buf_in_cnt) != 0)
+	{
+		int chr;
+
+		chr = kgdb_buf[kgdb_buf_out_inx++] ;
+		kgdb_buf_out_inx &= (GDB_BUF_SIZE - 1) ;
+		atomic_dec(&kgdb_buf_in_cnt) ;
+		return chr;
+	}
+
+	return -1; /* no data */
+}
+
+/*
+ * Wait until the interface can accept a char, then write it.
+ */
+static void
+write_buffer(char *buf, int len)
+{
+	int			total_len, eth_len, ip_len, udp_len;
+	struct in_device	*in_dev;
+	struct sk_buff		*skb;
+	struct udphdr		*udph;
+	struct iphdr		*iph;
+	struct ethhdr		*eth;
+
+	if (!(in_dev = (struct in_device *) kgdb_netdevice->ip_ptr)) {
+		panic("No in_device available for interface!\n");
+	}
+
+	if (!(in_dev->ifa_list)) {
+		panic("No interface address set for interface!\n");
+	}
+
+	udp_len = len + sizeof(struct udphdr);
+	ip_len = eth_len = udp_len + sizeof(struct iphdr);
+	total_len = eth_len + ETH_HLEN;
+
+	if (!(skb = alloc_skb(total_len, GFP_ATOMIC))) {
+		return;
+	}
+
+	atomic_set(&skb->users, 1);
+	skb_reserve(skb, total_len - 1);
+
+	memcpy(skb->data, (unsigned char *) buf, len);
+	skb->len += len;
+
+	udph = (struct udphdr *) skb_push(skb, sizeof(*udph));
+	udph->source = htons(kgdb_listenport);
+	udph->dest   = htons(kgdb_sendport);
+	udph->len    = htons(udp_len);
+	udph->check  = 0;
+
+	iph = (struct iphdr *)skb_push(skb, sizeof(*iph));
+	iph->version  = 4;
+	iph->ihl      = 5;
+	iph->tos      = 0;
+	iph->tot_len  = htons(ip_len);
+	iph->id       = 0;
+	iph->frag_off = 0;
+	iph->ttl      = 64;
+	iph->protocol = IPPROTO_UDP;
+	iph->check    = 0;
+	iph->saddr    = in_dev->ifa_list->ifa_address;
+	iph->daddr    = kgdb_remoteip;
+	iph->check    = ip_fast_csum((unsigned char *)iph, iph->ihl);
+
+	eth = (struct ethhdr *) skb_push(skb, ETH_HLEN);
+	eth->h_proto = htons(ETH_P_IP);
+	memcpy(eth->h_source, kgdb_localmac, kgdb_netdevice->addr_len);
+	memcpy(eth->h_dest, kgdb_remotemac, kgdb_netdevice->addr_len);
+
+	spin_lock(&kgdb_netdevice->xmit_lock);
+	kgdb_netdevice->xmit_lock_owner = smp_processor_id();
+	kgdb_netdevice->hard_start_xmit(skb, kgdb_netdevice);
+	kgdb_netdevice->xmit_lock_owner = -1;
+	spin_unlock(&kgdb_netdevice->xmit_lock);
+
+	/* kfree_skb(skb); */
+}
+
+
+/*
+ * In the interrupt state the target machine will not respond to any
+ * arp requests, so handle them here.
+ */
+
+static struct sk_buff *send_skb = NULL;
+
+void
+kgdb_eth_reply_arp(void)
+{
+	if (send_skb) {
+		spin_lock(&kgdb_netdevice->xmit_lock);
+		kgdb_netdevice->xmit_lock_owner = smp_processor_id();
+	    	kgdb_netdevice->hard_start_xmit(send_skb, kgdb_netdevice);
+	    	kgdb_netdevice->xmit_lock_owner = -1;
+	    	spin_unlock(&kgdb_netdevice->xmit_lock);
+		send_skb = NULL;
+	}
+}
+
+static int
+make_arp_request(struct sk_buff *skb)
+{
+	struct arphdr *arp;
+	unsigned char *arp_ptr;
+	int type = ARPOP_REPLY;
+	int ptype = ETH_P_ARP;
+	u32 sip, tip;
+	unsigned char *sha, *tha;
+	struct in_device *in_dev = (struct in_device *) kgdb_netdevice->ip_ptr;
+
+	/* No arp on this interface */
+
+	if (kgdb_netdevice->flags & IFF_NOARP) {
+		return 0;
+	}
+
+	if (!pskb_may_pull(skb, (sizeof(struct arphdr) +
+				 (2 * kgdb_netdevice->addr_len) +
+				 (2 * sizeof(u32))))) {
+		return 0;
+	}
+
+	skb->h.raw = skb->nh.raw = skb->data;
+	arp = skb->nh.arph;
+
+	if ((arp->ar_hrd != htons(ARPHRD_ETHER) &&
+	     arp->ar_hrd != htons(ARPHRD_IEEE802)) ||
+	    arp->ar_pro != htons(ETH_P_IP)) {
+		return 0;
+	}
+
+	/* Understand only these message types */
+
+	if (arp->ar_op != htons(ARPOP_REQUEST)) {
+		return 0;
+	}
+
+	/* Extract fields */
+
+	arp_ptr= (unsigned char *)(arp+1);
+	sha = arp_ptr;
+	arp_ptr += kgdb_netdevice->addr_len;
+	memcpy(&sip, arp_ptr, 4);
+	arp_ptr += 4;
+	tha = arp_ptr;
+	arp_ptr += kgdb_netdevice->addr_len;
+	memcpy(&tip, arp_ptr, 4);
+
+	if (tip != in_dev->ifa_list->ifa_address) {
+		return 0;
+	}
+
+	if (kgdb_remoteip != sip) {
+		return 0;
+	}
+
+	/*
+	 * Check for bad requests for 127.x.x.x and requests for multicast
+	 * addresses.  If this is one such, delete it.
+	 */
+
+	if (LOOPBACK(tip) || MULTICAST(tip)) {
+		return 0;
+	}
+
+	/* reply to the ARP request */
+
+	send_skb = alloc_skb(sizeof(struct arphdr) + 2 * (kgdb_netdevice->addr_len + 4) + LL_RESERVED_SPACE(kgdb_netdevice), GFP_ATOMIC);
+
+	if (send_skb == NULL) {
+		return 0;
+	}
+
+	skb_reserve(send_skb, LL_RESERVED_SPACE(kgdb_netdevice));
+	send_skb->nh.raw = send_skb->data;
+	arp = (struct arphdr *) skb_put(send_skb, sizeof(struct arphdr) + 2 * (kgdb_netdevice->addr_len + 4));
+	send_skb->dev = kgdb_netdevice;
+	send_skb->protocol = htons(ETH_P_ARP);
+
+	/* Fill the device header for the ARP frame */
+
+	if (kgdb_netdevice->hard_header &&
+	    kgdb_netdevice->hard_header(send_skb, kgdb_netdevice, ptype,
+				       kgdb_remotemac, kgdb_localmac,
+				       send_skb->len) < 0) {
+		kfree_skb(send_skb);
+		return 0;
+	}
+
+	/*
+	 * Fill out the arp protocol part.
+	 *
+	 * we only support ethernet device type,
+	 * which (according to RFC 1390) should always equal 1 (Ethernet).
+	 */
+
+	arp->ar_hrd = htons(kgdb_netdevice->type);
+	arp->ar_pro = htons(ETH_P_IP);
+
+	arp->ar_hln = kgdb_netdevice->addr_len;
+	arp->ar_pln = 4;
+	arp->ar_op = htons(type);
+
+	arp_ptr=(unsigned char *)(arp + 1);
+
+	memcpy(arp_ptr, kgdb_netdevice->dev_addr, kgdb_netdevice->addr_len);
+	arp_ptr += kgdb_netdevice->addr_len;
+	memcpy(arp_ptr, &tip, 4);
+	arp_ptr += 4;
+	memcpy(arp_ptr, kgdb_localmac, kgdb_netdevice->addr_len);
+	arp_ptr += kgdb_netdevice->addr_len;
+	memcpy(arp_ptr, &sip, 4);
+	return 0;
+}
+
+
+/*
+ * Accept an skbuff from net_device layer and add the payload onto
+ * kgdb buffer
+ *
+ * When the kgdb stub routine getDebugChar() is called it draws characters
+ * out of the buffer until it is empty and then reads directly from the
+ * serial port.
+ *
+ * We do not attempt to write chars from the interrupt routine since
+ * the stubs do all of that via putDebugChar() which writes one byte
+ * after waiting for the interface to become ready.
+ *
+ * The debug stubs like to run with interrupts disabled since, after all,
+ * they run as a consequence of a breakpoint in the kernel.
+ *
+ * NOTE: Return value of 1 means it was for us and is an indication to
+ * the calling driver to destroy the sk_buff and not send it up the stack.
+ */
+int
+kgdb_net_interrupt(struct sk_buff *skb)
+{
+	unsigned char	chr;
+	struct iphdr	*iph = (struct iphdr*)skb->data;
+	struct udphdr	*udph= (struct udphdr*)(skb->data+(iph->ihl<<2));
+	unsigned char	*data = (unsigned char *) udph + sizeof(struct udphdr);
+	int		len;
+	int		i;
+
+	if ((kgdb_eth != -1) && (!kgdb_netdevice) &&
+	    (iph->protocol == IPPROTO_UDP) &&
+	    (be16_to_cpu(udph->dest) == kgdb_listenport)) {
+		kgdb_sendport = be16_to_cpu(udph->source);
+
+		while(kgdb_eth_is_initializing);
+		if(!kgdb_netdevice)
+			kgdb_eth_hook();
+		if (!kgdb_netdevice) {
+			/* Lets not even try again. */
+			kgdb_eth = -1;
+			return 0;
+		}
+	}
+	if (!kgdb_netdevice) {
+		return 0;
+	}
+	if (skb->protocol == __constant_htons(ETH_P_ARP) && !send_skb) {
+		make_arp_request(skb);
+		return 0;
+	}
+	if (iph->protocol != IPPROTO_UDP) {
+		return 0;
+	}
+
+	if (be16_to_cpu(udph->dest) != kgdb_listenport) {
+		return 0;
+	}
+
+	len = (be16_to_cpu(iph->tot_len) -
+	       (sizeof(struct udphdr) + sizeof(struct iphdr)));
+
+	for (i = 0; i < len; i++) {
+		chr = *data++;
+		if (chr == 3)
+		{
+			BREAKPOINT;
+			continue;
+		}
+		if (atomic_read(&kgdb_buf_in_cnt) >= GDB_BUF_SIZE) {
+			/* buffer overflow, clear it */
+			kgdb_buf_in_inx = 0;
+			atomic_set(&kgdb_buf_in_cnt, 0);
+			kgdb_buf_out_inx = 0;
+			break;
+		}
+		kgdb_buf[kgdb_buf_in_inx++] = chr;
+		kgdb_buf_in_inx &= (GDB_BUF_SIZE - 1);
+		atomic_inc(&kgdb_buf_in_cnt) ;
+	}
+
+	return 1;
+}
+
+
+int
+kgdb_eth_hook(void)
+{
+	char kgdb_netdev[16];
+	extern void kgdb_respond_ok(void);
+
+	if (kgdb_remotemac[0] == 0xff) {
+		panic("ERROR! 'gdbeth_remotemac' option not set!\n");
+	}
+	if (kgdb_localmac[0] == 0xff) {
+		panic("ERROR! 'gdbeth_localmac' option not set!\n");
+	}
+	if (kgdb_remoteip == 0) {
+		panic("ERROR! 'gdbeth_remoteip' option not set!\n");
+	}
+
+	sprintf(kgdb_netdev,"eth%d",kgdb_eth);
+
+#ifdef CONFIG_SMP
+	if (num_online_cpus() > CONFIG_NO_KGDB_CPUS) {
+		printk("kgdb: too manu cpus. Cannot enable debugger with more than %d cpus\n", CONFIG_NO_KGDB_CPUS);
+		return -1;
+	}
+#endif
+	for (kgdb_netdevice = dev_base;
+		kgdb_netdevice != NULL;
+		kgdb_netdevice = kgdb_netdevice->next) {
+		if (strncmp(kgdb_netdevice->name, kgdb_netdev, IFNAMSIZ) == 0) {
+			break;
+		}
+	}
+	if (!kgdb_netdevice) {
+		printk("KGDB NET : Unable to find interface %s\n",kgdb_netdev);
+		return -ENODEV;
+	}
+
+	/*
+	 * Call GDB routine to setup the exception vectors for the debugger
+	 */
+	set_debug_traps();
+
+	/*
+	 * Call the breakpoint() routine in GDB to start the debugging
+	 * session.
+	 */
+	kgdb_eth_is_initializing = 1;
+	BREAKPOINT;
+	return 0;
+}
+
+/*
+ * getDebugChar
+ *
+ * This is a GDB stub routine.  It waits for a character from the
+ * serial interface and then returns it.  If there is no serial
+ * interface connection then it returns a bogus value which will
+ * almost certainly cause the system to hang.
+ */
+int
+eth_getDebugChar(void)
+{
+	volatile int	chr;
+
+	while ((chr = read_char()) < 0) {
+		if (send_skb) {
+			kgdb_eth_reply_arp();
+		}
+		if (kgdb_netdevice->kgdb_net_poll_rx) {
+			kgdb_netdevice->kgdb_net_poll_rx(kgdb_netdevice);
+		} else {
+			printk("KGDB NET: Error - Device %s is not supported!\n", kgdb_netdevice->name);
+			panic("Please add support for kgdb net to this driver");
+		}
+	}
+	return chr;
+}
+
+#define ETH_QUEUE_SIZE 256
+static char eth_queue[ETH_QUEUE_SIZE];
+static int outgoing_queue;
+
+void
+eth_flushDebugChar(void)
+{
+	if(outgoing_queue) {
+		write_buffer(eth_queue, outgoing_queue);
+
+		outgoing_queue = 0;
+	}
+}
+
+static void
+put_char_on_queue(int chr)
+{
+	eth_queue[outgoing_queue++] = chr;
+	if(outgoing_queue == ETH_QUEUE_SIZE)
+	{
+		eth_flushDebugChar();
+	}
+}
+
+/*
+ * eth_putDebugChar
+ *
+ * This is a GDB stub routine.  It waits until the interface is ready
+ * to transmit a char and then sends it.
+ */
+void
+eth_putDebugChar(int chr)
+{
+	put_char_on_queue(chr); /* this routine will wait */
+}
+
+void
+kgdb_eth_set_trapmode(int mode)
+{
+	if (!kgdb_netdevice) {
+		return;
+	}
+	kgdb_netdevice->kgdb_is_trapped = mode;
+}
+
+int
+kgdb_eth_is_trapped()
+{
+	if (!kgdb_netdevice) {
+		return 0;
+	}
+	return kgdb_netdevice->kgdb_is_trapped;
+}
diff -puN drivers/net/Makefile~kgdb-over-ethernet drivers/net/Makefile
--- 25/drivers/net/Makefile~kgdb-over-ethernet	2003-11-04 21:59:06.000000000 -0800
+++ 25-akpm/drivers/net/Makefile	2003-11-04 21:59:06.000000000 -0800
@@ -32,6 +32,8 @@ obj-$(CONFIG_BMAC) += bmac.o
 
 obj-$(CONFIG_OAKNET) += oaknet.o 8390.o
 
+obj-$(CONFIG_KGDB) += kgdb_eth.o
+
 obj-$(CONFIG_DGRS) += dgrs.o
 obj-$(CONFIG_RCPCI) += rcpci.o
 obj-$(CONFIG_VORTEX) += 3c59x.o
diff -puN include/asm-i386/kgdb.h~kgdb-over-ethernet include/asm-i386/kgdb.h
--- 25/include/asm-i386/kgdb.h~kgdb-over-ethernet	2003-11-04 21:59:06.000000000 -0800
+++ 25-akpm/include/asm-i386/kgdb.h	2003-11-04 21:59:08.000000000 -0800
@@ -18,6 +18,17 @@ extern void breakpoint(void);
 #ifndef BREAKPOINT
 #define BREAKPOINT   asm("   int $3")
 #endif
+
+extern int kgdb_eth;
+extern unsigned kgdb_remoteip;
+extern unsigned short kgdb_listenport;
+extern unsigned short kgdb_sendport;
+extern unsigned char kgdb_remotemac[6];
+extern unsigned char kgdb_localmac[6];
+
+extern int kgdb_tty_hook(void);
+extern int kgdb_eth_hook(void);
+
 /*
  * GDB debug stub (or any debug stub) can point the 'linux_debug_hook'
  * pointer to its routine and it will be entered as the first thing
@@ -30,10 +41,12 @@ extern void breakpoint(void);
  * anywhere it pleases.
  */
 struct pt_regs;
+struct sk_buff;
 
 extern int kgdb_handle_exception(int trapno,
 				 int signo, int err_code, struct pt_regs *regs);
 extern int in_kgdb(struct pt_regs *regs);
+extern int kgdb_net_interrupt(struct sk_buff *skb);
 
 #ifdef CONFIG_KGDB_TS
 void kgdb_tstamp(int line, char *source, int data0, int data1);
diff -puN include/linux/netdevice.h~kgdb-over-ethernet include/linux/netdevice.h
--- 25/include/linux/netdevice.h~kgdb-over-ethernet	2003-11-04 21:59:06.000000000 -0800
+++ 25-akpm/include/linux/netdevice.h	2003-11-04 21:59:06.000000000 -0800
@@ -456,6 +456,11 @@ struct net_device
 	/* bridge stuff */
 	struct net_bridge_port	*br_port;
 
+#ifdef CONFIG_KGDB
+	int			kgdb_is_trapped;
+	void			(*kgdb_net_poll_rx)(struct net_device *);
+#endif
+
 #ifdef CONFIG_NET_FASTROUTE
 #define NETDEV_FASTROUTE_HMASK 0xF
 	/* Semi-private data. Keep it at the end of device struct. */
@@ -533,6 +538,11 @@ extern int		dev_new_index(void);
 extern struct net_device	*dev_get_by_index(int ifindex);
 extern struct net_device	*__dev_get_by_index(int ifindex);
 extern int		dev_restart(struct net_device *dev);
+#ifdef CONFIG_KGDB
+extern int		kgdb_eth_is_trapped(void);
+extern int		kgdb_net_interrupt(struct sk_buff *skb);
+extern void		kgdb_send_arp_request(void);
+#endif
 
 typedef int gifconf_func_t(struct net_device * dev, char * bufptr, int len);
 extern int		register_gifconf(unsigned int family, gifconf_func_t * gifconf);
@@ -591,12 +601,22 @@ static inline void netif_start_queue(str
 
 static inline void netif_wake_queue(struct net_device *dev)
 {
+#ifdef CONFIG_KGDB
+	if (kgdb_eth_is_trapped()) {
+		return;
+	}
+#endif
 	if (test_and_clear_bit(__LINK_STATE_XOFF, &dev->state))
 		__netif_schedule(dev);
 }
 
 static inline void netif_stop_queue(struct net_device *dev)
 {
+#ifdef CONFIG_KGDB
+	if (kgdb_eth_is_trapped()) {
+		return;
+	}
+#endif
 	set_bit(__LINK_STATE_XOFF, &dev->state);
 }
 
diff -puN net/core/dev.c~kgdb-over-ethernet net/core/dev.c
--- 25/net/core/dev.c~kgdb-over-ethernet	2003-11-04 21:59:06.000000000 -0800
+++ 25-akpm/net/core/dev.c	2003-11-04 21:59:06.000000000 -0800
@@ -111,6 +111,10 @@
 #endif	/* CONFIG_NET_RADIO */
 #include <asm/current.h>
 
+#ifdef CONFIG_KGDB
+#include <asm/kgdb.h>
+#endif
+
 /* This define, if set, will randomly drop a packet when congestion
  * is more than moderate.  It helps fairness in the multi-interface
  * case when one of them is a hog, but it kills performance for the
@@ -1380,7 +1384,6 @@ static void sample_queue(unsigned long d
 }
 #endif
 
-
 /**
  *	netif_rx	-	post buffer to the network code
  *	@skb: buffer to post
@@ -1405,6 +1408,21 @@ int netif_rx(struct sk_buff *skb)
 	struct softnet_data *queue;
 	unsigned long flags;
 
+#ifdef CONFIG_KGDB
+	/* See if kgdb_eth wants this packet */
+	if (!kgdb_net_interrupt(skb)) {
+		/* No.. if we're 'trapped' then junk it */
+		if (kgdb_eth_is_trapped()) {
+			kfree_skb(skb);
+			return NET_RX_DROP;
+		}
+	} else {
+		/* kgdb_eth ate the packet... drop it silently */
+		kfree_skb(skb);
+		return NET_RX_DROP;
+	}
+#endif
+
 	if (!skb->stamp.tv_sec)
 		do_gettimeofday(&skb->stamp);
 

_