kernel_debugger_core: add interrupt-context debugger core

This provides kernel_debugger() which can be called from an interrupt
context low level debugger wedge to execute commands that inspect
kernel state.  It doesn't do much on its own.

Signed-off-by: Brian Swetland <swetland@google.com>

kernel_debugger_core: Add sysrq command.

sysrq <c> will run the sysrq command <c> and dump what
was added to the kernel log while the command ran.

Signed-off-by: Brian Swetland <swetland@google.com>
Signed-off-by: Arve Hjønnevåg <arve@android.com>
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 92dca5a..a1a4063 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -233,6 +233,13 @@
 	  driver (SCSI/ATA) which supports enclosures
 	  or a SCSI enclosure device (SES) to use these services.
 
+config KERNEL_DEBUGGER_CORE
+	bool "Kernel Debugger Core"
+	default n
+	---help---
+	  Generic kernel debugging command processor used by low level
+	  (interrupt context) platform-specific debuggers.
+
 config SGI_XP
 	tristate "Support communication between SGI SSIs"
 	depends on NET
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 7ae10db..9fcf389 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -22,6 +22,7 @@
 obj-$(CONFIG_ANDROID_PMEM)	+= pmem.o
 obj-$(CONFIG_SGI_IOC4)		+= ioc4.o
 obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o
+obj-$(CONFIG_KERNEL_DEBUGGER_CORE)	+= kernel_debugger.o
 obj-$(CONFIG_KGDB_TESTS)	+= kgdbts.o
 obj-$(CONFIG_SGI_XP)		+= sgi-xp/
 obj-$(CONFIG_SGI_GRU)		+= sgi-gru/
diff --git a/drivers/misc/kernel_debugger.c b/drivers/misc/kernel_debugger.c
new file mode 100644
index 0000000..44c19fd
--- /dev/null
+++ b/drivers/misc/kernel_debugger.c
@@ -0,0 +1,79 @@
+/* drivers/android/kernel_debugger.c
+ *
+ * Guts of the kernel debugger.
+ * Needs something to actually push commands to it.
+ *
+ * Copyright (C) 2007-2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/ctype.h>
+#include <linux/device.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/sysrq.h>
+#include <linux/kernel_debugger.h>
+
+#define dprintf(fmt...) (ctxt->printf(ctxt->cookie, fmt))
+
+static void do_ps(struct kdbg_ctxt *ctxt)
+{
+	struct task_struct *g, *p;
+	unsigned state;
+	static const char stat_nam[] = "RSDTtZX";
+
+	dprintf("pid   ppid  prio task            pc\n");
+	read_lock(&tasklist_lock);
+	do_each_thread(g, p) {
+		state = p->state ? __ffs(p->state) + 1 : 0;
+		dprintf("%5d %5d %4d ", p->pid, p->parent->pid, p->prio);
+		dprintf("%-13.13s %c", p->comm,
+			state >= sizeof(stat_nam) ? '?' : stat_nam[state]);
+		if (state == TASK_RUNNING)
+			dprintf(" running\n");
+		else
+			dprintf(" %08lx\n", thread_saved_pc(p));
+	} while_each_thread(g, p);
+	read_unlock(&tasklist_lock);
+}
+
+int log_buf_copy(char *dest, int idx, int len);
+extern int do_syslog(int type, char __user *bug, int count);
+static void do_sysrq(struct kdbg_ctxt *ctxt, char rq)
+{
+	char buf[128];
+	int ret;
+	int idx = 0;
+	do_syslog(5 /* clear */, NULL, 0);
+	handle_sysrq(rq, NULL);
+	while (1) {
+		ret = log_buf_copy(buf, idx, sizeof(buf) - 1);
+		if (ret <= 0)
+			break;
+		buf[ret] = 0;
+		dprintf("%s", buf);
+		idx += ret;
+	}
+}
+
+int kernel_debugger(struct kdbg_ctxt *ctxt, char *cmd)
+{
+	if (!strcmp(cmd, "ps"))
+		do_ps(ctxt);
+	if (!strcmp(cmd, "sysrq"))
+		do_sysrq(ctxt, 'h');
+	if (!strncmp(cmd, "sysrq ", 6))
+		do_sysrq(ctxt, cmd[6]);
+
+	return 0;
+}
+
diff --git a/include/linux/kernel_debugger.h b/include/linux/kernel_debugger.h
new file mode 100644
index 0000000..b4dbfe9
--- /dev/null
+++ b/include/linux/kernel_debugger.h
@@ -0,0 +1,41 @@
+/*
+ * include/linux/kernel_debugger.h
+ *
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _LINUX_KERNEL_DEBUGGER_H_
+#define _LINUX_KERNEL_DEBUGGER_H_
+
+struct kdbg_ctxt {
+	int (*printf)(void *cookie, const char *fmt, ...);
+	void *cookie;
+};
+
+/* kernel_debugger() is called from IRQ context and should
+ * use the kdbg_ctxt.printf to write output (do NOT call
+ * printk, do operations not safe from IRQ context, etc).
+ *
+ * kdbg_ctxt.printf will return -1 if there is not enough
+ * buffer space or if you are being aborted.  In this case
+ * you must return as soon as possible.
+ *
+ * Return non-zero if more data is available -- if buffer
+ * space ran and you had to stop, but could print more,
+ * for example.
+ *
+ * Additional calls where cmd is "more" will be made if
+ * the additional data is desired.
+ */
+int kernel_debugger(struct kdbg_ctxt *ctxt, char *cmd);
+
+#endif