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