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