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/boot_console.h>
  32 #include <sys/panic.h>
  33 #include <sys/ctype.h>
  34 #if defined(__xpv)
  35 #include <sys/hypervisor.h>
  36 #endif /* __xpv */
  37 
  38 #include "boot_serial.h"
  39 #include "boot_vga.h"
  40 
  41 #if defined(_BOOT)
  42 #include <dboot/dboot_asm.h>
  43 #include <dboot/dboot_xboot.h>
  44 #else /* _BOOT */
  45 #include <sys/bootconf.h>
  46 #if defined(__xpv)
  47 #include <sys/evtchn_impl.h>
  48 #endif /* __xpv */
  49 static char *defcons_buf;
  50 static char *defcons_cur;
  51 #endif /* _BOOT */
  52 
  53 #if defined(__xpv)
  54 extern void bcons_init_xen(char *);
  55 extern void bcons_putchar_xen(int);
  56 extern int bcons_getchar_xen(void);
  57 extern int bcons_ischar_xen(void);
  58 #endif /* __xpv */
  59 
  60 static int cons_color = CONS_COLOR;
  61 static int console = CONS_SCREEN_TEXT;
  62 static int tty_num = 0;
  63 static int tty_addr[] = {0x3f8, 0x2f8, 0x3e8, 0x2e8};
  64 static char *boot_line;
  65 static struct boot_env {
  66         char    *be_env;        /* ends with double ascii nul */
  67         size_t  be_size;        /* size of the environment, including nul */
  68 } boot_env;
  69 
  70 static int serial_ischar(void);
  71 static int serial_getchar(void);
  72 static void serial_putchar(int);
  73 static void serial_adjust_prop(void);
  74 
  75 #if !defined(_BOOT)
  76 /* Set if the console or mode are expressed in the boot line */
  77 static int console_set, console_mode_set;
  78 #endif
  79 
  80 #if defined(__xpv)
  81 static int console_hypervisor_redirect = B_FALSE;
  82 static int console_hypervisor_device = CONS_INVALID;
  83 static int console_hypervisor_tty_num = 0;
  84 
  85 /* Obtain the hypervisor console type */
  86 int
  87 console_hypervisor_dev_type(int *tnum)
  88 {
  89         if (tnum != NULL)
  90                 *tnum = console_hypervisor_tty_num;
  91         return (console_hypervisor_device);
  92 }
  93 #endif /* __xpv */
  94 
  95 /* Clear the screen and initialize VIDEO, XPOS and YPOS. */
  96 void
  97 clear_screen(void)
  98 {
  99         /*
 100          * XXX should set vga mode so we don't depend on the
 101          * state left by the boot loader.  Note that we have to
 102          * enable the cursor before clearing the screen since
 103          * the cursor position is dependant upon the cursor
 104          * skew, which is initialized by vga_cursor_display()
 105          */
 106         vga_cursor_display();
 107         vga_clear(cons_color);
 108         vga_setpos(0, 0);
 109 }
 110 
 111 /* Put the character C on the screen. */
 112 static void
 113 screen_putchar(int c)
 114 {
 115         int row, col;
 116 
 117         vga_getpos(&row, &col);
 118         switch (c) {
 119         case '\t':
 120                 col += 8 - (col % 8);
 121                 if (col == VGA_TEXT_COLS)
 122                         col = 79;
 123                 vga_setpos(row, col);
 124                 break;
 125 
 126         case '\r':
 127                 vga_setpos(row, 0);
 128                 break;
 129 
 130         case '\b':
 131                 if (col > 0)
 132                         vga_setpos(row, col - 1);
 133                 break;
 134 
 135         case '\n':
 136                 if (row < VGA_TEXT_ROWS - 1)
 137                         vga_setpos(row + 1, col);
 138                 else
 139                         vga_scroll(cons_color);
 140                 break;
 141 
 142         default:
 143                 vga_drawc(c, cons_color);
 144                 if (col < VGA_TEXT_COLS -1)
 145                         vga_setpos(row, col + 1);
 146                 else if (row < VGA_TEXT_ROWS - 1)
 147                         vga_setpos(row + 1, 0);
 148                 else {
 149                         vga_setpos(row, 0);
 150                         vga_scroll(cons_color);
 151                 }
 152                 break;
 153         }
 154 }
 155 
 156 static int port;
 157 
 158 static void
 159 serial_init(void)
 160 {
 161         port = tty_addr[tty_num];
 162 
 163         outb(port + ISR, 0x20);
 164         if (inb(port + ISR) & 0x20) {
 165                 /*
 166                  * 82510 chip is present
 167                  */
 168                 outb(port + DAT+7, 0x04);       /* clear status */
 169                 outb(port + ISR, 0x40);  /* set to bank 2 */
 170                 outb(port + MCR, 0x08);  /* IMD */
 171                 outb(port + DAT, 0x21);  /* FMD */
 172                 outb(port + ISR, 0x00);  /* set to bank 0 */
 173         } else {
 174                 /*
 175                  * set the UART in FIFO mode if it has FIFO buffers.
 176                  * use 16550 fifo reset sequence specified in NS
 177                  * application note. disable fifos until chip is
 178                  * initialized.
 179                  */
 180                 outb(port + FIFOR, 0x00);               /* clear */
 181                 outb(port + FIFOR, FIFO_ON);            /* enable */
 182                 outb(port + FIFOR, FIFO_ON|FIFORXFLSH);  /* reset */
 183                 outb(port + FIFOR,
 184                     FIFO_ON|FIFODMA|FIFOTXFLSH|FIFORXFLSH|0x80);
 185                 if ((inb(port + ISR) & 0xc0) != 0xc0) {
 186                         /*
 187                          * no fifo buffers so disable fifos.
 188                          * this is true for 8250's
 189                          */
 190                         outb(port + FIFOR, 0x00);
 191                 }
 192         }
 193 
 194         /* disable interrupts */
 195         outb(port + ICR, 0);
 196 
 197 #if !defined(_BOOT)
 198         if (IN_XPV_PANIC())
 199                 return;
 200 #endif
 201 
 202         /* adjust setting based on tty properties */
 203         serial_adjust_prop();
 204 
 205 #if defined(_BOOT)
 206         /*
 207          * Do a full reset to match console behavior.
 208          * 0x1B + c - reset everything
 209          */
 210         serial_putchar(0x1B);
 211         serial_putchar('c');
 212 #endif
 213 }
 214 
 215 /* Advance str pointer past white space */
 216 #define EAT_WHITE_SPACE(str)    {                       \
 217         while ((*str != '\0') && ISSPACE(*str))         \
 218                 str++;                                  \
 219 }
 220 
 221 /*
 222  * boot_line is set when we call here.  Search it for the argument name,
 223  * and if found, return a pointer to it.
 224  */
 225 static char *
 226 find_boot_line_prop(const char *name)
 227 {
 228         char *ptr;
 229         char *ret = NULL;
 230         char end_char;
 231         size_t len;
 232 
 233         if (boot_line == NULL)
 234                 return (NULL);
 235 
 236         len = strlen(name);
 237 
 238         /*
 239          * We have two nested loops here: the outer loop discards all options
 240          * except -B, and the inner loop parses the -B options looking for
 241          * the one we're interested in.
 242          */
 243         for (ptr = boot_line; *ptr != '\0'; ptr++) {
 244                 EAT_WHITE_SPACE(ptr);
 245 
 246                 if (*ptr == '-') {
 247                         ptr++;
 248                         while ((*ptr != '\0') && (*ptr != 'B') &&
 249                             !ISSPACE(*ptr))
 250                                 ptr++;
 251                         if (*ptr == '\0')
 252                                 goto out;
 253                         else if (*ptr != 'B')
 254                                 continue;
 255                 } else {
 256                         while ((*ptr != '\0') && !ISSPACE(*ptr))
 257                                 ptr++;
 258                         if (*ptr == '\0')
 259                                 goto out;
 260                         continue;
 261                 }
 262 
 263                 do {
 264                         ptr++;
 265                         EAT_WHITE_SPACE(ptr);
 266 
 267                         if ((strncmp(ptr, name, len) == 0) &&
 268                             (ptr[len] == '=')) {
 269                                 ptr += len + 1;
 270                                 if ((*ptr == '\'') || (*ptr == '"')) {
 271                                         ret = ptr + 1;
 272                                         end_char = *ptr;
 273                                         ptr++;
 274                                 } else {
 275                                         ret = ptr;
 276                                         end_char = ',';
 277                                 }
 278                                 goto consume_property;
 279                         }
 280 
 281                         /*
 282                          * We have a property, and it's not the one we're
 283                          * interested in.  Skip the property name.  A name
 284                          * can end with '=', a comma, or white space.
 285                          */
 286                         while ((*ptr != '\0') && (*ptr != '=') &&
 287                             (*ptr != ',') && (!ISSPACE(*ptr)))
 288                                 ptr++;
 289 
 290                         /*
 291                          * We only want to go through the rest of the inner
 292                          * loop if we have a comma.  If we have a property
 293                          * name without a value, either continue or break.
 294                          */
 295                         if (*ptr == '\0')
 296                                 goto out;
 297                         else if (*ptr == ',')
 298                                 continue;
 299                         else if (ISSPACE(*ptr))
 300                                 break;
 301                         ptr++;
 302 
 303                         /*
 304                          * Is the property quoted?
 305                          */
 306                         if ((*ptr == '\'') || (*ptr == '"')) {
 307                                 end_char = *ptr;
 308                                 ptr++;
 309                         } else {
 310                                 /*
 311                                  * Not quoted, so the string ends at a comma
 312                                  * or at white space.  Deal with white space
 313                                  * later.
 314                                  */
 315                                 end_char = ',';
 316                         }
 317 
 318                         /*
 319                          * Now, we can ignore any characters until we find
 320                          * end_char.
 321                          */
 322 consume_property:
 323                         for (; (*ptr != '\0') && (*ptr != end_char); ptr++) {
 324                                 if ((end_char == ',') && ISSPACE(*ptr))
 325                                         break;
 326                         }
 327                         if (*ptr && (*ptr != ',') && !ISSPACE(*ptr))
 328                                 ptr++;
 329                 } while (*ptr == ',');
 330         }
 331 out:
 332         return (ret);
 333 }
 334 
 335 /*
 336  * Find prop from boot env module. The data in module is list of C strings
 337  * name=value, the list is terminated by double nul.
 338  */
 339 static const char *
 340 find_boot_env_prop(const char *name)
 341 {
 342         char *ptr;
 343         size_t len;
 344         uintptr_t size;
 345 
 346         if (boot_env.be_env == NULL)
 347                 return (NULL);
 348 
 349         ptr = boot_env.be_env;
 350         len = strlen(name);
 351 
 352         /*
 353          * Make sure we have at least len + 2 bytes in the environment.
 354          * We are looking for name=value\0 constructs, and the environment
 355          * itself is terminated by '\0'.
 356          */
 357         if (boot_env.be_size < len + 2)
 358                 return (NULL);
 359 
 360         do {
 361                 if ((strncmp(ptr, name, len) == 0) && (ptr[len] == '=')) {
 362                         ptr += len + 1;
 363                         return (ptr);
 364                 }
 365                 /* find the first '\0' */
 366                 while (*ptr != '\0') {
 367                         ptr++;
 368                         size = (uintptr_t)ptr - (uintptr_t)boot_env.be_env;
 369                         if (size > boot_env.be_size)
 370                                 return (NULL);
 371                 }
 372                 ptr++;
 373 
 374                 /* If the remainder is shorter than name + 2, get out. */
 375                 size = (uintptr_t)ptr - (uintptr_t)boot_env.be_env;
 376                 if (boot_env.be_size - size < len + 2)
 377                         return (NULL);
 378         } while (*ptr != '\0');
 379         return (NULL);
 380 }
 381 
 382 /*
 383  * Get prop value from either command line or boot environment.
 384  * We always check kernel command line first, as this will keep the
 385  * functionality and will allow user to override the values in environment.
 386  */
 387 const char *
 388 find_boot_prop(const char *name)
 389 {
 390         const char *value = find_boot_line_prop(name);
 391 
 392         if (value == NULL)
 393                 value = find_boot_env_prop(name);
 394         return (value);
 395 }
 396 
 397 #define MATCHES(p, pat) \
 398         (strncmp(p, pat, strlen(pat)) == 0 ? (p += strlen(pat), 1) : 0)
 399 
 400 #define SKIP(p, c)                              \
 401         while (*(p) != 0 && *p != (c))          \
 402                 ++(p);                          \
 403         if (*(p) == (c))                        \
 404                 ++(p);
 405 
 406 /*
 407  * find a tty mode property either from cmdline or from boot properties
 408  */
 409 static const char *
 410 get_mode_value(char *name)
 411 {
 412         /*
 413          * when specified on boot line it looks like "name" "="....
 414          */
 415         if (boot_line != NULL) {
 416                 return (find_boot_prop(name));
 417         }
 418 
 419 #if defined(_BOOT)
 420         return (NULL);
 421 #else
 422         /*
 423          * if we're running in the full kernel we check the bootenv.rc settings
 424          */
 425         {
 426                 static char propval[20];
 427 
 428                 propval[0] = 0;
 429                 if (do_bsys_getproplen(NULL, name) <= 0)
 430                         return (NULL);
 431                 (void) do_bsys_getprop(NULL, name, propval);
 432                 return (propval);
 433         }
 434 #endif
 435 }
 436 
 437 /*
 438  * adjust serial port based on properties
 439  * These come either from the cmdline or from boot properties.
 440  */
 441 static void
 442 serial_adjust_prop(void)
 443 {
 444         char propname[20];
 445         const char *propval;
 446         const char *p;
 447         ulong_t baud;
 448         uchar_t lcr = 0;
 449         uchar_t mcr = DTR | RTS;
 450 
 451         (void) strcpy(propname, "ttyX-mode");
 452         propname[3] = 'a' + tty_num;
 453         propval = get_mode_value(propname);
 454         if (propval == NULL)
 455                 propval = "9600,8,n,1,-";
 456 #if !defined(_BOOT)
 457         else
 458                 console_mode_set = 1;
 459 #endif
 460 
 461         /* property is of the form: "9600,8,n,1,-" */
 462         p = propval;
 463         if (MATCHES(p, "110,"))
 464                 baud = ASY110;
 465         else if (MATCHES(p, "150,"))
 466                 baud = ASY150;
 467         else if (MATCHES(p, "300,"))
 468                 baud = ASY300;
 469         else if (MATCHES(p, "600,"))
 470                 baud = ASY600;
 471         else if (MATCHES(p, "1200,"))
 472                 baud = ASY1200;
 473         else if (MATCHES(p, "2400,"))
 474                 baud = ASY2400;
 475         else if (MATCHES(p, "4800,"))
 476                 baud = ASY4800;
 477         else if (MATCHES(p, "19200,"))
 478                 baud = ASY19200;
 479         else if (MATCHES(p, "38400,"))
 480                 baud = ASY38400;
 481         else if (MATCHES(p, "57600,"))
 482                 baud = ASY57600;
 483         else if (MATCHES(p, "115200,"))
 484                 baud = ASY115200;
 485         else {
 486                 baud = ASY9600;
 487                 SKIP(p, ',');
 488         }
 489         outb(port + LCR, DLAB);
 490         outb(port + DAT + DLL, baud & 0xff);
 491         outb(port + DAT + DLH, (baud >> 8) & 0xff);
 492 
 493         switch (*p) {
 494         case '5':
 495                 lcr |= BITS5;
 496                 ++p;
 497                 break;
 498         case '6':
 499                 lcr |= BITS6;
 500                 ++p;
 501                 break;
 502         case '7':
 503                 lcr |= BITS7;
 504                 ++p;
 505                 break;
 506         case '8':
 507                 ++p;
 508         default:
 509                 lcr |= BITS8;
 510                 break;
 511         }
 512 
 513         SKIP(p, ',');
 514 
 515         switch (*p) {
 516         case 'n':
 517                 lcr |= PARITY_NONE;
 518                 ++p;
 519                 break;
 520         case 'o':
 521                 lcr |= PARITY_ODD;
 522                 ++p;
 523                 break;
 524         case 'e':
 525                 ++p;
 526         default:
 527                 lcr |= PARITY_EVEN;
 528                 break;
 529         }
 530 
 531 
 532         SKIP(p, ',');
 533 
 534         switch (*p) {
 535         case '1':
 536                 /* STOP1 is 0 */
 537                 ++p;
 538                 break;
 539         default:
 540                 lcr |= STOP2;
 541                 break;
 542         }
 543         /* set parity bits */
 544         outb(port + LCR, lcr);
 545 
 546         (void) strcpy(propname, "ttyX-rts-dtr-off");
 547         propname[3] = 'a' + tty_num;
 548         propval = get_mode_value(propname);
 549         if (propval == NULL)
 550                 propval = "false";
 551         if (propval[0] != 'f' && propval[0] != 'F')
 552                 mcr = 0;
 553         /* set modem control bits */
 554         outb(port + MCR, mcr | OUT2);
 555 }
 556 
 557 /* Obtain the console type */
 558 int
 559 boot_console_type(int *tnum)
 560 {
 561         if (tnum != NULL)
 562                 *tnum = tty_num;
 563         return (console);
 564 }
 565 
 566 /*
 567  * A structure to map console names to values.
 568  */
 569 typedef struct {
 570         char *name;
 571         int value;
 572 } console_value_t;
 573 
 574 console_value_t console_devices[] = {
 575         { "ttya", CONS_TTY },   /* 0 */
 576         { "ttyb", CONS_TTY },   /* 1 */
 577         { "ttyc", CONS_TTY },   /* 2 */
 578         { "ttyd", CONS_TTY },   /* 3 */
 579         { "text", CONS_SCREEN_TEXT },
 580         { "graphics", CONS_SCREEN_GRAPHICS },
 581 #if defined(__xpv)
 582         { "hypervisor", CONS_HYPERVISOR },
 583 #endif
 584 #if !defined(_BOOT)
 585         { "usb-serial", CONS_USBSER },
 586 #endif
 587         { NULL, CONS_INVALID }
 588 };
 589 
 590 static void
 591 bcons_init_env(struct xboot_info *xbi)
 592 {
 593         uint32_t i;
 594         struct boot_modules *modules;
 595 
 596         modules = (struct boot_modules *)(uintptr_t)xbi->bi_modules;
 597         for (i = 0; i < xbi->bi_module_cnt; i++) {
 598                 if (modules[i].bm_type == BMT_ENV)
 599                         break;
 600         }
 601         if (i == xbi->bi_module_cnt)
 602                 return;
 603 
 604         boot_env.be_env = (char *)(uintptr_t)modules[i].bm_addr;
 605         boot_env.be_size = modules[i].bm_size;
 606 }
 607 
 608 void
 609 bcons_init(struct xboot_info *xbi)
 610 {
 611         console_value_t *consolep;
 612         size_t len, cons_len;
 613         const char *cons_str;
 614 #if !defined(_BOOT)
 615         static char console_text[] = "text";
 616         extern int post_fastreboot;
 617 #endif
 618 
 619         /* Set up data to fetch properties from commad line and boot env. */
 620         boot_line = (char *)(uintptr_t)xbi->bi_cmdline;
 621         bcons_init_env(xbi);
 622         console = CONS_INVALID;
 623 
 624 #if defined(__xpv)
 625         bcons_init_xen(boot_line);
 626 #endif /* __xpv */
 627 
 628         cons_str = find_boot_prop("console");
 629         if (cons_str == NULL)
 630                 cons_str = find_boot_prop("output-device");
 631 
 632 #if !defined(_BOOT)
 633         if (post_fastreboot && strcmp(cons_str, "graphics") == 0)
 634                 cons_str = console_text;
 635 #endif
 636 
 637         /*
 638          * Go through the console_devices array trying to match the string
 639          * we were given.  The string on the command line must end with
 640          * a comma or white space.
 641          */
 642         if (cons_str != NULL) {
 643                 int n;
 644 
 645                 cons_len = strlen(cons_str);
 646                 for (n = 0; console_devices[n].name != NULL; n++) {
 647                         consolep = &console_devices[n];
 648                         len = strlen(consolep->name);
 649                         if ((len <= cons_len) && ((cons_str[len] == '\0') ||
 650                             (cons_str[len] == ',') || (cons_str[len] == '\'') ||
 651                             (cons_str[len] == '"') || ISSPACE(cons_str[len])) &&
 652                             (strncmp(cons_str, consolep->name, len) == 0)) {
 653                                 console = consolep->value;
 654                                 if (console == CONS_TTY)
 655                                         tty_num = n;
 656                                 break;
 657                         }
 658                 }
 659         }
 660 
 661 #if defined(__xpv)
 662         /*
 663          * domU's always use the hypervisor regardless of what
 664          * the console variable may be set to.
 665          */
 666         if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
 667                 console = CONS_HYPERVISOR;
 668                 console_hypervisor_redirect = B_TRUE;
 669         }
 670 #endif /* __xpv */
 671 
 672         /*
 673          * If no console device specified, default to text.
 674          * Remember what was specified for second phase.
 675          */
 676         if (console == CONS_INVALID)
 677                 console = CONS_SCREEN_TEXT;
 678 #if !defined(_BOOT)
 679         else
 680                 console_set = 1;
 681 #endif
 682 
 683 #if defined(__xpv)
 684         if (DOMAIN_IS_INITDOMAIN(xen_info)) {
 685                 switch (HYPERVISOR_console_io(CONSOLEIO_get_device, 0, NULL)) {
 686                         case XEN_CONSOLE_COM1:
 687                         case XEN_CONSOLE_COM2:
 688                                 console_hypervisor_device = CONS_TTY;
 689                                 console_hypervisor_tty_num = tty_num;
 690                                 break;
 691                         case XEN_CONSOLE_VGA:
 692                                 /*
 693                                  * Currently xen doesn't really support
 694                                  * keyboard/display console devices.
 695                                  * What this setting means is that
 696                                  * "vga=keep" has been enabled, which is
 697                                  * more of a xen debugging tool that a
 698                                  * true console mode.  Hence, we're going
 699                                  * to ignore this xen "console" setting.
 700                                  */
 701                                 /*FALLTHROUGH*/
 702                         default:
 703                                 console_hypervisor_device = CONS_INVALID;
 704                 }
 705         }
 706 
 707         /*
 708          * if the hypervisor is using the currently selected serial
 709          * port then default to using the hypervisor as the console
 710          * device.
 711          */
 712         if (console == console_hypervisor_device) {
 713                 console = CONS_HYPERVISOR;
 714                 console_hypervisor_redirect = B_TRUE;
 715         }
 716 #endif /* __xpv */
 717 
 718         switch (console) {
 719         case CONS_TTY:
 720                 serial_init();
 721                 break;
 722 
 723         case CONS_HYPERVISOR:
 724                 break;
 725 
 726 #if !defined(_BOOT)
 727         case CONS_USBSER:
 728                 /*
 729                  * We can't do anything with the usb serial
 730                  * until we have memory management.
 731                  */
 732                 break;
 733 #endif
 734         case CONS_SCREEN_GRAPHICS:
 735                 kb_init();
 736                 break;
 737         case CONS_SCREEN_TEXT:
 738         default:
 739 #if defined(_BOOT)
 740                 clear_screen(); /* clears the grub or xen screen */
 741 #endif /* _BOOT */
 742                 kb_init();
 743                 break;
 744         }
 745 }
 746 
 747 #if !defined(_BOOT)
 748 /*
 749  * 2nd part of console initialization.
 750  * In the kernel (ie. fakebop), this can be used only to switch to
 751  * using a serial port instead of screen based on the contents
 752  * of the bootenv.rc file.
 753  */
 754 /*ARGSUSED*/
 755 void
 756 bcons_init2(char *inputdev, char *outputdev, char *consoledev)
 757 {
 758         int cons = CONS_INVALID;
 759         int ttyn;
 760         char *devnames[] = { consoledev, outputdev, inputdev, NULL };
 761         console_value_t *consolep;
 762         int i;
 763         extern int post_fastreboot;
 764 
 765         if (post_fastreboot && console == CONS_SCREEN_GRAPHICS)
 766                 console = CONS_SCREEN_TEXT;
 767 
 768         if (console != CONS_USBSER && console != CONS_SCREEN_GRAPHICS) {
 769                 if (console_set) {
 770                         /*
 771                          * If the console was set on the command line,
 772                          * but the ttyX-mode was not, we only need to
 773                          * check bootenv.rc for that setting.
 774                          */
 775                         if ((!console_mode_set) && (console == CONS_TTY))
 776                                 serial_init();
 777                         return;
 778                 }
 779 
 780                 for (i = 0; devnames[i] != NULL; i++) {
 781                         int n;
 782 
 783                         for (n = 0; console_devices[n].name != NULL; n++) {
 784                                 consolep = &console_devices[n];
 785                                 if (strcmp(devnames[i], consolep->name) == 0) {
 786                                         cons = consolep->value;
 787                                         if (cons == CONS_TTY)
 788                                                 ttyn = n;
 789                                 }
 790                         }
 791                         if (cons != CONS_INVALID)
 792                                 break;
 793                 }
 794 
 795 #if defined(__xpv)
 796                 /*
 797                  * if the hypervisor is using the currently selected console
 798                  * device then default to using the hypervisor as the console
 799                  * device.
 800                  */
 801                 if (cons == console_hypervisor_device) {
 802                         cons = CONS_HYPERVISOR;
 803                         console_hypervisor_redirect = B_TRUE;
 804                 }
 805 #endif /* __xpv */
 806 
 807                 if ((cons == CONS_INVALID) || (cons == console)) {
 808                         /*
 809                          * we're sticking with whatever the current setting is
 810                          */
 811                         return;
 812                 }
 813 
 814                 console = cons;
 815                 if (cons == CONS_TTY) {
 816                         tty_num = ttyn;
 817                         serial_init();
 818                         return;
 819                 }
 820         } else {
 821                 /*
 822                  * USB serial and GRAPHICS console
 823                  * we just collect data into a buffer
 824                  */
 825                 extern void *defcons_init(size_t);
 826                 defcons_buf = defcons_cur = defcons_init(MMU_PAGESIZE);
 827         }
 828 }
 829 
 830 #if defined(__xpv)
 831 boolean_t
 832 bcons_hypervisor_redirect(void)
 833 {
 834         return (console_hypervisor_redirect);
 835 }
 836 
 837 void
 838 bcons_device_change(int new_console)
 839 {
 840         if (new_console < CONS_MIN || new_console > CONS_MAX)
 841                 return;
 842 
 843         /*
 844          * If we are asked to switch the console to the hypervisor, that
 845          * really means to switch the console to whichever device the
 846          * hypervisor is/was using.
 847          */
 848         if (new_console == CONS_HYPERVISOR)
 849                 new_console = console_hypervisor_device;
 850 
 851         console = new_console;
 852 
 853         if (new_console == CONS_TTY) {
 854                 tty_num = console_hypervisor_tty_num;
 855                 serial_init();
 856         }
 857 }
 858 #endif /* __xpv */
 859 
 860 static void
 861 defcons_putchar(int c)
 862 {
 863         if (defcons_buf != NULL &&
 864             defcons_cur + 1 - defcons_buf < MMU_PAGESIZE) {
 865                 *defcons_cur++ = c;
 866                 *defcons_cur = 0;
 867         }
 868 }
 869 #endif  /* _BOOT */
 870 
 871 static void
 872 serial_putchar(int c)
 873 {
 874         int checks = 10000;
 875 
 876         while (((inb(port + LSR) & XHRE) == 0) && checks--)
 877                 ;
 878         outb(port + DAT, (char)c);
 879 }
 880 
 881 static int
 882 serial_getchar(void)
 883 {
 884         uchar_t lsr;
 885 
 886         while (serial_ischar() == 0)
 887                 ;
 888 
 889         lsr = inb(port + LSR);
 890         if (lsr & (SERIAL_BREAK | SERIAL_FRAME |
 891             SERIAL_PARITY | SERIAL_OVERRUN)) {
 892                 if (lsr & SERIAL_OVERRUN) {
 893                         return (inb(port + DAT));
 894                 } else {
 895                         /* Toss the garbage */
 896                         (void) inb(port + DAT);
 897                         return (0);
 898                 }
 899         }
 900         return (inb(port + DAT));
 901 }
 902 
 903 static int
 904 serial_ischar(void)
 905 {
 906         return (inb(port + LSR) & RCA);
 907 }
 908 
 909 static void
 910 _doputchar(int c)
 911 {
 912         switch (console) {
 913         case CONS_TTY:
 914                 serial_putchar(c);
 915                 return;
 916         case CONS_SCREEN_TEXT:
 917                 screen_putchar(c);
 918                 return;
 919         case CONS_SCREEN_GRAPHICS:
 920 #if !defined(_BOOT)
 921         case CONS_USBSER:
 922                 defcons_putchar(c);
 923 #endif /* _BOOT */
 924                 return;
 925         }
 926 }
 927 
 928 void
 929 bcons_putchar(int c)
 930 {
 931         static int bhcharpos = 0;
 932 
 933 #if defined(__xpv)
 934         if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
 935             console == CONS_HYPERVISOR) {
 936                 bcons_putchar_xen(c);
 937                 return;
 938         }
 939 #endif /* __xpv */
 940 
 941         if (c == '\t') {
 942                 do {
 943                         _doputchar(' ');
 944                 } while (++bhcharpos % 8);
 945                 return;
 946         } else  if (c == '\n' || c == '\r') {
 947                 bhcharpos = 0;
 948                 _doputchar('\r');
 949                 _doputchar(c);
 950                 return;
 951         } else if (c == '\b') {
 952                 if (bhcharpos)
 953                         bhcharpos--;
 954                 _doputchar(c);
 955                 return;
 956         }
 957 
 958         bhcharpos++;
 959         _doputchar(c);
 960 }
 961 
 962 /*
 963  * kernel character input functions
 964  */
 965 int
 966 bcons_getchar(void)
 967 {
 968 #if defined(__xpv)
 969         if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
 970             console == CONS_HYPERVISOR)
 971                 return (bcons_getchar_xen());
 972 #endif /* __xpv */
 973 
 974         switch (console) {
 975         case CONS_TTY:
 976                 return (serial_getchar());
 977         default:
 978                 return (kb_getchar());
 979         }
 980 }
 981 
 982 #if !defined(_BOOT)
 983 
 984 int
 985 bcons_ischar(void)
 986 {
 987 
 988 #if defined(__xpv)
 989         if (!DOMAIN_IS_INITDOMAIN(xen_info) ||
 990             console == CONS_HYPERVISOR)
 991                 return (bcons_ischar_xen());
 992 #endif /* __xpv */
 993 
 994         switch (console) {
 995         case CONS_TTY:
 996                 return (serial_ischar());
 997         default:
 998                 return (kb_ischar());
 999         }
1000 }
1001 
1002 #endif /* _BOOT */