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