MN10300: Create generic kernel debugger hooks

Create generic kernel debugger hooks in the MN10300 arch and make gdbstub use
them.  This is a preparation for KGDB support.

Signed-off-by: David Howells <dhowells@redhat.com>
diff --git a/arch/mn10300/Kconfig b/arch/mn10300/Kconfig
index 25e6a0b..6e9cac9 100644
--- a/arch/mn10300/Kconfig
+++ b/arch/mn10300/Kconfig
@@ -401,8 +401,8 @@
 comment "____Non-maskable interrupt levels____"
 comment "The following must be set to a higher priority than local_irq_disable() and on-chip serial"
 
-config GDBSTUB_IRQ_LEVEL
-	int "GDBSTUB interrupt priority"
+config DEBUGGER_IRQ_LEVEL
+	int "DEBUGGER interrupt priority"
 	depends on KERNEL_DEBUGGER
 	range 0 1 if LINUX_CLI_LEVEL = 2
 	range 0 2 if LINUX_CLI_LEVEL = 3
@@ -437,7 +437,7 @@
 	  EPSW.IM from 7.  Any interrupt is permitted for which the level is
 	  lower than EPSW.IM.
 
-	  Certain interrupts, such as GDBSTUB and virtual MN10300 on-chip
+	  Certain interrupts, such as DEBUGGER and virtual MN10300 on-chip
 	  serial DMA interrupts are allowed to interrupt normal disabled
 	  sections.
 
diff --git a/arch/mn10300/include/asm/debugger.h b/arch/mn10300/include/asm/debugger.h
index 4517f83..e1d3b08 100644
--- a/arch/mn10300/include/asm/debugger.h
+++ b/arch/mn10300/include/asm/debugger.h
@@ -14,6 +14,9 @@
 
 #if defined(CONFIG_KERNEL_DEBUGGER)
 
+extern int debugger_intercept(enum exception_code, int, int, struct pt_regs *);
+extern int at_debugger_breakpoint(struct pt_regs *);
+
 #ifndef CONFIG_MN10300_DEBUGGER_CACHE_NO_FLUSH
 extern void debugger_local_cache_flushinv(void);
 extern void debugger_local_cache_flushinv_one(u8 *);
@@ -24,5 +27,17 @@
 
 #else /* CONFIG_KERNEL_DEBUGGER */
 
+static inline int debugger_intercept(enum exception_code excep,
+				     int signo, int si_code,
+				     struct pt_regs *regs)
+{
+	return 0;
+}
+
+static inline int at_debugger_breakpoint(struct pt_regs *regs)
+{
+	return 0;
+}
+
 #endif /* CONFIG_KERNEL_DEBUGGER */
 #endif /* _ASM_DEBUGGER_H */
diff --git a/arch/mn10300/include/asm/fpu.h b/arch/mn10300/include/asm/fpu.h
index b7625de..738ff72 100644
--- a/arch/mn10300/include/asm/fpu.h
+++ b/arch/mn10300/include/asm/fpu.h
@@ -55,7 +55,6 @@
 
 extern asmlinkage void fpu_kill_state(struct task_struct *);
 extern asmlinkage void fpu_exception(struct pt_regs *, enum exception_code);
-extern asmlinkage void fpu_invalid_op(struct pt_regs *, enum exception_code);
 extern asmlinkage void fpu_init_state(void);
 extern asmlinkage void fpu_save(struct fpu_state_struct *);
 extern int fpu_setup_sigcontext(struct fpucontext *buf);
@@ -113,7 +112,6 @@
 
 extern asmlinkage
 void unexpected_fpu_exception(struct pt_regs *, enum exception_code);
-#define fpu_invalid_op unexpected_fpu_exception
 #define fpu_exception unexpected_fpu_exception
 
 struct task_struct;
