9929 uts: boot console mirror with diag-device
Reviewed by: John Levon <john.levon@joyent.com>
Approved by: Joshua M. Clulow <josh@sysmgr.org>
diff --git a/usr/src/uts/i86pc/boot/boot_console.c b/usr/src/uts/i86pc/boot/boot_console.c
index e1504b4..e5d8e73 100644
--- a/usr/src/uts/i86pc/boot/boot_console.c
+++ b/usr/src/uts/i86pc/boot/boot_console.c
@@ -59,6 +59,7 @@
 
 static int cons_color = CONS_COLOR;
 static int console = CONS_SCREEN_TEXT;
+static int diag = CONS_INVALID;
 static int tty_num = 0;
 static int tty_addr[] = {0x3f8, 0x2f8, 0x3e8, 0x2e8};
 static char *boot_line;
@@ -607,11 +608,45 @@
 	boot_env.be_size = modules[i].bm_size;
 }
 
+/*
+ * Go through the console_devices array trying to match the string
+ * we were given.  The string on the command line must end with
+ * a comma or white space.
+ *
+ * This function does set tty_num as an side effect, this does imply that
+ * only one of the main console and the diag-device can be using serial.
+ */
+static int
+lookup_console_devices(const char *cons_str)
+{
+	int n, cons;
+	size_t len, cons_len;
+	console_value_t *consolep;
+
+	cons = CONS_INVALID;
+	if (cons_str != NULL) {
+
+		cons_len = strlen(cons_str);
+		for (n = 0; console_devices[n].name != NULL; n++) {
+			consolep = &console_devices[n];
+			len = strlen(consolep->name);
+			if ((len <= cons_len) && ((cons_str[len] == '\0') ||
+			    (cons_str[len] == ',') || (cons_str[len] == '\'') ||
+			    (cons_str[len] == '"') || ISSPACE(cons_str[len])) &&
+			    (strncmp(cons_str, consolep->name, len) == 0)) {
+				cons = consolep->value;
+				if (cons == CONS_TTY)
+					tty_num = n;
+				break;
+			}
+		}
+	}
+	return (cons);
+}
+
 void
 bcons_init(struct xboot_info *xbi)
 {
-	console_value_t *consolep;
-	size_t len, cons_len;
 	const char *cons_str;
 #if !defined(_BOOT)
 	static char console_text[] = "text";
@@ -634,6 +669,13 @@
 	bcons_init_xen(boot_line);
 #endif /* __xpv */
 
+	/*
+	 * First check for diag-device.
+	 */
+	cons_str = find_boot_prop("diag-device");
+	if (cons_str != NULL)
+		diag = lookup_console_devices(cons_str);
+
 	cons_str = find_boot_prop("console");
 	if (cons_str == NULL)
 		cons_str = find_boot_prop("output-device");
@@ -643,29 +685,8 @@
 		cons_str = console_text;
 #endif
 
-	/*
-	 * Go through the console_devices array trying to match the string
-	 * we were given.  The string on the command line must end with
-	 * a comma or white space.
-	 */
-	if (cons_str != NULL) {
-		int n;
-
-		cons_len = strlen(cons_str);
-		for (n = 0; console_devices[n].name != NULL; n++) {
-			consolep = &console_devices[n];
-			len = strlen(consolep->name);
-			if ((len <= cons_len) && ((cons_str[len] == '\0') ||
-			    (cons_str[len] == ',') || (cons_str[len] == '\'') ||
-			    (cons_str[len] == '"') || ISSPACE(cons_str[len])) &&
-			    (strncmp(cons_str, consolep->name, len) == 0)) {
-				console = consolep->value;
-				if (console == CONS_TTY)
-					tty_num = n;
-				break;
-			}
-		}
-	}
+	if (cons_str != NULL)
+		console = lookup_console_devices(cons_str);
 
 #if defined(__xpv)
 	/*
@@ -751,6 +772,24 @@
 		kb_init();
 		break;
 	}
+
+	/*
+	 * Initialize diag device unless already done.
+	 */
+	switch (diag) {
+	case CONS_TTY:
+		if (console != CONS_TTY)
+			serial_init();
+		break;
+	case CONS_SCREEN_GRAPHICS:
+	case CONS_SCREEN_TEXT:
+		if (console != CONS_SCREEN_GRAPHICS &&
+		    console != CONS_SCREEN_TEXT)
+			kb_init();
+		break;
+	default:
+		break;
+	}
 }
 
 #if !defined(_BOOT)
@@ -916,9 +955,9 @@
 }
 
 static void
-_doputchar(int c)
+_doputchar(int device, int c)
 {
-	switch (console) {
+	switch (device) {
 	case CONS_TTY:
 		serial_putchar(c);
 		return;
@@ -930,6 +969,7 @@
 	case CONS_USBSER:
 		defcons_putchar(c);
 #endif /* _BOOT */
+	default:
 		return;
 	}
 }
@@ -949,23 +989,33 @@
 
 	if (c == '\t') {
 		do {
-			_doputchar(' ');
+			_doputchar(console, ' ');
+			if (diag != console)
+				_doputchar(diag, ' ');
 		} while (++bhcharpos % 8);
 		return;
 	} else  if (c == '\n' || c == '\r') {
 		bhcharpos = 0;
-		_doputchar('\r');
-		_doputchar(c);
+		_doputchar(console, '\r');
+		_doputchar(console, c);
+		if (diag != console) {
+			_doputchar(diag, '\r');
+			_doputchar(diag, c);
+		}
 		return;
 	} else if (c == '\b') {
 		if (bhcharpos)
 			bhcharpos--;
-		_doputchar(c);
+		_doputchar(console, c);
+		if (diag != console)
+			_doputchar(diag, c);
 		return;
 	}
 
 	bhcharpos++;
-	_doputchar(c);
+	_doputchar(console, c);
+	if (diag != console)
+		_doputchar(diag, c);
 }
 
 /*
@@ -980,11 +1030,15 @@
 		return (bcons_getchar_xen());
 #endif /* __xpv */
 
-	switch (console) {
-	case CONS_TTY:
-		return (serial_getchar());
-	default:
-		return (kb_getchar());
+	for (;;) {
+		if (console == CONS_TTY || diag == CONS_TTY) {
+			if (serial_ischar())
+				return (serial_getchar());
+		}
+		if (console != CONS_INVALID || diag != CONS_INVALID) {
+			if (kb_ischar())
+				return (kb_getchar());
+		}
 	}
 }
 
@@ -993,6 +1047,7 @@
 int
 bcons_ischar(void)
 {
+	int c = 0;
 
 #if defined(__xpv)
 	if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
@@ -1002,10 +1057,31 @@
 
 	switch (console) {
 	case CONS_TTY:
-		return (serial_ischar());
+		c = serial_ischar();
+		break;
+
+	case CONS_INVALID:
+		break;
+
 	default:
-		return (kb_ischar());
+		c = kb_ischar();
 	}
+	if (c != 0)
+		return (c);
+
+	switch (diag) {
+	case CONS_TTY:
+		c = serial_ischar();
+		break;
+
+	case CONS_INVALID:
+		break;
+
+	default:
+		c = kb_ischar();
+	}
+
+	return (c);
 }
 
 #endif /* _BOOT */