diff options
author | Daniel Thompson <daniel.thompson@linaro.org> | 2015-01-19 15:45:31 +0000 |
---|---|---|
committer | Daniel Thompson <daniel.thompson@linaro.org> | 2015-01-28 15:01:53 +0000 |
commit | cafa5e5a590ea41345a0bad8ae91b2bf73b46db1 (patch) | |
tree | 1516b7f0f71cd13137dd67f3fc88635f194e2124 | |
parent | 96da3dea0359a063e2d374c6d551661cb88f8878 (diff) | |
download | linux-cafa5e5a590ea41345a0bad8ae91b2bf73b46db1.tar.gz |
kgdb: Infrastructure to request NMI handling
This addes code to the kgdb_nmi code to install an explicit NMI handler
using the polling infrastructure. Whilst doing this we simplify even
further the relationships between kgdb core, kgdboc and kgdb_nmi.
Signed-off-by: Daniel Thompson <daniel.thompson@linaro.org>
-rw-r--r-- | drivers/tty/serial/kgdb_nmi.c | 54 | ||||
-rw-r--r-- | drivers/tty/serial/kgdboc.c | 20 | ||||
-rw-r--r-- | include/linux/kgdb.h | 5 |
3 files changed, 55 insertions, 24 deletions
diff --git a/drivers/tty/serial/kgdb_nmi.c b/drivers/tty/serial/kgdb_nmi.c index 129dc5be6028..1a6a4fb5e63c 100644 --- a/drivers/tty/serial/kgdb_nmi.c +++ b/drivers/tty/serial/kgdb_nmi.c @@ -29,6 +29,7 @@ #include <linux/kfifo.h> #include <linux/kgdb.h> #include <linux/kdb.h> +#include <linux/irq.h> static int kgdb_nmi_knock = 1; module_param_named(knock, kgdb_nmi_knock, int, 0600); @@ -44,15 +45,12 @@ MODULE_PARM_DESC(magic, "magic sequence to enter NMI debugger (default $3#33)"); static atomic_t kgdb_nmi_num_readers = ATOMIC_INIT(0); +static int kgdb_nmi_set_enable(int enabled); + static int kgdb_nmi_console_setup(struct console *co, char *options) { - arch_kgdb_ops.enable_nmi(1); + (void) kgdb_nmi_set_enable(1); - /* The NMI console uses the dbg_io_ops to issue console messages. To - * avoid duplicate messages during kdb sessions we must inform kdb's - * I/O utilities that messages sent to the console will automatically - * be displayed on the dbg_io. - */ dbg_io_ops->is_console = true; return 0; @@ -157,7 +155,7 @@ static int kgdb_nmi_poll_one_knock(void) } /** - * kgdb_nmi_poll_knock - Check if it is time to enter the debugger + * kgdb_handle_nmi_poll_knock - Check if it is time to enter the debugger * * "Serial ports are often noisy, especially when muxed over another port (we * often use serial over the headset connector). Noise on the async command @@ -170,23 +168,42 @@ static int kgdb_nmi_poll_one_knock(void) * of knocking, i.e. just pressing the return key is enough to enter the * debugger. And if knocking is disabled, the function always returns 1. */ -bool kgdb_nmi_poll_knock(void) +static irqreturn_t kgdb_handle_nmi_poll_knock(int irq, void *unused) { + int ret = IRQ_NONE; + if (kgdb_nmi_knock < 0) return 1; while (1) { - int ret; - - ret = kgdb_nmi_poll_one_knock(); - if (ret == NO_POLL_CHAR) - return 0; - else if (ret == 1) + switch (kgdb_nmi_poll_one_knock()) { + case NO_POLL_CHAR: + return ret; + case 1: + kgdb_handle_exception(1, 0, 0, get_irq_regs()); + return IRQ_HANDLED; + default: + ret = IRQ_HANDLED; break; + } } - return 1; + + return IRQ_HANDLED; } +static int kgdb_nmi_set_enable(int enabled) +{ + int res = 0; + + if (enabled) + res = dbg_io_ops->request_nmi(kgdb_handle_nmi_poll_knock); + else + dbg_io_ops->free_nmi(); + + return res; +} + + /* * The tasklet is cheap, it does not cause wakeups when reschedules itself, * instead it waits for the next tick. @@ -330,9 +347,6 @@ int kgdb_register_nmi_console(void) { int ret; - if (!arch_kgdb_ops.enable_nmi) - return 0; - kgdb_nmi_tty_driver = alloc_tty_driver(1); if (!kgdb_nmi_tty_driver) { pr_err("%s: cannot allocate tty\n", __func__); @@ -368,10 +382,6 @@ int kgdb_unregister_nmi_console(void) { int ret; - if (!arch_kgdb_ops.enable_nmi) - return 0; - arch_kgdb_ops.enable_nmi(0); - ret = unregister_console(&kgdb_nmi_console); if (ret) return ret; diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c index a260cde743e2..4cf5e0a8ede8 100644 --- a/drivers/tty/serial/kgdboc.c +++ b/drivers/tty/serial/kgdboc.c @@ -245,6 +245,24 @@ static void kgdboc_put_char(u8 chr) kgdb_tty_line, chr); } +static int kgdboc_request_nmi(irq_handler_t handler) +{ + if (!kgdb_tty_driver || !kgdb_tty_driver->ops->poll_request_nmi || + !kgdb_tty_driver->ops->poll_free_nmi) + return -EINVAL; + + return kgdb_tty_driver->ops->poll_request_nmi(kgdb_tty_driver, + kgdb_tty_line, handler); +} + +static void kgdboc_free_nmi(void) +{ + if (!kgdb_tty_driver) + return; + return kgdb_tty_driver->ops->poll_free_nmi(kgdb_tty_driver, + kgdb_tty_line); +} + static int param_set_kgdboc_var(const char *kmessage, struct kernel_param *kp) { int len = strlen(kmessage); @@ -308,6 +326,8 @@ static struct kgdb_io kgdboc_io_ops = { .name = "kgdboc", .read_char = kgdboc_get_char, .write_char = kgdboc_put_char, + .request_nmi = kgdboc_request_nmi, + .free_nmi = kgdboc_free_nmi, .pre_exception = kgdboc_pre_exp_handler, .post_exception = kgdboc_post_exp_handler, }; diff --git a/include/linux/kgdb.h b/include/linux/kgdb.h index e465bb15912d..0f01e28201a4 100644 --- a/include/linux/kgdb.h +++ b/include/linux/kgdb.h @@ -16,6 +16,7 @@ #include <linux/linkage.h> #include <linux/init.h> #include <linux/atomic.h> +#include <linux/interrupt.h> #ifdef CONFIG_HAVE_ARCH_KGDB #include <asm/kgdb.h> #endif @@ -274,6 +275,8 @@ struct kgdb_io { const char *name; int (*read_char) (void); void (*write_char) (u8); + int (*request_nmi) (irq_handler_t); + void (*free_nmi) (void); void (*flush) (void); int (*init) (void); void (*pre_exception) (void); @@ -288,11 +291,9 @@ extern unsigned long kgdb_arch_pc(int exception, struct pt_regs *regs); #ifdef CONFIG_SERIAL_KGDB_NMI extern int kgdb_register_nmi_console(void); extern int kgdb_unregister_nmi_console(void); -extern bool kgdb_nmi_poll_knock(void); #else static inline int kgdb_register_nmi_console(void) { return 0; } static inline int kgdb_unregister_nmi_console(void) { return 0; } -static inline bool kgdb_nmi_poll_knock(void) { return 1; } #endif extern int kgdb_register_io_module(struct kgdb_io *local_kgdb_io_ops); |