Added: trunk/drivers/net/kgdboe.c (0 => 6688)
--- trunk/drivers/net/kgdboe.c (rev 0)
+++ trunk/drivers/net/kgdboe.c 2009-06-12 13:56:17 UTC (rev 6688)
@@ -0,0 +1,312 @@
+/*
+ * drivers/net/kgdboe.c
+ *
+ * A network interface for GDB.
+ * Based upon 'gdbserial' by David Grothe <[email protected]>
+ * and Scott Foehner <[email protected]>
+ *
+ * Maintainer: Jason Wessel <[email protected]>
+ *
+ * 2004 (c) Amit S. Kale <[email protected]>
+ * 2004-2005 (c) MontaVista Software, Inc.
+ * 2005-2008 (c) Wind River Systems, Inc.
+ *
+ * Contributors at various stages not listed above:
+ * San Mehat <[email protected]>, Robert Walsh <[email protected]>,
+ * wangdi <[email protected]>, Matt Mackall <[email protected]>,
+ * Pavel Machek <[email protected]>, Jason Wessel <[email protected]>
+ *
+ * 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 OUT_BUF_SIZE 30 /* We don't want to send too big of a packet. */
+#define MAX_CONFIG_LEN 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;
+
+static atomic_t kgdb_break_tasklet_var;
+
+/*
+ * There are times a tasklet needs to be used vs a compiled in in
+ * break point so as to cause an exception outside a kgdb I/O module,
+ * such as is the case with kgdboe, where calling a breakpoint in the
+ * I/O driver itself would be fatal.
+ */
+static void kgdb_tasklet_bpt(unsigned long ing)
+{
+ kgdb_breakpoint();
+ atomic_set(&kgdb_break_tasklet_var, 0);
+}
+
+static DECLARE_TASKLET(kgdb_tasklet_breakpoint, kgdb_tasklet_bpt, 0);
+
+static void kgdb_schedule_breakpoint(void)
+{
+ if (atomic_read(&kgdb_break_tasklet_var) ||
+ atomic_read(&kgdb_active) != -1 ||
+ atomic_read(&kgdb_setting_breakpoint))
+ return;
+ atomic_inc(&kgdb_break_tasklet_var);
+ tasklet_schedule(&kgdb_tasklet_breakpoint);
+}
+
+MODULE_DESCRIPTION("KGDB driver for network interfaces");
+MODULE_LICENSE("GPL");
+static char config[MAX_CONFIG_LEN];
+static struct kparam_string kps = {
+ .string = config,
+ .maxlen = MAX_CONFIG_LEN,
+};
+
+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))
+ 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 = 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_CONFIG_LEN];
+
+ configured = 0;
+ /* If we're being given a new configuration, copy it in. */
+ if (opt != config)
+ strcpy(config, opt);
+
+ if (opt[0] == '\0')
+ return 1;
+
+ /* 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)
+{
+ if (option_setup(config))
+ return 0;
+
+ if (!configured) {
+ printk(KERN_ERR "kgdboe: configuration incorrect - kgdboe not "
+ "loaded.\n");
+ printk(KERN_ERR " Usage: kgdboe=[src-po...@[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_CONFIG_LEN];
+ int len = strlen(kmessage);
+
+ if (len >= MAX_CONFIG_LEN) {
+ printk(KERN_ERR "kgdboc: config string too long\n");
+ 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.
+ */
+ strcpy(config, kmessage);
+ /* Chop out \n char as a result of echo */
+ if (config[len - 1] == '\n')
+ config[len - 1] = '\0';
+
+ len--;
+ while (len > 0 && (config[len] < ',' || config[len] > 'f')) {
+ config[len] = '\0';
+ len--;
+ }
+
+ if (configured == 2) {
+ cleanup_kgdboe();
+ configured = 0;
+ }
+ if (config[0] == '\0')
+ return 0;
+
+ configure_kgdboe();
+
+ if (configured != 2)
+ return -EINVAL;
+
+ return 0;
+}
+
+static struct kgdb_io local_kgdb_io_ops = {
+ .name = "kgdboe",
+ .read_char = eth_get_char,
+ .write_char = eth_put_char,
+ .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, "[src-po...@[src-ip]/[dev],"
+ "[tgt-port]@<tgt-ip>/<tgt-macaddr>");
Modified: trunk/include/linux/netpoll.h (6687 => 6688)
--- trunk/include/linux/netpoll.h 2009-06-12 13:55:33 UTC (rev 6687)
+++ trunk/include/linux/netpoll.h 2009-06-12 13:56:17 UTC (rev 6688)
@@ -16,7 +16,7 @@
struct net_device *dev;
char dev_name[IFNAMSIZ];
const char *name;
- void (*rx_hook)(struct netpoll *, int, char *, int);
+ void (*rx_hook)(struct netpoll *, int, char *, int, struct sk_buff *);
__be32 local_ip, remote_ip;
u16 local_port, remote_port;