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