Re: Using the same serial port for both serial console and kgdb debugging

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Paul Fisher ha wrote:
Hello list,

I'm trying to get kgdb setup working. I have two PCs but unfortunately only one
serial port in each of those. So far everything worked great, but several days
ago the monitor of the second machine was taken away from my desk, and now one
of the machines is headless.

As I already said, I got only one serial port, and now I use it to provide a
serial console. Obviously, kgdb now doesn't work.. I've read somewhere that it
is practically impossible to use one serial link for both a serial console and
kgdb, but I think that's wrong, because kgdb documentation (
http://kernel.org/pub/linux/kernel/people/jwessel/kdb/ch03.html#kgdboc) says:

For kgdb/gdb, kgdboc is designed to work with a single serial port. It is
intended to cover the circumstance where you want to use a serial console as
your primary console as well as using it to perform kernel debugging. It is also
possible to use kgdb on a serial port which is not designated as a system
console.

So that should be possible. I googled a bit and found a kdmx (kgdb demux)
script, and in fact it works, but in a very limited and weird way. For one, you
can't send a BRK (break) from minicom (Ctrl-A f) because it operates on the
library level (read, write, select system calls) and the BRK is sent by means of
ioctl(). Sending a break is essential, because that's the way to stop kernel
execution and break into the debugger. Also detach gdb command freezes the whole
serial communication because of the way this script is written.

So, is there any reliable way to do this? Any help, hint, etc is appreciated.

Thank you,

--paul

--
To unsubscribe from this list: send an email with
"unsubscribe kernelnewbies" to ecartis@xxxxxxxxxxxx
Please read the FAQ at http://kernelnewbies.org/FAQ


Hi,
documentation about kgdb and his integration in the kernel is quite confusing. I've managed to get kgdb working some times ago with kernel 2.6.31.4. At the time people was saying that kgdb was fully integrated in the kernel but it wasn't. I don't know how is its state now (hoping it is better).

Anyway I had to apply the following patch to the kernel to get the break (Ctrl-C) working.
You need also to enable CONFIG_CONSOLE_POLL=y in your kernel configuration.

On the target machine:
   - add "console=ttyS0,115200n8 kgdboc=ttyS0,115200"
- break into kgdb with Alt-SysRq-g ( if you want to use Ctrl-C remotely you must apply the patch )

On the develop machine:
   - stty speed 115200 < /dev/ttyS0
   - gdb vmlinux
       > target remote /dev/ttyS0
       > you can now use gdb commands: r,c, ...

regards
Luca Ellero

>>>>>>>>>>>>>>>>>>>>>>>>>>>>

diff -Nur linux-2.6.31.4.original/drivers/char/tty_io.c linux-2.6.31.4/drivers/char/tty_io.c --- linux-2.6.31.4.original/drivers/char/tty_io.c 2009-10-12 22:15:40.000000000 +0200 +++ linux-2.6.31.4/drivers/char/tty_io.c 2009-11-27 11:00:28.484375000 +0100
@@ -311,14 +311,11 @@
    list_for_each_entry(p, &tty_drivers, tty_drivers) {
        if (strncmp(name, p->name, len) != 0)
            continue;
+
        stp = str;
-        if (*stp == ',')
-            stp++;
-        if (*stp == '\0')
-            stp = NULL;

        if (tty_line >= 0 && tty_line <= p->num && p->ops &&
-            p->ops->poll_init && !p->ops->poll_init(p, tty_line, stp)) {
+            p->ops->poll_init ) {
            res = tty_driver_kref_get(p);
            *line = tty_line;
            break;
diff -Nur linux-2.6.31.4.original/drivers/serial/8250.c linux-2.6.31.4/drivers/serial/8250.c --- linux-2.6.31.4.original/drivers/serial/8250.c 2009-10-12 22:15:40.000000000 +0200 +++ linux-2.6.31.4/drivers/serial/8250.c 2009-11-27 14:11:48.343750000 +0100
@@ -1441,6 +1441,12 @@
            else if (lsr & UART_LSR_FE)
                flag = TTY_FRAME;
        }
+ +#ifdef CONFIG_CONSOLE_POLL
+            if (up->port.poll_rx_cb && up->port.poll_rx_cb(ch))
+                goto ignore_char;
+#endif
+
        if (uart_handle_sysrq_char(&up->port, ch))
            goto ignore_char;

diff -Nur linux-2.6.31.4.original/drivers/serial/kgdboc.c linux-2.6.31.4/drivers/serial/kgdboc.c --- linux-2.6.31.4.original/drivers/serial/kgdboc.c 2009-10-12 22:15:40.000000000 +0200 +++ linux-2.6.31.4/drivers/serial/kgdboc.c 2009-11-27 11:43:42.125000000 +0100
@@ -43,6 +43,34 @@
    return 0;
}

