Attempt to make break-to-debugger and alternative break-to-debugger more
accessible:

(1) Always compile in support for breaking into the debugger if options KDB
    is present in the kernel.

(2) Disable both by default, but allow them to be enabled via tunables and
    sysctls debug.kdb.break_to_debugger and debug.kdb.alt_break_to_debugger.

(3) Make existing kernel configurations switch the default from disabled to
    enabled as appropriate (current kernel configurations will continue to
    work as expected).

(4) Migrate alternative break-to-debugger state machine logic out of
    individual device drivers into centralised KDB code.  This has a number
    of upsides, but also one downside: it's now tricky to release sio spin
    locks when entering the debugger, so we don't.  However, similar logic
    does not exist in other device drivers, including uart.

(5) dcons requires some special handling; unlike other console types, it
    allows overriding KDB's own debugger selection, so we need a new
    interface to KDB to allow that to work.

GENERIC kernels in -CURRENT will now support break-to-debugger as long as
appropriate boot/run-time options are set, which should improve the
debuggability of BETA kernels significantly.

MFC after:	3 weeks
Approved by:	re (xxx)


Index: powerpc/mambo/mambo_console.c
===================================================================
--- powerpc/mambo/mambo_console.c	(revision 224860)
+++ powerpc/mambo/mambo_console.c	(working copy)
@@ -60,7 +60,7 @@
 static struct callout		mambo_callout;
 static struct tty 		*tp = NULL;
 
-#if defined(KDB) && defined(ALT_BREAK_TO_DEBUGGER)
+#if defined(KDB)
 static int			alt_break_state;
 #endif
 
@@ -156,24 +156,8 @@
 	ch = mambocall(MAMBO_CONSOLE_READ);
 
 	if (ch > 0 && ch < 0xff) {
-#if defined(KDB) && defined(ALT_BREAK_TO_DEBUGGER)
-		int kdb_brk;
-
-		if ((kdb_brk = kdb_alt_break(ch, &alt_break_state)) != 0) {
-			switch (kdb_brk) {
-			case KDB_REQ_DEBUGGER:
-				kdb_enter(KDB_WHY_BREAK,
-				    "Break sequence on console");
-				break;
-			case KDB_REQ_PANIC:
-				kdb_panic("Panic sequence on console");
-				break;
-			case KDB_REQ_REBOOT:
-				kdb_reboot();
-				break;
-
-			}
-		}
+#if defined(KDB)
+		kdb_alt_break(ch, &alt_break_state);
 #endif
 		return (ch);
 	}
Index: kern/subr_kdb.c
===================================================================
--- kern/subr_kdb.c	(revision 224860)
+++ kern/subr_kdb.c	(working copy)
@@ -57,6 +57,18 @@
 struct thread *kdb_thread = NULL;
 struct trapframe *kdb_frame = NULL;
 
+#ifdef BREAK_TO_DEBUGGER
+static int	kdb_break_to_debugger = 1;
+#else
+static int	kdb_break_to_debugger = 0;
+#endif
+
+#ifdef ALT_BREAK_TO_DEBUGGER
+static int	kdb_alt_break_to_debugger = 1;
+#else
+static int	kdb_alt_break_to_debugger = 0;
+#endif
+
 KDB_BACKEND(null, NULL, NULL, NULL);
 SET_DECLARE(kdb_dbbe_set, struct kdb_dbbe);
 
@@ -87,6 +99,15 @@
 SYSCTL_PROC(_debug_kdb, OID_AUTO, trap_code, CTLTYPE_INT | CTLFLAG_RW, NULL, 0,
     kdb_sysctl_trap_code, "I", "set to cause a page fault via code access");
 
