diff -upN /dev/null current/Documentation/i386/kgdb/kgdbeth.txt --- /dev/null 2004-02-24 15:23:11.000000000 -0800 +++ current/Documentation/i386/kgdb/kgdbeth.txt 2004-04-30 12:55:10.000000000 -0700 @@ -0,0 +1,92 @@ +KGDB over ethernet +================== + +Authors +------- + +Robert Walsh (2.6 port) +wangdi (2.6 port) +Matt Mackall (netpoll api) +San Mehat (original 2.4 code) + + +Introduction +------------ + +KGDB supports debugging over ethernet (kgdboe) via polling of a given +network interface. Most cards should be supported automatically. +Debugging facilities are available as soon as the network driver and +kgdboe have initialized. Unfortunately, this is too late in the boot +process for debugging some issues, but works quite well for many +others. This should not interfere with normal network usage and +doesn't require a dedicated NIC. + +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 option on the TARGET kernel: + + kgdboe=[tgt-port]@/[dev],[host-port]@/[host-macaddr] + + where + tgt-port source for UDP packets (defaults to 6443) + tgt-ip source IP to use (interface address) + dev network interface (eth0) + host-port HOST UDP port (6442) (not really used) + host-ip IP address for HOST machine + host-macaddr ethernet MAC address for HOST (ff:ff:ff:ff:ff:ff) + + examples: + + kgdboe=7000@192.168.0.1/eth1,7001@192.168.0.2/00:05:3C:04:47:5D + this machine is 192.168.0.1 on eth1 + remote machine is 192.168.0.2 with MAC address 00:05:3C:04:47:5D + listen for gdb packets on port 7000 + send unsolicited gdb packets to port 7001 + + kgdboe=@192.168.0.1/,@192.168.0.2/ + this machine is 192.168.0.1 on default interface eth0 + remote machine is 192.168.0.2, use default broadcast MAC address + listen for gdb packets on default port 6443 + send unsolicited gdb packets to port 6442 + +Only packets originating from the configured HOST IP address will be +accepted by the debugger. + +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. + +Limitations +----------- + +The current release of this code is exclusive of using kgdb on a +serial interface, so you must boot without the kgdboe option to use +serial debugging. Trying to debug the network driver while using it +will prove interesting. + +Bug reports +----------- + +Send bug reports to Robert Walsh and Matt +Mackall . diff -upN reference/arch/i386/kernel/irq.c current/arch/i386/kernel/irq.c --- reference/arch/i386/kernel/irq.c 2004-04-30 11:23:02.000000000 -0700 +++ current/arch/i386/kernel/irq.c 2004-04-30 12:55:10.000000000 -0700 @@ -570,6 +570,8 @@ out: irq_exit(); + kgdb_process_breakpoint(); + return 1; } diff -upN reference/arch/i386/kernel/kgdb_stub.c current/arch/i386/kernel/kgdb_stub.c --- reference/arch/i386/kernel/kgdb_stub.c 2004-04-30 12:50:17.000000000 -0700 +++ current/arch/i386/kernel/kgdb_stub.c 2004-04-30 12:55:10.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,8 @@ #include #include #include +#include +#include /************************************************************************ * @@ -122,8 +130,12 @@ 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 */ /************************************************************************/ /* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/ @@ -264,6 +276,41 @@ malloc(int size) } /* + * I/O dispatch functions... + * Based upon kgdboe, either call the ethernet + * handler or the serial one.. + */ +void +putDebugChar(int c) +{ + if (!kgdboe) { + tty_putDebugChar(c); + } else { + eth_putDebugChar(c); + } +} + +int +getDebugChar(void) +{ + if (!kgdboe) { + return tty_getDebugChar(); + } else { + return eth_getDebugChar(); + } +} + +void +flushDebugChar(void) +{ + if (!kgdboe) { + 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 +476,7 @@ getpacket(char *buffer) if (remote_debug) printk("R:%s\n", buffer); + flushDebugChar(); } /* send the packet in buffer. */ @@ -441,25 +489,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 (!kgdboe) { + 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(); + + } while ((getDebugChar() & 0x7f) != '+'); + } else { + /* + * For udp, we can not transfer too much bytes once. + * We only transfer MAX_SEND_COUNT size bytes each time + */ - putDebugChar('#'); - putDebugChar(hexchars[checksum >> 4]); - putDebugChar(hexchars[checksum % 16]); +#define MAX_SEND_COUNT 30 - } while ((getDebugChar() & 0x7f) != '+'); + 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]; @@ -1048,9 +1138,9 @@ in_kgdb(struct pt_regs *regs) */ in_kgdb_entry_log[cpu]++; in_kgdb_here_log[cpu] = regs; - if (cpu == spinlock_cpu || waiting_cpus[cpu].task) { + if (cpu == spinlock_cpu || waiting_cpus[cpu].task) goto exit_in_kgdb; - } + /* * For protection of the initilization of the spin locks by kgdb * it locks the kgdb spinlock before it gets the wait locks set @@ -1059,16 +1149,18 @@ in_kgdb(struct pt_regs *regs) * sequence where the wait lock is removed prior to the kgdb lock * so if kgdb gets unlocked, we just exit. */ + while (spin_is_locked(&kgdb_spinlock) && !spin_is_locked(waitlocks + cpu)) ; - if (!spin_is_locked(&kgdb_spinlock)) { + if (!spin_is_locked(&kgdb_spinlock)) goto exit_in_kgdb; - } + waiting_cpus[cpu].task = current; waiting_cpus[cpu].pid = (current->pid) ? : (PID_MAX + cpu); waiting_cpus[cpu].regs = regs; spin_unlock_wait(waitlocks + cpu); + /* * log departure of this cpu */ @@ -1180,6 +1272,12 @@ kgdb_handle_exception(int exceptionVecto print_regs(®s); return (0); } + /* + * If we're using eth mode, set the 'mode' in the netdevice. + */ + + if (kgdboe) + netpoll_set_trap(1); kgdb_local_irq_save(flags); @@ -1234,10 +1332,12 @@ kgdb_handle_exception(int exceptionVecto if (num_online_cpus() > 1) { int me_in_kgdb = in_kgdb_entry_log[smp_processor_id()]; smp_send_nmi_allbutself(); + while (i < num_online_cpus() && time != end_time) { int j; for (j = 0; j < MAX_NO_CPUS; j++) { if (waiting_cpus[j].task && + waiting_cpus[j].task != NOCPU && !cpu_logged_in[j]) { i++; cpu_logged_in[j] = 1; @@ -1626,6 +1726,10 @@ kgdb_handle_exception(int exceptionVecto } } } + + if (kgdboe) + netpoll_set_trap(0); + correct_hw_break(); asm volatile ("movl %0, %%db6\n"::"r" (0)); goto exit_kgdb; @@ -2331,3 +2435,23 @@ 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 kgdb_need_breakpoint[NR_CPUS]; + +void kgdb_schedule_breakpoint(void) +{ + kgdb_need_breakpoint[smp_processor_id()] = 1; +} + +void kgdb_process_breakpoint(void) +{ + /* + * Handle a breakpoint queued from inside network driver code + * to avoid reentrancy issues + */ + if (kgdb_need_breakpoint[smp_processor_id()]) { + kgdb_need_breakpoint[smp_processor_id()] = 0; + BREAKPOINT; + } +} + diff -upN reference/arch/i386/lib/kgdb_serial.c current/arch/i386/lib/kgdb_serial.c --- reference/arch/i386/lib/kgdb_serial.c 2004-04-30 12:50:17.000000000 -0700 +++ current/arch/i386/lib/kgdb_serial.c 2004-04-30 12:55:10.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 (kgdboe) { + 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 -upN reference/drivers/net/Makefile current/drivers/net/Makefile --- reference/drivers/net/Makefile 2004-04-30 11:23:22.000000000 -0700 +++ current/drivers/net/Makefile 2004-04-30 12:55:10.000000000 -0700 @@ -189,4 +189,6 @@ obj-$(CONFIG_NET_TULIP) += tulip/ obj-$(CONFIG_HAMRADIO) += hamradio/ obj-$(CONFIG_IRDA) += irda/ +# Must come after all NICs that might use them obj-$(CONFIG_NETCONSOLE) += netconsole.o +obj-$(CONFIG_KGDB) += kgdb_eth.o diff -upN /dev/null current/drivers/net/kgdb_eth.c --- /dev/null 2004-02-24 15:23:11.000000000 -0800 +++ current/drivers/net/kgdb_eth.c 2004-04-30 12:55:10.000000000 -0700 @@ -0,0 +1,131 @@ +/* + * 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 . + * + * Refactored for netpoll API by Matt Mackall + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define IN_BUF_SIZE 512 /* power of 2, please */ +#define OUT_BUF_SIZE 256 + +static char in_buf[IN_BUF_SIZE], out_buf[OUT_BUF_SIZE]; +static int in_head, in_tail, out_count; +static atomic_t in_count; +int kgdboe = 0; /* Default to tty mode */ + +extern void set_debug_traps(void); +extern void breakpoint(void); +static void rx_hook(struct netpoll *np, int port, char *msg, int len); + +static struct netpoll np = { + .name = "kgdboe", + .dev_name = "eth0", + .rx_hook = rx_hook, + .local_port = 6443, + .remote_port = 6442, + .remote_mac = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, +}; + +int eth_getDebugChar(void) +{ + int chr; + + while (atomic_read(&in_count) == 0) + netpoll_poll(&np); + + chr = in_buf[in_tail++]; + in_tail &= (IN_BUF_SIZE - 1); + atomic_dec(&in_count); + return chr; +} + +void eth_flushDebugChar(void) +{ + if(out_count && np.dev) { + netpoll_send_udp(&np, out_buf, out_count); + out_count = 0; + } +} + +void eth_putDebugChar(int chr) +{ + out_buf[out_count++] = chr; + if(out_count == OUT_BUF_SIZE) + eth_flushDebugChar(); +} + +static void rx_hook(struct netpoll *np, int port, char *msg, int len) +{ + int i; + + np->remote_port = port; + + /* Is this gdb trying to attach? */ + if (!netpoll_trap() && len == 8 && !strncmp(msg, "$Hc-1#09", 8)) + kgdb_schedule_breakpoint(); + + for (i = 0; i < len; i++) { + if (msg[i] == 3) + kgdb_schedule_breakpoint(); + + if (atomic_read(&in_count) >= IN_BUF_SIZE) { + /* buffer overflow, clear it */ + in_head = in_tail = 0; + atomic_set(&in_count, 0); + break; + } + in_buf[in_head++] = msg[i]; + in_head &= (IN_BUF_SIZE - 1); + atomic_inc(&in_count); + } +} + +static int option_setup(char *opt) +{ + return netpoll_parse_options(&np, opt); +} + +__setup("kgdboe=", option_setup); + +static int init_kgdboe(void) +{ +#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 + + set_debug_traps(); + + if(!np.remote_ip || netpoll_setup(&np)) + return 1; + + kgdboe = 1; + printk(KERN_INFO "kgdb: debugging over ethernet enabled\n"); + + return 0; +} + +module_init(init_kgdboe); diff -upN reference/include/asm-i386/kgdb.h current/include/asm-i386/kgdb.h --- reference/include/asm-i386/kgdb.h 2004-04-30 12:50:18.000000000 -0700 +++ current/include/asm-i386/kgdb.h 2004-04-30 12:55:10.000000000 -0700 @@ -18,6 +18,14 @@ extern void breakpoint(void); #ifndef BREAKPOINT #define BREAKPOINT asm(" int $3") #endif + +extern void kgdb_schedule_breakpoint(void); +extern void kgdb_process_breakpoint(void); + +extern int kgdb_tty_hook(void); +extern int kgdb_eth_hook(void); +extern int kgdboe; + /* * 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 @@ -55,5 +63,7 @@ void kgdb_tstamp(int line, char *source, #define kgdb_handle_exception #define breakpoint #define INIT_KGDB_INTS +#define kgdb_process_breakpoint() do {} while(0) + #endif #endif /* __KGDB */ diff -upN reference/net/Kconfig current/net/Kconfig --- reference/net/Kconfig 2004-04-30 11:23:57.000000000 -0700 +++ current/net/Kconfig 2004-04-30 12:55:10.000000000 -0700 @@ -650,18 +650,17 @@ endmenu endmenu +config KGDBOE + def_bool X86 && KGDB + config NETPOLL - def_bool NETCONSOLE + def_bool NETCONSOLE || KGDBOE config NETPOLL_RX - bool "Netpoll support for trapping incoming packets" - default n - depends on NETPOLL + def_bool KGDBOE config NETPOLL_TRAP - bool "Netpoll traffic trapping" - default n - depends on NETPOLL + def_bool KGDBOE config NET_POLL_CONTROLLER def_bool NETPOLL diff -upN reference/net/core/dev.c current/net/core/dev.c --- reference/net/core/dev.c 2004-04-30 11:23:58.000000000 -0700 +++ current/net/core/dev.c 2004-04-30 12:55:10.000000000 -0700 @@ -1523,7 +1523,6 @@ static void sample_queue(unsigned long d } #endif - /** * netif_rx - post buffer to the network code * @skb: buffer to post @@ -1843,7 +1842,6 @@ static void net_rx_action(struct softirq unsigned long start_time = jiffies; int budget = netdev_max_backlog; - local_irq_disable(); while (!list_empty(&queue->poll_list)) { @@ -1869,6 +1867,8 @@ static void net_rx_action(struct softirq dev_put(dev); local_irq_disable(); } + + kgdb_process_breakpoint(); } out: local_irq_enable();