+static int buffered_char = -1;
+static u8 break_char;
+static int no_polled_breaks;
+static int schedule_breakpoints;
+
+/* Return 1 if a the next layer up should discard the character,
+ * else return 0
+ */
+static int kgdboc_rx_callback(u8 c)
+{
+       if (likely(atomic_read(&kgdb_active) == -1)) {
+               if (no_polled_breaks)
+                       return 0;
+               if (c != break_char)
+                       buffered_char = c;
+               if (c == break_char ||
+                   (c == '$' && !kgdb_connected && break_char == 0x03)) {
+                       if (schedule_breakpoints)
+                               kgdb_schedule_breakpoint();
+                       else
+                               kgdb_breakpoint();
+                       return 1;
+               }
+               return 0;
+       }
+       return 1;
+}
+
__setup("kgdboc=", kgdboc_option_setup);

static int configure_kgdboc(void)
@@ -50,12 +78,18 @@
    struct tty_driver *p;
    int tty_line = 0;
    int err;
+       char *str;

    err = kgdboc_option_setup(config);
    if (err || !strlen(config) || isspace(config[0]))
        goto noconfig;

    err = -ENODEV;
+       /* If a driver was previously configured remove it now */
+       if (kgdb_tty_driver)
+ kgdb_tty_driver->ops->poll_init(kgdb_tty_driver, kgdb_tty_line,
+                                               NULL, (void *)-1);
+       kgdb_tty_driver = NULL;

    p = tty_find_polling_driver(config, &tty_line);
    if (!p)
@@ -63,6 +97,26 @@

    kgdb_tty_driver = p;
    kgdb_tty_line = tty_line;
+       /* Set defaults and parse optional configuration information */
+       no_polled_breaks = 0;
+       schedule_breakpoints = 1;
+       break_char = 0x03;
+       if (strstr(config, ",n"))
+               no_polled_breaks = 1;
+       if (strstr(config, ",B"))
+               schedule_breakpoints = 0;
+       str = strstr(config, ",c");
+       if (str)
+               break_char = simple_strtoul(str+2, &str, 10);
+       str = strrchr(config, ','); /* pointer to baud for init callback */
+       if (str) {
+               str++;
+               if (!(*str >= '0' && *str <= '9'))
+                       str = NULL;
+       }
+       /* Initialize the HW level driver for polling */
+       if (p->ops->poll_init(p, tty_line, str, kgdboc_rx_callback))
+               goto noconfig;

    err = kgdb_register_io_module(&kgdboc_io_ops);
    if (err)
@@ -73,6 +127,10 @@
    return 0;

noconfig:
+       if (kgdb_tty_driver)
+ kgdb_tty_driver->ops->poll_init(kgdb_tty_driver, kgdb_tty_line,
+                                               NULL, (void *)-1);
+       kgdb_tty_driver = NULL;
    config[0] = 0;
    configured = 0;

@@ -96,8 +154,11 @@

static int kgdboc_get_char(void)
{
+       if (buffered_char >= 0)
+               return xchg(&buffered_char, -1);
+
    return kgdb_tty_driver->ops->poll_get_char(kgdb_tty_driver,
-                        kgdb_tty_line);
+                                                  kgdb_tty_line);
}

static void kgdboc_put_char(u8 chr)
@@ -165,6 +226,13 @@
module_init(init_kgdboc);
module_exit(cleanup_kgdboc);
module_param_call(kgdboc, param_set_kgdboc_var, param_get_string, &kps, 0644);
-MODULE_PARM_DESC(kgdboc, "<serial_device>[,baud]");
+/* The optional paramters to the config string are:
+ * ,n == no monitoring the port for a break char
+ * ,B == monitor the port for a break char and issue a breakpoint in line
+ * ,c### == Use an alternate break character 1-255 instead of ^C (3)
+ * The baud parameter must always be last, if used
+ * ,baud == A baud rate parameter IE: 115200n81
+ */
+MODULE_PARM_DESC(kgdboc, "<serial_device>[,n][,B][,c###][,baud]");
MODULE_DESCRIPTION("KGDB Console TTY Driver");
MODULE_LICENSE("GPL");
diff -Nur linux-2.6.31.4.original/drivers/serial/serial_core.c linux-2.6.31.4/drivers/serial/serial_core.c --- linux-2.6.31.4.original/drivers/serial/serial_core.c 2009-10-12 22:15:40.000000000 +0200 +++ linux-2.6.31.4/drivers/serial/serial_core.c 2009-11-27 11:15:21.921875000 +0100
@@ -2222,7 +2222,21 @@

#ifdef CONFIG_CONSOLE_POLL

