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