[PATCH 6/21] KGDB: This is the simple KGDB over Ethernet I/O driverthat NET_POLL

From: Jason Wessel
Date: Mon Oct 15 2007 - 14:36:59 EST


Signed-off-by: Jason Wessel <jason.wessel@xxxxxxxxxxxxx>
eth.patch

From: Jason Wessel <jason.wessel@xxxxxxxxxxxxx>
CC: mpm@xxxxxxxxxxx
Subject: [PATCH] This is the simple KGDB over Ethernet I/O driver that NET_POLL

At one point this was very similar to the version Matt Mackall wrote
to allow for the use of KGDB over ethernet. Since then it has been
reworked to fit into the unified KGDB model.

Signed-off-by: Tom Rini <trini@xxxxxxxxxxxxxxxxxxx>
Signed-off-by: Jason Wessel <jason.wessel@xxxxxxxxxxxxx>

---
Documentation/kernel-parameters.txt | 7
drivers/net/Makefile | 1
drivers/net/kgdboe.c | 294 ++++++++++++++++++++++++++++++++++++
lib/Kconfig.kgdb | 22 ++
4 files changed, 324 insertions(+)
create mode 100644 drivers/net/kgdboe.c

--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -231,6 +231,7 @@ obj-$(CONFIG_ETRAX_ETHERNET) += cris/
obj-$(CONFIG_ENP2611_MSF_NET) += ixp2000/

obj-$(CONFIG_NETCONSOLE) += netconsole.o
+obj-$(CONFIG_KGDBOE) += kgdboe.o

obj-$(CONFIG_FS_ENET) += fs_enet/

