aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Thompson <daniel.thompson@linaro.org>2015-01-19 15:45:31 +0000
committerDaniel Thompson <daniel.thompson@linaro.org>2015-01-28 15:01:53 +0000
commitcafa5e5a590ea41345a0bad8ae91b2bf73b46db1 (patch)
tree1516b7f0f71cd13137dd67f3fc88635f194e2124
parent96da3dea0359a063e2d374c6d551661cb88f8878 (diff)
downloadlinux-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.c54
-rw-r--r--drivers/tty/serial/kgdboc.c20
-rw-r--r--include/linux/kgdb.h5
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);