+SYSCTL_INT(_debug_kdb, OID_AUTO, break_to_debugger, CTLTYPE_INT | CTLFLAG_RW |
+    CTLFLAG_TUN, &kdb_break_to_debugger, 0, "Enable break to debugger");
+TUNABLE_INT("debug.kdb.break_to_debugger", &kdb_break_to_debugger);
+
+SYSCTL_INT(_debug_kdb, OID_AUTO, alt_break_to_debugger, CTLTYPE_INT |
+    CTLFLAG_RW | CTLFLAG_TUN, &kdb_alt_break_to_debugger, 0,
+    "Enable alternative break to debugger");
+TUNABLE_INT("debug.kdb.alt_break_to_debugger", &kdb_alt_break_to_debugger);
+
 /*
  * Flag to indicate to debuggers why the debugger was entered.
  */
@@ -241,8 +262,18 @@
 };
 
 int
-kdb_alt_break(int key, int *state)
+kdb_break(void)
 {
+
+	if (!kdb_break_to_debugger)
+		return (0);
+	kdb_enter(KDB_WHY_BREAK, "Break to debugger");
+	return (KDB_REQ_DEBUGGER);
+}
+
+static int
+kdb_alt_break_state(int key, int *state)
+{
 	int brk;
 
 	/* All states transition to KDB_ALT_BREAK_SEEN_CR on a CR. */
@@ -275,7 +306,62 @@
 	return (brk);
 }
 
