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