xref: /illumos-gate/usr/src/uts/i86pc/boot/boot_console.c (revision b6805bf78d2bbbeeaea8909a05623587b42d58b3)
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  *
24  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 #include <sys/types.h>
29 #include <sys/systm.h>
30 #include <sys/archsystm.h>
31 #include <sys/boot_console.h>
32 #include <sys/panic.h>
33 #include <sys/ctype.h>
34 #if defined(__xpv)
35 #include <sys/hypervisor.h>
36 #endif /* __xpv */
37 
38 #include "boot_serial.h"
39 #include "boot_vga.h"
40 
41 #if defined(_BOOT)
42 #include <dboot/dboot_asm.h>
43 #include <dboot/dboot_xboot.h>
44 #else /* _BOOT */
45 #include <sys/bootconf.h>
46 #if defined(__xpv)
47 #include <sys/evtchn_impl.h>
48 #endif /* __xpv */
49 static char *defcons_buf;
50 static char *defcons_cur;
51 #endif /* _BOOT */
52 
53 #if defined(__xpv)
54 extern void bcons_init_xen(char *);
55 extern void bcons_putchar_xen(int);
56 extern int bcons_getchar_xen(void);
57 extern int bcons_ischar_xen(void);
58 #endif /* __xpv */
59 
60 static int cons_color = CONS_COLOR;
61 static int console = CONS_SCREEN_TEXT;
62 static int tty_num = 0;
63 static int tty_addr[] = {0x3f8, 0x2f8, 0x3e8, 0x2e8};
64 #if defined(__xpv)
65 static int console_hypervisor_redirect = B_FALSE;
66 static int console_hypervisor_device = CONS_INVALID;
67 static int console_hypervisor_tty_num = 0;
68 
69 /* Obtain the hypervisor console type */
70 int
71 console_hypervisor_dev_type(int *tnum)
72 {
73 	if (tnum != NULL)
74 		*tnum = console_hypervisor_tty_num;
75 	return (console_hypervisor_device);
76 }
77 #endif /* __xpv */
78 
79 static int serial_ischar(void);
80 static int serial_getchar(void);
81 static void serial_putchar(int);
82 static void serial_adjust_prop(void);
83 
84 static char *boot_line = NULL;
85 
86 #if !defined(_BOOT)
87 /* Set if the console or mode are expressed in the boot line */
88 static int console_set, console_mode_set;
89 #endif
90 
91 /* Clear the screen and initialize VIDEO, XPOS and YPOS. */
92 void
93 clear_screen(void)
94 {
95 	/*
96 	 * XXX should set vga mode so we don't depend on the
97 	 * state left by the boot loader.  Note that we have to
98 	 * enable the cursor before clearing the screen since
99 	 * the cursor position is dependant upon the cursor
100 	 * skew, which is initialized by vga_cursor_display()
101 	 */
102 	vga_cursor_display();
103 	vga_clear(cons_color);
104 	vga_setpos(0, 0);
105 }
106 
107 /* Put the character C on the screen. */
108 static void
109 screen_putchar(int c)
110 {
111 	int row, col;
112 
113 	vga_getpos(&row, &col);
114 	switch (c) {
115 	case '\t':
116 		col += 8 - (col % 8);
117 		if (col == VGA_TEXT_COLS)
118 			col = 79;
119 		vga_setpos(row, col);
120 		break;
121 
122 	case '\r':
123 		vga_setpos(row, 0);
124 		break;
125 
126 	case '\b':
127 		if (col > 0)
128 			vga_setpos(row, col - 1);
129 		break;
130 
131 	case '\n':
132 		if (row < VGA_TEXT_ROWS - 1)
133 			vga_setpos(row + 1, col);
134 		else
135 			vga_scroll(cons_color);
136 		break;
137 
138 	default:
139 		vga_drawc(c, cons_color);
140 		if (col < VGA_TEXT_COLS -1)
141 			vga_setpos(row, col + 1);
142 		else if (row < VGA_TEXT_ROWS - 1)
143 			vga_setpos(row + 1, 0);
144 		else {
145 			vga_setpos(row, 0);
146 			vga_scroll(cons_color);
147 		}
148 		break;
149 	}
150 }
151 
152 static int port;
153 
154 static void
155 serial_init(void)
156 {
157 	port = tty_addr[tty_num];
158 
159 	outb(port + ISR, 0x20);
160 	if (inb(port + ISR) & 0x20) {
161 		/*
162 		 * 82510 chip is present
163 		 */
164 		outb(port + DAT+7, 0x04);	/* clear status */
165 		outb(port + ISR, 0x40);  /* set to bank 2 */
166 		outb(port + MCR, 0x08);  /* IMD */
167 		outb(port + DAT, 0x21);  /* FMD */
168 		outb(port + ISR, 0x00);  /* set to bank 0 */
169 	} else {
170 		/*
171 		 * set the UART in FIFO mode if it has FIFO buffers.
172 		 * use 16550 fifo reset sequence specified in NS
173 		 * application note. disable fifos until chip is
174 		 * initialized.
175 		 */
176 		outb(port + FIFOR, 0x00);		/* clear */
177 		outb(port + FIFOR, FIFO_ON);		/* enable */
178 		outb(port + FIFOR, FIFO_ON|FIFORXFLSH);  /* reset */
179 		outb(port + FIFOR,
180 		    FIFO_ON|FIFODMA|FIFOTXFLSH|FIFORXFLSH|0x80);
181 		if ((inb(port + ISR) & 0xc0) != 0xc0) {
182 			/*
183 			 * no fifo buffers so disable fifos.
184 			 * this is true for 8250's
185 			 */
186 			outb(port + FIFOR, 0x00);
187 		}
188 	}
189 
190 	/* disable interrupts */
191 	outb(port + ICR, 0);
192 
193 #if !defined(_BOOT)
194 	if (IN_XPV_PANIC())
195 		return;
196 #endif
197 
198 	/* adjust setting based on tty properties */
199 	serial_adjust_prop();
200 
201 #if defined(_BOOT)
202 	/*
203 	 * Do a full reset to match console behavior.
204 	 * 0x1B + c - reset everything
205 	 */
206 	serial_putchar(0x1B);
207 	serial_putchar('c');
208 #endif
209 }
210 
211 /* Advance str pointer past white space */
212 #define	EAT_WHITE_SPACE(str)	{			\
213 	while ((*str != '\0') && ISSPACE(*str))		\
214 		str++;					\
215 }
216 
217 /*
218  * boot_line is set when we call here.  Search it for the argument name,
219  * and if found, return a pointer to it.
220  */
221 static char *
222 find_boot_line_prop(const char *name)
223 {
224 	char *ptr;
225 	char *ret = NULL;
226 	char end_char;
227 	size_t len;
228 
229 	if (boot_line == NULL)
230 		return (NULL);
231 
232 	len = strlen(name);
233 
234 	/*
235 	 * We have two nested loops here: the outer loop discards all options
236 	 * except -B, and the inner loop parses the -B options looking for
237 	 * the one we're interested in.
238 	 */
239 	for (ptr = boot_line; *ptr != '\0'; ptr++) {
240 		EAT_WHITE_SPACE(ptr);
241 
242 		if (*ptr == '-') {
243 			ptr++;
244 			while ((*ptr != '\0') && (*ptr != 'B') &&
245 			    !ISSPACE(*ptr))
246 				ptr++;
247 			if (*ptr == '\0')
248 				goto out;
249 			else if (*ptr != 'B')
250 				continue;
251 		} else {
252 			while ((*ptr != '\0') && !ISSPACE(*ptr))
253 				ptr++;
254 			if (*ptr == '\0')
255 				goto out;
256 			continue;
257 		}
258 
259 		do {
260 			ptr++;
261 			EAT_WHITE_SPACE(ptr);
262 
263 			if ((strncmp(ptr, name, len) == 0) &&
264 			    (ptr[len] == '=')) {
265 				ptr += len + 1;
266 				if ((*ptr == '\'') || (*ptr == '"')) {
267 					ret = ptr + 1;
268 					end_char = *ptr;
269 					ptr++;
270 				} else {
271 					ret = ptr;
272 					end_char = ',';
273 				}
274 				goto consume_property;
275 			}
276 
277 			/*
278 			 * We have a property, and it's not the one we're
279 			 * interested in.  Skip the property name.  A name
280 			 * can end with '=', a comma, or white space.
281 			 */
282 			while ((*ptr != '\0') && (*ptr != '=') &&
283 			    (*ptr != ',') && (!ISSPACE(*ptr)))
284 				ptr++;
285 
286 			/*
287 			 * We only want to go through the rest of the inner
288 			 * loop if we have a comma.  If we have a property
289 			 * name without a value, either continue or break.
290 			 */
291 			if (*ptr == '\0')
292 				goto out;
293 			else if (*ptr == ',')
294 				continue;
295 			else if (ISSPACE(*ptr))
296 				break;
297 			ptr++;
298 
299 			/*
300 			 * Is the property quoted?
301 			 */
302 			if ((*ptr == '\'') || (*ptr == '"')) {
303 				end_char = *ptr;
304 				ptr++;
305 			} else {
306 				/*
307 				 * Not quoted, so the string ends at a comma
308 				 * or at white space.  Deal with white space
309 				 * later.
310 				 */
311 				end_char = ',';
312 			}
313 
314 			/*
315 			 * Now, we can ignore any characters until we find
316 			 * end_char.
317 			 */
318 consume_property:
319 			for (; (*ptr != '\0') && (*ptr != end_char); ptr++) {
320 				if ((end_char == ',') && ISSPACE(*ptr))
321 					break;
322 			}
323 			if (*ptr && (*ptr != ',') && !ISSPACE(*ptr))
324 				ptr++;
325 		} while (*ptr == ',');
326 	}
327 out:
328 	return (ret);
329 }
330 
331 
332 #define	MATCHES(p, pat)	\
333 	(strncmp(p, pat, strlen(pat)) == 0 ? (p += strlen(pat), 1) : 0)
334 
335 #define	SKIP(p, c)				\
336 	while (*(p) != 0 && *p != (c))		\
337 		++(p);				\
338 	if (*(p) == (c))			\
339 		++(p);
340 
341 /*
342  * find a tty mode property either from cmdline or from boot properties
343  */
344 static char *
345 get_mode_value(char *name)
346 {
347 	/*
348 	 * when specified on boot line it looks like "name" "="....
349 	 */
350 	if (boot_line != NULL) {
351 		return (find_boot_line_prop(name));
352 	}
353 
354 #if defined(_BOOT)
355 	return (NULL);
356 #else
357 	/*
358 	 * if we're running in the full kernel we check the bootenv.rc settings
359 	 */
360 	{
361 		static char propval[20];
362 
363 		propval[0] = 0;
364 		if (do_bsys_getproplen(NULL, name) <= 0)
365 			return (NULL);
366 		(void) do_bsys_getprop(NULL, name, propval);
367 		return (propval);
368 	}
369 #endif
370 }
371 
372 /*
373  * adjust serial port based on properties
374  * These come either from the cmdline or from boot properties.
375  */
376 static void
377 serial_adjust_prop(void)
378 {
379 	char propname[20];
380 	char *propval;
381 	char *p;
382 	ulong_t baud;
383 	uchar_t lcr = 0;
384 	uchar_t mcr = DTR | RTS;
385 
386 	(void) strcpy(propname, "ttyX-mode");
387 	propname[3] = 'a' + tty_num;
388 	propval = get_mode_value(propname);
389 	if (propval == NULL)
390 		propval = "9600,8,n,1,-";
391 #if !defined(_BOOT)
392 	else
393 		console_mode_set = 1;
394 #endif
395 
396 	/* property is of the form: "9600,8,n,1,-" */
397 	p = propval;
398 	if (MATCHES(p, "110,"))
399 		baud = ASY110;
400 	else if (MATCHES(p, "150,"))
401 		baud = ASY150;
402 	else if (MATCHES(p, "300,"))
403 		baud = ASY300;
404 	else if (MATCHES(p, "600,"))
405 		baud = ASY600;
406 	else if (MATCHES(p, "1200,"))
407 		baud = ASY1200;
408 	else if (MATCHES(p, "2400,"))
409 		baud = ASY2400;
410 	else if (MATCHES(p, "4800,"))
411 		baud = ASY4800;
412 	else if (MATCHES(p, "19200,"))
413 		baud = ASY19200;
414 	else if (MATCHES(p, "38400,"))
415 		baud = ASY38400;
416 	else if (MATCHES(p, "57600,"))
417 		baud = ASY57600;
418 	else if (MATCHES(p, "115200,"))
419 		baud = ASY115200;
420 	else {
421 		baud = ASY9600;
422 		SKIP(p, ',');
423 	}
424 	outb(port + LCR, DLAB);
425 	outb(port + DAT + DLL, baud & 0xff);
426 	outb(port + DAT + DLH, (baud >> 8) & 0xff);
427 
428 	switch (*p) {
429 	case '5':
430 		lcr |= BITS5;
431 		++p;
432 		break;
433 	case '6':
434 		lcr |= BITS6;
435 		++p;
436 		break;
437 	case '7':
438 		lcr |= BITS7;
439 		++p;
440 		break;
441 	case '8':
442 		++p;
443 	default:
444 		lcr |= BITS8;
445 		break;
446 	}
447 
448 	SKIP(p, ',');
449 
450 	switch (*p) {
451 	case 'n':
452 		lcr |= PARITY_NONE;
453 		++p;
454 		break;
455 	case 'o':
456 		lcr |= PARITY_ODD;
457 		++p;
458 		break;
459 	case 'e':
460 		++p;
461 	default:
462 		lcr |= PARITY_EVEN;
463 		break;
464 	}
465 
466 
467 	SKIP(p, ',');
468 
469 	switch (*p) {
470 	case '1':
471 		/* STOP1 is 0 */
472 		++p;
473 		break;
474 	default:
475 		lcr |= STOP2;
476 		break;
477 	}
478 	/* set parity bits */
479 	outb(port + LCR, lcr);
480 
481 	(void) strcpy(propname, "ttyX-rts-dtr-off");
482 	propname[3] = 'a' + tty_num;
483 	propval = get_mode_value(propname);
484 	if (propval == NULL)
485 		propval = "false";
486 	if (propval[0] != 'f' && propval[0] != 'F')
487 		mcr = 0;
488 	/* set modem control bits */
489 	outb(port + MCR, mcr | OUT2);
490 }
491 
492 /* Obtain the console type */
493 int
494 boot_console_type(int *tnum)
495 {
496 	if (tnum != NULL)
497 		*tnum = tty_num;
498 	return (console);
499 }
500 
501 /*
502  * A structure to map console names to values.
503  */
504 typedef struct {
505 	char *name;
506 	int value;
507 } console_value_t;
508 
509 console_value_t console_devices[] = {
510 	{ "ttya", CONS_TTY },	/* 0 */
511 	{ "ttyb", CONS_TTY },	/* 1 */
512 	{ "ttyc", CONS_TTY },	/* 2 */
513 	{ "ttyd", CONS_TTY },	/* 3 */
514 	{ "text", CONS_SCREEN_TEXT },
515 	{ "graphics", CONS_SCREEN_GRAPHICS },
516 #if defined(__xpv)
517 	{ "hypervisor", CONS_HYPERVISOR },
518 #endif
519 #if !defined(_BOOT)
520 	{ "usb-serial", CONS_USBSER },
521 #endif
522 	{ NULL, CONS_INVALID }
523 };
524 
525 void
526 bcons_init(char *bootstr)
527 {
528 	console_value_t *consolep;
529 	size_t len, cons_len;
530 	char *cons_str;
531 #if !defined(_BOOT)
532 	static char console_text[] = "text";
533 	extern int post_fastreboot;
534 #endif
535 
536 	boot_line = bootstr;
537 	console = CONS_INVALID;
538 
539 #if defined(__xpv)
540 	bcons_init_xen(bootstr);
541 #endif /* __xpv */
542 
543 	cons_str = find_boot_line_prop("console");
544 	if (cons_str == NULL)
545 		cons_str = find_boot_line_prop("output-device");
546 
547 #if !defined(_BOOT)
548 	if (post_fastreboot && strcmp(cons_str, "graphics") == 0)
549 		cons_str = console_text;
550 #endif
551 
552 	/*
553 	 * Go through the console_devices array trying to match the string
554 	 * we were given.  The string on the command line must end with
555 	 * a comma or white space.
556 	 */
557 	if (cons_str != NULL) {
558 		int n;
559 
560 		cons_len = strlen(cons_str);
561 		for (n = 0; console_devices[n].name != NULL; n++) {
562 			consolep = &console_devices[n];
563 			len = strlen(consolep->name);
564 			if ((len <= cons_len) && ((cons_str[len] == '\0') ||
565 			    (cons_str[len] == ',') || (cons_str[len] == '\'') ||
566 			    (cons_str[len] == '"') || ISSPACE(cons_str[len])) &&
567 			    (strncmp(cons_str, consolep->name, len) == 0)) {
568 				console = consolep->value;
569 				if (console == CONS_TTY)
570 					tty_num = n;
571 				break;
572 			}
573 		}
574 	}
575 
576 #if defined(__xpv)
577 	/*
578 	 * domU's always use the hypervisor regardless of what
579 	 * the console variable may be set to.
580 	 */
581 	if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
582 		console = CONS_HYPERVISOR;
583 		console_hypervisor_redirect = B_TRUE;
584 	}
585 #endif /* __xpv */
586 
587 	/*
588 	 * If no console device specified, default to text.
589 	 * Remember what was specified for second phase.
590 	 */
591 	if (console == CONS_INVALID)
592 		console = CONS_SCREEN_TEXT;
593 #if !defined(_BOOT)
594 	else
595 		console_set = 1;
596 #endif
597 
598 #if defined(__xpv)
599 	if (DOMAIN_IS_INITDOMAIN(xen_info)) {
600 		switch (HYPERVISOR_console_io(CONSOLEIO_get_device, 0, NULL)) {
601 			case XEN_CONSOLE_COM1:
602 			case XEN_CONSOLE_COM2:
603 				console_hypervisor_device = CONS_TTY;
604 				console_hypervisor_tty_num = tty_num;
605 				break;
606 			case XEN_CONSOLE_VGA:
607 				/*
608 				 * Currently xen doesn't really support
609 				 * keyboard/display console devices.
610 				 * What this setting means is that
611 				 * "vga=keep" has been enabled, which is
612 				 * more of a xen debugging tool that a
613 				 * true console mode.  Hence, we're going
614 				 * to ignore this xen "console" setting.
615 				 */
616 				/*FALLTHROUGH*/
617 			default:
618 				console_hypervisor_device = CONS_INVALID;
619 		}
620 	}
621 
622 	/*
623 	 * if the hypervisor is using the currently selected serial
624 	 * port then default to using the hypervisor as the console
625 	 * device.
626 	 */
627 	if (console == console_hypervisor_device) {
628 		console = CONS_HYPERVISOR;
629 		console_hypervisor_redirect = B_TRUE;
630 	}
631 #endif /* __xpv */
632 
633 	switch (console) {
634 	case CONS_TTY:
635 		serial_init();
636 		break;
637 
638 	case CONS_HYPERVISOR:
639 		break;
640 
641 #if !defined(_BOOT)
642 	case CONS_USBSER:
643 		/*
644 		 * We can't do anything with the usb serial
645 		 * until we have memory management.
646 		 */
647 		break;
648 #endif
649 	case CONS_SCREEN_GRAPHICS:
650 		kb_init();
651 		break;
652 	case CONS_SCREEN_TEXT:
653 	default:
654 #if defined(_BOOT)
655 		clear_screen();	/* clears the grub or xen screen */
656 #endif /* _BOOT */
657 		kb_init();
658 		break;
659 	}
660 	boot_line = NULL;
661 }
662 
663 #if !defined(_BOOT)
664 /*
665  * 2nd part of console initialization.
666  * In the kernel (ie. fakebop), this can be used only to switch to
667  * using a serial port instead of screen based on the contents
668  * of the bootenv.rc file.
669  */
670 /*ARGSUSED*/
671 void
672 bcons_init2(char *inputdev, char *outputdev, char *consoledev)
673 {
674 	int cons = CONS_INVALID;
675 	int ttyn;
676 	char *devnames[] = { consoledev, outputdev, inputdev, NULL };
677 	console_value_t *consolep;
678 	int i;
679 	extern int post_fastreboot;
680 
681 	if (post_fastreboot && console == CONS_SCREEN_GRAPHICS)
682 		console = CONS_SCREEN_TEXT;
683 
684 	if (console != CONS_USBSER && console != CONS_SCREEN_GRAPHICS) {
685 		if (console_set) {
686 			/*
687 			 * If the console was set on the command line,
688 			 * but the ttyX-mode was not, we only need to
689 			 * check bootenv.rc for that setting.
690 			 */
691 			if ((!console_mode_set) && (console == CONS_TTY))
692 				serial_init();
693 			return;
694 		}
695 
696 		for (i = 0; devnames[i] != NULL; i++) {
697 			int n;
698 
699 			for (n = 0; console_devices[n].name != NULL; n++) {
700 				consolep = &console_devices[n];
701 				if (strcmp(devnames[i], consolep->name) == 0) {
702 					cons = consolep->value;
703 					if (cons == CONS_TTY)
704 						ttyn = n;
705 				}
706 			}
707 			if (cons != CONS_INVALID)
708 				break;
709 		}
710 
711 #if defined(__xpv)
712 		/*
713 		 * if the hypervisor is using the currently selected console
714 		 * device then default to using the hypervisor as the console
715 		 * device.
716 		 */
717 		if (cons == console_hypervisor_device) {
718 			cons = CONS_HYPERVISOR;
719 			console_hypervisor_redirect = B_TRUE;
720 		}
721 #endif /* __xpv */
722 
723 		if ((cons == CONS_INVALID) || (cons == console)) {
724 			/*
725 			 * we're sticking with whatever the current setting is
726 			 */
727 			return;
728 		}
729 
730 		console = cons;
731 		if (cons == CONS_TTY) {
732 			tty_num = ttyn;
733 			serial_init();
734 			return;
735 		}
736 	} else {
737 		/*
738 		 * USB serial and GRAPHICS console
739 		 * we just collect data into a buffer
740 		 */
741 		extern void *defcons_init(size_t);
742 		defcons_buf = defcons_cur = defcons_init(MMU_PAGESIZE);
743 	}
744 }
745 
746 #if defined(__xpv)
747 boolean_t
748 bcons_hypervisor_redirect(void)
749 {
750 	return (console_hypervisor_redirect);
751 }
752 
753 void
754 bcons_device_change(int new_console)
755 {
756 	if (new_console < CONS_MIN || new_console > CONS_MAX)
757 		return;
758 
759 	/*
760 	 * If we are asked to switch the console to the hypervisor, that
761 	 * really means to switch the console to whichever device the
762 	 * hypervisor is/was using.
763 	 */
764 	if (new_console == CONS_HYPERVISOR)
765 		new_console = console_hypervisor_device;
766 
767 	console = new_console;
768 
769 	if (new_console == CONS_TTY) {
770 		tty_num = console_hypervisor_tty_num;
771 		serial_init();
772 	}
773 }
774 #endif /* __xpv */
775 
776 static void
777 defcons_putchar(int c)
778 {
779 	if (defcons_buf != NULL &&
780 	    defcons_cur + 1 - defcons_buf < MMU_PAGESIZE) {
781 		*defcons_cur++ = c;
782 		*defcons_cur = 0;
783 	}
784 }
785 #endif	/* _BOOT */
786 
787 static void
788 serial_putchar(int c)
789 {
790 	int checks = 10000;
791 
792 	while (((inb(port + LSR) & XHRE) == 0) && checks--)
793 		;
794 	outb(port + DAT, (char)c);
795 }
796 
797 static int
798 serial_getchar(void)
799 {
800 	uchar_t lsr;
801 
802 	while (serial_ischar() == 0)
803 		;
804 
805 	lsr = inb(port + LSR);
806 	if (lsr & (SERIAL_BREAK | SERIAL_FRAME |
807 	    SERIAL_PARITY | SERIAL_OVERRUN)) {
808 		if (lsr & SERIAL_OVERRUN) {
809 			return (inb(port + DAT));
810 		} else {
811 			/* Toss the garbage */
812 			(void) inb(port + DAT);
813 			return (0);
814 		}
815 	}
816 	return (inb(port + DAT));
817 }
818 
819 static int
820 serial_ischar(void)
821 {
822 	return (inb(port + LSR) & RCA);
823 }
824 
825 static void
826 _doputchar(int c)
827 {
828 	switch (console) {
829 	case CONS_TTY:
830 		serial_putchar(c);
831 		return;
832 	case CONS_SCREEN_TEXT:
833 		screen_putchar(c);
834 		return;
835 	case CONS_SCREEN_GRAPHICS:
836 #if !defined(_BOOT)
837 	case CONS_USBSER:
838 		defcons_putchar(c);
839 #endif /* _BOOT */
840 		return;
841 	}
842 }
843 
844 void
845 bcons_putchar(int c)
846 {
847 	static int bhcharpos = 0;
848 
849 #if defined(__xpv)
850 	if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
851 	    console == CONS_HYPERVISOR) {
852 		bcons_putchar_xen(c);
853 		return;
854 	}
855 #endif /* __xpv */
856 
857 	if (c == '\t') {
858 		do {
859 			_doputchar(' ');
860 		} while (++bhcharpos % 8);
861 		return;
862 	} else  if (c == '\n' || c == '\r') {
863 		bhcharpos = 0;
864 		_doputchar('\r');
865 		_doputchar(c);
866 		return;
867 	} else if (c == '\b') {
868 		if (bhcharpos)
869 			bhcharpos--;
870 		_doputchar(c);
871 		return;
872 	}
873 
874 	bhcharpos++;
875 	_doputchar(c);
876 }
877 
878 /*
879  * kernel character input functions
880  */
881 int
882 bcons_getchar(void)
883 {
884 #if defined(__xpv)
885 	if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
886 	    console == CONS_HYPERVISOR)
887 		return (bcons_getchar_xen());
888 #endif /* __xpv */
889 
890 	switch (console) {
891 	case CONS_TTY:
892 		return (serial_getchar());
893 	default:
894 		return (kb_getchar());
895 	}
896 }
897 
898 #if !defined(_BOOT)
899 
900 int
901 bcons_ischar(void)
902 {
903 
904 #if defined(__xpv)
905 	if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
906 	    console == CONS_HYPERVISOR)
907 		return (bcons_ischar_xen());
908 #endif /* __xpv */
909 
910 	switch (console) {
911 	case CONS_TTY:
912 		return (serial_ischar());
913 	default:
914 		return (kb_ischar());
915 	}
916 }
917 
918 #endif /* _BOOT */
919