diff --git a/arch/mn10300/include/asm/irqflags.h b/arch/mn10300/include/asm/irqflags.h
index 7a7ae12..678f68d 100644
--- a/arch/mn10300/include/asm/irqflags.h
+++ b/arch/mn10300/include/asm/irqflags.h
@@ -20,7 +20,7 @@
 /*
  * interrupt control
  * - "disabled": run in IM1/2
- *   - level 0 - GDB stub
+ *   - level 0 - kernel debugger
  *   - level 1 - virtual serial DMA (if present)
  *   - level 5 - normal interrupt priority
  *   - level 6 - timer interrupt
diff --git a/arch/mn10300/include/asm/smp.h b/arch/mn10300/include/asm/smp.h
index a3930e4..e3d13a8 100644
--- a/arch/mn10300/include/asm/smp.h
+++ b/arch/mn10300/include/asm/smp.h
@@ -34,7 +34,7 @@
 #define LOCAL_TIMER_IPI		193
 #define FLUSH_CACHE_IPI		194
 #define CALL_FUNCTION_NMI_IPI	195
-#define GDB_NMI_IPI		196
+#define DEBUGGER_NMI_IPI	196
 
 #define SMP_BOOT_IRQ		195
 
@@ -43,6 +43,7 @@
 #define LOCAL_TIMER_GxICR_LV	GxICR_LEVEL_4
 #define FLUSH_CACHE_GxICR_LV	GxICR_LEVEL_0
 #define SMP_BOOT_GxICR_LV	GxICR_LEVEL_0
+#define DEBUGGER_GxICR_LV	CONFIG_DEBUGGER_IRQ_LEVEL
 
 #define TIME_OUT_COUNT_BOOT_IPI	100
 #define DELAY_TIME_BOOT_IPI	75000
diff --git a/arch/mn10300/kernel/entry.S b/arch/mn10300/kernel/entry.S
index 8e79a04..fb93ad7 100644
--- a/arch/mn10300/kernel/entry.S
+++ b/arch/mn10300/kernel/entry.S
@@ -266,7 +266,11 @@
 
 ###############################################################################
 #
-# Miscellaneous exception entry points
+# NMI exception entry points
+#
+# This is used by ordinary interrupt channels that have the GxICR_NMI bit set
+# in addition to the main NMI and Watchdog channels.  SMP NMI IPIs use this
+# facility.
 #
 ###############################################################################
 ENTRY(nmi_handler)
@@ -281,7 +285,7 @@
 	and	NMIAGR_GN,d0
 	lsr	0x2,d0
 	cmp	CALL_FUNCTION_NMI_IPI,d0
-	bne	5f			# if not call function, jump
+	bne	nmi_not_smp_callfunc	# if not call function, jump
 
 	# function call nmi ipi
 	add	4,sp			# no need to store TBR
@@ -295,30 +299,38 @@
 	call	smp_nmi_call_function_interrupt[],0
 	RESTORE_ALL
 
-5:
-#ifdef CONFIG_GDBSTUB
-	cmp	GDB_NMI_IPI,d0
-	bne	3f			# if not gdb nmi ipi, jump
+nmi_not_smp_callfunc:
+#ifdef CONFIG_KERNEL_DEBUGGER
+	cmp	DEBUGGER_NMI_IPI,d0
+	bne	nmi_not_debugger	# if not kernel debugger NMI IPI, jump
 
-	# gdb nmi ipi
+	# kernel debugger NMI IPI
 	add	4,sp			# no need to store TBR
 	mov	GxICR_DETECT,d0		# clear NMI
-	movbu	d0,(GxICR(GDB_NMI_IPI))
-	movhu	(GxICR(GDB_NMI_IPI)),d0
+	movbu	d0,(GxICR(DEBUGGER_NMI_IPI))
+	movhu	(GxICR(DEBUGGER_NMI_IPI)),d0
 	and	~EPSW_NMID,epsw		# enable NMI
 
 	mov     (sp),d0
 	SAVE_ALL
-	call    gdbstub_nmi_wait[],0
+	mov	fp,d0			# arg 0: stacked register file
+	mov	a2,d1			# arg 1: exception number
+	call    debugger_nmi_interrupt[],0
 	RESTORE_ALL
-3:
-#endif /* CONFIG_GDBSTUB */
+
+nmi_not_debugger:
+#endif /* CONFIG_KERNEL_DEBUGGER */
 	mov     (sp),d0                 # restore TBR to d0
 	add     4,sp
 #endif /* CONFIG_SMP */
 
 	bra	__common_exception_nonmi
 
+###############################################################################
+#
+# General exception entry point
+#
+###############################################################################
 ENTRY(__common_exception)
 	add	-4,sp
 	mov	d0,(sp)
diff --git a/arch/mn10300/kernel/fpu.c b/arch/mn10300/kernel/fpu.c
index 5f9c3fa..bb5fa7d 100644
--- a/arch/mn10300/kernel/fpu.c
+++ b/arch/mn10300/kernel/fpu.c
@@ -70,24 +70,6 @@
 }
 
 /*
- * handle an FPU invalid_op exception
- * - Derived from DO_EINFO() macro in arch/mn10300/kernel/traps.c
- */
-asmlinkage void fpu_invalid_op(struct pt_regs *regs, enum exception_code code)
-{
-	siginfo_t info;
-
-	if (!user_mode(regs))
-		die_if_no_fixup("FPU invalid opcode", regs, code);
-
-	info.si_signo = SIGILL;
-	info.si_errno = 0;
-	info.si_code = ILL_COPROC;
-	info.si_addr = (void *) regs->pc;
-	force_sig_info(info.si_signo, &info, current);
-}
-
-/*
  * save the FPU state to a signal context
  */
 int fpu_setup_sigcontext(struct fpucontext *fpucontext)
diff --git a/arch/mn10300/kernel/gdb-io-ttysm.c b/arch/mn10300/kernel/gdb-io-ttysm.c
index abdeea1..c859cac 100644
--- a/arch/mn10300/kernel/gdb-io-ttysm.c
+++ b/arch/mn10300/kernel/gdb-io-ttysm.c
@@ -59,10 +59,10 @@
 
 	/* we want to get serial receive interrupts */
 	set_intr_level(gdbstub_port->rx_irq,
-		NUM2GxICR_LEVEL(CONFIG_GDBSTUB_IRQ_LEVEL));
+		NUM2GxICR_LEVEL(CONFIG_DEBUGGER_IRQ_LEVEL));
 	set_intr_level(gdbstub_port->tx_irq,
-		NUM2GxICR_LEVEL(CONFIG_GDBSTUB_IRQ_LEVEL));
-	set_intr_stub(NUM2EXCEP_IRQ_LEVEL(CONFIG_GDBSTUB_IRQ_LEVEL),
+		NUM2GxICR_LEVEL(CONFIG_DEBUGGER_IRQ_LEVEL));
+	set_intr_stub(NUM2EXCEP_IRQ_LEVEL(CONFIG_DEBUGGER_IRQ_LEVEL),
 		gdbstub_io_rx_handler);
 
 	*gdbstub_port->rx_icr |= GxICR_ENABLE;
