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