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 (c) 2012 Gary Mills 23 * 24 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 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 *defcons_buf; 50 static char *defcons_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 static int console = CONS_SCREEN_TEXT; 62 static int diag = CONS_INVALID; 63 static int tty_num = 0; 64 static int tty_addr[] = {0x3f8, 0x2f8, 0x3e8, 0x2e8}; 65 static char *boot_line; 66 static struct boot_env { 67 char *be_env; /* ends with double ascii nul */ 68 size_t be_size; /* size of the environment, including nul */ 69 } boot_env; 70 71 static int serial_ischar(void); 72 static int serial_getchar(void); 73 static void serial_putchar(int); 74 static void serial_adjust_prop(void); 75 76 #if !defined(_BOOT) 77 /* Set if the console or mode are expressed in the boot line */ 78 static int console_set, console_mode_set; 79 #endif 80 81 #if defined(__xpv) 82 static int console_hypervisor_redirect = B_FALSE; 83 static int console_hypervisor_device = CONS_INVALID; 84 static int console_hypervisor_tty_num = 0; 85 86 /* Obtain the hypervisor console type */ 87 int 88 console_hypervisor_dev_type(int *tnum) 89 { 90 if (tnum != NULL) 91 *tnum = console_hypervisor_tty_num; 92 return (console_hypervisor_device); 93 } 94 #endif /* __xpv */ 95 96 /* Clear the screen and initialize VIDEO, XPOS and YPOS. */ 97 void 98 clear_screen(void) 99 { 100 /* 101 * XXX should set vga mode so we don't depend on the 102 * state left by the boot loader. Note that we have to 103 * enable the cursor before clearing the screen since 104 * the cursor position is dependant upon the cursor 105 * skew, which is initialized by vga_cursor_display() 106 */ 107 vga_cursor_display(); 108 vga_clear(cons_color); 109 vga_setpos(0, 0); 110 } 111 112 /* Put the character C on the screen. */ 113 static void 114 screen_putchar(int c) 115 { 116 int row, col; 117 118 vga_getpos(&row, &col); 119 switch (c) { 120 case '\t': 121 col += 8 - (col % 8); 122 if (col == VGA_TEXT_COLS) 123 col = 79; 124 vga_setpos(row, col); 125 break; 126 127 case '\r': 128 vga_setpos(row, 0); 129 break; 130 131 case '\b': 132 if (col > 0) 133 vga_setpos(row, col - 1); 134 break; 135 136 case '\n': 137 if (row < VGA_TEXT_ROWS - 1) 138 vga_setpos(row + 1, col); 139 else 140 vga_scroll(cons_color); 141 break; 142 143 default: 144 vga_drawc(c, cons_color); 145 if (col < VGA_TEXT_COLS -1) 146 vga_setpos(row, col + 1); 147 else if (row < VGA_TEXT_ROWS - 1) 148 vga_setpos(row + 1, 0); 149 else { 150 vga_setpos(row, 0); 151 vga_scroll(cons_color); 152 } 153 break; 154 } 155 } 156 157 static int port; 158 159 static void 160 serial_init(void) 161 { 162 port = tty_addr[tty_num]; 163 164 outb(port + ISR, 0x20); 165 if (inb(port + ISR) & 0x20) { 166 /* 167 * 82510 chip is present 168 */ 169 outb(port + DAT+7, 0x04); /* clear status */ 170 outb(port + ISR, 0x40); /* set to bank 2 */ 171 outb(port + MCR, 0x08); /* IMD */ 172 outb(port + DAT, 0x21); /* FMD */ 173 outb(port + ISR, 0x00); /* set to bank 0 */ 174 } else { 175 /* 176 * set the UART in FIFO mode if it has FIFO buffers. 177 * use 16550 fifo reset sequence specified in NS 178 * application note. disable fifos until chip is 179 * initialized. 180 */ 181 outb(port + FIFOR, 0x00); /* clear */ 182 outb(port + FIFOR, FIFO_ON); /* enable */ 183 outb(port + FIFOR, FIFO_ON|FIFORXFLSH); /* reset */ 184 outb(port + FIFOR, 185 FIFO_ON|FIFODMA|FIFOTXFLSH|FIFORXFLSH|0x80); 186 if ((inb(port + ISR) & 0xc0) != 0xc0) { 187 /* 188 * no fifo buffers so disable fifos. 189 * this is true for 8250's 190 */ 191 outb(port + FIFOR, 0x00); 192 } 193 } 194 195 /* disable interrupts */ 196 outb(port + ICR, 0); 197 198 #if !defined(_BOOT) 199 if (IN_XPV_PANIC()) 200 return; 201 #endif 202 203 /* adjust setting based on tty properties */ 204 serial_adjust_prop(); 205 206 #if defined(_BOOT) 207 /* 208 * Do a full reset to match console behavior. 209 * 0x1B + c - reset everything 210 */ 211 serial_putchar(0x1B); 212 serial_putchar('c'); 213 #endif 214 } 215 216 /* Advance str pointer past white space */ 217 #define EAT_WHITE_SPACE(str) { \ 218 while ((*str != '\0') && ISSPACE(*str)) \ 219 str++; \ 220 } 221 222 /* 223 * boot_line is set when we call here. Search it for the argument name, 224 * and if found, return a pointer to it. 225 */ 226 static char * 227 find_boot_line_prop(const char *name) 228 { 229 char *ptr; 230 char *ret = NULL; 231 char end_char; 232 size_t len; 233 234 if (boot_line == NULL) 235 return (NULL); 236 237 len = strlen(name); 238 239 /* 240 * We have two nested loops here: the outer loop discards all options 241 * except -B, and the inner loop parses the -B options looking for 242 * the one we're interested in. 243 */ 244 for (ptr = boot_line; *ptr != '\0'; ptr++) { 245 EAT_WHITE_SPACE(ptr); 246 247 if (*ptr == '-') { 248 ptr++; 249 while ((*ptr != '\0') && (*ptr != 'B') && 250 !ISSPACE(*ptr)) 251 ptr++; 252 if (*ptr == '\0') 253 goto out; 254 else if (*ptr != 'B') 255 continue; 256 } else { 257 while ((*ptr != '\0') && !ISSPACE(*ptr)) 258 ptr++; 259 if (*ptr == '\0') 260 goto out; 261 continue; 262 } 263 264 do { 265 ptr++; 266 EAT_WHITE_SPACE(ptr); 267 268 if ((strncmp(ptr, name, len) == 0) && 269 (ptr[len] == '=')) { 270 ptr += len + 1; 271 if ((*ptr == '\'') || (*ptr == '"')) { 272 ret = ptr + 1; 273 end_char = *ptr; 274 ptr++; 275 } else { 276 ret = ptr; 277 end_char = ','; 278 } 279 goto consume_property; 280 } 281 282 /* 283 * We have a property, and it's not the one we're 284 * interested in. Skip the property name. A name 285 * can end with '=', a comma, or white space. 286 */ 287 while ((*ptr != '\0') && (*ptr != '=') && 288 (*ptr != ',') && (!ISSPACE(*ptr))) 289 ptr++; 290 291 /* 292 * We only want to go through the rest of the inner 293 * loop if we have a comma. If we have a property 294 * name without a value, either continue or break. 295 */ 296 if (*ptr == '\0') 297 goto out; 298 else if (*ptr == ',') 299 continue; 300 else if (ISSPACE(*ptr)) 301 break; 302 ptr++; 303 304 /* 305 * Is the property quoted? 306 */ 307 if ((*ptr == '\'') || (*ptr == '"')) { 308 end_char = *ptr; 309 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 consume_property: 324 for (; (*ptr != '\0') && (*ptr != end_char); ptr++) { 325 if ((end_char == ',') && ISSPACE(*ptr)) 326 break; 327 } 328 if (*ptr && (*ptr != ',') && !ISSPACE(*ptr)) 329 ptr++; 330 } while (*ptr == ','); 331 } 332 out: 333 return (ret); 334 } 335 336 /* 337 * Find prop from boot env module. The data in module is list of C strings 338 * name=value, the list is terminated by double nul. 339 */ 340 static const char * 341 find_boot_env_prop(const char *name) 342 { 343 char *ptr; 344 size_t len; 345 uintptr_t size; 346 347 if (boot_env.be_env == NULL) 348 return (NULL); 349 350 ptr = boot_env.be_env; 351 len = strlen(name); 352 353 /* 354 * Make sure we have at least len + 2 bytes in the environment. 355 * We are looking for name=value\0 constructs, and the environment 356 * itself is terminated by '\0'. 357 */ 358 if (boot_env.be_size < len + 2) 359 return (NULL); 360 361 do { 362 if ((strncmp(ptr, name, len) == 0) && (ptr[len] == '=')) { 363 ptr += len + 1; 364 return (ptr); 365 } 366 /* find the first '\0' */ 367 while (*ptr != '\0') { 368 ptr++; 369 size = (uintptr_t)ptr - (uintptr_t)boot_env.be_env; 370 if (size > boot_env.be_size) 371 return (NULL); 372 } 373 ptr++; 374 375 /* If the remainder is shorter than name + 2, get out. */ 376 size = (uintptr_t)ptr - (uintptr_t)boot_env.be_env; 377 if (boot_env.be_size - size < len + 2) 378 return (NULL); 379 } while (*ptr != '\0'); 380 return (NULL); 381 } 382 383 /* 384 * Get prop value from either command line or boot environment. 385 * We always check kernel command line first, as this will keep the 386 * functionality and will allow user to override the values in environment. 387 */ 388 const char * 389 find_boot_prop(const char *name) 390 { 391 const char *value = find_boot_line_prop(name); 392 393 if (value == NULL) 394 value = find_boot_env_prop(name); 395 return (value); 396 } 397 398 #define MATCHES(p, pat) \ 399 (strncmp(p, pat, strlen(pat)) == 0 ? (p += strlen(pat), 1) : 0) 400 401 #define SKIP(p, c) \ 402 while (*(p) != 0 && *p != (c)) \ 403 ++(p); \ 404 if (*(p) == (c)) \ 405 ++(p); 406 407 /* 408 * find a tty mode property either from cmdline or from boot properties 409 */ 410 static const char * 411 get_mode_value(char *name) 412 { 413 /* 414 * when specified on boot line it looks like "name" "=".... 415 */ 416 if (boot_line != NULL) { 417 return (find_boot_prop(name)); 418 } 419 420 #if defined(_BOOT) 421 return (NULL); 422 #else 423 /* 424 * if we're running in the full kernel we check the bootenv.rc settings 425 */ 426 { 427 static char propval[20]; 428 429 propval[0] = 0; 430 if (do_bsys_getproplen(NULL, name) <= 0) 431 return (NULL); 432 (void) do_bsys_getprop(NULL, name, propval); 433 return (propval); 434 } 435 #endif 436 } 437 438 /* 439 * adjust serial port based on properties 440 * These come either from the cmdline or from boot properties. 441 */ 442 static void 443 serial_adjust_prop(void) 444 { 445 char propname[20]; 446 const char *propval; 447 const char *p; 448 ulong_t baud; 449 uchar_t lcr = 0; 450 uchar_t mcr = DTR | RTS; 451 452 (void) strcpy(propname, "ttyX-mode"); 453 propname[3] = 'a' + tty_num; 454 propval = get_mode_value(propname); 455 if (propval == NULL) 456 propval = "9600,8,n,1,-"; 457 #if !defined(_BOOT) 458 else 459 console_mode_set = 1; 460 #endif 461 462 /* property is of the form: "9600,8,n,1,-" */ 463 p = propval; 464 if (MATCHES(p, "110,")) 465 baud = ASY110; 466 else if (MATCHES(p, "150,")) 467 baud = ASY150; 468 else if (MATCHES(p, "300,")) 469 baud = ASY300; 470 else if (MATCHES(p, "600,")) 471 baud = ASY600; 472 else if (MATCHES(p, "1200,")) 473 baud = ASY1200; 474 else if (MATCHES(p, "2400,")) 475 baud = ASY2400; 476 else if (MATCHES(p, "4800,")) 477 baud = ASY4800; 478 else if (MATCHES(p, "19200,")) 479 baud = ASY19200; 480 else if (MATCHES(p, "38400,")) 481 baud = ASY38400; 482 else if (MATCHES(p, "57600,")) 483 baud = ASY57600; 484 else if (MATCHES(p, "115200,")) 485 baud = ASY115200; 486 else { 487 baud = ASY9600; 488 SKIP(p, ','); 489 } 490 outb(port + LCR, DLAB); 491 outb(port + DAT + DLL, baud & 0xff); 492 outb(port + DAT + DLH, (baud >> 8) & 0xff); 493 494 switch (*p) { 495 case '5': 496 lcr |= BITS5; 497 ++p; 498 break; 499 case '6': 500 lcr |= BITS6; 501 ++p; 502 break; 503 case '7': 504 lcr |= BITS7; 505 ++p; 506 break; 507 case '8': 508 ++p; 509 /* FALLTHROUGH */ 510 default: 511 lcr |= BITS8; 512 break; 513 } 514 515 SKIP(p, ','); 516 517 switch (*p) { 518 case 'n': 519 lcr |= PARITY_NONE; 520 ++p; 521 break; 522 case 'o': 523 lcr |= PARITY_ODD; 524 ++p; 525 break; 526 case 'e': 527 ++p; 528 /* FALLTHROUGH */ 529 default: 530 lcr |= PARITY_EVEN; 531 break; 532 } 533 534 535 SKIP(p, ','); 536 537 switch (*p) { 538 case '1': 539 /* STOP1 is 0 */ 540 ++p; 541 break; 542 default: 543 lcr |= STOP2; 544 break; 545 } 546 /* set parity bits */ 547 outb(port + LCR, lcr); 548 549 (void) strcpy(propname, "ttyX-rts-dtr-off"); 550 propname[3] = 'a' + tty_num; 551 propval = get_mode_value(propname); 552 if (propval == NULL) 553 propval = "false"; 554 if (propval[0] != 'f' && propval[0] != 'F') 555 mcr = 0; 556 /* set modem control bits */ 557 outb(port + MCR, mcr | OUT2); 558 } 559 560 /* Obtain the console type */ 561 int 562 boot_console_type(int *tnum) 563 { 564 if (tnum != NULL) 565 *tnum = tty_num; 566 return (console); 567 } 568 569 /* 570 * A structure to map console names to values. 571 */ 572 typedef struct { 573 char *name; 574 int value; 575 } console_value_t; 576 577 console_value_t console_devices[] = { 578 { "ttya", CONS_TTY }, /* 0 */ 579 { "ttyb", CONS_TTY }, /* 1 */ 580 { "ttyc", CONS_TTY }, /* 2 */ 581 { "ttyd", CONS_TTY }, /* 3 */ 582 { "text", CONS_SCREEN_TEXT }, 583 { "graphics", CONS_SCREEN_GRAPHICS }, 584 #if defined(__xpv) 585 { "hypervisor", CONS_HYPERVISOR }, 586 #endif 587 #if !defined(_BOOT) 588 { "usb-serial", CONS_USBSER }, 589 #endif 590 { NULL, CONS_INVALID } 591 }; 592 593 static void 594 bcons_init_env(struct xboot_info *xbi) 595 { 596 uint32_t i; 597 struct boot_modules *modules; 598 599 modules = (struct boot_modules *)(uintptr_t)xbi->bi_modules; 600 for (i = 0; i < xbi->bi_module_cnt; i++) { 601 if (modules[i].bm_type == BMT_ENV) 602 break; 603 } 604 if (i == xbi->bi_module_cnt) 605 return; 606 607 boot_env.be_env = (char *)(uintptr_t)modules[i].bm_addr; 608 boot_env.be_size = modules[i].bm_size; 609 } 610 611 /* 612 * Go through the console_devices array trying to match the string 613 * we were given. The string on the command line must end with 614 * a comma or white space. 615 * 616 * This function does set tty_num as an side effect, this does imply that 617 * only one of the main console and the diag-device can be using serial. 618 */ 619 static int 620 lookup_console_devices(const char *cons_str) 621 { 622 int n, cons; 623 size_t len, cons_len; 624 console_value_t *consolep; 625 626 cons = CONS_INVALID; 627 if (cons_str != NULL) { 628 629 cons_len = strlen(cons_str); 630 for (n = 0; console_devices[n].name != NULL; n++) { 631 consolep = &console_devices[n]; 632 len = strlen(consolep->name); 633 if ((len <= cons_len) && ((cons_str[len] == '\0') || 634 (cons_str[len] == ',') || (cons_str[len] == '\'') || 635 (cons_str[len] == '"') || ISSPACE(cons_str[len])) && 636 (strncmp(cons_str, consolep->name, len) == 0)) { 637 cons = consolep->value; 638 if (cons == CONS_TTY) 639 tty_num = n; 640 break; 641 } 642 } 643 } 644 return (cons); 645 } 646 647 void 648 bcons_init(struct xboot_info *xbi) 649 { 650 const char *cons_str; 651 #if !defined(_BOOT) 652 static char console_text[] = "text"; 653 extern int post_fastreboot; 654 #endif 655 656 if (xbi == NULL) { 657 /* This is very early dboot console, set up ttya. */ 658 console = CONS_TTY; 659 serial_init(); 660 return; 661 } 662 663 /* Set up data to fetch properties from commad line and boot env. */ 664 boot_line = (char *)(uintptr_t)xbi->bi_cmdline; 665 bcons_init_env(xbi); 666 console = CONS_INVALID; 667 668 #if defined(__xpv) 669 bcons_init_xen(boot_line); 670 #endif /* __xpv */ 671 672 /* 673 * First check for diag-device. 674 */ 675 cons_str = find_boot_prop("diag-device"); 676 if (cons_str != NULL) 677 diag = lookup_console_devices(cons_str); 678 679 cons_str = find_boot_prop("console"); 680 if (cons_str == NULL) 681 cons_str = find_boot_prop("output-device"); 682 683 #if !defined(_BOOT) 684 if (post_fastreboot && strcmp(cons_str, "graphics") == 0) 685 cons_str = console_text; 686 #endif 687 688 if (cons_str != NULL) 689 console = lookup_console_devices(cons_str); 690 691 #if defined(__xpv) 692 /* 693 * domU's always use the hypervisor regardless of what 694 * the console variable may be set to. 695 */ 696 if (!DOMAIN_IS_INITDOMAIN(xen_info)) { 697 console = CONS_HYPERVISOR; 698 console_hypervisor_redirect = B_TRUE; 699 } 700 #endif /* __xpv */ 701 702 /* 703 * If no console device specified, default to text. 704 * Remember what was specified for second phase. 705 */ 706 if (console == CONS_INVALID) 707 console = CONS_SCREEN_TEXT; 708 #if !defined(_BOOT) 709 else 710 console_set = 1; 711 #endif 712 713 #if defined(__xpv) 714 if (DOMAIN_IS_INITDOMAIN(xen_info)) { 715 switch (HYPERVISOR_console_io(CONSOLEIO_get_device, 0, NULL)) { 716 case XEN_CONSOLE_COM1: 717 case XEN_CONSOLE_COM2: 718 console_hypervisor_device = CONS_TTY; 719 console_hypervisor_tty_num = tty_num; 720 break; 721 case XEN_CONSOLE_VGA: 722 /* 723 * Currently xen doesn't really support 724 * keyboard/display console devices. 725 * What this setting means is that 726 * "vga=keep" has been enabled, which is 727 * more of a xen debugging tool that a 728 * true console mode. Hence, we're going 729 * to ignore this xen "console" setting. 730 */ 731 /*FALLTHROUGH*/ 732 default: 733 console_hypervisor_device = CONS_INVALID; 734 } 735 } 736 737 /* 738 * if the hypervisor is using the currently selected serial 739 * port then default to using the hypervisor as the console 740 * device. 741 */ 742 if (console == console_hypervisor_device) { 743 console = CONS_HYPERVISOR; 744 console_hypervisor_redirect = B_TRUE; 745 } 746 #endif /* __xpv */ 747 748 switch (console) { 749 case CONS_TTY: 750 serial_init(); 751 break; 752 753 case CONS_HYPERVISOR: 754 break; 755 756 #if !defined(_BOOT) 757 case CONS_USBSER: 758 /* 759 * We can't do anything with the usb serial 760 * until we have memory management. 761 */ 762 break; 763 #endif 764 case CONS_SCREEN_GRAPHICS: 765 kb_init(); 766 break; 767 case CONS_SCREEN_TEXT: 768 default: 769 #if defined(_BOOT) 770 clear_screen(); /* clears the grub or xen screen */ 771 #endif /* _BOOT */ 772 kb_init(); 773 break; 774 } 775 776 /* 777 * Initialize diag device unless already done. 778 */ 779 switch (diag) { 780 case CONS_TTY: 781 if (console != CONS_TTY) 782 serial_init(); 783 break; 784 case CONS_SCREEN_GRAPHICS: 785 case CONS_SCREEN_TEXT: 786 if (console != CONS_SCREEN_GRAPHICS && 787 console != CONS_SCREEN_TEXT) 788 kb_init(); 789 break; 790 default: 791 break; 792 } 793 } 794 795 #if !defined(_BOOT) 796 /* 797 * 2nd part of console initialization. 798 * In the kernel (ie. fakebop), this can be used only to switch to 799 * using a serial port instead of screen based on the contents 800 * of the bootenv.rc file. 801 */ 802 /*ARGSUSED*/ 803 void 804 bcons_init2(char *inputdev, char *outputdev, char *consoledev) 805 { 806 int cons = CONS_INVALID; 807 int ttyn; 808 char *devnames[] = { consoledev, outputdev, inputdev, NULL }; 809 console_value_t *consolep; 810 int i; 811 extern int post_fastreboot; 812 813 if (post_fastreboot && console == CONS_SCREEN_GRAPHICS) 814 console = CONS_SCREEN_TEXT; 815 816 if (console != CONS_USBSER && console != CONS_SCREEN_GRAPHICS) { 817 if (console_set) { 818 /* 819 * If the console was set on the command line, 820 * but the ttyX-mode was not, we only need to 821 * check bootenv.rc for that setting. 822 */ 823 if ((!console_mode_set) && (console == CONS_TTY)) 824 serial_init(); 825 return; 826 } 827 828 for (i = 0; devnames[i] != NULL; i++) { 829 int n; 830 831 for (n = 0; console_devices[n].name != NULL; n++) { 832 consolep = &console_devices[n]; 833 if (strcmp(devnames[i], consolep->name) == 0) { 834 cons = consolep->value; 835 if (cons == CONS_TTY) 836 ttyn = n; 837 } 838 } 839 if (cons != CONS_INVALID) 840 break; 841 } 842 843 #if defined(__xpv) 844 /* 845 * if the hypervisor is using the currently selected console 846 * device then default to using the hypervisor as the console 847 * device. 848 */ 849 if (cons == console_hypervisor_device) { 850 cons = CONS_HYPERVISOR; 851 console_hypervisor_redirect = B_TRUE; 852 } 853 #endif /* __xpv */ 854 855 if ((cons == CONS_INVALID) || (cons == console)) { 856 /* 857 * we're sticking with whatever the current setting is 858 */ 859 return; 860 } 861 862 console = cons; 863 if (cons == CONS_TTY) { 864 tty_num = ttyn; 865 serial_init(); 866 return; 867 } 868 } else { 869 /* 870 * USB serial and GRAPHICS console 871 * we just collect data into a buffer 872 */ 873 extern void *defcons_init(size_t); 874 defcons_buf = defcons_cur = defcons_init(MMU_PAGESIZE); 875 } 876 } 877 878 #if defined(__xpv) 879 boolean_t 880 bcons_hypervisor_redirect(void) 881 { 882 return (console_hypervisor_redirect); 883 } 884 885 void 886 bcons_device_change(int new_console) 887 { 888 if (new_console < CONS_MIN || new_console > CONS_MAX) 889 return; 890 891 /* 892 * If we are asked to switch the console to the hypervisor, that 893 * really means to switch the console to whichever device the 894 * hypervisor is/was using. 895 */ 896 if (new_console == CONS_HYPERVISOR) 897 new_console = console_hypervisor_device; 898 899 console = new_console; 900 901 if (new_console == CONS_TTY) { 902 tty_num = console_hypervisor_tty_num; 903 serial_init(); 904 } 905 } 906 #endif /* __xpv */ 907 908 static void 909 defcons_putchar(int c) 910 { 911 if (defcons_buf != NULL && 912 defcons_cur + 1 - defcons_buf < MMU_PAGESIZE) { 913 *defcons_cur++ = c; 914 *defcons_cur = 0; 915 } 916 } 917 #endif /* _BOOT */ 918 919 static void 920 serial_putchar(int c) 921 { 922 int checks = 10000; 923 924 while (((inb(port + LSR) & XHRE) == 0) && checks--) 925 ; 926 outb(port + DAT, (char)c); 927 } 928 929 static int 930 serial_getchar(void) 931 { 932 uchar_t lsr; 933 934 while (serial_ischar() == 0) 935 ; 936 937 lsr = inb(port + LSR); 938 if (lsr & (SERIAL_BREAK | SERIAL_FRAME | 939 SERIAL_PARITY | SERIAL_OVERRUN)) { 940 if (lsr & SERIAL_OVERRUN) { 941 return (inb(port + DAT)); 942 } else { 943 /* Toss the garbage */ 944 (void) inb(port + DAT); 945 return (0); 946 } 947 } 948 return (inb(port + DAT)); 949 } 950 951 static int 952 serial_ischar(void) 953 { 954 return (inb(port + LSR) & RCA); 955 } 956 957 static void 958 _doputchar(int device, int c) 959 { 960 switch (device) { 961 case CONS_TTY: 962 serial_putchar(c); 963 return; 964 case CONS_SCREEN_TEXT: 965 screen_putchar(c); 966 return; 967 case CONS_SCREEN_GRAPHICS: 968 #if !defined(_BOOT) 969 case CONS_USBSER: 970 defcons_putchar(c); 971 #endif /* _BOOT */ 972 default: 973 return; 974 } 975 } 976 977 void 978 bcons_putchar(int c) 979 { 980 static int bhcharpos = 0; 981 982 #if defined(__xpv) 983 if (!DOMAIN_IS_INITDOMAIN(xen_info) || 984 console == CONS_HYPERVISOR) { 985 bcons_putchar_xen(c); 986 return; 987 } 988 #endif /* __xpv */ 989 990 if (c == '\t') { 991 do { 992 _doputchar(console, ' '); 993 if (diag != console) 994 _doputchar(diag, ' '); 995 } while (++bhcharpos % 8); 996 return; 997 } else if (c == '\n' || c == '\r') { 998 bhcharpos = 0; 999 _doputchar(console, '\r'); 1000 _doputchar(console, c); 1001 if (diag != console) { 1002 _doputchar(diag, '\r'); 1003 _doputchar(diag, c); 1004 } 1005 return; 1006 } else if (c == '\b') { 1007 if (bhcharpos) 1008 bhcharpos--; 1009 _doputchar(console, c); 1010 if (diag != console) 1011 _doputchar(diag, c); 1012 return; 1013 } 1014 1015 bhcharpos++; 1016 _doputchar(console, c); 1017 if (diag != console) 1018 _doputchar(diag, c); 1019 } 1020 1021 /* 1022 * kernel character input functions 1023 */ 1024 int 1025 bcons_getchar(void) 1026 { 1027 #if defined(__xpv) 1028 if (!DOMAIN_IS_INITDOMAIN(xen_info) || 1029 console == CONS_HYPERVISOR) 1030 return (bcons_getchar_xen()); 1031 #endif /* __xpv */ 1032 1033 for (;;) { 1034 if (console == CONS_TTY || diag == CONS_TTY) { 1035 if (serial_ischar()) 1036 return (serial_getchar()); 1037 } 1038 if (console != CONS_INVALID || diag != CONS_INVALID) { 1039 if (kb_ischar()) 1040 return (kb_getchar()); 1041 } 1042 } 1043 } 1044 1045 #if !defined(_BOOT) 1046 1047 int 1048 bcons_ischar(void) 1049 { 1050 int c = 0; 1051 1052 #if defined(__xpv) 1053 if (!DOMAIN_IS_INITDOMAIN(xen_info) || 1054 console == CONS_HYPERVISOR) 1055 return (bcons_ischar_xen()); 1056 #endif /* __xpv */ 1057 1058 switch (console) { 1059 case CONS_TTY: 1060 c = serial_ischar(); 1061 break; 1062 1063 case CONS_INVALID: 1064 break; 1065 1066 default: 1067 c = kb_ischar(); 1068 } 1069 if (c != 0) 1070 return (c); 1071 1072 switch (diag) { 1073 case CONS_TTY: 1074 c = serial_ischar(); 1075 break; 1076 1077 case CONS_INVALID: 1078 break; 1079 1080 default: 1081 c = kb_ischar(); 1082 } 1083 1084 return (c); 1085 } 1086 1087 #endif /* _BOOT */ 1088