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         console_value_t *consolep;
1207         int i;
1208         extern int post_fastreboot;
1209 
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 */