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