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, ¶m); 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, ¶m); 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 } 608