-static int uart_poll_init(struct tty_driver *driver, int line, char *options)
+/**
+ *     uart_poll_init - setup the console polling device
+ *     @driver: pointer to high level tty driver
+ *     @line: tty line number
+ *     @options: baud string for uart initialization
+ *     @rx_callback: call back for character processing
+ *
+ *      uart_poll_init activates the low level initialization of the
+ *      uart device for use with polling access to the uart while the
+ *      interrupts are off, which is primarily used for the debugger.
+ *      If rx_callback is set to -1, the specified tty driver and line
+ *      will have the call back function set to NULL uart_poll_init
+ *      will return immediately.
+ */
+static int uart_poll_init(struct tty_driver *driver, int line, char *options, void *rx_callback)
{
    struct uart_driver *drv = driver->driver_state;
    struct uart_state *state = drv->state + line;
@@ -2236,9 +2250,16 @@
        return -1;

    port = state->port;
+        if (rx_callback + 1 == 0) {
+                port->poll_rx_cb = NULL;
+                return 0;
+        }
+
    if (!(port->ops->poll_get_char && port->ops->poll_put_char))
        return -1;

+        port->poll_rx_cb = rx_callback;
+
    if (options) {
        uart_parse_options(options, &baud, &parity, &bits, &flow);
        return uart_set_options(port, NULL, baud, parity, bits, flow);
diff -Nur linux-2.6.31.4.original/include/linux/kgdb.h linux-2.6.31.4/include/linux/kgdb.h --- linux-2.6.31.4.original/include/linux/kgdb.h 2009-10-12 22:15:40.000000000 +0200 +++ linux-2.6.31.4/include/linux/kgdb.h 2009-11-27 14:01:29.531250000 +0100
@@ -279,5 +279,6 @@

extern int            kgdb_single_step;
extern atomic_t            kgdb_active;
+extern void kgdb_schedule_breakpoint(void);

#endif /* _KGDB_H_ */
diff -Nur linux-2.6.31.4.original/include/linux/serial_core.h linux-2.6.31.4/include/linux/serial_core.h --- linux-2.6.31.4.original/include/linux/serial_core.h 2009-10-12 22:15:40.000000000 +0200 +++ linux-2.6.31.4/include/linux/serial_core.h 2009-11-27 11:18:09.796875000 +0100
@@ -332,6 +332,9 @@
    unsigned char        suspended;
    unsigned char        unused[2];
void *private_data; /* generic platform data pointer */
+#ifdef CONFIG_CONSOLE_POLL
+       int             (*poll_rx_cb)(u8);
+#endif
};

/*
diff -Nur linux-2.6.31.4.original/include/linux/tty_driver.h linux-2.6.31.4/include/linux/tty_driver.h --- linux-2.6.31.4.original/include/linux/tty_driver.h 2009-10-12 22:15:40.000000000 +0200 +++ linux-2.6.31.4/include/linux/tty_driver.h 2009-11-27 12:06:25.250000000 +0100
@@ -260,7 +260,7 @@
    int (*resize)(struct tty_struct *tty, struct winsize *ws);
    int (*set_termiox)(struct tty_struct *tty, struct termiox *tnew);
#ifdef CONFIG_CONSOLE_POLL
-    int (*poll_init)(struct tty_driver *driver, int line, char *options);
+ int (*poll_init)(struct tty_driver *driver, int line, char *options, void *rx_callback);
    int (*poll_get_char)(struct tty_driver *driver, int line);
    void (*poll_put_char)(struct tty_driver *driver, int line, char ch);
#endif
diff -Nur linux-2.6.31.4.original/kernel/kgdb.c linux-2.6.31.4/kernel/kgdb.c
--- linux-2.6.31.4.original/kernel/kgdb.c 2009-10-12 22:15:40.000000000 +0200
+++ linux-2.6.31.4/kernel/kgdb.c    2009-11-27 14:04:13.765625000 +0100
@@ -116,6 +116,7 @@
 * The CPU# of the active CPU, or -1 if none:
 */
atomic_t            kgdb_active = ATOMIC_INIT(-1);
+EXPORT_SYMBOL_GPL(kgdb_active);

/*
 * We use NR_CPUs not PERCPU, in case kgdb is used to debug early
@@ -123,6 +124,7 @@
 */
static atomic_t            passive_cpu_wait[NR_CPUS];
static atomic_t            cpu_in_kgdb[NR_CPUS];
+static atomic_t            kgdb_break_tasklet_var;
atomic_t            kgdb_setting_breakpoint;

struct task_struct        *kgdb_usethread;
@@ -1623,6 +1625,33 @@
    }
}

+/*
+ * 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);
+
+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);
+}
+EXPORT_SYMBOL_GPL(kgdb_schedule_breakpoint);
+
+
+
static void kgdb_initial_breakpoint(void)
{
    kgdb_break_asap = 0;







--
To unsubscribe from this list: send an email with
"unsubscribe kernelnewbies" to ecartis@xxxxxxxxxxxx
Please read the FAQ at http://kernelnewbies.org/FAQ



[Index of Archives]     [Newbies FAQ]     [Linux Kernel Mentors]     [Linux Kernel Development]     [IETF Annouce]     [Git]     [Networking]     [Security]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux SCSI]     [Linux ACPI]
  Powered by Linux