+int
+kdb_alt_break(int key, int *state)
+{
+	int brk;
+
+	if (!kdb_alt_break_to_debugger)
+		return (0);
+	brk = kdb_alt_break_state(key, state);
+	switch (brk) {
+	case KDB_REQ_DEBUGGER:
+		kdb_enter(KDB_WHY_BREAK, "Break to debugger");
+		break;
+
+	case KDB_REQ_PANIC:
+		kdb_panic("Panic sequence on console");
+		break;
+
+	case KDB_REQ_REBOOT:
+		kdb_reboot();
+		break;
+	}
+	return (brk);
+}
+
 /*
+ * This variation on kdb_alt_break() is used only by dcons, which has its own
+ * configuration flag to force GDB use regardless of the global KDB
+ * configuration.
+ */
+int
+kdb_alt_break_gdb(int key, int *state)
+{
+	int brk;
+
+	if (!kdb_alt_break_to_debugger)
+		return (0);
+	brk = kdb_alt_break_state(key, state);
+	switch (brk) {
+	case KDB_REQ_DEBUGGER:
+		kdb_dbbe_select("gdb");
+		kdb_enter(KDB_WHY_BREAK, "Break to debugger");
+		break;
+
+	case KDB_REQ_PANIC:
+		kdb_dbbe_select("gdb");
+		kdb_panic("Panic sequence on console");
+		break;
+
+	case KDB_REQ_REBOOT:
+		kdb_reboot();
+		break;
+	}
+	return (0);
+}
+
+/*
  * Print a backtrace of the calling thread. The backtrace is generated by
  * the selected debugger, provided it supports backtraces. If no debugger
  * is selected or the current debugger does not support backtraces, this
Index: dev/syscons/syscons.c
===================================================================
--- dev/syscons/syscons.c	(revision 224860)
+++ dev/syscons/syscons.c	(working copy)
@@ -3514,7 +3514,7 @@
 	    case DBG:
 #ifndef SC_DISABLE_KDBKEY
 		if (enable_kdbkey)
-			kdb_enter(KDB_WHY_BREAK, "manual escape to debugger");
+			kdb_break();
 #endif
 		break;
 
Index: dev/uart/uart_core.c
===================================================================
--- dev/uart/uart_core.c	(revision 224860)
+++ dev/uart/uart_core.c	(working copy)
@@ -118,10 +118,10 @@
 {
 	struct uart_softc *sc = arg;
 
-#if defined(KDB) && defined(BREAK_TO_DEBUGGER)
+#if defined(KDB)
 	if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE) {
-		kdb_enter(KDB_WHY_BREAK, "Line break on console");
-		return (0);
+		if (kdb_break())
+			return (0);
 	}
 #endif
 	if (sc->sc_opened)
@@ -170,26 +170,10 @@
 
 	rxp = sc->sc_rxput;
 	UART_RECEIVE(sc);
-#if defined(KDB) && defined(ALT_BREAK_TO_DEBUGGER)
+#if defined(KDB)
 	if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE) {
 		while (rxp != sc->sc_rxput) {
-			int kdb_brk;
-
-			if ((kdb_brk = kdb_alt_break(sc->sc_rxbuf[rxp++],
-			    &sc->sc_altbrk)) != 0) {
-				switch (kdb_brk) {
-				case KDB_REQ_DEBUGGER:
-					kdb_enter(KDB_WHY_BREAK,
-					    "Break sequence on console");
-					break;
-				case KDB_REQ_PANIC:
-					kdb_panic("Panic sequence on console");
-					break;
-				case KDB_REQ_REBOOT:
-					kdb_reboot();
-					break;
-				}
-			}
+			kdb_alt_break(sc->sc_rxbuf[rxp++], &sc->sc_altbrk);
 			if (rxp == sc->sc_rxbufsz)
 				rxp = 0;
 		}
Index: dev/sio/sio.c
===================================================================
--- dev/sio/sio.c	(revision 224860)
+++ dev/sio/sio.c	(working copy)
@@ -228,7 +228,7 @@
 
 	struct	pps_state pps;
 	int	pps_bit;
-#ifdef ALT_BREAK_TO_DEBUGGER
+#ifdef KDB
 	int	alt_brk_state;
 #endif
 
@@ -1102,8 +1102,7 @@
 		}
 		if (ret)
 			device_printf(dev, "could not activate interrupt\n");
-#if defined(KDB) && (defined(BREAK_TO_DEBUGGER) || \
-    defined(ALT_BREAK_TO_DEBUGGER))
+#if defined(KDB)
 		/*
 		 * Enable interrupts for early break-to-debugger support
 		 * on the console.
@@ -1196,8 +1195,7 @@
 	com->poll_output = FALSE;
 	sio_setreg(com, com_cfcr, com->cfcr_image &= ~CFCR_SBREAK);
 
-#if defined(KDB) && (defined(BREAK_TO_DEBUGGER) || \
-    defined(ALT_BREAK_TO_DEBUGGER))
+#if defined(KDB)
 	/*
 	 * Leave interrupts enabled and don't clear DTR if this is the
 	 * console. This allows us to detect break-to-debugger events
@@ -1483,9 +1481,8 @@
 	u_char	modem_status;
 	u_char	*ioptr;
 	u_char	recv_data;
-#if defined(KDB) && defined(ALT_BREAK_TO_DEBUGGER)
-	int	kdb_brk;
 
+#ifdef KDB
 again:
 #endif
 
@@ -1518,27 +1515,9 @@
 			else
 				recv_data = inb(com->data_port);
 #ifdef KDB
-#ifdef ALT_BREAK_TO_DEBUGGER
 			if (com->unit == comconsole &&
-			    (kdb_brk = kdb_alt_break(recv_data,
-					&com->alt_brk_state)) != 0) {
-				mtx_unlock_spin(&sio_lock);
-				switch (kdb_brk) {
-				case KDB_REQ_DEBUGGER:
-					kdb_enter(KDB_WHY_BREAK,
-					    "Break sequence on console");
-					break;
-				case KDB_REQ_PANIC:
-					kdb_panic("panic on console");
-					break;
-				case KDB_REQ_REBOOT:
-					kdb_reboot();
-					break;
-				}
-				mtx_lock_spin(&sio_lock);
+			    kdb_alt_break(recv_data, &com->alt_brk_state) != 0)
 				goto again;
-			}
-#endif /* ALT_BREAK_TO_DEBUGGER */
 #endif /* KDB */
 			if (line_status & (LSR_BI | LSR_FE | LSR_PE)) {
 				/*
@@ -1554,10 +1533,9 @@
 				 * Note: BI together with FE/PE means just BI.
 				 */
 				if (line_status & LSR_BI) {
-#if defined(KDB) && defined(BREAK_TO_DEBUGGER)
+#if defined(KDB)
 					if (com->unit == comconsole) {
-						kdb_enter(KDB_WHY_BREAK,
-						    "Line break on console");
+						kdb_break();
 						goto cont;
 					}
 #endif
Index: dev/cfe/cfe_console.c
===================================================================
--- dev/cfe/cfe_console.c	(revision 224860)
+++ dev/cfe/cfe_console.c	(working copy)
@@ -67,7 +67,7 @@
 static struct callout_handle	cfe_timeouthandle
     = CALLOUT_HANDLE_INITIALIZER(&cfe_timeouthandle);
 
-#if defined(KDB) && defined(ALT_BREAK_TO_DEBUGGER)
+#if defined(KDB)
 static int			alt_break_state;
 #endif
 
@@ -191,24 +191,8 @@
 	unsigned char ch;
 
 	if (cfe_read(conhandle, &ch, 1) == 1) {
-#if defined(KDB) && defined(ALT_BREAK_TO_DEBUGGER)
-		int kdb_brk;
-
-		if ((kdb_brk = kdb_alt_break(ch, &alt_break_state)) != 0) {
-			switch (kdb_brk) {
-			case KDB_REQ_DEBUGGER:
-				kdb_enter(KDB_WHY_BREAK,
-				    "Break sequence on console");
-				break;
-			case KDB_REQ_PANIC:
-				kdb_panic("Panic sequence on console");
-				break;
-			case KDB_REQ_REBOOT:
-				kdb_reboot();
-				break;
-
-			}
-		}
+#if defined(KDB)
+		kdb_alt_break(ch, &alt_break_state);
 #endif
 		return (ch);
 	}
Index: dev/dcons/dcons_os.c
===================================================================
--- dev/dcons/dcons_os.c	(revision 224860)
+++ dev/dcons/dcons_os.c	(working copy)
@@ -133,38 +133,21 @@
 	.tsw_outwakeup  = dcons_outwakeup,
 };
 
-#if (defined(GDB) || defined(DDB)) && defined(ALT_BREAK_TO_DEBUGGER)
+#if (defined(GDB) || defined(DDB))
 static int
 dcons_check_break(struct dcons_softc *dc, int c)
 {
-	int kdb_brk;
 
 	if (c < 0)
 		return (c);
 
-	if ((kdb_brk = kdb_alt_break(c, &dc->brk_state)) != 0) {
-		switch (kdb_brk) {
-		case KDB_REQ_DEBUGGER:
-			if ((dc->flags & DC_GDB) != 0) {
 #ifdef GDB
-				if (gdb_cur == &dcons_gdb_dbgport) {
-					kdb_dbbe_select("gdb");
-					kdb_enter(KDB_WHY_BREAK,
-					    "Break sequence on dcons gdb port");
-				}
+	if ((dc->flags & DC_GDB) != 0 && gdb_cur == &dcons_gdb_dbgport)
+		kdb_alt_break_gdb(c, &dc->brk_state);
+	else
 #endif
-			} else
-				kdb_enter(KDB_WHY_BREAK,
-				    "Break sequence on dcons console port");
-			break;
-		case KDB_REQ_PANIC:
-			kdb_panic("Panic sequence on dcons console port");
-			break;
-		case KDB_REQ_REBOOT:
-			kdb_reboot();
-			break;
-		}
-	}
+		kdb_alt_break(c, &dc->brk_state);
+
 	return (c);
 }
 #else
Index: dev/ofw/ofw_console.c
===================================================================
--- dev/ofw/ofw_console.c	(revision 224860)
+++ dev/ofw/ofw_console.c	(working copy)
@@ -64,7 +64,7 @@
 static struct callout_handle	ofw_timeouthandle
     = CALLOUT_HANDLE_INITIALIZER(&ofw_timeouthandle);
 
-#if defined(KDB) && defined(ALT_BREAK_TO_DEBUGGER)
+#if defined(KDB)
 static int			alt_break_state;
 #endif
 
@@ -199,24 +199,8 @@
 	unsigned char ch;
 
 	if (OF_read(stdin, &ch, 1) > 0) {
-#if defined(KDB) && defined(ALT_BREAK_TO_DEBUGGER)
-		int kdb_brk;
-
-		if ((kdb_brk = kdb_alt_break(ch, &alt_break_state)) != 0) {
-			switch (kdb_brk) {
-			case KDB_REQ_DEBUGGER:
-				kdb_enter(KDB_WHY_BREAK,
-				    "Break sequence on console");
-				break;
-			case KDB_REQ_PANIC:
-				kdb_panic("Panic sequence on console");
-				break;
-			case KDB_REQ_REBOOT:
-				kdb_reboot();
-				break;
-
-			}
-		}
+#if defined(KDB)
+		kdb_alt_break(ch, &alt_break_state);
 #endif
 		return (ch);
 	}
Index: pc98/cbus/sio.c
===================================================================
--- pc98/cbus/sio.c	(revision 224860)
+++ pc98/cbus/sio.c	(working copy)
@@ -310,7 +310,7 @@
 
 	struct	pps_state pps;
 	int	pps_bit;
-#ifdef ALT_BREAK_TO_DEBUGGER
+#ifdef KDB
 	int	alt_brk_state;
 #endif
 
@@ -1752,8 +1752,7 @@
 		}
 		if (ret)
 			device_printf(dev, "could not activate interrupt\n");
