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