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 - 255 */ 633 propval = find_boot_prop("tem.fg_color"); 634 if (propval != NULL) { 635 intval = atoi(propval); 636 if (intval >= 0 && intval <= 255) 637 fb_info.fg_color = intval; 638 } 639 640 /* color values are 0 - 255 */ 641 propval = find_boot_prop("tem.bg_color"); 642 if (propval != NULL && ISDIGIT(*propval)) { 643 intval = atoi(propval); 644 if (intval >= 0 && intval <= 255) 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) 662 /* 663 * Load cursor position from bootloader only in dboot, 664 * dboot will pass cursor position to kernel via xboot info. 665 */ 666 propval = find_boot_prop("tem.cursor.row"); 667 if (propval != NULL) { 668 intval = atoi(propval); 669 if (intval >= 0 && intval <= 0xFFFF) 670 fb_info.cursor.pos.y = intval; 671 } 672 673 propval = find_boot_prop("tem.cursor.col"); 674 if (propval != NULL) { 675 intval = atoi(propval); 676 if (intval >= 0 && intval <= 0xFFFF) 677 fb_info.cursor.pos.x = intval; 678 } 679 #endif 680 } 681 682 /* 683 * Go through the console_devices array trying to match the string 684 * we were given. The string on the command line must end with 685 * a comma or white space. 686 * 687 * This function does set tty_num as an side effect, this does imply that 688 * only one of the main console and the diag-device can be using serial. 689 */ 690 static int 691 lookup_console_devices(const char *cons_str) 692 { 693 int n, cons; 694 size_t len, cons_len; 695 console_value_t *consolep; 696 697 cons = CONS_INVALID; 698 if (cons_str != NULL) { 699 700 cons_len = strlen(cons_str); 701 for (n = 0; console_devices[n].name != NULL; n++) { 702 consolep = &console_devices[n]; 703 len = strlen(consolep->name); 704 if ((len <= cons_len) && ((cons_str[len] == '\0') || 705 (cons_str[len] == ',') || (cons_str[len] == '\'') || 706 (cons_str[len] == '"') || ISSPACE(cons_str[len])) && 707 (strncmp(cons_str, consolep->name, len) == 0)) { 708 cons = consolep->value; 709 if (cons == CONS_TTY) 710 tty_num = n; 711 break; 712 } 713 } 714 } 715 return (cons); 716 } 717 718 void 719 bcons_init(struct xboot_info *xbi) 720 { 721 const char *cons_str; 722 #if !defined(_BOOT) 723 static char console_text[] = "text"; 724 extern int post_fastreboot; 725 #endif 726 727 if (xbi == NULL) { 728 /* This is very early dboot console, set up ttya. */ 729 console = CONS_TTY; 730 serial_init(); 731 return; 732 } 733 734 /* Set up data to fetch properties from commad line and boot env. */ 735 boot_line = (char *)(uintptr_t)xbi->bi_cmdline; 736 bcons_init_env(xbi); 737 console = CONS_INVALID; 738 739 /* set up initial fb_info */ 740 bcons_init_fb(); 741 742 #if defined(__xpv) 743 bcons_init_xen(boot_line); 744 #endif /* __xpv */ 745 746 /* 747 * First check for diag-device. 748 */ 749 cons_str = find_boot_prop("diag-device"); 750 if (cons_str != NULL) 751 diag = lookup_console_devices(cons_str); 752 753 cons_str = find_boot_prop("console"); 754 if (cons_str == NULL) 755 cons_str = find_boot_prop("output-device"); 756 757 #if !defined(_BOOT) 758 if (post_fastreboot && strcmp(cons_str, "graphics") == 0) 759 cons_str = console_text; 760 #endif 761 762 if (cons_str != NULL) 763 console = lookup_console_devices(cons_str); 764 765 #if defined(__xpv) 766 /* 767 * domU's always use the hypervisor regardless of what 768 * the console variable may be set to. 769 */ 770 if (!DOMAIN_IS_INITDOMAIN(xen_info)) { 771 console = CONS_HYPERVISOR; 772 console_hypervisor_redirect = B_TRUE; 773 } 774 #endif /* __xpv */ 775 776 /* 777 * If no console device specified, default to text. 778 * Remember what was specified for second phase. 779 */ 780 if (console == CONS_INVALID) 781 console = CONS_SCREEN_TEXT; 782 #if !defined(_BOOT) 783 else 784 console_set = 1; 785 #endif 786 787 #if defined(__xpv) 788 if (DOMAIN_IS_INITDOMAIN(xen_info)) { 789 switch (HYPERVISOR_console_io(CONSOLEIO_get_device, 0, NULL)) { 790 case XEN_CONSOLE_COM1: 791 case XEN_CONSOLE_COM2: 792 console_hypervisor_device = CONS_TTY; 793 console_hypervisor_tty_num = tty_num; 794 break; 795 case XEN_CONSOLE_VGA: 796 /* 797 * Currently xen doesn't really support 798 * keyboard/display console devices. 799 * What this setting means is that 800 * "vga=keep" has been enabled, which is 801 * more of a xen debugging tool that a 802 * true console mode. Hence, we're going 803 * to ignore this xen "console" setting. 804 */ 805 /*FALLTHROUGH*/ 806 default: 807 console_hypervisor_device = CONS_INVALID; 808 } 809 } 810 811 /* 812 * if the hypervisor is using the currently selected serial 813 * port then default to using the hypervisor as the console 814 * device. 815 */ 816 if (console == console_hypervisor_device) { 817 console = CONS_HYPERVISOR; 818 console_hypervisor_redirect = B_TRUE; 819 } 820 #endif /* __xpv */ 821 822 /* make sure the FB is set up if present */ 823 console = boot_fb(xbi, console); 824 switch (console) { 825 case CONS_TTY: 826 serial_init(); 827 break; 828 829 case CONS_HYPERVISOR: 830 break; 831 832 #if !defined(_BOOT) 833 case CONS_USBSER: 834 /* 835 * We can't do anything with the usb serial 836 * until we have memory management. 837 */ 838 break; 839 #endif 840 case CONS_SCREEN_GRAPHICS: 841 kb_init(); 842 break; 843 case CONS_SCREEN_TEXT: 844 boot_vga_init(&bcons_dev); 845 /* Fall through */ 846 default: 847 kb_init(); 848 break; 849 } 850 851 /* 852 * Initialize diag device unless already done. 853 */ 854 switch (diag) { 855 case CONS_TTY: 856 if (console != CONS_TTY) 857 serial_init(); 858 break; 859 case CONS_SCREEN_GRAPHICS: 860 case CONS_SCREEN_TEXT: 861 if (console != CONS_SCREEN_GRAPHICS && 862 console != CONS_SCREEN_TEXT) 863 kb_init(); 864 break; 865 default: 866 break; 867 } 868 } 869 870 #if !defined(_BOOT) 871 /* 872 * 2nd part of console initialization. 873 * In the kernel (ie. fakebop), this can be used only to switch to 874 * using a serial port instead of screen based on the contents 875 * of the bootenv.rc file. 876 */ 877 /*ARGSUSED*/ 878 void 879 bcons_init2(char *inputdev, char *outputdev, char *consoledev) 880 { 881 int cons = CONS_INVALID; 882 int ttyn; 883 char *devnames[] = { consoledev, outputdev, inputdev, NULL }; 884 console_value_t *consolep; 885 int i; 886 extern int post_fastreboot; 887 888 if (post_fastreboot && console == CONS_SCREEN_GRAPHICS) 889 console = CONS_SCREEN_TEXT; 890 891 if (console != CONS_USBSER && console != CONS_SCREEN_GRAPHICS) { 892 if (console_set) { 893 /* 894 * If the console was set on the command line, 895 * but the ttyX-mode was not, we only need to 896 * check bootenv.rc for that setting. 897 */ 898 if ((!console_mode_set) && (console == CONS_TTY)) 899 serial_init(); 900 return; 901 } 902 903 for (i = 0; devnames[i] != NULL; i++) { 904 int n; 905 906 for (n = 0; console_devices[n].name != NULL; n++) { 907 consolep = &console_devices[n]; 908 if (strcmp(devnames[i], consolep->name) == 0) { 909 cons = consolep->value; 910 if (cons == CONS_TTY) 911 ttyn = n; 912 } 913 } 914 if (cons != CONS_INVALID) 915 break; 916 } 917 918 #if defined(__xpv) 919 /* 920 * if the hypervisor is using the currently selected console 921 * device then default to using the hypervisor as the console 922 * device. 923 */ 924 if (cons == console_hypervisor_device) { 925 cons = CONS_HYPERVISOR; 926 console_hypervisor_redirect = B_TRUE; 927 } 928 #endif /* __xpv */ 929 930 if ((cons == CONS_INVALID) || (cons == console)) { 931 /* 932 * we're sticking with whatever the current setting is 933 */ 934 return; 935 } 936 937 console = cons; 938 if (cons == CONS_TTY) { 939 tty_num = ttyn; 940 serial_init(); 941 return; 942 } 943 } else { 944 /* 945 * USB serial and GRAPHICS console 946 * we just collect data into a buffer 947 */ 948 extern void *defcons_init(size_t); 949 defcons_buf = defcons_cur = defcons_init(MMU_PAGESIZE); 950 } 951 } 952 953 #if defined(__xpv) 954 boolean_t 955 bcons_hypervisor_redirect(void) 956 { 957 return (console_hypervisor_redirect); 958 } 959 960 void 961 bcons_device_change(int new_console) 962 { 963 if (new_console < CONS_MIN || new_console > CONS_MAX) 964 return; 965 966 /* 967 * If we are asked to switch the console to the hypervisor, that 968 * really means to switch the console to whichever device the 969 * hypervisor is/was using. 970 */ 971 if (new_console == CONS_HYPERVISOR) 972 new_console = console_hypervisor_device; 973 974 console = new_console; 975 976 if (new_console == CONS_TTY) { 977 tty_num = console_hypervisor_tty_num; 978 serial_init(); 979 } 980 } 981 #endif /* __xpv */ 982 983 static void 984 defcons_putchar(int c) 985 { 986 if (defcons_buf != NULL && 987 defcons_cur + 1 - defcons_buf < MMU_PAGESIZE) { 988 *defcons_cur++ = c; 989 *defcons_cur = 0; 990 } 991 } 992 #endif /* _BOOT */ 993 994 static void 995 serial_putchar(int c) 996 { 997 int checks = 10000; 998 999 while (((inb(port + LSR) & XHRE) == 0) && checks--) 1000 ; 1001 outb(port + DAT, (char)c); 1002 } 1003 1004 static int 1005 serial_getchar(void) 1006 { 1007 uchar_t lsr; 1008 1009 while (serial_ischar() == 0) 1010 ; 1011 1012 lsr = inb(port + LSR); 1013 if (lsr & (SERIAL_BREAK | SERIAL_FRAME | 1014 SERIAL_PARITY | SERIAL_OVERRUN)) { 1015 if (lsr & SERIAL_OVERRUN) { 1016 return (inb(port + DAT)); 1017 } else { 1018 /* Toss the garbage */ 1019 (void) inb(port + DAT); 1020 return (0); 1021 } 1022 } 1023 return (inb(port + DAT)); 1024 } 1025 1026 static int 1027 serial_ischar(void) 1028 { 1029 return (inb(port + LSR) & RCA); 1030 } 1031 1032 static void 1033 btem_control(btem_state_t *btem, int c) 1034 { 1035 int y, rows, cols; 1036 1037 rows = fb_info.cursor.pos.y; 1038 cols = fb_info.cursor.pos.x; 1039 1040 btem->btem_state = A_STATE_START; 1041 switch (c) { 1042 case A_BS: 1043 bcons_dev.bd_setpos(rows, cols - 1); 1044 break; 1045 1046 case A_HT: 1047 cols += 8 - (cols % 8); 1048 if (cols >= fb_info.terminal.x) 1049 cols = fb_info.terminal.x - 1; 1050 bcons_dev.bd_setpos(rows, cols); 1051 break; 1052 1053 case A_CR: 1054 bcons_dev.bd_setpos(rows, 0); 1055 break; 1056 1057 case A_FF: 1058 for (y = 0; y < fb_info.terminal.y; y++) { 1059 bcons_dev.bd_setpos(y, 0); 1060 bcons_dev.bd_eraseline(); 1061 } 1062 bcons_dev.bd_setpos(0, 0); 1063 break; 1064 1065 case A_ESC: 1066 btem->btem_state = A_STATE_ESC; 1067 break; 1068 1069 default: 1070 bcons_dev.bd_putchar(c); 1071 break; 1072 } 1073 } 1074 1075 /* 1076 * if parameters [0..count - 1] are not set, set them to the value 1077 * of newparam. 1078 */ 1079 static void 1080 btem_setparam(btem_state_t *btem, int count, int newparam) 1081 { 1082 int i; 1083 1084 for (i = 0; i < count; i++) { 1085 if (btem->btem_params[i] == -1) 1086 btem->btem_params[i] = newparam; 1087 } 1088 } 1089 1090 static void 1091 btem_chkparam(btem_state_t *btem, int c) 1092 { 1093 int rows, cols; 1094 1095 rows = fb_info.cursor.pos.y; 1096 cols = fb_info.cursor.pos.x; 1097 switch (c) { 1098 case '@': /* insert char */ 1099 btem_setparam(btem, 1, 1); 1100 bcons_dev.bd_shift(btem->btem_params[0]); 1101 break; 1102 1103 case 'A': /* cursor up */ 1104 btem_setparam(btem, 1, 1); 1105 bcons_dev.bd_setpos(rows - btem->btem_params[0], cols); 1106 break; 1107 1108 case 'B': /* cursor down */ 1109 btem_setparam(btem, 1, 1); 1110 bcons_dev.bd_setpos(rows + btem->btem_params[0], cols); 1111 break; 1112 1113 case 'C': /* cursor right */ 1114 btem_setparam(btem, 1, 1); 1115 bcons_dev.bd_setpos(rows, cols + btem->btem_params[0]); 1116 break; 1117 1118 case 'D': /* cursor left */ 1119 btem_setparam(btem, 1, 1); 1120 bcons_dev.bd_setpos(rows, cols - btem->btem_params[0]); 1121 break; 1122 1123 case 'K': 1124 bcons_dev.bd_eraseline(); 1125 break; 1126 default: 1127 /* bcons_dev.bd_putchar(c); */ 1128 break; 1129 } 1130 btem->btem_state = A_STATE_START; 1131 } 1132 1133 static void 1134 btem_getparams(btem_state_t *btem, int c) 1135 { 1136 if (isdigit(c)) { 1137 btem->btem_paramval = btem->btem_paramval * 10 + c - '0'; 1138 btem->btem_gotparam = B_TRUE; 1139 return; 1140 } 1141 1142 if (btem->btem_curparam < BTEM_MAXPARAMS) { 1143 if (btem->btem_gotparam == B_TRUE) { 1144 btem->btem_params[btem->btem_curparam] = 1145 btem->btem_paramval; 1146 } 1147 btem->btem_curparam++; 1148 } 1149 1150 if (c == ';') { 1151 /* Restart parameter search */ 1152 btem->btem_gotparam = B_FALSE; 1153 btem->btem_paramval = 0; 1154 } else { 1155 btem_chkparam(btem, c); 1156 } 1157 } 1158 1159 /* Simple boot terminal parser. */ 1160 static void 1161 btem_parse(btem_state_t *btem, int c) 1162 { 1163 int i; 1164 1165 /* Normal state? */ 1166 if (btem->btem_state == A_STATE_START) { 1167 if (c == A_CSI || c < ' ') 1168 btem_control(btem, c); 1169 else 1170 bcons_dev.bd_putchar(c); 1171 return; 1172 } 1173 1174 /* In <ESC> sequence */ 1175 if (btem->btem_state != A_STATE_ESC) { 1176 btem_getparams(btem, c); 1177 return; 1178 } 1179 1180 /* Previous char was <ESC> */ 1181 switch (c) { 1182 case '[': 1183 btem->btem_curparam = 0; 1184 btem->btem_paramval = 0; 1185 btem->btem_gotparam = B_FALSE; 1186 /* clear the parameters */ 1187 for (i = 0; i < BTEM_MAXPARAMS; i++) 1188 btem->btem_params[i] = -1; 1189 btem->btem_state = A_STATE_CSI; 1190 return; 1191 1192 case 'Q': /* <ESC>Q */ 1193 case 'C': /* <ESC>C */ 1194 btem->btem_state = A_STATE_START; 1195 return; 1196 1197 default: 1198 btem->btem_state = A_STATE_START; 1199 break; 1200 } 1201 1202 if (c < ' ') 1203 btem_control(btem, c); 1204 else 1205 bcons_dev.bd_putchar(c); 1206 } 1207 1208 static void 1209 _doputchar(int device, int c) 1210 { 1211 switch (device) { 1212 case CONS_TTY: 1213 serial_putchar(c); 1214 return; 1215 case CONS_SCREEN_TEXT: 1216 case CONS_FRAMEBUFFER: 1217 bcons_dev.bd_cursor(B_FALSE); 1218 btem_parse(&boot_tem, c); 1219 bcons_dev.bd_cursor(B_TRUE); 1220 return; 1221 case CONS_SCREEN_GRAPHICS: 1222 #if !defined(_BOOT) 1223 case CONS_USBSER: 1224 defcons_putchar(c); 1225 #endif /* _BOOT */ 1226 default: 1227 return; 1228 } 1229 } 1230 1231 void 1232 bcons_putchar(int c) 1233 { 1234 #if defined(__xpv) 1235 if (!DOMAIN_IS_INITDOMAIN(xen_info) || 1236 console == CONS_HYPERVISOR) { 1237 bcons_putchar_xen(c); 1238 return; 1239 } 1240 #endif /* __xpv */ 1241 1242 if (c == '\n') { 1243 _doputchar(console, '\r'); 1244 if (diag != console) 1245 _doputchar(diag, '\r'); 1246 } 1247 _doputchar(console, c); 1248 if (diag != console) 1249 _doputchar(diag, c); 1250 } 1251 1252 /* 1253 * kernel character input functions 1254 */ 1255 int 1256 bcons_getchar(void) 1257 { 1258 #if defined(__xpv) 1259 if (!DOMAIN_IS_INITDOMAIN(xen_info) || 1260 console == CONS_HYPERVISOR) 1261 return (bcons_getchar_xen()); 1262 #endif /* __xpv */ 1263 1264 for (;;) { 1265 if (console == CONS_TTY || diag == CONS_TTY) { 1266 if (serial_ischar()) 1267 return (serial_getchar()); 1268 } 1269 if (console != CONS_INVALID || diag != CONS_INVALID) { 1270 if (kb_ischar()) 1271 return (kb_getchar()); 1272 } 1273 } 1274 } 1275 1276 #if !defined(_BOOT) 1277 1278 int 1279 bcons_ischar(void) 1280 { 1281 int c = 0; 1282 1283 #if defined(__xpv) 1284 if (!DOMAIN_IS_INITDOMAIN(xen_info) || 1285 console == CONS_HYPERVISOR) 1286 return (bcons_ischar_xen()); 1287 #endif /* __xpv */ 1288 1289 switch (console) { 1290 case CONS_TTY: 1291 c = serial_ischar(); 1292 break; 1293 1294 case CONS_INVALID: 1295 break; 1296 1297 default: 1298 c = kb_ischar(); 1299 } 1300 if (c != 0) 1301 return (c); 1302 1303 switch (diag) { 1304 case CONS_TTY: 1305 c = serial_ischar(); 1306 break; 1307 1308 case CONS_INVALID: 1309 break; 1310 1311 default: 1312 c = kb_ischar(); 1313 } 1314 1315 return (c); 1316 } 1317 1318 #endif /* _BOOT */