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