From: Robert Walsh 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-09-27 19:21:59.000000000 -0700 +++ 25-akpm/arch/i386/kernel/kgdb_stub.c 2003-09-27 19:22:03.000000000 -0700 @@ -30,6 +30,8 @@ * * Written by: Glenn Engel $ * Updated by: David Grothe + * Updated by: Robert Walsh + * Updated by: wangdi * 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 + * and wangdi , 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 #include #include +#include /************************************************************************ * @@ -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; /* $#. */ - 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(®s); 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-09-27 19:21:59.000000000 -0700 +++ 25-akpm/arch/i386/lib/kgdb_serial.c 2003-09-27 19:21:59.000000000 -0700 @@ -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 + * and wangdi , 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-09-27 19:21:59.000000000 -0700 @@ -0,0 +1,118 @@ +KGDB over ethernet +================== + +Authors +------- + +Robert Walsh (2.6 port) +wangdi (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 . diff -puN drivers/net/3c59x.c~kgdb-over-ethernet drivers/net/3c59x.c --- 25/drivers/net/3c59x.c~kgdb-over-ethernet 2003-09-27 19:21:59.000000000 -0700 +++ 25-akpm/drivers/net/3c59x.c 2003-09-27 19:21:59.000000000 -0700 @@ -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-09-27 19:21:59.000000000 -0700 +++ 25-akpm/drivers/net/e100/e100_main.c 2003-09-27 19:21:59.000000000 -0700 @@ -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-09-27 19:22:03.000000000 -0700 @@ -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 + * and wangdi . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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-09-27 19:21:59.000000000 -0700 +++ 25-akpm/drivers/net/Makefile 2003-09-27 19:21:59.000000000 -0700 @@ -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-09-27 19:21:59.000000000 -0700 +++ 25-akpm/include/asm-i386/kgdb.h 2003-09-27 19:22:03.000000000 -0700 @@ -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-09-27 19:21:59.000000000 -0700 +++ 25-akpm/include/linux/netdevice.h 2003-09-27 19:21:59.000000000 -0700 @@ -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. */ @@ -530,6 +535,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); @@ -588,12 +598,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-09-27 19:21:59.000000000 -0700 +++ 25-akpm/net/core/dev.c 2003-09-27 19:21:59.000000000 -0700 @@ -111,6 +111,10 @@ #endif /* CONFIG_NET_RADIO */ #include +#ifdef CONFIG_KGDB +#include +#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 @@ -1322,7 +1326,6 @@ static void sample_queue(unsigned long d } #endif - /** * netif_rx - post buffer to the network code * @skb: buffer to post @@ -1347,6 +1350,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); _