@@ -88,7 +88,7 @@
 
 	/* permit level 0 IRQs only */
 	arch_local_change_intr_mask_level(
-		NUM2EPSW_IM(CONFIG_GDBSTUB_IRQ_LEVEL + 1));
+		NUM2EPSW_IM(CONFIG_DEBUGGER_IRQ_LEVEL + 1));
 }
 
 /*
diff --git a/arch/mn10300/kernel/gdb-stub.c b/arch/mn10300/kernel/gdb-stub.c
index 1e8f24f..538266b 100644
--- a/arch/mn10300/kernel/gdb-stub.c
+++ b/arch/mn10300/kernel/gdb-stub.c
@@ -1173,7 +1173,7 @@
 
 /*
  * This function does all command processing for interfacing to gdb
- * - returns 1 if the exception should be skipped, 0 otherwise.
+ * - returns 0 if the exception should be skipped, -ERROR otherwise.
  */
 static int gdbstub(struct pt_regs *regs, enum exception_code excep)
 {
@@ -1188,7 +1188,7 @@
 	int loop;
 
 	if (excep == EXCEP_FPU_DISABLED)
-		return 0;
+		return -ENOTSUPP;
 
 	gdbstub_flush_caches = 0;
 
@@ -1197,7 +1197,7 @@
 	asm volatile("mov mdr,%0" : "=d"(mdr));
 	local_save_flags(epsw);
 	arch_local_change_intr_mask_level(
-		NUM2EPSW_IM(CONFIG_GDBSTUB_IRQ_LEVEL + 1));
+		NUM2EPSW_IM(CONFIG_DEBUGGER_IRQ_LEVEL + 1));
 
 	gdbstub_store_fpu();
 
@@ -1675,14 +1675,23 @@
 	touch_softlockup_watchdog();
 
 	local_irq_restore(epsw);
-	return 1;
+	return 0;
+}
+
+/*
+ * Determine if we hit a debugger special breakpoint that needs skipping over
+ * automatically.
+ */
+int at_debugger_breakpoint(struct pt_regs *regs)
+{
+	return 0;
 }
 
 /*
  * handle event interception
  */
-asmlinkage int gdbstub_intercept(struct pt_regs *regs,
-				 enum exception_code excep)
+asmlinkage int debugger_intercept(enum exception_code excep,
+				  int signo, int si_code, struct pt_regs *regs)
 {
 	static u8 notfirst = 1;
 	int ret;
@@ -1696,7 +1705,7 @@
 		asm("mov mdr,%0" : "=d"(mdr));
 
 		gdbstub_entry(
-			"--> gdbstub_intercept(%p,%04x) [MDR=%lx PC=%lx]\n",
+			"--> debugger_intercept(%p,%04x) [MDR=%lx PC=%lx]\n",
 			regs, excep, mdr, regs->pc);
 
 		gdbstub_entry(
@@ -1730,7 +1739,7 @@
 
 	ret = gdbstub(regs, excep);
 
-	gdbstub_entry("<-- gdbstub_intercept()\n");
+	gdbstub_entry("<-- debugger_intercept()\n");
 	gdbstub_busy = 0;
 	return ret;
 }
diff --git a/arch/mn10300/kernel/internal.h b/arch/mn10300/kernel/internal.h
index ea94661..a5ac755 100644
--- a/arch/mn10300/kernel/internal.h
+++ b/arch/mn10300/kernel/internal.h
@@ -30,6 +30,13 @@
 #endif
 
 /*
+ * smp.c
+ */
+#ifdef CONFIG_SMP
+extern void smp_jump_to_debugger(void);
+#endif
+
+/*
  * time.c
  */
 extern irqreturn_t local_timer_interrupt(void);
diff --git a/arch/mn10300/kernel/irq.c b/arch/mn10300/kernel/irq.c
index f09fed5..5f7fc3e 100644
--- a/arch/mn10300/kernel/irq.c
+++ b/arch/mn10300/kernel/irq.c
@@ -153,7 +153,7 @@
 	case LOCAL_TIMER_IPI:
 	case FLUSH_CACHE_IPI:
 	case CALL_FUNCTION_NMI_IPI:
-	case GDB_NMI_IPI:
+	case DEBUGGER_NMI_IPI:
 #ifdef CONFIG_MN10300_TTYSM0
 	case SC0RXIRQ:
 	case SC0TXIRQ:
diff --git a/arch/mn10300/kernel/smp.c b/arch/mn10300/kernel/smp.c
index 1ebb79f..51c02f9 100644
--- a/arch/mn10300/kernel/smp.c
+++ b/arch/mn10300/kernel/smp.c
@@ -440,6 +440,22 @@
 }
 
 /**
+ * smp_jump_to_debugger - Make other CPUs enter the debugger by sending an IPI
+ *
+ * Send a non-maskable request to all other CPUs in the system, instructing
+ * them to jump into the debugger.  The caller is responsible for checking that
+ * the other CPUs responded to the instruction.
+ *
+ * The caller should make sure that this CPU's debugger IPI is disabled.
+ */
+void smp_jump_to_debugger(void)
+{
+	if (num_online_cpus() > 1)
+		/* Send a message to all other CPUs */
+		send_IPI_allbutself(DEBUGGER_NMI_IPI);
+}
+
+/**
  * stop_this_cpu - Callback to stop a CPU.
  * @unused: Callback context (ignored).
  */
