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 /*
  23  * Copyright (c) 2012 Gary Mills
  24  *
  25  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  26  * Use is subject to license terms.
  27  */
  28 
  29 /*
  30  * isa-specific console configuration routines
  31  */
  32 
  33 #include <sys/types.h>
  34 #include <sys/param.h>
  35 #include <sys/cmn_err.h>
  36 #include <sys/systm.h>
  37 #include <sys/conf.h>
  38 #include <sys/debug.h>
  39 #include <sys/ddi.h>
  40 #include <sys/sunddi.h>
  41 #include <sys/sunndi.h>
  42 #include <sys/esunddi.h>
  43 #include <sys/ddi_impldefs.h>
  44 #include <sys/promif.h>
  45 #include <sys/modctl.h>
  46 #include <sys/termios.h>
  47 #include <sys/pci.h>
  48 #include <sys/framebuffer.h>
  49 #include <sys/boot_console.h>
  50 #if defined(__xpv)
  51 #include <sys/hypervisor.h>
  52 #endif
  53 
  54 extern int pseudo_isa;
  55 
  56 int
  57 plat_use_polled_debug()
  58 {
  59         return (0);
  60 }
  61 
  62 int
  63 plat_support_serial_kbd_and_ms()
  64 {
  65         return (0);
  66 }
  67 
  68 #define A_CNT(arr)      (sizeof (arr) / sizeof (arr[0]))
  69 
  70 #ifndef CONS_INVALID
  71 #define CONS_INVALID            -1
  72 #define CONS_SCREEN_TEXT        0
  73 #define CONS_TTY                1
  74 #define CONS_XXX                2       /* Unused */
  75 #define CONS_USBSER             3
  76 #define CONS_HYPERVISOR         4
  77 #define CONS_SCREEN_GRAPHICS    5
  78 #endif  /* CONS_INVALID */
  79 
  80 char *plat_fbpath(void);
  81 
  82 static int
  83 console_type(int *tnum)
  84 {
  85         static int boot_console = CONS_INVALID;
  86         static int tty_num = 0;
  87 
  88         char *cons;
  89         dev_info_t *root;
  90 
  91         /* If we already have determined the console, just return it. */
  92         if (boot_console != CONS_INVALID) {
  93                 if (tnum != NULL)
  94                         *tnum = tty_num;
  95                 return (boot_console);
  96         }
  97 
  98 #if defined(__xpv)
  99         if (!DOMAIN_IS_INITDOMAIN(xen_info) || bcons_hypervisor_redirect()) {
 100                 boot_console = CONS_HYPERVISOR;
 101                 if (tnum != NULL)
 102                         *tnum = tty_num;
 103                 return (boot_console);
 104         }
 105 #endif /* __xpv */
 106 
 107         /*
 108          * console is defined by "console" property, with
 109          * fallback on the old "input-device" property.
 110          * If "input-device" is not defined either, also check "output-device".
 111          */
 112         boot_console = CONS_SCREEN_TEXT;        /* default is screen/kb */
 113         root = ddi_root_node();
 114         if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, root,
 115             DDI_PROP_DONTPASS, "console", &cons) == DDI_SUCCESS) ||
 116             (ddi_prop_lookup_string(DDI_DEV_T_ANY, root,
 117             DDI_PROP_DONTPASS, "input-device", &cons) == DDI_SUCCESS) ||
 118             (ddi_prop_lookup_string(DDI_DEV_T_ANY, root,
 119             DDI_PROP_DONTPASS, "output-device", &cons) == DDI_SUCCESS)) {
 120                 if (strlen(cons) == 4 && strncmp(cons, "tty", 3) == 0 &&
 121                     cons[3] >= 'a' && cons[3] <= 'd') {
 122                         boot_console = CONS_TTY;
 123                         tty_num = cons[3] - 'a';
 124                 } else if (strcmp(cons, "usb-serial") == 0) {
 125                         (void) i_ddi_attach_hw_nodes("xhci");
 126                         (void) i_ddi_attach_hw_nodes("ehci");
 127                         (void) i_ddi_attach_hw_nodes("uhci");
 128                         (void) i_ddi_attach_hw_nodes("ohci");
 129                         /*
 130                          * USB device enumerate asynchronously.
 131                          * Wait 2 seconds for USB serial devices to attach.
 132                          */
 133                         delay(drv_usectohz(2000000));
 134                         boot_console = CONS_USBSER;
 135 #if defined(__xpv)
 136                 } else if (strcmp(cons, "hypervisor") == 0) {
 137                         boot_console = CONS_HYPERVISOR;
 138 #endif /* __xpv */
 139                 }
 140                 ddi_prop_free(cons);
 141         }
 142 
 143         /*
 144          * If the console is configured to use a framebuffer but none
 145          * could be found, fallback to "ttya" since it's likely to exist
 146          * and it matches longstanding behavior on SPARC.
 147          */
 148         if (boot_console == CONS_SCREEN_TEXT && plat_fbpath() == NULL) {
 149                 boot_console = CONS_TTY;
 150                 tty_num = 0;
 151         }
 152 
 153         if (tnum != NULL)
 154                 *tnum = tty_num;
 155         return (boot_console);
 156 }
 157 
 158 int
 159 plat_stdin_is_keyboard(void)
 160 {
 161         return (console_type(NULL) == CONS_SCREEN_TEXT);
 162 }
 163 
 164 int
 165 plat_stdout_is_framebuffer(void)
 166 {
 167         return (console_type(NULL) == CONS_SCREEN_TEXT);
 168 }
 169 
 170 static char *
 171 plat_devpath(char *name, char *path)
 172 {
 173         major_t major;
 174         dev_info_t *dip, *pdip;
 175 
 176         if ((major = ddi_name_to_major(name)) == (major_t)-1)
 177                 return (NULL);
 178 
 179         if ((dip = devnamesp[major].dn_head) == NULL)
 180                 return (NULL);
 181 
 182         pdip = ddi_get_parent(dip);
 183         if (i_ddi_attach_node_hierarchy(pdip) != DDI_SUCCESS)
 184                 return (NULL);
 185         if (ddi_initchild(pdip, dip) != DDI_SUCCESS)
 186                 return (NULL);
 187 
 188         (void) ddi_pathname(dip, path);
 189 
 190         return (path);
 191 }
 192 
 193 /*
 194  * Return generic path to keyboard device from the alias.
 195  */
 196 char *
 197 plat_kbdpath(void)
 198 {
 199         static char kbpath[MAXPATHLEN];
 200 
 201         /*
 202          * Hardcode to isa keyboard path
 203          * XXX make it settable via bootprop?
 204          */
 205         if (pseudo_isa)
 206                 return ("/isa/i8042@1,60/keyboard@0");
 207 
 208         if (plat_devpath("kb8042", kbpath) == NULL)
 209                 return (NULL);
 210 
 211         return (kbpath);
 212 }
 213 
 214 /*
 215  * NOTE: this function is duplicated here and in gfx_private/vgatext while
 216  *       we work on a set of commitable interfaces to sunpci.c.
 217  *
 218  * Use the class code to determine if the device is a PCI-to-PCI bridge.
 219  * Returns:  B_TRUE  if the device is a bridge.
 220  *           B_FALSE if the device is not a bridge or the property cannot be
 221  *                   retrieved.
 222  */
 223 static boolean_t
 224 is_pci_bridge(dev_info_t *dip)
 225 {
 226         uint32_t class_code;
 227 
 228         class_code = (uint32_t)ddi_prop_get_int(DDI_DEV_T_ANY, dip,
 229             DDI_PROP_DONTPASS, "class-code", 0xffffffff);
 230 
 231         if (class_code == 0xffffffff || class_code == DDI_PROP_NOT_FOUND)
 232                 return (B_FALSE);
 233 
 234         class_code &= 0x00ffff00;
 235         if (class_code == ((PCI_CLASS_BRIDGE << 16) | (PCI_BRIDGE_PCI << 8)))
 236                 return (B_TRUE);
 237 
 238         return (B_FALSE);
 239 }
 240 
 241 /*
 242  * Type for the parameter of find_fb_dev()
 243  */
 244 struct find_fb_dev_param
 245 {
 246         dev_info_t *found_dip;  /* dip found for VGA console */
 247         int vga_enable;         /* check PCI_BCNF_BCNTRL_VGA_ENABLE or not */
 248 };
 249 
 250 /*
 251  * The VGA device could be under a subtractive PCI bridge on some systems.
 252  * Though the PCI_BCNF_BCNTRL_VGA_ENABLE bit is not set on such subtractive
 253  * PCI bridge, the subtractive PCI bridge can forward VGA access if no other
 254  * agent claims the access.
 255  * The vga_enable element in param acts as a flag, if not set, ignore the
 256  * checking for the PCI_BCNF_BCNTRL_VGA_ENABLE bit of the PCI bridge during
 257  * the search.
 258  */
 259 static int
 260 find_fb_dev(dev_info_t *dip, void *param)
 261 {
 262         struct find_fb_dev_param *p = param;
 263         char *dev_type;
 264         dev_info_t *pdip;
 265         char *parent_type;
 266 
 267         if (dip == ddi_root_node())
 268                 return (DDI_WALK_CONTINUE);
 269 
 270         if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
 271             "device_type", &dev_type) != DDI_SUCCESS)
 272                 return (DDI_WALK_PRUNECHILD);
 273 
 274         if ((strcmp(dev_type, "isa") == 0) || (strcmp(dev_type, "eisa") == 0)) {
 275                 ddi_prop_free(dev_type);
 276                 return (DDI_WALK_CONTINUE);
 277         }
 278 
 279         if ((strcmp(dev_type, "pci") == 0) ||
 280             (strcmp(dev_type, "pciex") == 0)) {
 281                 ddi_acc_handle_t pci_conf;
 282                 uint16_t data16;
 283                 char *nodename;
 284 
 285                 ddi_prop_free(dev_type);
 286 
 287                 if (!p->vga_enable)
 288                         return (DDI_WALK_CONTINUE);
 289 
 290                 nodename = ddi_node_name(dip);
 291 
 292                 /*
 293                  * If the node is not a PCI-to-PCI bridge, continue traversing
 294                  * (it could be the root node), otherwise, check for the
 295                  * VGAEnable bit to be set in the Bridge Control Register.
 296                  */
 297                 if (strcmp(nodename, "pci") == 0) {
 298                         if (is_pci_bridge(dip) == B_FALSE)
 299                                 return (DDI_WALK_CONTINUE);
 300                 }
 301 
 302                 if (i_ddi_attach_node_hierarchy(dip) != DDI_SUCCESS)
 303                         return (DDI_WALK_PRUNECHILD);
 304 
 305                 if (pci_config_setup(dip, &pci_conf) != DDI_SUCCESS)
 306                         return (DDI_WALK_PRUNECHILD);
 307 
 308                 data16 = pci_config_get16(pci_conf, PCI_BCNF_BCNTRL);
 309                 pci_config_teardown(&pci_conf);
 310 
 311                 if (data16 & PCI_BCNF_BCNTRL_VGA_ENABLE)
 312                         return (DDI_WALK_CONTINUE);
 313 
 314                 return (DDI_WALK_PRUNECHILD);
 315         }
 316 
 317         if (strcmp(dev_type, "display") != 0) {
 318                 ddi_prop_free(dev_type);
 319                 return (DDI_WALK_CONTINUE);
 320         }
 321 
 322         ddi_prop_free(dev_type);
 323 
 324         if ((pdip = ddi_get_parent(dip)) == NULL)
 325                 return (DDI_WALK_PRUNECHILD);
 326 
 327         if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip, DDI_PROP_DONTPASS,
 328             "device_type", &parent_type) != DDI_SUCCESS)
 329                 return (DDI_WALK_PRUNECHILD);
 330 
 331         if ((strcmp(parent_type, "isa") == 0) ||
 332             (strcmp(parent_type, "eisa") == 0)) {
 333                 p->found_dip = dip;
 334                 ddi_prop_free(parent_type);
 335                 return (DDI_WALK_TERMINATE);
 336         }
 337 
 338         if ((strcmp(parent_type, "pci") == 0) ||
 339             (strcmp(parent_type, "pciex") == 0)) {
 340                 ddi_acc_handle_t pci_conf;
 341                 uint16_t data16;
 342 
 343                 ddi_prop_free(parent_type);
 344 
 345                 if (i_ddi_attach_node_hierarchy(dip) != DDI_SUCCESS)
 346                         return (DDI_WALK_PRUNECHILD);
 347 
 348                 if (pci_config_setup(dip, &pci_conf) != DDI_SUCCESS)
 349                         return (DDI_WALK_PRUNECHILD);
 350 
 351                 data16 = pci_config_get16(pci_conf, PCI_CONF_COMM);
 352                 pci_config_teardown(&pci_conf);
 353 
 354                 if (!(data16 & PCI_COMM_IO))
 355                         return (DDI_WALK_PRUNECHILD);
 356 
 357                 p->found_dip = dip;
 358                 return (DDI_WALK_TERMINATE);
 359         }
 360 
 361         ddi_prop_free(parent_type);
 362         return (DDI_WALK_PRUNECHILD);
 363 }
 364 
 365 /*
 366  * The first round search is to find:
 367  * 1) a VGA device.
 368  * 2) a PCI VGA compatible device whose IO space is enabled
 369  *    and the VGA Enable bit of any PCI-PCI bridge above it is set.
 370  * If the first round search succeeds, prune the second round search.
 371  *
 372  * The second round seach does not check the VGA Enable bit.
 373  *
 374  * Return the device path as the console fb path.
 375  */
 376 char *
 377 plat_fbpath(void)
 378 {
 379         struct find_fb_dev_param param;
 380         static char *fbpath = NULL;
 381         static char fbpath_buf[MAXPATHLEN];
 382 
 383         /* first round search */
 384         param.found_dip = NULL;
 385         param.vga_enable = 1;
 386         ddi_walk_devs(ddi_root_node(), find_fb_dev, &param);
 387 
 388         if (param.found_dip != NULL) {
 389                 (void) ddi_pathname(param.found_dip, fbpath_buf);
 390                 fbpath = fbpath_buf;
 391                 return (fbpath);
 392         }
 393 
 394         /*
 395          * second round search, do not check the
 396          * PCI_BCNF_BCNTRL_VGA_ENABLE bit
 397          */
 398         param.found_dip = NULL;
 399         param.vga_enable = 0;
 400         ddi_walk_devs(ddi_root_node(), find_fb_dev, &param);
 401 
 402         if (param.found_dip == NULL)
 403                 return (NULL);
 404 
 405         (void) ddi_pathname(param.found_dip, fbpath_buf);
 406         fbpath = fbpath_buf;
 407         return (fbpath);
 408 }
 409 
 410 char *
 411 plat_mousepath(void)
 412 {
 413         static char mpath[MAXPATHLEN];
 414 
 415         /*
 416          * Hardcode to isa mouse path
 417          * XXX make it settable via bootprop?
 418          */
 419         if (pseudo_isa)
 420                 return ("/isa/i8042@1,60/mouse@1");
 421 
 422         if (plat_devpath("mouse8042", mpath) == NULL)
 423                 return (NULL);
 424 
 425         return (mpath);
 426 }
 427 
 428 /* return path of first usb serial device */
 429 static char *
 430 plat_usbser_path(void)
 431 {
 432         extern dev_info_t *usbser_first_device(void);
 433 
 434         dev_info_t *us_dip;
 435         static char *us_path = NULL;
 436 
 437         if (us_path)
 438                 return (us_path);
 439 
 440         us_dip = usbser_first_device();
 441         if (us_dip == NULL)
 442                 return (NULL);
 443 
 444         us_path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
 445         (void) ddi_pathname(us_dip, us_path);
 446         ndi_rele_devi(us_dip);  /* held from usbser_first_device */
 447         return (us_path);
 448 }
 449 
 450 static char *
 451 plat_ttypath(int inum)
 452 {
 453         static char *defaultpath[] = {
 454             "/isa/asy@1,3f8:a",
 455             "/isa/asy@1,2f8:b",
 456             "/isa/asy@1,3e8:c",
 457             "/isa/asy@1,2e8:d"
 458         };
 459         static char path[MAXPATHLEN];
 460         char *bp;
 461         major_t major;
 462         dev_info_t *dip;
 463 
 464         if (pseudo_isa)
 465                 return (defaultpath[inum]);
 466 
 467         if ((major = ddi_name_to_major("asy")) == (major_t)-1)
 468                 return (NULL);
 469 
 470         if ((dip = devnamesp[major].dn_head) == NULL)
 471                 return (NULL);
 472 
 473         for (; dip != NULL; dip = ddi_get_next(dip)) {
 474                 if (i_ddi_attach_node_hierarchy(dip) != DDI_SUCCESS)
 475                         return (NULL);
 476 
 477                 if (DEVI(dip)->devi_minor->ddm_name[0] == ('a' + (char)inum))
 478                         break;
 479         }
 480         if (dip == NULL)
 481                 return (NULL);
 482 
 483         (void) ddi_pathname(dip, path);
 484         bp = path + strlen(path);
 485         (void) snprintf(bp, 3, ":%s", DEVI(dip)->devi_minor->ddm_name);
 486 
 487         return (path);
 488 }
 489 
 490 /*
 491  * Another possible enhancement could be to use properties
 492  * for the port mapping rather than simply hard-code them.
 493  */
 494 char *
 495 plat_stdinpath(void)
 496 {
 497         int tty_num = 0;
 498 
 499         switch (console_type(&tty_num)) {
 500 #if defined(__xpv)
 501         case CONS_HYPERVISOR:
 502                 return ("/xpvd/xencons@0");
 503 #endif /* __xpv */
 504         case CONS_TTY:
 505                 return (plat_ttypath(tty_num));
 506         case CONS_USBSER:
 507                 return (plat_usbser_path());
 508         case CONS_SCREEN_TEXT:
 509         default:
 510                 break;
 511         };
 512         return (plat_kbdpath());
 513 }
 514 
 515 char *
 516 plat_stdoutpath(void)
 517 {
 518         int tty_num = 0;
 519 
 520         switch (console_type(&tty_num)) {
 521 #if defined(__xpv)
 522         case CONS_HYPERVISOR:
 523                 return ("/xpvd/xencons@0");
 524 #endif /* __xpv */
 525         case CONS_TTY:
 526                 return (plat_ttypath(tty_num));
 527         case CONS_USBSER:
 528                 return (plat_usbser_path());
 529         case CONS_SCREEN_TEXT:
 530         default:
 531                 break;
 532         };
 533         return (plat_fbpath());
 534 }
 535 
 536 char *
 537 plat_diagpath(void)
 538 {
 539         dev_info_t *root;
 540         char *diag;
 541         int tty_num = -1;
 542 
 543         root = ddi_root_node();
 544 
 545         if (ddi_prop_lookup_string(DDI_DEV_T_ANY, root, DDI_PROP_DONTPASS,
 546             "diag-device", &diag) == DDI_SUCCESS) {
 547                 if (strlen(diag) == 4 && strncmp(diag, "tty", 3) == 0 &&
 548                     diag[3] >= 'a' && diag[3] <= 'd') {
 549                         tty_num = diag[3] - 'a';
 550                 }
 551                 ddi_prop_free(diag);
 552         }
 553 
 554         if (tty_num != -1)
 555                 return (plat_ttypath(tty_num));
 556         return (NULL);
 557 }
 558 
 559 /*
 560  * If VIS_PIXEL mode will be implemented on x86, these following
 561  * functions should be re-considered. Now these functions are
 562  * unused on x86.
 563  */
 564 void
 565 plat_tem_get_colors(uint8_t *fg, uint8_t *bg)
 566 {
 567         *fg = fb_info.fg_color;
 568         *bg = fb_info.bg_color;
 569 }
 570 
 571 void
 572 plat_tem_get_inverses(int *inverse, int *inverse_screen)
 573 {
 574         *inverse = fb_info.inverse == B_TRUE? 1 : 0;
 575         *inverse_screen = fb_info.inverse_screen == B_TRUE? 1 : 0;
 576 }
 577 
 578 void
 579 plat_tem_get_prom_font_size(int *charheight, int *windowtop)
 580 {
 581         *charheight = fb_info.font_height;
 582         *windowtop = fb_info.terminal_origin.y;
 583 }
 584 
 585 /*ARGSUSED*/
 586 void
 587 plat_tem_get_prom_size(size_t *height, size_t *width)
 588 {
 589         *height = fb_info.terminal.y;
 590         *width = fb_info.terminal.x;
 591 }
 592 
 593 /* this gets called once at boot time and only in case of VIS_PIXEL */
 594 void
 595 plat_tem_hide_prom_cursor(void)
 596 {
 597         if (boot_console_type(NULL) == CONS_FRAMEBUFFER)
 598                 boot_fb_cursor(B_FALSE);
 599 }
 600 
 601 /*ARGSUSED*/
 602 void
 603 plat_tem_get_prom_pos(uint32_t *row, uint32_t *col)
 604 {
 605         *row = fb_info.cursor.pos.y;
 606         *col = fb_info.cursor.pos.x;
 607 }