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 #if !defined(_BOOT)
514 	static char console_text[] = "text";
515 	extern int post_fastreboot;
516 #endif
517 
518 	boot_line = bootstr;
519 	console = CONS_INVALID;
520 
521 #if defined(__xpv)
522 	bcons_init_xen(bootstr);
523 #endif /* __xpv */
524 
525 	cons_str = find_boot_line_prop("console");
526 	if (cons_str == NULL)
527 		cons_str = find_boot_line_prop("output-device");
528 
529 #if !defined(_BOOT)
530 	if (post_fastreboot && strcmp(cons_str, "graphics") == 0)
531 		cons_str = console_text;
532 #endif
533 
534 	/*
535 	 * Go through the console_devices array trying to match the string
536 	 * we were given.  The string on the command line must end with
537 	 * a comma or white space.
538 	 */
539 	if (cons_str != NULL) {
540 		cons_len = strlen(cons_str);
541 		consolep = console_devices;
542 		for (; consolep->name[0] != '\0'; consolep++) {
543 			len = strlen(consolep->name);
544 			if ((len <= cons_len) && ((cons_str[len] == '\0') ||
545 			    (cons_str[len] == ',') || (cons_str[len] == '\'') ||
546 			    (cons_str[len] == '"') || ISSPACE(cons_str[len])) &&
547 			    (strncmp(cons_str, consolep->name, len) == 0)) {
548 				console = consolep->value;
549 				break;
550 			}
551 		}
552 	}
553 
554 #if defined(__xpv)
555 	/*
556 	 * domU's always use the hypervisor regardless of what
557 	 * the console variable may be set to.
558 	 */
559 	if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
560 		console = CONS_HYPERVISOR;
561 		console_hypervisor_redirect = B_TRUE;
562 	}
563 #endif /* __xpv */
564 
565 	/*
566 	 * If no console device specified, default to text.
567 	 * Remember what was specified for second phase.
568 	 */
569 	if (console == CONS_INVALID)
570 		console = CONS_SCREEN_TEXT;
571 #if !defined(_BOOT)
572 	else
573 		console_set = 1;
574 #endif
575 
576 #if defined(__xpv)
577 	if (DOMAIN_IS_INITDOMAIN(xen_info)) {
578 		switch (HYPERVISOR_console_io(CONSOLEIO_get_device, 0, NULL)) {
579 			case XEN_CONSOLE_COM1:
580 				console_hypervisor_device = CONS_TTYA;
581 				break;
582 			case XEN_CONSOLE_COM2:
583 				console_hypervisor_device = CONS_TTYB;
584 				break;
585 			case XEN_CONSOLE_VGA:
586 				/*
587 				 * Currently xen doesn't really support
588 				 * keyboard/display console devices.
589 				 * What this setting means is that
590 				 * "vga=keep" has been enabled, which is
591 				 * more of a xen debugging tool that a
592 				 * true console mode.  Hence, we're going
593 				 * to ignore this xen "console" setting.
594 				 */
595 				/*FALLTHROUGH*/
596 			default:
597 				console_hypervisor_device = CONS_INVALID;
598 		}
599 	}
600 
601 	/*
602 	 * if the hypervisor is using the currently selected serial
603 	 * port then default to using the hypervisor as the console
604 	 * device.
605 	 */
606 	if (console == console_hypervisor_device) {
607 		console = CONS_HYPERVISOR;
608 		console_hypervisor_redirect = B_TRUE;
609 	}
610 #endif /* __xpv */
611 
612 	switch (console) {
613 	case CONS_TTYA:
614 	case CONS_TTYB:
615 		serial_init();
616 		break;
617 
618 	case CONS_HYPERVISOR:
619 		break;
620 
621 #if !defined(_BOOT)
622 	case CONS_USBSER:
623 		/*
624 		 * We can't do anything with the usb serial
625 		 * until we have memory management.
626 		 */
627 		break;
628 #endif
629 	case CONS_SCREEN_GRAPHICS:
630 		kb_init();
631 		break;
632 	case CONS_SCREEN_TEXT:
633 	default:
634 #if defined(_BOOT)
635 		clear_screen();	/* clears the grub or xen screen */
636 #endif /* _BOOT */
637 		kb_init();
638 		break;
639 	}
640 	boot_line = NULL;
641 }
642 
643 #if !defined(_BOOT)
644 /*
645  * 2nd part of console initialization.
646  * In the kernel (ie. fakebop), this can be used only to switch to
647  * using a serial port instead of screen based on the contents
648  * of the bootenv.rc file.
649  */
650 /*ARGSUSED*/
651 void
652 bcons_init2(char *inputdev, char *outputdev, char *consoledev)
653 {
654 	int cons = CONS_INVALID;
655 	char *devnames[] = { consoledev, outputdev, inputdev, NULL };
656 	console_value_t *consolep;
657 	int i;
658 	extern int post_fastreboot;
659 
660 	if (post_fastreboot && console == CONS_SCREEN_GRAPHICS)
661 		console = CONS_SCREEN_TEXT;
662 
663 	if (console != CONS_USBSER && console != CONS_SCREEN_GRAPHICS) {
664 		if (console_set) {
665 			/*
666 			 * If the console was set on the command line,
667 			 * but the ttyX-mode was not, we only need to
668 			 * check bootenv.rc for that setting.
669 			 */
670 			if ((!console_mode_set) &&
671 			    (console == CONS_TTYA || console == CONS_TTYB))
672 				serial_init();
673 			return;
674 		}
675 
676 		for (i = 0; devnames[i] != NULL; i++) {
677 			consolep = console_devices;
678 			for (; consolep->name[0] != '\0'; consolep++) {
679 				if (strcmp(devnames[i], consolep->name) == 0) {
680 					cons = consolep->value;
681 				}
682 			}
683 			if (cons != CONS_INVALID)
684 				break;
685 		}
686 
687 #if defined(__xpv)
688 		/*
689 		 * if the hypervisor is using the currently selected console
690 		 * device then default to using the hypervisor as the console
691 		 * device.
692 		 */
693 		if (cons == console_hypervisor_device) {
694 			cons = CONS_HYPERVISOR;
695 			console_hypervisor_redirect = B_TRUE;
696 		}
697 #endif /* __xpv */
698 
699 		if ((cons == CONS_INVALID) || (cons == console)) {
700 			/*
701 			 * we're sticking with whatever the current setting is
702 			 */
703 			return;
704 		}
705 
706 		console = cons;
707 		if (cons == CONS_TTYA || cons == CONS_TTYB) {
708 			serial_init();
709 			return;
710 		}
711 	} else {
712 		/*
713 		 * USB serial and GRAPHICS console
714 		 * we just collect data into a buffer
715 		 */
716 		extern void *defcons_init(size_t);
717 		defcons_buf = defcons_cur = defcons_init(MMU_PAGESIZE);
718 	}
719 }
720 
721 #if defined(__xpv)
722 boolean_t
723 bcons_hypervisor_redirect(void)
724 {
725 	return (console_hypervisor_redirect);
726 }
727 
728 void
729 bcons_device_change(int new_console)
730 {
731 	if (new_console < CONS_MIN || new_console > CONS_MAX)
732 		return;
733 
734 	/*
735 	 * If we are asked to switch the console to the hypervisor, that
736 	 * really means to switch the console to whichever device the
737 	 * hypervisor is/was using.
738 	 */
739 	if (new_console == CONS_HYPERVISOR)
740 		new_console = console_hypervisor_device;
741 
742 	console = new_console;
743 
744 	if (new_console == CONS_TTYA || new_console == CONS_TTYB)
745 		serial_init();
746 }
747 #endif /* __xpv */
748 
749 static void
750 defcons_putchar(int c)
751 {
752 	if (defcons_buf != NULL &&
753 	    defcons_cur + 1 - defcons_buf < MMU_PAGESIZE) {
754 		*defcons_cur++ = c;
755 		*defcons_cur = 0;
756 	}
757 }
758 #endif	/* _BOOT */
759 
760 static void
761 serial_putchar(int c)
762 {
763 	int checks = 10000;
764 
765 	while (((inb(port + LSR) & XHRE) == 0) && checks--)
766 		;
767 	outb(port + DAT, (char)c);
768 }
769 
770 static int
771 serial_getchar(void)
772 {
773 	uchar_t lsr;
774 
775 	while (serial_ischar() == 0)
776 		;
777 
778 	lsr = inb(port + LSR);
779 	if (lsr & (SERIAL_BREAK | SERIAL_FRAME |
780 	    SERIAL_PARITY | SERIAL_OVERRUN)) {
781 		if (lsr & SERIAL_OVERRUN) {
782 			return (inb(port + DAT));
783 		} else {
784 			/* Toss the garbage */
785 			(void) inb(port + DAT);
786 			return (0);
787 		}
788 	}
789 	return (inb(port + DAT));
790 }
791 
792 static int
793 serial_ischar(void)
794 {
795 	return (inb(port + LSR) & RCA);
796 }
797 
798 static void
799 _doputchar(int c)
800 {
801 	switch (console) {
802 	case CONS_TTYA:
803 	case CONS_TTYB:
804 		serial_putchar(c);
805 		return;
806 	case CONS_SCREEN_TEXT:
807 		screen_putchar(c);
808 		return;
809 	case CONS_SCREEN_GRAPHICS:
810 #if !defined(_BOOT)
811 	case CONS_USBSER:
812 		defcons_putchar(c);
813 #endif /* _BOOT */
814 		return;
815 	}
816 }
817 
818 void
819 bcons_putchar(int c)
820 {
821 	static int bhcharpos = 0;
822 
823 #if defined(__xpv)
824 	if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
825 	    console == CONS_HYPERVISOR) {
826 		bcons_putchar_xen(c);
827 		return;
828 	}
829 #endif /* __xpv */
830 
831 	if (c == '\t') {
832 		do {
833 			_doputchar(' ');
834 		} while (++bhcharpos % 8);
835 		return;
836 	} else  if (c == '\n' || c == '\r') {
837 		bhcharpos = 0;
838 		_doputchar('\r');
839 		_doputchar(c);
840 		return;
841 	} else if (c == '\b') {
842 		if (bhcharpos)
843 			bhcharpos--;
844 		_doputchar(c);
845 		return;
846 	}
847 
848 	bhcharpos++;
849 	_doputchar(c);
850 }
851 
852 /*
853  * kernel character input functions
854  */
855 int
856 bcons_getchar(void)
857 {
858 #if defined(__xpv)
859 	if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
860 	    console == CONS_HYPERVISOR)
861 		return (bcons_getchar_xen());
862 #endif /* __xpv */
863 
864 	switch (console) {
865 	case CONS_TTYA:
866 	case CONS_TTYB:
867 		return (serial_getchar());
868 	default:
869 		return (kb_getchar());
870 	}
871 }
872 
873 #if !defined(_BOOT)
874 
875 int
876 bcons_ischar(void)
877 {
878 
879 #if defined(__xpv)
880 	if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
881 	    console == CONS_HYPERVISOR)
882 		return (bcons_ischar_xen());
883 #endif /* __xpv */
884 
885 	switch (console) {
886 	case CONS_TTYA:
887 	case CONS_TTYB:
888 		return (serial_ischar());
889 	default:
890 		return (kb_ischar());
891 	}
892 }
893 
894 #endif /* _BOOT */
895