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