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