--- /dev/null
+++ b/drivers/net/kgdboe.c
@@ -0,0 +1,294 @@
+/*
+ * drivers/net/kgdboe.c
+ *
+ * A network interface for GDB.
+ * Based upon 'gdbserial' by David Grothe <dave@xxxxxxxx>
+ * and Scott Foehner <sfoehner@xxxxxxxxxxxx>
+ *
+ * Maintainers: Amit S. Kale <amitkale@xxxxxxxxxxxxxx> and
+ * Tom Rini <trini@xxxxxxxxxxxxxxxxxxx>
+ *
+ * 2004 (c) Amit S. Kale <amitkale@xxxxxxxxxxxxxx>
+ * 2004-2005 (c) MontaVista Software, Inc.
+ * 2005-2007 (c) Wind River Systems, Inc.
+ *
+ * Contributors at various stages not listed above:
+ * San Mehat <nettwerk@xxxxxxxxxxx>, Robert Walsh <rjwalsh@xxxxxxxxxxxx>,
+ * wangdi <wangdi@xxxxxxxxxxxxx>, Matt Mackall <mpm@xxxxxxxxxxx>,
+ * Pavel Machek <pavel@xxxxxxx>, Jason Wessel <jason.wessel@xxxxxxxxxxxxx>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/string.h>
+#include <linux/kgdb.h>
+#include <linux/netpoll.h>
+#include <linux/init.h>
+
+#include <asm/atomic.h>
+
+#define IN_BUF_SIZE 512 /* power of 2, please */
+#define NOT_CONFIGURED_STRING "not_configured"
+#define OUT_BUF_SIZE 30 /* We don't want to send too big of a packet. */
+#define MAX_KGDBOE_CONFIG_STR 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;
+/* 0 = unconfigured, 1 = netpoll options parsed, 2 = fully configured. */
+static int configured;
+static struct kgdb_io local_kgdb_io_ops;
+static int use_dynamic_mac;
+
+MODULE_DESCRIPTION("KGDB driver for network interfaces");
+MODULE_LICENSE("GPL");
+static char config[MAX_KGDBOE_CONFIG_STR] = NOT_CONFIGURED_STRING;
+static struct kparam_string kps = {
+ .string = config,
+ .maxlen = MAX_KGDBOE_CONFIG_STR,
+};
+
+static void rx_hook(struct netpoll *np, int port, char *msg, int len,
+ struct sk_buff *skb)
+{
+ int i;
+
+ np->remote_port = port;
+
+ /* Copy the MAC address if we need to. */
+ if (use_dynamic_mac) {
+ memcpy(np->remote_mac, eth_hdr(skb)->h_source,
+ sizeof(np->remote_mac));
+ use_dynamic_mac = 0;
+ }
+
+ /*
+ * This could be GDB trying to attach. But it could also be GDB
+ * finishing up a session, with kgdb_connected=0 but GDB sending
+ * an ACK for the final packet. To make sure we don't try and
+ * make a breakpoint when GDB is leaving, make sure that if
+ * !kgdb_connected the only len == 1 packet we allow is ^C.
+ */
+ if (!kgdb_connected && (len != 1 || msg[0] == 3) &&
+ !atomic_read(&kgdb_setting_breakpoint))
+ tasklet_schedule(&kgdb_tasklet_breakpoint);
+
+ for (i = 0; i < len; i++) {
+ if (msg[i] == 3)
+ tasklet_schedule(&kgdb_tasklet_breakpoint);
+
+ if (atomic_read(&in_count) >= IN_BUF_SIZE) {
+ /* buffer overflow, clear it */
+ in_head = 0;
+ 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 struct netpoll np = {
+ .dev_name = "eth0",
+ .name = "kgdboe",
+ .rx_hook = rx_hook,
+ .local_port = 6443,
+ .remote_port = 6442,
+ .remote_mac = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
+};
+
+static void eth_pre_exception_handler(void)
+{
+ /* Increment the module count when the debugger is active */
+ if (!kgdb_connected)
+ try_module_get(THIS_MODULE);
+ netpoll_set_trap(1);
+}
+
+static void eth_post_exception_handler(void)
+{
+ /* decrement the module count when the debugger detaches */
+ if (!kgdb_connected)
+ module_put(THIS_MODULE);
+ netpoll_set_trap(0);
+}
+
+static int eth_get_char(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;
+}
+
+static void eth_flush_buf(void)
+{
+ if (out_count && np.dev) {
+ netpoll_send_udp(&np, out_buf, out_count);
+ memset(out_buf, 0, sizeof(out_buf));
+ out_count = 0;
+ }
+}
+
+static void eth_put_char(u8 chr)
+{
+ out_buf[out_count++] = chr;
+ if (out_count == OUT_BUF_SIZE)
+ eth_flush_buf();
+}
+
+static int option_setup(char *opt)
+{
+ char opt_scratch[MAX_KGDBOE_CONFIG_STR];
+
+ /* If we're being given a new configuration, copy it in. */
+ if (opt != config)
+ strcpy(config, opt);
+ /* But work on a copy as netpoll_parse_options will eat it. */
+ strcpy(opt_scratch, opt);
+ configured = !netpoll_parse_options(&np, opt_scratch);
+
+ use_dynamic_mac = 1;
+
+ return 0;
+}
+__setup("kgdboe=", option_setup);
+
+/* With our config string set by some means, configure kgdboe. */
+static int configure_kgdboe(void)
+{
+ /* Try out the string. */
+ option_setup(config);
+
+ if (!configured) {
+ printk(KERN_ERR "kgdboe: configuration incorrect - kgdboe not "
+ "loaded.\n");
+ printk(KERN_ERR " Usage: kgdboe=[src-port]@[src-ip]/[dev],"
+ "[tgt-port]@<tgt-ip>/<tgt-macaddr>\n");
+ return -EINVAL;
+ }
+
+ /* Bring it up. */
+ if (netpoll_setup(&np)) {
+ printk(KERN_ERR "kgdboe: netpoll_setup failed kgdboe failed\n");
+ return -EINVAL;
+ }
+
+ if (kgdb_register_io_module(&local_kgdb_io_ops)) {
+ netpoll_cleanup(&np);
+ return -EINVAL;
+ }
+
+ configured = 2;
+
+ return 0;
+}
+
+static int init_kgdboe(void)
+{
+ int ret;
+
+ /* Already done? */
+ if (configured == 2)
+ return 0;
+
+ /* OK, go ahead and do it. */
+ ret = configure_kgdboe();
+
+ if (configured == 2)
+ printk(KERN_INFO "kgdboe: debugging over ethernet enabled\n");
+
+ return ret;
+}
+
+static void cleanup_kgdboe(void)
+{
+ netpoll_cleanup(&np);
+ configured = 0;
+ kgdb_unregister_io_module(&local_kgdb_io_ops);
+}
+
+static int param_set_kgdboe_var(const char *kmessage, struct kernel_param *kp)
+{
+ char kmessage_save[MAX_KGDBOE_CONFIG_STR];
+ int msg_len = strlen(kmessage);
+
+ if (msg_len + 1 > MAX_KGDBOE_CONFIG_STR) {
+ printk(KERN_ERR "%s: string doesn't fit in %u chars.\n",
+ kp->name, MAX_KGDBOE_CONFIG_STR - 1);
+ return -ENOSPC;
+ }
+
+ if (kgdb_connected) {
+ printk(KERN_ERR "kgdboe: Cannot reconfigure while KGDB is "
+ "connected.\n");
+ return 0;
+ }
+
+ /* Start the reconfiguration process by saving the old string */
+ strncpy(kmessage_save, config, sizeof(kmessage_save));
+
+
+ /* Copy in the new param and strip out invalid characters so we
+ * can optionally specify the MAC.
+ */
+ strncpy(config, kmessage, sizeof(config));
+ msg_len--;
+ while (msg_len > 0 &&
+ (config[msg_len] < ',' || config[msg_len] > 'f')) {
+ config[msg_len] = '\0';
+ msg_len--;
+ }
+
+ /* Check to see if we are unconfiguring the io module and that it
+ * was in a fully configured state, as this is the only time that
+ * netpoll_cleanup should get called
+ */
+ if (configured == 2 && strcmp(config, NOT_CONFIGURED_STRING) == 0) {
+ printk(KERN_INFO "kgdboe: reverting to unconfigured state\n");
+ cleanup_kgdboe();
+ return 0;
+ } else
+ /* Go and configure with the new params. */
+ configure_kgdboe();
+
+ if (configured == 2)
+ return 0;
+
+ /* If the new string was invalid, revert to the previous state, which
+ * is at a minimum not_configured. */
+ strncpy(config, kmessage_save, sizeof(config));
+ if (strcmp(kmessage_save, NOT_CONFIGURED_STRING) != 0) {
+ printk(KERN_INFO "kgdboe: reverting to prior configuration\n");
+ /* revert back to the original config */
+ strncpy(config, kmessage_save, sizeof(config));
+ configure_kgdboe();
+ }
+ return 0;
+}
+
+static struct kgdb_io local_kgdb_io_ops = {
+ .read_char = eth_get_char,
+ .write_char = eth_put_char,
+ .init = init_kgdboe,
+ .flush = eth_flush_buf,
+ .pre_exception = eth_pre_exception_handler,
+ .post_exception = eth_post_exception_handler
+};
+
+module_init(init_kgdboe);
+module_exit(cleanup_kgdboe);
+module_param_call(kgdboe, param_set_kgdboe_var, param_get_string, &kps, 0644);
+MODULE_PARM_DESC(kgdboe, " kgdboe=[src-port]@[src-ip]/[dev],"
+ "[tgt-port]@<tgt-ip>/<tgt-macaddr>\n");
--- a/lib/Kconfig.kgdb
+++ b/lib/Kconfig.kgdb
@@ -65,8 +65,30 @@ config KGDB_8250_NOMODULE
Uses generic serial port (8250) to communicate with the host
GDB. This is independent of the normal (SERIAL_8250) driver
for this chipset.
+
+config KGDBOE_NOMODULE
+ bool "KGDB: On ethernet - in kernel"
+ depends on NET
+ select KGDBOE
+ help
+ Uses the NETPOLL API to communicate with the host GDB via UDP.
+ In order for this to work, the ethernet interface specified must
+ support the NETPOLL API, and this must be initialized at boot.
+ See the documentation for syntax.
endchoice

+config KGDBOE
+ tristate "KGDB: On ethernet" if !KGDBOE_NOMODULE
+ depends on m && KGDB && NET
+ select NETPOLL
+ select NETPOLL_TRAP
+ select NET_POLL_CONTROLLER
+ help
+ Uses the NETPOLL API to communicate with the host GDB via UDP.
+ In order for this to work, the ethernet interface specified must
+ support the NETPOLL API, and this must be initialized at boot.
+ See the documentation for syntax.
+
config KGDB_8250
tristate "KGDB: On generic serial port (8250)" if !KGDB_8250_NOMODULE
depends on m && KGDB_ONLY_MODULES
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -780,6 +780,13 @@ and is between 256 and 4096 characters.
ips= [HW,SCSI] Adaptec / IBM ServeRAID controller
See header of drivers/scsi/ips.c.

+ kgdboe= [HW] Setup the local ip information and host ip
+ information when the network driver supports
+ NETPOLL and kgdboe is configured as a built in.
+ Options are:
+ [src-port]@<src-ip>/[dev],[tgt-port]@<tgt-ip>/<tgt-mac>
+ IE: kgdboe=@xxxxxxxx/,@10.0.0.1.3/
+
ports= [IP_VS_FTP] IPVS ftp helper module
Default is 21.
Up to 8 (IP_VS_APP_MAX_PORTS) ports