@@ -603,7 +619,7 @@
 /**
  * smp_prepare_cpu_init - Initialise CPU in startup_secondary
  *
- * Set interrupt level 0-6 setting and init ICR of gdbstub.
+ * Set interrupt level 0-6 setting and init ICR of the kernel debugger.
  */
 void smp_prepare_cpu_init(void)
 {
@@ -622,15 +638,15 @@
 	for (loop = 0; loop < GxICR_NUM_IRQS; loop++)
 		GxICR(loop) = GxICR_LEVEL_6 | GxICR_DETECT;
 
-#ifdef CONFIG_GDBSTUB
-	/* initialise GDB-stub */
+#ifdef CONFIG_KERNEL_DEBUGGER
+	/* initialise the kernel debugger interrupt */
 	do {
 		unsigned long flags;
 		u16 tmp16;
 
 		flags = arch_local_cli_save();
-		GxICR(GDB_NMI_IPI) = GxICR_NMI | GxICR_ENABLE | GxICR_DETECT;
-		tmp16 = GxICR(GDB_NMI_IPI);
+		GxICR(DEBUGGER_NMI_IPI) = GxICR_NMI | GxICR_ENABLE | GxICR_DETECT;
+		tmp16 = GxICR(DEBUGGER_NMI_IPI);
 		arch_local_irq_restore(flags);
 	} while (0);
 #endif
diff --git a/arch/mn10300/kernel/traps.c b/arch/mn10300/kernel/traps.c
index b90c3f1..f03cb27 100644
--- a/arch/mn10300/kernel/traps.c
+++ b/arch/mn10300/kernel/traps.c
@@ -38,8 +38,9 @@
 #include <asm/busctl-regs.h>
 #include <unit/leds.h>
 #include <asm/fpu.h>
-#include <asm/gdb-stub.h>
 #include <asm/sections.h>
+#include <asm/debugger.h>
+#include "internal.h"
 
 #if (CONFIG_INTERRUPT_VECTOR_BASE & 0xffffff)
 #error "INTERRUPT_VECTOR_BASE not aligned to 16MiB boundary!"
@@ -49,74 +50,178 @@
 
 spinlock_t die_lock = __SPIN_LOCK_UNLOCKED(die_lock);
 
-ATOMIC_NOTIFIER_HEAD(mn10300_die_chain);
+struct exception_to_signal_map {
+	u8	signo;
+	u32	si_code;
+};
+
+static const struct exception_to_signal_map exception_to_signal_map[256] = {
+	/* MMU exceptions */
+	[EXCEP_ITLBMISS >> 3]	= { 0, 0 },
+	[EXCEP_DTLBMISS >> 3]	= { 0, 0 },
+	[EXCEP_IAERROR >> 3]	= { 0, 0 },
+	[EXCEP_DAERROR >> 3]	= { 0, 0 },
+
+	/* system exceptions */
+	[EXCEP_TRAP >> 3]	= { SIGTRAP,	TRAP_BRKPT },
+	[EXCEP_ISTEP >> 3]	= { SIGTRAP,	TRAP_TRACE },	/* Monitor */
+	[EXCEP_IBREAK >> 3]	= { SIGTRAP,	TRAP_HWBKPT },	/* Monitor */
+	[EXCEP_OBREAK >> 3]	= { SIGTRAP,	TRAP_HWBKPT },	/* Monitor */
+	[EXCEP_PRIVINS >> 3]	= { SIGILL,	ILL_PRVOPC },
+	[EXCEP_UNIMPINS >> 3]	= { SIGILL,	ILL_ILLOPC },
+	[EXCEP_UNIMPEXINS >> 3]	= { SIGILL,	ILL_ILLOPC },
+	[EXCEP_MEMERR >> 3]	= { SIGSEGV,	SEGV_ACCERR },
+	[EXCEP_MISALIGN >> 3]	= { SIGBUS,	BUS_ADRALN },
+	[EXCEP_BUSERROR >> 3]	= { SIGBUS,	BUS_ADRERR },
+	[EXCEP_ILLINSACC >> 3]	= { SIGSEGV,	SEGV_ACCERR },
+	[EXCEP_ILLDATACC >> 3]	= { SIGSEGV,	SEGV_ACCERR },
+	[EXCEP_IOINSACC >> 3]	= { SIGSEGV,	SEGV_ACCERR },
+	[EXCEP_PRIVINSACC >> 3]	= { SIGSEGV,	SEGV_ACCERR }, /* userspace */
+	[EXCEP_PRIVDATACC >> 3]	= { SIGSEGV,	SEGV_ACCERR }, /* userspace */
+	[EXCEP_DATINSACC >> 3]	= { SIGSEGV,	SEGV_ACCERR },
+	[EXCEP_DOUBLE_FAULT >> 3] = { SIGILL,	ILL_BADSTK },
+
+	/* FPU exceptions */
+	[EXCEP_FPU_DISABLED >> 3] = { SIGILL,	ILL_COPROC },
+	[EXCEP_FPU_UNIMPINS >> 3] = { SIGILL,	ILL_COPROC },
+	[EXCEP_FPU_OPERATION >> 3] = { SIGFPE,	FPE_INTDIV },
+
+	/* interrupts */
+	[EXCEP_WDT >> 3]	= { SIGALRM,	0 },
+	[EXCEP_NMI >> 3]	= { SIGQUIT,	0 },
+	[EXCEP_IRQ_LEVEL0 >> 3]	= { SIGINT,	0 },
+	[EXCEP_IRQ_LEVEL1 >> 3]	= { 0, 0 },
+	[EXCEP_IRQ_LEVEL2 >> 3]	= { 0, 0 },
+	[EXCEP_IRQ_LEVEL3 >> 3]	= { 0, 0 },
+	[EXCEP_IRQ_LEVEL4 >> 3]	= { 0, 0 },
+	[EXCEP_IRQ_LEVEL5 >> 3]	= { 0, 0 },
+	[EXCEP_IRQ_LEVEL6 >> 3]	= { 0, 0 },
+
+	/* system calls */
+	[EXCEP_SYSCALL0 >> 3]	= { 0, 0 },
+	[EXCEP_SYSCALL1 >> 3]	= { SIGILL,	ILL_ILLTRP },
+	[EXCEP_SYSCALL2 >> 3]	= { SIGILL,	ILL_ILLTRP },
+	[EXCEP_SYSCALL3 >> 3]	= { SIGILL,	ILL_ILLTRP },
+	[EXCEP_SYSCALL4 >> 3]	= { SIGILL,	ILL_ILLTRP },
+	[EXCEP_SYSCALL5 >> 3]	= { SIGILL,	ILL_ILLTRP },
+	[EXCEP_SYSCALL6 >> 3]	= { SIGILL,	ILL_ILLTRP },
+	[EXCEP_SYSCALL7 >> 3]	= { SIGILL,	ILL_ILLTRP },
+	[EXCEP_SYSCALL8 >> 3]	= { SIGILL,	ILL_ILLTRP },
+	[EXCEP_SYSCALL9 >> 3]	= { SIGILL,	ILL_ILLTRP },
+	[EXCEP_SYSCALL10 >> 3]	= { SIGILL,	ILL_ILLTRP },
+	[EXCEP_SYSCALL11 >> 3]	= { SIGILL,	ILL_ILLTRP },
+	[EXCEP_SYSCALL12 >> 3]	= { SIGILL,	ILL_ILLTRP },
+	[EXCEP_SYSCALL13 >> 3]	= { SIGILL,	ILL_ILLTRP },
+	[EXCEP_SYSCALL14 >> 3]	= { SIGILL,	ILL_ILLTRP },
+	[EXCEP_SYSCALL15 >> 3]	= { SIGABRT,	0 },
+};
 
 /*
- * These constants are for searching for possible module text
- * segments. MODULE_RANGE is a guess of how much space is likely
- * to be vmalloced.
+ * Handle kernel exceptions.
+ *
+ * See if there's a fixup handler we can force a jump to when an exception
+ * happens due to something kernel code did
  */
-#define MODULE_RANGE (8 * 1024 * 1024)
+int die_if_no_fixup(const char *str, struct pt_regs *regs,
+		    enum exception_code code)
+{
+	u8 opcode;
+	int signo, si_code;
 
-#define DO_ERROR(signr, prologue, str, name)			\
-asmlinkage void name(struct pt_regs *regs, u32 intcode)		\
-{								\
-	prologue;						\
-	if (die_if_no_fixup(str, regs, intcode))		\
-		return;						\
-	force_sig(signr, current);				\
+	if (user_mode(regs))
+		return 0;
+
+	peripheral_leds_display_exception(code);
+
+	signo = exception_to_signal_map[code >> 3].signo;
+	si_code = exception_to_signal_map[code >> 3].si_code;
+
+	switch (code) {
+		/* see if we can fixup the kernel accessing memory */
+	case EXCEP_ITLBMISS:
+	case EXCEP_DTLBMISS:
+	case EXCEP_IAERROR:
+	case EXCEP_DAERROR:
+	case EXCEP_MEMERR:
+	case EXCEP_MISALIGN:
+	case EXCEP_BUSERROR:
+	case EXCEP_ILLDATACC:
+	case EXCEP_IOINSACC:
+	case EXCEP_PRIVINSACC:
+	case EXCEP_PRIVDATACC:
+	case EXCEP_DATINSACC:
+		if (fixup_exception(regs))
+			return 1;
+		break;
+
+	case EXCEP_TRAP:
+	case EXCEP_UNIMPINS:
+		if (get_user(opcode, (uint8_t __user *)regs->pc) != 0)
+			break;
+		if (opcode == 0xff) {
+			if (notify_die(DIE_BREAKPOINT, str, regs, code, 0, 0))
+				return 1;
+			if (at_debugger_breakpoint(regs))
+				regs->pc++;
+			signo = SIGTRAP;
+			si_code = TRAP_BRKPT;
+		}
+		break;
+
+	case EXCEP_SYSCALL1 ... EXCEP_SYSCALL14:
+		/* syscall return addr is _after_ the instruction */
+		regs->pc -= 2;
+		break;
+
+	case EXCEP_SYSCALL15:
+		if (report_bug(regs->pc, regs) == BUG_TRAP_TYPE_WARN)
+			return 1;
+
+		/* syscall return addr is _after_ the instruction */
+		regs->pc -= 2;
+		break;
+
+	default:
+		break;
+	}
+
+	if (debugger_intercept(code, signo, si_code, regs) == 0)
+		return 1;
+
+	if (notify_die(DIE_GPF, str, regs, code, 0, 0))
+		return 1;
+
+	/* make the process die as the last resort */
+	die(str, regs, code);
 }
 
-#define DO_EINFO(signr, prologue, str, name, sicode)			\
-asmlinkage void name(struct pt_regs *regs, u32 intcode)			\
-{									\
-	siginfo_t info;							\
-	prologue;							\
-	if (die_if_no_fixup(str, regs, intcode))			\
-		return;							\
-	info.si_signo = signr;						\
-	if (signr == SIGILL && sicode == ILL_ILLOPC) {			\
-		uint8_t opcode;						\
-		if (get_user(opcode, (uint8_t __user *)regs->pc) == 0)	\
-			if (opcode == 0xff)				\
-				info.si_signo = SIGTRAP;		\
-	}								\
-	info.si_errno = 0;						\
-	info.si_code = sicode;						\
-	info.si_addr = (void *) regs->pc;				\
-	force_sig_info(info.si_signo, &info, current);			\
+/*
+ * General exception handler
+ */
+asmlinkage void handle_exception(struct pt_regs *regs, u32 intcode)
+{
+	siginfo_t info;
+
+	/* deal with kernel exceptions here */
+	if (die_if_no_fixup(NULL, regs, intcode))
+		return;
+
+	/* otherwise it's a userspace exception */
+	info.si_signo = exception_to_signal_map[intcode >> 3].signo;
+	info.si_code = exception_to_signal_map[intcode >> 3].si_code;
+	info.si_errno = 0;
+	info.si_addr = (void *) regs->pc;
+	force_sig_info(info.si_signo, &info, current);
 }
 
-DO_ERROR(SIGTRAP, {}, "trap",			trap);
-DO_ERROR(SIGSEGV, {}, "ibreak",			ibreak);
-DO_ERROR(SIGSEGV, {}, "obreak",			obreak);
-DO_EINFO(SIGSEGV, {}, "access error",		access_error,	SEGV_ACCERR);
-DO_EINFO(SIGSEGV, {}, "insn access error",	insn_acc_error,	SEGV_ACCERR);
-DO_EINFO(SIGSEGV, {}, "data access error",	data_acc_error,	SEGV_ACCERR);
-DO_EINFO(SIGILL,  {}, "privileged opcode",	priv_op,	ILL_PRVOPC);
-DO_EINFO(SIGILL,  {}, "invalid opcode",		invalid_op,	ILL_ILLOPC);
-DO_EINFO(SIGILL,  {}, "invalid ex opcode",	invalid_exop,	ILL_ILLOPC);
-DO_EINFO(SIGBUS,  {}, "invalid address",	mem_error,	BUS_ADRERR);
-DO_EINFO(SIGBUS,  {}, "bus error",		bus_error,	BUS_ADRERR);
-
-DO_ERROR(SIGTRAP,
-#ifndef CONFIG_MN10300_USING_JTAG
-	 DCR &= ~0x0001,
-#else
-	 {},
-#endif
-	 "single step", istep);
-
 /*
  * handle NMI
  */
 asmlinkage void nmi(struct pt_regs *regs, enum exception_code code)
 {
 	/* see if gdbstub wants to deal with it */
-#ifdef CONFIG_GDBSTUB
-	if (gdbstub_intercept(regs, code))
+	if (debugger_intercept(code, SIGQUIT, 0, regs))
 		return;
-#endif
 
 	printk(KERN_WARNING "--- Register Dump ---\n");
 	show_registers(regs);
@@ -128,29 +233,36 @@
  */
 void show_trace(unsigned long *sp)
 {
-	unsigned long *stack, addr, module_start, module_end;
-	int i;
+	unsigned long bottom, stack, addr, fp, raslot;
 
-	printk(KERN_EMERG "\nCall Trace:");
+	printk(KERN_EMERG "\nCall Trace:\n");
 
-	stack = sp;
-	i = 0;
-	module_start = VMALLOC_START;
-	module_end = VMALLOC_END;
+	//stack = (unsigned long)sp;
+	asm("mov sp,%0" : "=a"(stack));
+	asm("mov a3,%0" : "=r"(fp));
 
-	while (((long) stack & (THREAD_SIZE - 1)) != 0) {
-		addr = *stack++;
+	raslot = ULONG_MAX;
+	bottom = (stack + THREAD_SIZE) & ~(THREAD_SIZE - 1);
+	for (; stack < bottom; stack += sizeof(addr)) {
+		addr = *(unsigned long *)stack;
+		if (stack == fp) {
+			if (addr > stack && addr < bottom) {
+				fp = addr;
+				raslot = stack + sizeof(addr);
+				continue;
+			}
+			fp = 0;
+			raslot = ULONG_MAX;
+		}
+
 		if (__kernel_text_address(addr)) {
-#if 1
 			printk(" [<%08lx>]", addr);
+			if (stack >= raslot)
+				raslot = ULONG_MAX;
+			else
+				printk(" ?");
 			print_symbol(" %s", addr);
 			printk("\n");
-#else
-			if ((i % 6) == 0)
-				printk(KERN_EMERG "  ");
-			printk("[<%08lx>] ", addr);
-			i++;
-#endif
 		}
 	}
 
@@ -323,86 +435,6 @@
 }
 
 /*
- * see if there's a fixup handler we can force a jump to when an exception
- * happens due to something kernel code did
- */
-int die_if_no_fixup(const char *str, struct pt_regs *regs,
-		    enum exception_code code)
-{
-	if (user_mode(regs))
-		return 0;
-
-	peripheral_leds_display_exception(code);
-
-	switch (code) {
-		/* see if we can fixup the kernel accessing memory */
-	case EXCEP_ITLBMISS:
-	case EXCEP_DTLBMISS:
-	case EXCEP_IAERROR:
-	case EXCEP_DAERROR:
-	case EXCEP_MEMERR:
-	case EXCEP_MISALIGN:
-	case EXCEP_BUSERROR:
-	case EXCEP_ILLDATACC:
-	case EXCEP_IOINSACC:
-	case EXCEP_PRIVINSACC:
-	case EXCEP_PRIVDATACC:
-	case EXCEP_DATINSACC:
-		if (fixup_exception(regs))
-			return 1;
-	case EXCEP_UNIMPINS:
-		if (regs->pc && *(uint8_t *)regs->pc == 0xff)
-			if (notify_die(DIE_BREAKPOINT, str, regs, code, 0, 0))
-				return 1;
-		break;
-	default:
-		break;
-	}
-
-	/* see if gdbstub wants to deal with it */
-#ifdef CONFIG_GDBSTUB
-	if (gdbstub_intercept(regs, code))
-		return 1;
-#endif
-
-	if (notify_die(DIE_GPF, str, regs, code, 0, 0))
-		return 1;
-
-	/* make the process die as the last resort */
-	die(str, regs, code);
-}
-
-/*
- * handle unsupported syscall instructions (syscall 1-15)
- */
-static asmlinkage void unsupported_syscall(struct pt_regs *regs,
-					   enum exception_code code)
-{
-	struct task_struct *tsk = current;
-	siginfo_t info;
-
-	/* catch a kernel BUG() */
-	if (code == EXCEP_SYSCALL15 && !user_mode(regs)) {
-		if (report_bug(regs->pc, regs) == BUG_TRAP_TYPE_BUG) {
-#ifdef CONFIG_GDBSTUB
-			gdbstub_intercept(regs, code);
-#endif
-		}
-	}
-
-	regs->pc -= 2; /* syscall return addr is _after_ the instruction */
-
-	die_if_no_fixup("An unsupported syscall insn was used by the kernel\n",
-			regs, code);
-
-	info.si_signo	= SIGILL;
-	info.si_errno	= ENOSYS;
-	info.si_code	= ILL_ILLTRP;
-	info.si_addr	= (void *) regs->pc;
-	force_sig_info(SIGILL, &info, tsk);
-}
-
-/*
  * display the register file when the stack pointer gets clobbered
  */
 asmlinkage void do_double_fault(struct pt_regs *regs)
@@ -481,10 +513,8 @@
 {
 
 	/* see if gdbstub wants to deal with it */
-#ifdef CONFIG_GDBSTUB
-	if (gdbstub_intercept(regs, code))
+	if (debugger_intercept(code, SIGSYS, 0, regs) == 0)
 		return;
-#endif
 
 	peripheral_leds_display_exception(code);
 	printk(KERN_EMERG "Uninitialised Exception 0x%04x\n", code & 0xFFFF);
@@ -549,43 +579,43 @@
  */
 void __init trap_init(void)
 {
-	set_excp_vector(EXCEP_TRAP,		trap);
-	set_excp_vector(EXCEP_ISTEP,		istep);
-	set_excp_vector(EXCEP_IBREAK,		ibreak);
-	set_excp_vector(EXCEP_OBREAK,		obreak);
+	set_excp_vector(EXCEP_TRAP,		handle_exception);
+	set_excp_vector(EXCEP_ISTEP,		handle_exception);
+	set_excp_vector(EXCEP_IBREAK,		handle_exception);
+	set_excp_vector(EXCEP_OBREAK,		handle_exception);
 
-	set_excp_vector(EXCEP_PRIVINS,		priv_op);
-	set_excp_vector(EXCEP_UNIMPINS,		invalid_op);
-	set_excp_vector(EXCEP_UNIMPEXINS,	invalid_exop);
-	set_excp_vector(EXCEP_MEMERR,		mem_error);
+	set_excp_vector(EXCEP_PRIVINS,		handle_exception);
+	set_excp_vector(EXCEP_UNIMPINS,		handle_exception);
+	set_excp_vector(EXCEP_UNIMPEXINS,	handle_exception);
+	set_excp_vector(EXCEP_MEMERR,		handle_exception);
 	set_excp_vector(EXCEP_MISALIGN,		misalignment);
-	set_excp_vector(EXCEP_BUSERROR,		bus_error);
-	set_excp_vector(EXCEP_ILLINSACC,	insn_acc_error);
-	set_excp_vector(EXCEP_ILLDATACC,	data_acc_error);
-	set_excp_vector(EXCEP_IOINSACC,		insn_acc_error);
-	set_excp_vector(EXCEP_PRIVINSACC,	insn_acc_error);
-	set_excp_vector(EXCEP_PRIVDATACC,	data_acc_error);
-	set_excp_vector(EXCEP_DATINSACC,	insn_acc_error);
-	set_excp_vector(EXCEP_FPU_UNIMPINS,	fpu_invalid_op);
+	set_excp_vector(EXCEP_BUSERROR,		handle_exception);
+	set_excp_vector(EXCEP_ILLINSACC,	handle_exception);
+	set_excp_vector(EXCEP_ILLDATACC,	handle_exception);
+	set_excp_vector(EXCEP_IOINSACC,		handle_exception);
+	set_excp_vector(EXCEP_PRIVINSACC,	handle_exception);
+	set_excp_vector(EXCEP_PRIVDATACC,	handle_exception);
+	set_excp_vector(EXCEP_DATINSACC,	handle_exception);
+	set_excp_vector(EXCEP_FPU_UNIMPINS,	handle_exception);
 	set_excp_vector(EXCEP_FPU_OPERATION,	fpu_exception);
 
 	set_excp_vector(EXCEP_NMI,		nmi);
 
-	set_excp_vector(EXCEP_SYSCALL1,		unsupported_syscall);
-	set_excp_vector(EXCEP_SYSCALL2,		unsupported_syscall);
-	set_excp_vector(EXCEP_SYSCALL3,		unsupported_syscall);
-	set_excp_vector(EXCEP_SYSCALL4,		unsupported_syscall);
-	set_excp_vector(EXCEP_SYSCALL5,		unsupported_syscall);
-	set_excp_vector(EXCEP_SYSCALL6,		unsupported_syscall);
-	set_excp_vector(EXCEP_SYSCALL7,		unsupported_syscall);
-	set_excp_vector(EXCEP_SYSCALL8,		unsupported_syscall);
-	set_excp_vector(EXCEP_SYSCALL9,		unsupported_syscall);
-	set_excp_vector(EXCEP_SYSCALL10,	unsupported_syscall);
-	set_excp_vector(EXCEP_SYSCALL11,	unsupported_syscall);
-	set_excp_vector(EXCEP_SYSCALL12,	unsupported_syscall);
-	set_excp_vector(EXCEP_SYSCALL13,	unsupported_syscall);
-	set_excp_vector(EXCEP_SYSCALL14,	unsupported_syscall);
-	set_excp_vector(EXCEP_SYSCALL15,	unsupported_syscall);
+	set_excp_vector(EXCEP_SYSCALL1,		handle_exception);
+	set_excp_vector(EXCEP_SYSCALL2,		handle_exception);
+	set_excp_vector(EXCEP_SYSCALL3,		handle_exception);
+	set_excp_vector(EXCEP_SYSCALL4,		handle_exception);
+	set_excp_vector(EXCEP_SYSCALL5,		handle_exception);
+	set_excp_vector(EXCEP_SYSCALL6,		handle_exception);
+	set_excp_vector(EXCEP_SYSCALL7,		handle_exception);
+	set_excp_vector(EXCEP_SYSCALL8,		handle_exception);
+	set_excp_vector(EXCEP_SYSCALL9,		handle_exception);
+	set_excp_vector(EXCEP_SYSCALL10,	handle_exception);
+	set_excp_vector(EXCEP_SYSCALL11,	handle_exception);
+	set_excp_vector(EXCEP_SYSCALL12,	handle_exception);
+	set_excp_vector(EXCEP_SYSCALL13,	handle_exception);
+	set_excp_vector(EXCEP_SYSCALL14,	handle_exception);
+	set_excp_vector(EXCEP_SYSCALL15,	handle_exception);
 }
 
 /*
diff --git a/arch/mn10300/mm/fault.c b/arch/mn10300/mm/fault.c
index 59c3da4..0945409 100644
--- a/arch/mn10300/mm/fault.c
+++ b/arch/mn10300/mm/fault.c
@@ -28,8 +28,9 @@
 #include <asm/uaccess.h>
 #include <asm/pgalloc.h>
 #include <asm/hardirq.h>
-#include <asm/gdb-stub.h>
 #include <asm/cpu-regs.h>
+#include <asm/debugger.h>
+#include <asm/gdb-stub.h>
 
 /*
  * Unlock any spinlocks which will prevent us from getting the
@@ -306,10 +307,8 @@
 	printk(" printing pc:\n");
 	printk(KERN_ALERT "%08lx\n", regs->pc);
 
-#ifdef CONFIG_GDBSTUB
-	gdbstub_intercept(
-		regs, fault_code & 0x00010000 ? EXCEP_IAERROR : EXCEP_DAERROR);
-#endif
+	debugger_intercept(fault_code & 0x00010000 ? EXCEP_IAERROR : EXCEP_DAERROR,
+			   SIGSEGV, SEGV_ACCERR, regs);
 
 	page = PTBR;
 	page = ((unsigned long *) __va(page))[address >> 22];