xref: /illumos-gate/usr/src/uts/i86pc/boot/boot_console.c (revision 32ef71ecd1ec34bcd80f26b24b049e15ebe51045)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 2012 Gary Mills
23  * Copyright 2020 Joyent, Inc.
24  * Copyright 2025 Oxide Computer Company
25  *
26  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
27  * Use is subject to license terms.
28  */
29 
30 /*
31  * Boot console support.  Most of the file is shared between dboot, and the
32  * early kernel / fakebop.
33  */
34 
35 #include <sys/types.h>
36 #include <sys/systm.h>
37 #include <sys/archsystm.h>
38 #include <sys/framebuffer.h>
39 #include <sys/boot_console.h>
40 #include <sys/panic.h>
41 #include <sys/ctype.h>
42 #include <sys/ascii.h>
43 #include <sys/vgareg.h>
44 #if defined(__xpv)
45 #include <sys/hypervisor.h>
46 #endif /* __xpv */
47 
48 #include "boot_console_impl.h"
49 #include "boot_serial.h"
50 
51 #if defined(_BOOT)
52 #include <dboot/dboot_asm.h>
53 #include <dboot/dboot_xboot.h>
54 #else /* _BOOT */
55 #include <sys/bootconf.h>
56 #if defined(__xpv)
57 #include <sys/evtchn_impl.h>
58 #endif /* __xpv */
59 static char *defcons_buf;
60 static char *defcons_cur;
61 #endif /* _BOOT */
62 
63 #if defined(__xpv)
64 extern void bcons_init_xen(char *);
65 extern void bcons_putchar_xen(int);
66 extern int bcons_getchar_xen(void);
67 extern int bcons_ischar_xen(void);
68 #endif /* __xpv */
69 
70 fb_info_t fb_info;
71 static bcons_dev_t bcons_dev;				/* Device callbacks */
72 static int console = CONS_SCREEN_TEXT;
73 static int diag = CONS_INVALID;
74 static int tty_num = 0;
75 static int tty_addr[] = {0x3f8, 0x2f8, 0x3e8, 0x2e8};
76 static char *boot_line;
77 static struct boot_env {
78 	char	*be_env;	/* ends with double ascii nul */
79 	size_t	be_size;	/* size of the environment, including nul */
80 } boot_env;
81 
82 /*
83  * Simple console terminal emulator for early boot.
84  * We need this to support kmdb, all other console output is supposed
85  * to be simple text output.
86  */
87 typedef enum btem_state_type {
88 	A_STATE_START,
89 	A_STATE_ESC,
90 	A_STATE_CSI,
91 	A_STATE_CSI_QMARK,
92 	A_STATE_CSI_EQUAL
93 } btem_state_type_t;
94 
95 #define	BTEM_MAXPARAMS	5
96 typedef struct btem_state {
97 	btem_state_type_t btem_state;
98 	boolean_t btem_gotparam;
99 	int btem_curparam;
100 	int btem_paramval;
101 	int btem_params[BTEM_MAXPARAMS];
102 } btem_state_t;
103 
104 static btem_state_t boot_tem;
105 
106 static int serial_ischar(void);
107 static int serial_getchar(void);
108 static void serial_putchar(int);
109 static void serial_adjust_prop(void);
110 
111 static void defcons_putchar(int);
112 
113 #if !defined(_BOOT)
114 static boolean_t bootprop_set_tty_mode;
115 #endif
116 
117 #if defined(__xpv)
118 static int console_hypervisor_redirect = B_FALSE;
119 static int console_hypervisor_device = CONS_INVALID;
120 static int console_hypervisor_tty_num = 0;
121 
122 /* Obtain the hypervisor console type */
123 int
console_hypervisor_dev_type(int * tnum)124 console_hypervisor_dev_type(int *tnum)
125 {
126 	if (tnum != NULL)
127 		*tnum = console_hypervisor_tty_num;
128 	return (console_hypervisor_device);
129 }
130 #endif /* __xpv */
131 
132 static int port;
133 
134 static void
serial_init(void)135 serial_init(void)
136 {
137 	port = tty_addr[tty_num];
138 
139 	outb(port + ISR, 0x20);
140 	if (inb(port + ISR) & 0x20) {
141 		/*
142 		 * 82510 chip is present
143 		 */
144 		outb(port + DAT+7, 0x04);	/* clear status */
145 		outb(port + ISR, 0x40);  /* set to bank 2 */
146 		outb(port + MCR, 0x08);  /* IMD */
147 		outb(port + DAT, 0x21);  /* FMD */
148 		outb(port + ISR, 0x00);  /* set to bank 0 */
149 	} else {
150 		/*
151 		 * set the UART in FIFO mode if it has FIFO buffers.
152 		 * use 16550 fifo reset sequence specified in NS
153 		 * application note. disable fifos until chip is
154 		 * initialized.
155 		 */
156 		outb(port + FIFOR, 0x00);		/* clear */
157 		outb(port + FIFOR, FIFO_ON);		/* enable */
158 		outb(port + FIFOR, FIFO_ON|FIFORXFLSH);  /* reset */
159 		outb(port + FIFOR,
160 		    FIFO_ON|FIFODMA|FIFOTXFLSH|FIFORXFLSH|0x80);
161 		if ((inb(port + ISR) & 0xc0) != 0xc0) {
162 			/*
163 			 * no fifo buffers so disable fifos.
164 			 * this is true for 8250's
165 			 */
166 			outb(port + FIFOR, 0x00);
167 		}
168 	}
169 
170 	/* disable interrupts */
171 	outb(port + ICR, 0);
172 
173 #if !defined(_BOOT)
174 	if (IN_XPV_PANIC())
175 		return;
176 #endif
177 
178 	/* adjust setting based on tty properties */
179 	serial_adjust_prop();
180 }
181 
182 /* Advance str pointer past white space */
183 #define	EAT_WHITE_SPACE(str)	{			\
184 	while ((*str != '\0') && ISSPACE(*str))		\
185 		str++;					\
186 }
187 
188 /*
189  * boot_line is set when we call here.  Search it for the argument name,
190  * and if found, return a pointer to it.
191  */
192 static char *
find_boot_line_prop(const char * name)193 find_boot_line_prop(const char *name)
194 {
195 	char *ptr;
196 	char *ret = NULL;
197 	char end_char;
198 	size_t len;
199 
200 	if (boot_line == NULL)
201 		return (NULL);
202 
203 	len = strlen(name);
204 
205 	/*
206 	 * We have two nested loops here: the outer loop discards all options
207 	 * except -B, and the inner loop parses the -B options looking for
208 	 * the one we're interested in.
209 	 */
210 	for (ptr = boot_line; *ptr != '\0'; ptr++) {
211 		EAT_WHITE_SPACE(ptr);
212 
213 		if (*ptr == '-') {
214 			ptr++;
215 			while ((*ptr != '\0') && (*ptr != 'B') &&
216 			    !ISSPACE(*ptr))
217 				ptr++;
218 			if (*ptr == '\0')
219 				goto out;
220 			else if (*ptr != 'B')
221 				continue;
222 		} else {
223 			while ((*ptr != '\0') && !ISSPACE(*ptr))
224 				ptr++;
225 			if (*ptr == '\0')
226 				goto out;
227 			continue;
228 		}
229 
230 		do {
231 			ptr++;
232 			EAT_WHITE_SPACE(ptr);
233 
234 			if ((strncmp(ptr, name, len) == 0) &&
235 			    (ptr[len] == '=')) {
236 				ptr += len + 1;
237 				if ((*ptr == '\'') || (*ptr == '"')) {
238 					ret = ptr + 1;
239 					end_char = *ptr;
240 					ptr++;
241 				} else {
242 					ret = ptr;
243 					end_char = ',';
244 				}
245 				goto consume_property;
246 			}
247 
248 			/*
249 			 * We have a property, and it's not the one we're
250 			 * interested in.  Skip the property name.  A name
251 			 * can end with '=', a comma, or white space.
252 			 */
253 			while ((*ptr != '\0') && (*ptr != '=') &&
254 			    (*ptr != ',') && (!ISSPACE(*ptr)))
255 				ptr++;
256 
257 			/*
258 			 * We only want to go through the rest of the inner
259 			 * loop if we have a comma.  If we have a property
260 			 * name without a value, either continue or break.
261 			 */
262 			if (*ptr == '\0')
263 				goto out;
264 			else if (*ptr == ',')
265 				continue;
266 			else if (ISSPACE(*ptr))
267 				break;
268 			ptr++;
269 
270 			/*
271 			 * Is the property quoted?
272 			 */
273 			if ((*ptr == '\'') || (*ptr == '"')) {
274 				end_char = *ptr;
275 				ptr++;
276 			} else {
277 				/*
278 				 * Not quoted, so the string ends at a comma
279 				 * or at white space.  Deal with white space
280 				 * later.
281 				 */
282 				end_char = ',';
283 			}
284 
285 			/*
286 			 * Now, we can ignore any characters until we find
287 			 * end_char.
288 			 */
289 consume_property:
290 			for (; (*ptr != '\0') && (*ptr != end_char); ptr++) {
291 				if ((end_char == ',') && ISSPACE(*ptr))
292 					break;
293 			}
294 			if (*ptr && (*ptr != ',') && !ISSPACE(*ptr))
295 				ptr++;
296 		} while (*ptr == ',');
297 	}
298 out:
299 	return (ret);
300 }
301 
302 /*
303  * Find prop from boot env module. The data in module is list of C strings
304  * name=value, the list is terminated by double nul.
305  */
306 static const char *
find_boot_env_prop(const char * name)307 find_boot_env_prop(const char *name)
308 {
309 	char *ptr;
310 	size_t len;
311 	uintptr_t size;
312 
313 	if (boot_env.be_env == NULL)
314 		return (NULL);
315 
316 	ptr = boot_env.be_env;
317 	len = strlen(name);
318 
319 	/*
320 	 * Make sure we have at least len + 2 bytes in the environment.
321 	 * We are looking for name=value\0 constructs, and the environment
322 	 * itself is terminated by '\0'.
323 	 */
324 	if (boot_env.be_size < len + 2)
325 		return (NULL);
326 
327 	do {
328 		if ((strncmp(ptr, name, len) == 0) && (ptr[len] == '=')) {
329 			ptr += len + 1;
330 			return (ptr);
331 		}
332 		/* find the first '\0' */
333 		while (*ptr != '\0') {
334 			ptr++;
335 			size = (uintptr_t)ptr - (uintptr_t)boot_env.be_env;
336 			if (size > boot_env.be_size)
337 				return (NULL);
338 		}
339 		ptr++;
340 
341 		/* If the remainder is shorter than name + 2, get out. */
342 		size = (uintptr_t)ptr - (uintptr_t)boot_env.be_env;
343 		if (boot_env.be_size - size < len + 2)
344 			return (NULL);
345 	} while (*ptr != '\0');
346 	return (NULL);
347 }
348 
349 /*
350  * Get prop value from either command line or boot environment.
351  * We always check kernel command line first, as this will keep the
352  * functionality and will allow user to override the values in environment.
353  */
354 const char *
find_boot_prop(const char * name)355 find_boot_prop(const char *name)
356 {
357 	const char *value = find_boot_line_prop(name);
358 
359 	if (value == NULL)
360 		value = find_boot_env_prop(name);
361 	return (value);
362 }
363 
364 #define	MATCHES(p, pat)	\
365 	(strncmp(p, pat, strlen(pat)) == 0 ? (p += strlen(pat), 1) : 0)
366 
367 #define	SKIP(p, c)				\
368 	while (*(p) != 0 && *p != (c))		\
369 		++(p);				\
370 	if (*(p) == (c))			\
371 		++(p);
372 
373 /*
374  * find a tty mode property either from cmdline or from boot properties
375  */
376 static const char *
get_mode_value(char * name)377 get_mode_value(char *name)
378 {
379 	/*
380 	 * when specified on boot line it looks like "name" "="....
381 	 */
382 	if (boot_line != NULL) {
383 		return (find_boot_prop(name));
384 	}
385 
386 #if defined(_BOOT)
387 	return (NULL);
388 #else
389 	/*
390 	 * if we're running in the full kernel we check the bootenv.rc settings
391 	 */
392 	{
393 		static char propval[20];
394 
395 		propval[0] = 0;
396 		if (do_bsys_getproplen(NULL, name) <= 0)
397 			return (NULL);
398 		(void) do_bsys_getprop(NULL, name, propval);
399 		return (propval);
400 	}
401 #endif
402 }
403 
404 /*
405  * adjust serial port based on properties
406  * These come either from the cmdline or from boot properties.
407  */
408 static void
serial_adjust_prop(void)409 serial_adjust_prop(void)
410 {
411 	char propname[20];
412 	const char *propval;
413 	const char *p;
414 	ulong_t baud;
415 	uchar_t lcr = 0;
416 	uchar_t mcr = DTR | RTS;
417 
418 	(void) strcpy(propname, "ttyX-mode");
419 	propname[3] = 'a' + tty_num;
420 	propval = get_mode_value(propname);
421 #if !defined(_BOOT)
422 	if (propval != NULL)
423 		bootprop_set_tty_mode = B_TRUE;
424 #endif
425 	if (propval == NULL)
426 		propval = "9600,8,n,1,-";
427 
428 	/* property is of the form: "9600,8,n,1,-" */
429 	p = propval;
430 	if (MATCHES(p, "110,"))
431 		baud = ASY110;
432 	else if (MATCHES(p, "150,"))
433 		baud = ASY150;
434 	else if (MATCHES(p, "300,"))
435 		baud = ASY300;
436 	else if (MATCHES(p, "600,"))
437 		baud = ASY600;
438 	else if (MATCHES(p, "1200,"))
439 		baud = ASY1200;
440 	else if (MATCHES(p, "2400,"))
441 		baud = ASY2400;
442 	else if (MATCHES(p, "4800,"))
443 		baud = ASY4800;
444 	else if (MATCHES(p, "19200,"))
445 		baud = ASY19200;
446 	else if (MATCHES(p, "38400,"))
447 		baud = ASY38400;
448 	else if (MATCHES(p, "57600,"))
449 		baud = ASY57600;
450 	else if (MATCHES(p, "115200,"))
451 		baud = ASY115200;
452 	else {
453 		baud = ASY9600;
454 		SKIP(p, ',');
455 	}
456 	outb(port + LCR, DLAB);
457 	outb(port + DAT + DLL, baud & 0xff);
458 	outb(port + DAT + DLH, (baud >> 8) & 0xff);
459 
460 	switch (*p) {
461 	case '5':
462 		lcr |= BITS5;
463 		++p;
464 		break;
465 	case '6':
466 		lcr |= BITS6;
467 		++p;
468 		break;
469 	case '7':
470 		lcr |= BITS7;
471 		++p;
472 		break;
473 	case '8':
474 		++p;
475 		/* FALLTHROUGH */
476 	default:
477 		lcr |= BITS8;
478 		break;
479 	}
480 
481 	SKIP(p, ',');
482 
483 	switch (*p) {
484 	case 'n':
485 		lcr |= PARITY_NONE;
486 		++p;
487 		break;
488 	case 'o':
489 		lcr |= PARITY_ODD;
490 		++p;
491 		break;
492 	case 'e':
493 		++p;
494 		/* FALLTHROUGH */
495 	default:
496 		lcr |= PARITY_EVEN;
497 		break;
498 	}
499 
500 
501 	SKIP(p, ',');
502 
503 	switch (*p) {
504 	case '1':
505 		/* STOP1 is 0 */
506 		++p;
507 		break;
508 	default:
509 		lcr |= STOP2;
510 		break;
511 	}
512 	/* set parity bits */
513 	outb(port + LCR, lcr);
514 
515 	(void) strcpy(propname, "ttyX-rts-dtr-off");
516 	propname[3] = 'a' + tty_num;
517 	propval = get_mode_value(propname);
518 	if (propval == NULL)
519 		propval = "false";
520 	if (propval[0] != 'f' && propval[0] != 'F')
521 		mcr = 0;
522 	/* set modem control bits */
523 	outb(port + MCR, mcr | OUT2);
524 }
525 
526 /* Obtain the console type */
527 int
boot_console_type(int * tnum)528 boot_console_type(int *tnum)
529 {
530 	if (tnum != NULL)
531 		*tnum = tty_num;
532 	return (console);
533 }
534 
535 /*
536  * A structure to map console names to values.
537  */
538 typedef struct {
539 	char *name;
540 	int value;
541 } console_value_t;
542 
543 console_value_t console_devices[] = {
544 	{ "ttya", CONS_TTY },	/* 0 */
545 	{ "ttyb", CONS_TTY },	/* 1 */
546 	{ "ttyc", CONS_TTY },	/* 2 */
547 	{ "ttyd", CONS_TTY },	/* 3 */
548 	{ "text", CONS_SCREEN_TEXT },
549 	{ "graphics", CONS_SCREEN_GRAPHICS },
550 #if defined(__xpv)
551 	{ "hypervisor", CONS_HYPERVISOR },
552 #endif
553 #if !defined(_BOOT)
554 	{ "usb-serial", CONS_USBSER },
555 #endif
556 	{ NULL, CONS_INVALID }
557 };
558 
559 static void
bcons_init_env(struct xboot_info * xbi)560 bcons_init_env(struct xboot_info *xbi)
561 {
562 	uint32_t i;
563 	struct boot_modules *modules;
564 
565 	modules = (struct boot_modules *)(uintptr_t)xbi->bi_modules;
566 	for (i = 0; i < xbi->bi_module_cnt; i++) {
567 		if (modules[i].bm_type == BMT_ENV)
568 			break;
569 	}
570 	if (i == xbi->bi_module_cnt)
571 		return;
572 
573 	boot_env.be_env = (char *)(uintptr_t)modules[i].bm_addr;
574 	boot_env.be_size = modules[i].bm_size;
575 }
576 
577 int
boot_fb(struct xboot_info * xbi,int console)578 boot_fb(struct xboot_info *xbi, int console)
579 {
580 	if (xbi_fb_init(xbi, &bcons_dev) == B_FALSE)
581 		return (console);
582 
583 	/*
584 	 * The framebuffer address is not set; fall back to the serial console.
585 	 */
586 	if (fb_info.paddr == 0)
587 		return (CONS_TTY);
588 
589 #if defined(_BOOT)
590 	/*
591 	 * If the firmware placed the framebuffer mapping above the 32-bit
592 	 * boundary, we cannot use it from dboot.
593 	 */
594 	if (fb_info.paddr >= UINTPTR_MAX)
595 		return (CONS_TTY);
596 #endif
597 
598 	fb_info.terminal.x = VGA_TEXT_COLS;
599 	fb_info.terminal.y = VGA_TEXT_ROWS;
600 	boot_fb_init(CONS_FRAMEBUFFER);
601 
602 	if (console == CONS_SCREEN_TEXT)
603 		return (CONS_FRAMEBUFFER);
604 	return (console);
605 }
606 
607 /*
608  * TODO.
609  * quick and dirty local atoi. Perhaps should build with strtol, but
610  * dboot & early boot mix does overcomplicate things much.
611  * Stolen from libc anyhow.
612  */
613 static int
atoi(const char * p)614 atoi(const char *p)
615 {
616 	int n, c, neg = 0;
617 	unsigned char *up = (unsigned char *)p;
618 
619 	if (!isdigit(c = *up)) {
620 		while (isspace(c))
621 			c = *++up;
622 		switch (c) {
623 		case '-':
624 			neg++;
625 			/* FALLTHROUGH */
626 		case '+':
627 			c = *++up;
628 		}
629 		if (!isdigit(c))
630 			return (0);
631 	}
632 	for (n = '0' - c; isdigit(c = *++up);) {
633 		n *= 10; /* two steps to avoid unnecessary overflow */
634 		n += '0' - c; /* accum neg to avoid surprises at MAX */
635 	}
636 	return (neg ? n : -n);
637 }
638 
639 static void
bcons_init_fb(void)640 bcons_init_fb(void)
641 {
642 	const char *propval;
643 	int intval;
644 
645 	/* initialize with explicit default values */
646 	fb_info.fg_color = CONS_COLOR;
647 	fb_info.bg_color = 0;
648 	fb_info.inverse = B_FALSE;
649 	fb_info.inverse_screen = B_FALSE;
650 
651 	/* color values are 0 - 255 */
652 	propval = find_boot_prop("tem.fg_color");
653 	if (propval != NULL) {
654 		intval = atoi(propval);
655 		if (intval >= 0 && intval <= 255)
656 			fb_info.fg_color = intval;
657 	}
658 
659 	/* color values are 0 - 255 */
660 	propval = find_boot_prop("tem.bg_color");
661 	if (propval != NULL && ISDIGIT(*propval)) {
662 		intval = atoi(propval);
663 		if (intval >= 0 && intval <= 255)
664 			fb_info.bg_color = intval;
665 	}
666 
667 	/* get inverses. allow 0, 1, true, false */
668 	propval = find_boot_prop("tem.inverse");
669 	if (propval != NULL) {
670 		if (*propval == '1' || MATCHES(propval, "true"))
671 			fb_info.inverse = B_TRUE;
672 	}
673 
674 	propval = find_boot_prop("tem.inverse-screen");
675 	if (propval != NULL) {
676 		if (*propval == '1' || MATCHES(propval, "true"))
677 			fb_info.inverse_screen = B_TRUE;
678 	}
679 
680 #if defined(_BOOT)
681 	/*
682 	 * Load cursor position from bootloader only in dboot,
683 	 * dboot will pass cursor position to kernel via xboot info.
684 	 */
685 	propval = find_boot_prop("tem.cursor.row");
686 	if (propval != NULL) {
687 		intval = atoi(propval);
688 		if (intval >= 0 && intval <= 0xFFFF)
689 			fb_info.cursor.pos.y = intval;
690 	}
691 
692 	propval = find_boot_prop("tem.cursor.col");
693 	if (propval != NULL) {
694 		intval = atoi(propval);
695 		if (intval >= 0 && intval <= 0xFFFF)
696 			fb_info.cursor.pos.x = intval;
697 	}
698 #endif
699 }
700 
701 /*
702  * Go through the known console device names trying to match the string we were
703  * given.  The string on the command line must end with a comma or white space.
704  *
705  * For convenience, we provide the caller with an integer index for the CONS_TTY
706  * case.
707  */
708 static int
lookup_console_device(const char * cons_str,int * indexp)709 lookup_console_device(const char *cons_str, int *indexp)
710 {
711 	int n, cons;
712 	size_t len, cons_len;
713 	console_value_t *consolep;
714 
715 	cons = CONS_INVALID;
716 	if (cons_str != NULL) {
717 
718 		cons_len = strlen(cons_str);
719 		for (n = 0; console_devices[n].name != NULL; n++) {
720 			consolep = &console_devices[n];
721 			len = strlen(consolep->name);
722 			if ((len <= cons_len) && ((cons_str[len] == '\0') ||
723 			    (cons_str[len] == ',') || (cons_str[len] == '\'') ||
724 			    (cons_str[len] == '"') || ISSPACE(cons_str[len])) &&
725 			    (strncmp(cons_str, consolep->name, len) == 0)) {
726 				cons = consolep->value;
727 				if (cons == CONS_TTY)
728 					*indexp = n;
729 				break;
730 			}
731 		}
732 	}
733 	return (cons);
734 }
735 
736 void
bcons_init(struct xboot_info * xbi)737 bcons_init(struct xboot_info *xbi)
738 {
739 	const char *cons_str;
740 #if !defined(_BOOT)
741 	static char console_text[] = "text";
742 	extern int post_fastreboot;
743 #endif
744 
745 	if (xbi == NULL) {
746 		/* This is very early dboot console, set up ttya. */
747 		console = CONS_TTY;
748 		serial_init();
749 		return;
750 	}
751 
752 	/* Set up data to fetch properties from commad line and boot env. */
753 	boot_line = (char *)(uintptr_t)xbi->bi_cmdline;
754 	bcons_init_env(xbi);
755 	console = CONS_INVALID;
756 
757 	/* set up initial fb_info */
758 	bcons_init_fb();
759 
760 #if defined(__xpv)
761 	bcons_init_xen(boot_line);
762 #endif /* __xpv */
763 
764 	/*
765 	 * First check for diag-device.
766 	 */
767 	cons_str = find_boot_prop("diag-device");
768 	if (cons_str != NULL)
769 		diag = lookup_console_device(cons_str, &tty_num);
770 
771 	cons_str = find_boot_prop("console");
772 	if (cons_str == NULL)
773 		cons_str = find_boot_prop("output-device");
774 
775 #if !defined(_BOOT)
776 	if (post_fastreboot && strcmp(cons_str, "graphics") == 0)
777 		cons_str = console_text;
778 #endif
779 
780 	if (cons_str != NULL)
781 		console = lookup_console_device(cons_str, &tty_num);
782 
783 #if defined(__xpv)
784 	/*
785 	 * domU's always use the hypervisor regardless of what
786 	 * the console variable may be set to.
787 	 */
788 	if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
789 		console = CONS_HYPERVISOR;
790 		console_hypervisor_redirect = B_TRUE;
791 	}
792 #endif /* __xpv */
793 
794 	if (console == CONS_INVALID)
795 		console = CONS_SCREEN_TEXT;
796 
797 #if defined(__xpv)
798 	if (DOMAIN_IS_INITDOMAIN(xen_info)) {
799 		switch (HYPERVISOR_console_io(CONSOLEIO_get_device, 0, NULL)) {
800 			case XEN_CONSOLE_COM1:
801 			case XEN_CONSOLE_COM2:
802 				console_hypervisor_device = CONS_TTY;
803 				console_hypervisor_tty_num = tty_num;
804 				break;
805 			case XEN_CONSOLE_VGA:
806 				/*
807 				 * Currently xen doesn't really support
808 				 * keyboard/display console devices.
809 				 * What this setting means is that
810 				 * "vga=keep" has been enabled, which is
811 				 * more of a xen debugging tool that a
812 				 * true console mode.  Hence, we're going
813 				 * to ignore this xen "console" setting.
814 				 */
815 				/*FALLTHROUGH*/
816 			default:
817 				console_hypervisor_device = CONS_INVALID;
818 		}
819 	}
820 
821 	/*
822 	 * if the hypervisor is using the currently selected serial
823 	 * port then default to using the hypervisor as the console
824 	 * device.
825 	 */
826 	if (console == console_hypervisor_device) {
827 		console = CONS_HYPERVISOR;
828 		console_hypervisor_redirect = B_TRUE;
829 	}
830 #endif /* __xpv */
831 
832 	/* make sure the FB is set up if present */
833 	console = boot_fb(xbi, console);
834 	switch (console) {
835 	case CONS_TTY:
836 		serial_init();
837 		break;
838 
839 	case CONS_HYPERVISOR:
840 		break;
841 
842 #if !defined(_BOOT)
843 	case CONS_USBSER:
844 		/*
845 		 * We can't do anything with the usb serial
846 		 * until we have memory management.
847 		 */
848 		break;
849 #endif
850 	case CONS_SCREEN_GRAPHICS:
851 		kb_init();
852 		break;
853 	case CONS_SCREEN_TEXT:
854 		boot_vga_init(&bcons_dev);
855 		/* Fall through */
856 	default:
857 		kb_init();
858 		break;
859 	}
860 
861 	/*
862 	 * Initialize diag device unless already done.
863 	 */
864 	switch (diag) {
865 	case CONS_TTY:
866 		if (console != CONS_TTY)
867 			serial_init();
868 		break;
869 	case CONS_SCREEN_GRAPHICS:
870 	case CONS_SCREEN_TEXT:
871 		if (console != CONS_SCREEN_GRAPHICS &&
872 		    console != CONS_SCREEN_TEXT)
873 			kb_init();
874 		break;
875 	default:
876 		break;
877 	}
878 }
879 
880 static void
serial_putchar(int c)881 serial_putchar(int c)
882 {
883 	int checks = 10000;
884 
885 	while (((inb(port + LSR) & XHRE) == 0) && checks--)
886 		;
887 	outb(port + DAT, (char)c);
888 }
889 
890 static int
serial_getchar(void)891 serial_getchar(void)
892 {
893 	uchar_t lsr;
894 
895 	while (serial_ischar() == 0)
896 		;
897 
898 	lsr = inb(port + LSR);
899 	if (lsr & (SERIAL_BREAK | SERIAL_FRAME |
900 	    SERIAL_PARITY | SERIAL_OVERRUN)) {
901 		if (lsr & SERIAL_OVERRUN) {
902 			return (inb(port + DAT));
903 		} else {
904 			/* Toss the garbage */
905 			(void) inb(port + DAT);
906 			return (0);
907 		}
908 	}
909 	return (inb(port + DAT));
910 }
911 
912 static int
serial_ischar(void)913 serial_ischar(void)
914 {
915 	return (inb(port + LSR) & RCA);
916 }
917 
918 static void
btem_control(btem_state_t * btem,int c)919 btem_control(btem_state_t *btem, int c)
920 {
921 	int y, rows, cols;
922 
923 	rows = fb_info.cursor.pos.y;
924 	cols = fb_info.cursor.pos.x;
925 
926 	btem->btem_state = A_STATE_START;
927 	switch (c) {
928 	case A_BS:
929 		bcons_dev.bd_setpos(rows, cols - 1);
930 		break;
931 
932 	case A_HT:
933 		cols += 8 - (cols % 8);
934 		if (cols >= fb_info.terminal.x)
935 			cols = fb_info.terminal.x - 1;
936 		bcons_dev.bd_setpos(rows, cols);
937 		break;
938 
939 	case A_CR:
940 		bcons_dev.bd_setpos(rows, 0);
941 		break;
942 
943 	case A_FF:
944 		for (y = 0; y < fb_info.terminal.y; y++) {
945 			bcons_dev.bd_setpos(y, 0);
946 			bcons_dev.bd_eraseline();
947 		}
948 		bcons_dev.bd_setpos(0, 0);
949 		break;
950 
951 	case A_ESC:
952 		btem->btem_state = A_STATE_ESC;
953 		break;
954 
955 	default:
956 		bcons_dev.bd_putchar(c);
957 		break;
958 	}
959 }
960 
961 /*
962  * if parameters [0..count - 1] are not set, set them to the value
963  * of newparam.
964  */
965 static void
btem_setparam(btem_state_t * btem,int count,int newparam)966 btem_setparam(btem_state_t *btem, int count, int newparam)
967 {
968 	int i;
969 
970 	for (i = 0; i < count; i++) {
971 		if (btem->btem_params[i] == -1)
972 			btem->btem_params[i] = newparam;
973 	}
974 }
975 
976 static void
btem_chkparam(btem_state_t * btem,int c)977 btem_chkparam(btem_state_t *btem, int c)
978 {
979 	int rows, cols;
980 
981 	rows = fb_info.cursor.pos.y;
982 	cols = fb_info.cursor.pos.x;
983 	switch (c) {
984 	case '@':			/* insert char */
985 		btem_setparam(btem, 1, 1);
986 		bcons_dev.bd_shift(btem->btem_params[0]);
987 		break;
988 
989 	case 'A':			/* cursor up */
990 		btem_setparam(btem, 1, 1);
991 		bcons_dev.bd_setpos(rows - btem->btem_params[0], cols);
992 		break;
993 
994 	case 'B':			/* cursor down */
995 		btem_setparam(btem, 1, 1);
996 		bcons_dev.bd_setpos(rows + btem->btem_params[0], cols);
997 		break;
998 
999 	case 'C':			/* cursor right */
1000 		btem_setparam(btem, 1, 1);
1001 		bcons_dev.bd_setpos(rows, cols + btem->btem_params[0]);
1002 		break;
1003 
1004 	case 'D':			/* cursor left */
1005 		btem_setparam(btem, 1, 1);
1006 		bcons_dev.bd_setpos(rows, cols - btem->btem_params[0]);
1007 		break;
1008 
1009 	case 'K':
1010 		bcons_dev.bd_eraseline();
1011 		break;
1012 	default:
1013 		/* bcons_dev.bd_putchar(c); */
1014 		break;
1015 	}
1016 	btem->btem_state = A_STATE_START;
1017 }
1018 
1019 static void
btem_chkparam_qmark(btem_state_t * btem,int c)1020 btem_chkparam_qmark(btem_state_t *btem, int c)
1021 {
1022 	/*
1023 	 * This code is intentionally NOP, we do process
1024 	 * \E[?25h and \E[?25l, but our cursor is always shown.
1025 	 */
1026 	switch (c) {
1027 	case 'h': /* DEC private mode set */
1028 		btem_setparam(btem, 1, 1);
1029 		switch (btem->btem_params[0]) {
1030 		case 25: /* show cursor */
1031 			break;
1032 		}
1033 		break;
1034 	case 'l':
1035 		/* DEC private mode reset */
1036 		btem_setparam(btem, 1, 1);
1037 		switch (btem->btem_params[0]) {
1038 		case 25: /* hide cursor */
1039 			break;
1040 		}
1041 		break;
1042 	}
1043 	btem->btem_state = A_STATE_START;
1044 }
1045 
1046 static void
btem_getparams(btem_state_t * btem,int c)1047 btem_getparams(btem_state_t *btem, int c)
1048 {
1049 	if (isdigit(c)) {
1050 		btem->btem_paramval = btem->btem_paramval * 10 + c - '0';
1051 		btem->btem_gotparam = B_TRUE;
1052 		return;
1053 	}
1054 
1055 	if (btem->btem_curparam < BTEM_MAXPARAMS) {
1056 		if (btem->btem_gotparam == B_TRUE) {
1057 			btem->btem_params[btem->btem_curparam] =
1058 			    btem->btem_paramval;
1059 		}
1060 		btem->btem_curparam++;
1061 	}
1062 
1063 	if (c == ';') {
1064 		/* Restart parameter search */
1065 		btem->btem_gotparam = B_FALSE;
1066 		btem->btem_paramval = 0;
1067 	} else {
1068 		if (btem->btem_state == A_STATE_CSI_QMARK)
1069 			btem_chkparam_qmark(btem, c);
1070 		else
1071 			btem_chkparam(btem, c);
1072 	}
1073 }
1074 
1075 /* Simple boot terminal parser. */
1076 static void
btem_parse(btem_state_t * btem,int c)1077 btem_parse(btem_state_t *btem, int c)
1078 {
1079 	int i;
1080 
1081 	/* Normal state? */
1082 	if (btem->btem_state == A_STATE_START) {
1083 		if (c == A_CSI || c < ' ')
1084 			btem_control(btem, c);
1085 		else
1086 			bcons_dev.bd_putchar(c);
1087 		return;
1088 	}
1089 
1090 	/* In <ESC> sequence */
1091 	if (btem->btem_state != A_STATE_ESC) {
1092 		if (btem->btem_state != A_STATE_CSI) {
1093 			btem_getparams(btem, c);
1094 			return;
1095 		}
1096 
1097 		switch (c) {
1098 		case '?':
1099 			btem->btem_state = A_STATE_CSI_QMARK;
1100 			return;
1101 		default:
1102 			btem_getparams(btem, c);
1103 			return;
1104 		}
1105 	}
1106 
1107 	/* Previous char was <ESC> */
1108 	switch (c) {
1109 	case '[':
1110 		btem->btem_curparam = 0;
1111 		btem->btem_paramval = 0;
1112 		btem->btem_gotparam = B_FALSE;
1113 		/* clear the parameters */
1114 		for (i = 0; i < BTEM_MAXPARAMS; i++)
1115 			btem->btem_params[i] = -1;
1116 		btem->btem_state = A_STATE_CSI;
1117 		return;
1118 
1119 	case 'Q':	/* <ESC>Q */
1120 	case 'C':	/* <ESC>C */
1121 		btem->btem_state = A_STATE_START;
1122 		return;
1123 
1124 	default:
1125 		btem->btem_state = A_STATE_START;
1126 		break;
1127 	}
1128 
1129 	if (c < ' ')
1130 		btem_control(btem, c);
1131 	else
1132 		bcons_dev.bd_putchar(c);
1133 }
1134 
1135 static void
_doputchar(int device,int c)1136 _doputchar(int device, int c)
1137 {
1138 	switch (device) {
1139 	case CONS_TTY:
1140 		serial_putchar(c);
1141 		return;
1142 	case CONS_SCREEN_TEXT:
1143 	case CONS_FRAMEBUFFER:
1144 		bcons_dev.bd_cursor(B_FALSE);
1145 		btem_parse(&boot_tem, c);
1146 		bcons_dev.bd_cursor(B_TRUE);
1147 		return;
1148 	case CONS_SCREEN_GRAPHICS:
1149 #if !defined(_BOOT)
1150 	case CONS_USBSER:
1151 		defcons_putchar(c);
1152 #endif /* _BOOT */
1153 	default:
1154 		return;
1155 	}
1156 }
1157 
1158 void
bcons_putchar(int c)1159 bcons_putchar(int c)
1160 {
1161 #if defined(__xpv)
1162 	if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
1163 	    console == CONS_HYPERVISOR) {
1164 		bcons_putchar_xen(c);
1165 		return;
1166 	}
1167 #endif /* __xpv */
1168 
1169 	if (c == '\n') {
1170 		_doputchar(console, '\r');
1171 		if (diag != console)
1172 			_doputchar(diag, '\r');
1173 	}
1174 	_doputchar(console, c);
1175 	if (diag != console)
1176 		_doputchar(diag, c);
1177 }
1178 
1179 /*
1180  * kernel character input functions
1181  */
1182 int
bcons_getchar(void)1183 bcons_getchar(void)
1184 {
1185 #if defined(__xpv)
1186 	if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
1187 	    console == CONS_HYPERVISOR)
1188 		return (bcons_getchar_xen());
1189 #endif /* __xpv */
1190 
1191 	for (;;) {
1192 		if (console == CONS_TTY || diag == CONS_TTY) {
1193 			if (serial_ischar())
1194 				return (serial_getchar());
1195 		}
1196 		if (console != CONS_INVALID || diag != CONS_INVALID) {
1197 			if (kb_ischar())
1198 				return (kb_getchar());
1199 		}
1200 	}
1201 }
1202 
1203 /*
1204  * Nothing below is used by dboot.
1205  */
1206 #if !defined(_BOOT)
1207 
1208 int
bcons_ischar(void)1209 bcons_ischar(void)
1210 {
1211 	int c = 0;
1212 
1213 #if defined(__xpv)
1214 	if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
1215 	    console == CONS_HYPERVISOR)
1216 		return (bcons_ischar_xen());
1217 #endif /* __xpv */
1218 
1219 	switch (console) {
1220 	case CONS_TTY:
1221 		c = serial_ischar();
1222 		break;
1223 
1224 	case CONS_INVALID:
1225 		break;
1226 
1227 	default:
1228 		c = kb_ischar();
1229 	}
1230 	if (c != 0)
1231 		return (c);
1232 
1233 	switch (diag) {
1234 	case CONS_TTY:
1235 		c = serial_ischar();
1236 		break;
1237 
1238 	case CONS_INVALID:
1239 		break;
1240 
1241 	default:
1242 		c = kb_ischar();
1243 	}
1244 
1245 	return (c);
1246 }
1247 
1248 /*
1249  * 2nd part of console initialization: we've now processed bootenv.rc; update
1250  * console settings as appropriate. This only really processes serial console
1251  * modifications.
1252  */
1253 void
bcons_post_bootenvrc(char * inputdev,char * outputdev,char * consoledev)1254 bcons_post_bootenvrc(char *inputdev, char *outputdev, char *consoledev)
1255 {
1256 	int cons = CONS_INVALID;
1257 	int ttyn;
1258 	char *devnames[] = { consoledev, outputdev, inputdev, NULL };
1259 	int i;
1260 	extern int post_fastreboot;
1261 
1262 	ttyn = 0;
1263 	if (post_fastreboot && console == CONS_SCREEN_GRAPHICS)
1264 		console = CONS_SCREEN_TEXT;
1265 
1266 	/*
1267 	 * USB serial and GRAPHICS console: we just collect data into a buffer.
1268 	 */
1269 	if (console == CONS_USBSER || console == CONS_SCREEN_GRAPHICS) {
1270 		extern void *defcons_init(size_t);
1271 		defcons_buf = defcons_cur = defcons_init(MMU_PAGESIZE);
1272 		return;
1273 	}
1274 
1275 	for (i = 0; devnames[i] != NULL; i++) {
1276 		cons = lookup_console_device(devnames[i], &ttyn);
1277 		if (cons != CONS_INVALID)
1278 			break;
1279 	}
1280 
1281 	if (cons == CONS_INVALID) {
1282 		/*
1283 		 * No console change, but let's see if bootenv.rc had a mode
1284 		 * setting we should apply.
1285 		 */
1286 		if (console == CONS_TTY && !bootprop_set_tty_mode)
1287 			serial_init();
1288 		return;
1289 	}
1290 
1291 #if defined(__xpv)
1292 	/*
1293 	 * if the hypervisor is using the currently selected console device then
1294 	 * default to using the hypervisor as the console device.
1295 	 */
1296 	if (cons == console_hypervisor_device) {
1297 		cons = CONS_HYPERVISOR;
1298 		console_hypervisor_redirect = B_TRUE;
1299 	}
1300 #endif /* __xpv */
1301 
1302 	console = cons;
1303 
1304 	if (console == CONS_TTY) {
1305 		tty_num = ttyn;
1306 		serial_init();
1307 	}
1308 }
1309 
1310 #if defined(__xpv)
1311 boolean_t
bcons_hypervisor_redirect(void)1312 bcons_hypervisor_redirect(void)
1313 {
1314 	return (console_hypervisor_redirect);
1315 }
1316 
1317 void
bcons_device_change(int new_console)1318 bcons_device_change(int new_console)
1319 {
1320 	if (new_console < CONS_MIN || new_console > CONS_MAX)
1321 		return;
1322 
1323 	/*
1324 	 * If we are asked to switch the console to the hypervisor, that
1325 	 * really means to switch the console to whichever device the
1326 	 * hypervisor is/was using.
1327 	 */
1328 	if (new_console == CONS_HYPERVISOR)
1329 		new_console = console_hypervisor_device;
1330 
1331 	console = new_console;
1332 
1333 	if (new_console == CONS_TTY) {
1334 		tty_num = console_hypervisor_tty_num;
1335 		serial_init();
1336 	}
1337 }
1338 #endif /* __xpv */
1339 
1340 static void
defcons_putchar(int c)1341 defcons_putchar(int c)
1342 {
1343 	if (defcons_buf != NULL &&
1344 	    defcons_cur + 1 - defcons_buf < MMU_PAGESIZE) {
1345 		*defcons_cur++ = c;
1346 		*defcons_cur = 0;
1347 	}
1348 }
1349 
1350 #endif /* _BOOT */
1351