-#if defined(KDB) && (defined(BREAK_TO_DEBUGGER) || \
-    defined(ALT_BREAK_TO_DEBUGGER))
+#if defined(KDB)
 		/*
 		 * Enable interrupts for early break-to-debugger support
 		 * on the console.
@@ -1896,8 +1895,7 @@
 	sio_setreg(com, com_cfcr, com->cfcr_image &= ~CFCR_SBREAK);
 #endif
 
-#if defined(KDB) && (defined(BREAK_TO_DEBUGGER) || \
-    defined(ALT_BREAK_TO_DEBUGGER))
+#if defined(KDB)
 	/*
 	 * Leave interrupts enabled and don't clear DTR if this is the
 	 * console. This allows us to detect break-to-debugger events
@@ -2272,7 +2270,7 @@
 	u_char	rsa_buf_status = 0;
 	int	rsa_tx_fifo_size = 0;
 #endif /* PC98 */
-#if defined(KDB) && defined(ALT_BREAK_TO_DEBUGGER)
+#if defined(KDB)
 	int	kdb_brk;
 
 again:
@@ -2369,27 +2367,11 @@
 			else
 				recv_data = inb(com->data_port);
 #ifdef KDB
-#ifdef ALT_BREAK_TO_DEBUGGER
 			if (com->unit == comconsole &&
 			    (kdb_brk = kdb_alt_break(recv_data,
 					&com->alt_brk_state)) != 0) {
-				mtx_unlock_spin(&sio_lock);
-				switch (kdb_brk) {
-				case KDB_REQ_DEBUGGER:
-					kdb_enter(KDB_WHY_BREAK,
-					    "Break sequence on console");
-					break;
-				case KDB_REQ_PANIC:
-					kdb_panic("panic on console");
-					break;
-				case KDB_REQ_REBOOT:
-					kdb_reboot();
-					break;
-				}
-				mtx_lock_spin(&sio_lock);
 				goto again;
 			}
-#endif /* ALT_BREAK_TO_DEBUGGER */
 #endif /* KDB */
 			if (line_status & (LSR_BI | LSR_FE | LSR_PE)) {
 				/*
@@ -2405,7 +2387,7 @@
 				 * Note: BI together with FE/PE means just BI.
 				 */
 				if (line_status & LSR_BI) {
-#if defined(KDB) && defined(BREAK_TO_DEBUGGER)
+#if defined(KDB)
 					if (com->unit == comconsole) {
 						kdb_enter(KDB_WHY_BREAK,
 						    "Line break on console");
Index: sys/kdb.h
===================================================================
--- sys/kdb.h	(revision 224860)
+++ sys/kdb.h	(working copy)
@@ -64,6 +64,8 @@
 extern struct thread *kdb_thread;	/* Current thread. */
 
 int	kdb_alt_break(int, int *);
+int	kdb_alt_break_gdb(int, int *);
+int	kdb_break(void);
 void	kdb_backtrace(void);
 int	kdb_dbbe_select(const char *);
 void	kdb_enter(const char *, const char *);
