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