ARM: fiq_debugger: add support for kgdb
authorColin Cross <ccross@android.com>
Thu, 15 Mar 2012 02:23:29 +0000 (19:23 -0700)
committerJohn Stultz <john.stultz@linaro.org>
Tue, 16 Feb 2016 21:52:21 +0000 (13:52 -0800)
Adds polling tty ops to the fiq debugger console tty, which allows
kgdb to run against an fiq debugger console.

Add a check in do_sysrq to prevent enabling kgdb from the fiq
debugger unless a flag (writable only by root) has been set. This
should make it safe to enable KGDB on a production device.

Also add a shortcut to enable the console and kgdb together, to
allow kgdb to be enabled when the shell on the console is not
responding.

Change-Id: Ifc65239ca96c9887431a6a36b9b44a539002f544
Signed-off-by: Colin Cross <ccross@android.com>
arch/arm/common/fiq_debugger.c

index 909ef56596e8baaf9db280aaf86bcf554bc02de2..1f64d7dc83b4fb4ae604d69ba2216b877de565dd 100644 (file)
@@ -106,9 +106,12 @@ static bool initial_debug_enable;
 static bool initial_console_enable;
 #endif
 
+static bool fiq_kgdb_enable;
+
 module_param_named(no_sleep, initial_no_sleep, bool, 0644);
 module_param_named(debug_enable, initial_debug_enable, bool, 0644);
 module_param_named(console_enable, initial_console_enable, bool, 0644);
+module_param_named(kgdb_enable, fiq_kgdb_enable, bool, 0644);
 
 #ifdef CONFIG_FIQ_DEBUGGER_WAKEUP_IRQ_ALWAYS_ON
 static inline void enable_wakeup_irq(struct fiq_debugger_state *state) {}
@@ -526,11 +529,29 @@ static void end_syslog_dump(struct fiq_debugger_state *state)
 
 static void do_sysrq(struct fiq_debugger_state *state, char rq)
 {
+       if ((rq == 'g' || rq == 'G') && !fiq_kgdb_enable) {
+               debug_printf(state, "sysrq-g blocked\n");
+               return;
+       }
        begin_syslog_dump(state);
        handle_sysrq(rq);
        end_syslog_dump(state);
 }
 
+#ifdef CONFIG_KGDB
+static void do_kgdb(struct fiq_debugger_state *state)
+{
+       if (!fiq_kgdb_enable) {
+               debug_printf(state, "kgdb through fiq debugger not enabled\n");
+               return;
+       }
+
+       debug_printf(state, "enabling console and triggering kgdb\n");
+       state->console_enable = true;
+       handle_sysrq('g');
+}
+#endif
+
 /* This function CANNOT be called in FIQ context */
 static void debug_irq_exec(struct fiq_debugger_state *state, char *cmd)
 {
@@ -540,6 +561,10 @@ static void debug_irq_exec(struct fiq_debugger_state *state, char *cmd)
                do_sysrq(state, 'h');
        if (!strncmp(cmd, "sysrq ", 6))
                do_sysrq(state, cmd[6]);
+#ifdef CONFIG_KGDB
+       if (!strcmp(cmd, "kgdb"))
+               do_kgdb(state);
+#endif
 }
 
 static void debug_help(struct fiq_debugger_state *state)
@@ -561,6 +586,9 @@ static void debug_help(struct fiq_debugger_state *state)
        debug_printf(state,     " ps            Process list\n"
                                " sysrq         sysrq options\n"
                                " sysrq <param> Execute sysrq with <param>\n");
+#ifdef CONFIG_KGDB
+       debug_printf(state,     " kgdb          Enter kernel debugger\n");
+#endif
 }
 
 static void take_affinity(void *info)
@@ -724,7 +752,8 @@ static void debug_handle_irq_context(struct fiq_debugger_state *state)
 #endif
        if (state->debug_busy) {
                debug_irq_exec(state, state->debug_cmd);
-               debug_prompt(state);
+               if (!state->console_enable)
+                       debug_prompt(state);
                state->debug_busy = 0;
        }
 }
@@ -957,11 +986,53 @@ int  fiq_tty_write_room(struct tty_struct *tty)
        return 1024;
 }
 
+#ifdef CONFIG_CONSOLE_POLL
+static int fiq_tty_poll_init(struct tty_driver *driver, int line, char *options)
+{
+       return 0;
+}
+
+static int fiq_tty_poll_get_char(struct tty_driver *driver, int line)
+{
+       struct fiq_debugger_state *state = driver->ttys[line]->driver_data;
+       int c = NO_POLL_CHAR;
+
+       debug_uart_enable(state);
+       if (debug_have_fiq(state)) {
+               int count = fiq_debugger_ringbuf_level(state->tty_rbuf);
+               if (count > 0) {
+                       c = fiq_debugger_ringbuf_peek(state->tty_rbuf, 0);
+                       fiq_debugger_ringbuf_consume(state->tty_rbuf, 1);
+               }
+       } else {
+               c = debug_getc(state);
+               if (c == FIQ_DEBUGGER_NO_CHAR)
+                       c = NO_POLL_CHAR;
+       }
+       debug_uart_disable(state);
+
+       return c;
+}
+
+static void fiq_tty_poll_put_char(struct tty_driver *driver, int line, char ch)
+{
+       struct fiq_debugger_state *state = driver->ttys[line]->driver_data;
+       debug_uart_enable(state);
+       debug_putc(state, ch);
+       debug_uart_disable(state);
+}
+#endif
+
 static const struct tty_operations fiq_tty_driver_ops = {
        .write = fiq_tty_write,
        .write_room = fiq_tty_write_room,
        .open = fiq_tty_open,
        .close = fiq_tty_close,
+#ifdef CONFIG_CONSOLE_POLL
+       .poll_init = fiq_tty_poll_init,
+       .poll_get_char = fiq_tty_poll_get_char,
+       .poll_put_char = fiq_tty_poll_put_char,
+#endif
 };
 
 static int fiq_debugger_tty_init(struct fiq_debugger_state *state)