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 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * isa-specific console configuration routines 29 */ 30 31 #include <sys/types.h> 32 #include <sys/param.h> 33 #include <sys/cmn_err.h> 34 #include <sys/systm.h> 35 #include <sys/conf.h> 36 #include <sys/debug.h> 37 #include <sys/ddi.h> 38 #include <sys/sunddi.h> 39 #include <sys/sunndi.h> 40 #include <sys/esunddi.h> 41 #include <sys/ddi_impldefs.h> 42 #include <sys/promif.h> 43 #include <sys/modctl.h> 44 #include <sys/termios.h> 45 #include <sys/pci.h> 46 #if defined(__xpv) 47 #include <sys/hypervisor.h> 48 #include <sys/boot_console.h> 49 #endif 50 51 extern int pseudo_isa; 52 53 int 54 plat_use_polled_debug() { 55 return (0); 56 } 57 58 int 59 plat_support_serial_kbd_and_ms() { 60 return (0); 61 } 62 63 #define A_CNT(arr) (sizeof (arr) / sizeof (arr[0])) 64 65 #define CONS_INVALID -1 66 #define CONS_SCREEN 0 67 #define CONS_TTYA 1 68 #define CONS_TTYB 2 69 #define CONS_USBSER 3 70 #define CONS_HYPERVISOR 4 71 72 char *plat_fbpath(void); 73 74 static int 75 console_type() 76 { 77 static int boot_console = CONS_INVALID; 78 79 char *cons; 80 dev_info_t *root; 81 82 if (boot_console != CONS_INVALID) 83 return (boot_console); 84 85 #if defined(__xpv) 86 if (!DOMAIN_IS_INITDOMAIN(xen_info) || bcons_hypervisor_redirect()) { 87 boot_console = CONS_HYPERVISOR; 88 return (boot_console); 89 } 90 #endif /* __xpv */ 91 92 /* 93 * console is defined by "console" property, with 94 * fallback on the old "input-device" property. 95 * If "input-device" is not defined either, also check "output-device". 96 */ 97 boot_console = CONS_SCREEN; /* default is screen/kb */ 98 root = ddi_root_node(); 99 if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, root, 100 DDI_PROP_DONTPASS, "console", &cons) == DDI_SUCCESS) || 101 (ddi_prop_lookup_string(DDI_DEV_T_ANY, root, 102 DDI_PROP_DONTPASS, "input-device", &cons) == DDI_SUCCESS) || 103 (ddi_prop_lookup_string(DDI_DEV_T_ANY, root, 104 DDI_PROP_DONTPASS, "output-device", &cons) == DDI_SUCCESS)) { 105 if (strcmp(cons, "ttya") == 0) { 106 boot_console = CONS_TTYA; 107 } else if (strcmp(cons, "ttyb") == 0) { 108 boot_console = CONS_TTYB; 109 } else if (strcmp(cons, "usb-serial") == 0) { 110 (void) i_ddi_attach_hw_nodes("ehci"); 111 (void) i_ddi_attach_hw_nodes("uhci"); 112 (void) i_ddi_attach_hw_nodes("ohci"); 113 /* 114 * USB device enumerate asynchronously. 115 * Wait 2 seconds for USB serial devices to attach. 116 */ 117 delay(drv_usectohz(2000000)); 118 boot_console = CONS_USBSER; 119 #if defined(__xpv) 120 } else if (strcmp(cons, "hypervisor") == 0) { 121 boot_console = CONS_HYPERVISOR; 122 #endif /* __xpv */ 123 } 124 ddi_prop_free(cons); 125 } 126 127 /* 128 * If the console is configured to use a framebuffer but none 129 * could be found, fallback to "ttya" since it's likely to exist 130 * and it matches longstanding behavior on SPARC. 131 */ 132 if (boot_console == CONS_SCREEN && plat_fbpath() == NULL) 133 boot_console = CONS_TTYA; 134 135 return (boot_console); 136 } 137 138 int 139 plat_stdin_is_keyboard(void) 140 { 141 return (console_type() == CONS_SCREEN); 142 } 143 144 int 145 plat_stdout_is_framebuffer(void) 146 { 147 return (console_type() == CONS_SCREEN); 148 } 149 150 static char * 151 plat_devpath(char *name, char *path) 152 { 153 major_t major; 154 dev_info_t *dip, *pdip; 155 156 if ((major = ddi_name_to_major(name)) == (major_t)-1) 157 return (NULL); 158 159 if ((dip = devnamesp[major].dn_head) == NULL) 160 return (NULL); 161 162 pdip = ddi_get_parent(dip); 163 if (i_ddi_attach_node_hierarchy(pdip) != DDI_SUCCESS) 164 return (NULL); 165 if (ddi_initchild(pdip, dip) != DDI_SUCCESS) 166 return (NULL); 167 168 (void) ddi_pathname(dip, path); 169 170 return (path); 171 } 172 173 /* 174 * Return generic path to keyboard device from the alias. 175 */ 176 char * 177 plat_kbdpath(void) 178 { 179 static char kbpath[MAXPATHLEN]; 180 181 /* 182 * Hardcode to isa keyboard path 183 * XXX make it settable via bootprop? 184 */ 185 if (pseudo_isa) 186 return ("/isa/i8042@1,60/keyboard@0"); 187 188 if (plat_devpath("kb8042", kbpath) == NULL) 189 return (NULL); 190 191 return (kbpath); 192 } 193 194 static int 195 find_fb_dev(dev_info_t *dip, void *found_dip) 196 { 197 char *dev_type; 198 dev_info_t *pdip; 199 char *parent_type; 200 201 if (dip == ddi_root_node()) 202 return (DDI_WALK_CONTINUE); 203 204 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 205 "device_type", &dev_type) != DDI_SUCCESS) 206 return (DDI_WALK_PRUNECHILD); 207 208 if ((strcmp(dev_type, "isa") == 0) || (strcmp(dev_type, "eisa") == 0)) { 209 ddi_prop_free(dev_type); 210 return (DDI_WALK_CONTINUE); 211 } 212 213 if ((strcmp(dev_type, "pci") == 0) || 214 (strcmp(dev_type, "pciex") == 0)) { 215 ddi_acc_handle_t pci_conf; 216 uint16_t data16; 217 218 ddi_prop_free(dev_type); 219 220 if (i_ddi_attach_node_hierarchy(dip) != DDI_SUCCESS) 221 return (DDI_WALK_CONTINUE); 222 223 if (pci_config_setup(dip, &pci_conf) != DDI_SUCCESS) { 224 /* This happends when it's the host bridge */ 225 return (DDI_WALK_CONTINUE); 226 } 227 228 data16 = pci_config_get16(pci_conf, PCI_BCNF_BCNTRL); 229 pci_config_teardown(&pci_conf); 230 231 if (data16 & PCI_BCNF_BCNTRL_VGA_ENABLE) 232 return (DDI_WALK_CONTINUE); 233 234 return (DDI_WALK_PRUNECHILD); 235 } 236 237 if (strcmp(dev_type, "display") != 0) { 238 ddi_prop_free(dev_type); 239 return (DDI_WALK_CONTINUE); 240 } 241 242 ddi_prop_free(dev_type); 243 244 if ((pdip = ddi_get_parent(dip)) == NULL) 245 return (DDI_WALK_CONTINUE); 246 247 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip, DDI_PROP_DONTPASS, 248 "device_type", &parent_type) != DDI_SUCCESS) 249 return (DDI_WALK_CONTINUE); 250 251 if ((strcmp(parent_type, "isa") == 0) || 252 (strcmp(parent_type, "eisa") == 0)) { 253 *(dev_info_t **)found_dip = dip; 254 ddi_prop_free(parent_type); 255 return (DDI_WALK_TERMINATE); 256 } 257 258 if ((strcmp(parent_type, "pci") == 0) || 259 (strcmp(parent_type, "pciex") == 0)) { 260 ddi_acc_handle_t pci_conf; 261 uint16_t data16; 262 263 ddi_prop_free(parent_type); 264 265 if (i_ddi_attach_node_hierarchy(dip) != DDI_SUCCESS) 266 return (DDI_WALK_CONTINUE); 267 268 if (pci_config_setup(dip, &pci_conf) != DDI_SUCCESS) 269 return (DDI_WALK_CONTINUE); 270 271 data16 = pci_config_get16(pci_conf, PCI_CONF_COMM); 272 pci_config_teardown(&pci_conf); 273 274 if (!(data16 & PCI_COMM_IO)) 275 return (DDI_WALK_CONTINUE); 276 277 *(dev_info_t **)found_dip = dip; 278 return (DDI_WALK_TERMINATE); 279 } 280 281 ddi_prop_free(parent_type); 282 return (DDI_WALK_CONTINUE); 283 } 284 285 /* 286 * Conduct a width-first traverse searching for a display device which 287 * has either: 288 * 1) a VGA device. 289 * 2) a PCI VGA compatible device whose IO space is enabled 290 * and the VGA Enable bit of any PCI-PCI bridge above it is set. 291 * 292 * Return the device path as the console fb path. 293 */ 294 char * 295 plat_fbpath(void) 296 { 297 dev_info_t *fb_dip = NULL; 298 static char *fbpath = NULL; 299 static char fbpath_buf[MAXPATHLEN]; 300 301 ddi_walk_devs(ddi_root_node(), find_fb_dev, &fb_dip); 302 303 if (fb_dip == NULL) 304 return (NULL); 305 306 (void) ddi_pathname(fb_dip, fbpath_buf); 307 fbpath = fbpath_buf; 308 309 return (fbpath); 310 } 311 312 char * 313 plat_mousepath(void) 314 { 315 static char mpath[MAXPATHLEN]; 316 317 /* 318 * Hardcode to isa mouse path 319 * XXX make it settable via bootprop? 320 */ 321 if (pseudo_isa) 322 return ("/isa/i8042@1,60/mouse@1"); 323 324 if (plat_devpath("mouse8042", mpath) == NULL) 325 return (NULL); 326 327 return (mpath); 328 } 329 330 /* return path of first usb serial device */ 331 static char * 332 plat_usbser_path(void) 333 { 334 extern dev_info_t *usbser_first_device(void); 335 336 dev_info_t *us_dip; 337 static char *us_path = NULL; 338 339 if (us_path) 340 return (us_path); 341 342 us_dip = usbser_first_device(); 343 if (us_dip == NULL) 344 return (NULL); 345 346 us_path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 347 (void) ddi_pathname(us_dip, us_path); 348 ndi_rele_devi(us_dip); /* held from usbser_first_device */ 349 return (us_path); 350 } 351 352 static char * 353 plat_ttypath(int inum) 354 { 355 static char *defaultpath[] = { 356 "/isa/asy@1,3f8:a", 357 "/isa/asy@1,2f8:b" 358 }; 359 static char path[MAXPATHLEN]; 360 char *bp; 361 major_t major; 362 dev_info_t *dip; 363 364 if (pseudo_isa) 365 return (defaultpath[inum]); 366 367 if ((major = ddi_name_to_major("asy")) == (major_t)-1) 368 return (NULL); 369 370 if ((dip = devnamesp[major].dn_head) == NULL) 371 return (NULL); 372 373 for (; dip != NULL; dip = ddi_get_next(dip)) { 374 if (i_ddi_attach_node_hierarchy(dip) != DDI_SUCCESS) 375 return (NULL); 376 377 if (DEVI(dip)->devi_minor->ddm_name[0] == ('a' + (char)inum)) 378 break; 379 } 380 if (dip == NULL) 381 return (NULL); 382 383 (void) ddi_pathname(dip, path); 384 bp = path + strlen(path); 385 (void) snprintf(bp, 3, ":%s", DEVI(dip)->devi_minor->ddm_name); 386 387 return (path); 388 } 389 390 /* 391 * Lacking support for com2 and com3, if that matters. 392 * Another possible enhancement could be to use properties 393 * for the port mapping rather than simply hard-code them. 394 */ 395 char * 396 plat_stdinpath(void) 397 { 398 switch (console_type()) { 399 #if defined(__xpv) 400 case CONS_HYPERVISOR: 401 return ("/xpvd/xencons@0"); 402 #endif /* __xpv */ 403 case CONS_TTYA: 404 return (plat_ttypath(0)); 405 case CONS_TTYB: 406 return (plat_ttypath(1)); 407 case CONS_USBSER: 408 return (plat_usbser_path()); 409 case CONS_SCREEN: 410 default: 411 break; 412 }; 413 return (plat_kbdpath()); 414 } 415 416 char * 417 plat_stdoutpath(void) 418 { 419 switch (console_type()) { 420 #if defined(__xpv) 421 case CONS_HYPERVISOR: 422 return ("/xpvd/xencons@0"); 423 #endif /* __xpv */ 424 case CONS_TTYA: 425 return (plat_ttypath(0)); 426 case CONS_TTYB: 427 return (plat_ttypath(1)); 428 case CONS_USBSER: 429 return (plat_usbser_path()); 430 case CONS_SCREEN: 431 default: 432 break; 433 }; 434 return (plat_fbpath()); 435 } 436 437 /* 438 * If VIS_PIXEL mode will be implemented on x86, these following 439 * functions should be re-considered. Now these functions are 440 * unused on x86. 441 */ 442 void 443 plat_tem_get_inverses(int *inverse, int *inverse_screen) 444 { 445 *inverse = 0; 446 *inverse_screen = 0; 447 } 448 449 void 450 plat_tem_get_prom_font_size(int *charheight, int *windowtop) 451 { 452 *charheight = 0; 453 *windowtop = 0; 454 } 455 456 /*ARGSUSED*/ 457 void 458 plat_tem_get_prom_size(size_t *height, size_t *width) 459 { 460 panic("unimplemented at line %d of %s", __LINE__, __FILE__); 461 } 462 463 void 464 plat_tem_hide_prom_cursor(void) 465 { 466 panic("unimplemented at line %d of %s", __LINE__, __FILE__); 467 } 468 469 /*ARGSUSED*/ 470 void 471 plat_tem_get_prom_pos(uint32_t *row, uint32_t *col) 472 { 473 panic("unimplemented at line %d of %s", __LINE__, __FILE__); 474 } 475