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