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