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 2010 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
plat_use_polled_debug()54 plat_use_polled_debug() {
55 return (0);
56 }
57
58 int
plat_support_serial_kbd_and_ms()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
console_type()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
plat_stdin_is_keyboard(void)139 plat_stdin_is_keyboard(void)
140 {
141 return (console_type() == CONS_SCREEN);
142 }
143
144 int
plat_stdout_is_framebuffer(void)145 plat_stdout_is_framebuffer(void)
146 {
147 return (console_type() == CONS_SCREEN);
148 }
149
150 static char *
plat_devpath(char * name,char * path)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 *
plat_kbdpath(void)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 /*
195 * NOTE: this function is duplicated here and in gfx_private/vgatext while
196 * we work on a set of commitable interfaces to sunpci.c.
197 *
198 * Use the class code to determine if the device is a PCI-to-PCI bridge.
199 * Returns: B_TRUE if the device is a bridge.
200 * B_FALSE if the device is not a bridge or the property cannot be
201 * retrieved.
202 */
203 static boolean_t
is_pci_bridge(dev_info_t * dip)204 is_pci_bridge(dev_info_t *dip)
205 {
206 uint32_t class_code;
207
208 class_code = (uint32_t)ddi_prop_get_int(DDI_DEV_T_ANY, dip,
209 DDI_PROP_DONTPASS, "class-code", 0xffffffff);
210
211 if (class_code == 0xffffffff || class_code == DDI_PROP_NOT_FOUND)
212 return (B_FALSE);
213
214 class_code &= 0x00ffff00;
215 if (class_code == ((PCI_CLASS_BRIDGE << 16) | (PCI_BRIDGE_PCI << 8)))
216 return (B_TRUE);
217
218 return (B_FALSE);
219 }
220
221 /*
222 * Type for the parameter of find_fb_dev()
223 */
224 struct find_fb_dev_param
225 {
226 dev_info_t *found_dip; /* dip found for VGA console */
227 int vga_enable; /* check PCI_BCNF_BCNTRL_VGA_ENABLE or not */
228 };
229
230 /*
231 * The VGA device could be under a subtractive PCI bridge on some systems.
232 * Though the PCI_BCNF_BCNTRL_VGA_ENABLE bit is not set on such subtractive
233 * PCI bridge, the subtractive PCI bridge can forward VGA access if no other
234 * agent claims the access.
235 * The vga_enable element in param acts as a flag, if not set, ignore the
236 * checking for the PCI_BCNF_BCNTRL_VGA_ENABLE bit of the PCI bridge during
237 * the search.
238 */
239 static int
find_fb_dev(dev_info_t * dip,void * param)240 find_fb_dev(dev_info_t *dip, void *param)
241 {
242 struct find_fb_dev_param *p = param;
243 char *dev_type;
244 dev_info_t *pdip;
245 char *parent_type;
246
247 if (dip == ddi_root_node())
248 return (DDI_WALK_CONTINUE);
249
250 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
251 "device_type", &dev_type) != DDI_SUCCESS)
252 return (DDI_WALK_PRUNECHILD);
253
254 if ((strcmp(dev_type, "isa") == 0) || (strcmp(dev_type, "eisa") == 0)) {
255 ddi_prop_free(dev_type);
256 return (DDI_WALK_CONTINUE);
257 }
258
259 if ((strcmp(dev_type, "pci") == 0) ||
260 (strcmp(dev_type, "pciex") == 0)) {
261 ddi_acc_handle_t pci_conf;
262 uint16_t data16;
263 char *nodename;
264
265 ddi_prop_free(dev_type);
266
267 if (!p->vga_enable)
268 return (DDI_WALK_CONTINUE);
269
270 nodename = ddi_node_name(dip);
271
272 /*
273 * If the node is not a PCI-to-PCI bridge, continue traversing
274 * (it could be the root node), otherwise, check for the
275 * VGAEnable bit to be set in the Bridge Control Register.
276 */
277 if (strcmp(nodename, "pci") == 0) {
278 if (is_pci_bridge(dip) == B_FALSE)
279 return (DDI_WALK_CONTINUE);
280 }
281
282 if (i_ddi_attach_node_hierarchy(dip) != DDI_SUCCESS)
283 return (DDI_WALK_PRUNECHILD);
284
285 if (pci_config_setup(dip, &pci_conf) != DDI_SUCCESS)
286 return (DDI_WALK_PRUNECHILD);
287
288 data16 = pci_config_get16(pci_conf, PCI_BCNF_BCNTRL);
289 pci_config_teardown(&pci_conf);
290
291 if (data16 & PCI_BCNF_BCNTRL_VGA_ENABLE)
292 return (DDI_WALK_CONTINUE);
293
294 return (DDI_WALK_PRUNECHILD);
295 }
296
297 if (strcmp(dev_type, "display") != 0) {
298 ddi_prop_free(dev_type);
299 return (DDI_WALK_CONTINUE);
300 }
301
302 ddi_prop_free(dev_type);
303
304 if ((pdip = ddi_get_parent(dip)) == NULL)
305 return (DDI_WALK_PRUNECHILD);
306
307 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip, DDI_PROP_DONTPASS,
308 "device_type", &parent_type) != DDI_SUCCESS)
309 return (DDI_WALK_PRUNECHILD);
310
311 if ((strcmp(parent_type, "isa") == 0) ||
312 (strcmp(parent_type, "eisa") == 0)) {
313 p->found_dip = dip;
314 ddi_prop_free(parent_type);
315 return (DDI_WALK_TERMINATE);
316 }
317
318 if ((strcmp(parent_type, "pci") == 0) ||
319 (strcmp(parent_type, "pciex") == 0)) {
320 ddi_acc_handle_t pci_conf;
321 uint16_t data16;
322
323 ddi_prop_free(parent_type);
324
325 if (i_ddi_attach_node_hierarchy(dip) != DDI_SUCCESS)
326 return (DDI_WALK_PRUNECHILD);
327
328 if (pci_config_setup(dip, &pci_conf) != DDI_SUCCESS)
329 return (DDI_WALK_PRUNECHILD);
330
331 data16 = pci_config_get16(pci_conf, PCI_CONF_COMM);
332 pci_config_teardown(&pci_conf);
333
334 if (!(data16 & PCI_COMM_IO))
335 return (DDI_WALK_PRUNECHILD);
336
337 p->found_dip = dip;
338 return (DDI_WALK_TERMINATE);
339 }
340
341 ddi_prop_free(parent_type);
342 return (DDI_WALK_PRUNECHILD);
343 }
344
345 /*
346 * The first round search is to find:
347 * 1) a VGA device.
348 * 2) a PCI VGA compatible device whose IO space is enabled
349 * and the VGA Enable bit of any PCI-PCI bridge above it is set.
350 * If the first round search succeeds, prune the second round search.
351 *
352 * The second round seach does not check the VGA Enable bit.
353 *
354 * Return the device path as the console fb path.
355 */
356 char *
plat_fbpath(void)357 plat_fbpath(void)
358 {
359 struct find_fb_dev_param param;
360 static char *fbpath = NULL;
361 static char fbpath_buf[MAXPATHLEN];
362
363 /* first round search */
364 param.found_dip = NULL;
365 param.vga_enable = 1;
366 ddi_walk_devs(ddi_root_node(), find_fb_dev, ¶m);
367
368 if (param.found_dip != NULL) {
369 (void) ddi_pathname(param.found_dip, fbpath_buf);
370 fbpath = fbpath_buf;
371 return (fbpath);
372 }
373
374 /*
375 * second round search, do not check the
376 * PCI_BCNF_BCNTRL_VGA_ENABLE bit
377 */
378 param.found_dip = NULL;
379 param.vga_enable = 0;
380 ddi_walk_devs(ddi_root_node(), find_fb_dev, ¶m);
381
382 if (param.found_dip == NULL)
383 return (NULL);
384
385 (void) ddi_pathname(param.found_dip, fbpath_buf);
386 fbpath = fbpath_buf;
387 return (fbpath);
388 }
389
390 char *
plat_mousepath(void)391 plat_mousepath(void)
392 {
393 static char mpath[MAXPATHLEN];
394
395 /*
396 * Hardcode to isa mouse path
397 * XXX make it settable via bootprop?
398 */
399 if (pseudo_isa)
400 return ("/isa/i8042@1,60/mouse@1");
401
402 if (plat_devpath("mouse8042", mpath) == NULL)
403 return (NULL);
404
405 return (mpath);
406 }
407
408 /* return path of first usb serial device */
409 static char *
plat_usbser_path(void)410 plat_usbser_path(void)
411 {
412 extern dev_info_t *usbser_first_device(void);
413
414 dev_info_t *us_dip;
415 static char *us_path = NULL;
416
417 if (us_path)
418 return (us_path);
419
420 us_dip = usbser_first_device();
421 if (us_dip == NULL)
422 return (NULL);
423
424 us_path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
425 (void) ddi_pathname(us_dip, us_path);
426 ndi_rele_devi(us_dip); /* held from usbser_first_device */
427 return (us_path);
428 }
429
430 static char *
plat_ttypath(int inum)431 plat_ttypath(int inum)
432 {
433 static char *defaultpath[] = {
434 "/isa/asy@1,3f8:a",
435 "/isa/asy@1,2f8:b"
436 };
437 static char path[MAXPATHLEN];
438 char *bp;
439 major_t major;
440 dev_info_t *dip;
441
442 if (pseudo_isa)
443 return (defaultpath[inum]);
444
445 if ((major = ddi_name_to_major("asy")) == (major_t)-1)
446 return (NULL);
447
448 if ((dip = devnamesp[major].dn_head) == NULL)
449 return (NULL);
450
451 for (; dip != NULL; dip = ddi_get_next(dip)) {
452 if (i_ddi_attach_node_hierarchy(dip) != DDI_SUCCESS)
453 return (NULL);
454
455 if (DEVI(dip)->devi_minor->ddm_name[0] == ('a' + (char)inum))
456 break;
457 }
458 if (dip == NULL)
459 return (NULL);
460
461 (void) ddi_pathname(dip, path);
462 bp = path + strlen(path);
463 (void) snprintf(bp, 3, ":%s", DEVI(dip)->devi_minor->ddm_name);
464
465 return (path);
466 }
467
468 /*
469 * Lacking support for com2 and com3, if that matters.
470 * Another possible enhancement could be to use properties
471 * for the port mapping rather than simply hard-code them.
472 */
473 char *
plat_stdinpath(void)474 plat_stdinpath(void)
475 {
476 switch (console_type()) {
477 #if defined(__xpv)
478 case CONS_HYPERVISOR:
479 return ("/xpvd/xencons@0");
480 #endif /* __xpv */
481 case CONS_TTYA:
482 return (plat_ttypath(0));
483 case CONS_TTYB:
484 return (plat_ttypath(1));
485 case CONS_USBSER:
486 return (plat_usbser_path());
487 case CONS_SCREEN:
488 default:
489 break;
490 };
491 return (plat_kbdpath());
492 }
493
494 char *
plat_stdoutpath(void)495 plat_stdoutpath(void)
496 {
497 switch (console_type()) {
498 #if defined(__xpv)
499 case CONS_HYPERVISOR:
500 return ("/xpvd/xencons@0");
501 #endif /* __xpv */
502 case CONS_TTYA:
503 return (plat_ttypath(0));
504 case CONS_TTYB:
505 return (plat_ttypath(1));
506 case CONS_USBSER:
507 return (plat_usbser_path());
508 case CONS_SCREEN:
509 default:
510 break;
511 };
512 return (plat_fbpath());
513 }
514
515 /*
516 * If VIS_PIXEL mode will be implemented on x86, these following
517 * functions should be re-considered. Now these functions are
518 * unused on x86.
519 */
520 void
plat_tem_get_inverses(int * inverse,int * inverse_screen)521 plat_tem_get_inverses(int *inverse, int *inverse_screen)
522 {
523 *inverse = 0;
524 *inverse_screen = 0;
525 }
526
527 void
plat_tem_get_prom_font_size(int * charheight,int * windowtop)528 plat_tem_get_prom_font_size(int *charheight, int *windowtop)
529 {
530 *charheight = 0;
531 *windowtop = 0;
532 }
533
534 /*ARGSUSED*/
535 void
plat_tem_get_prom_size(size_t * height,size_t * width)536 plat_tem_get_prom_size(size_t *height, size_t *width)
537 {
538 panic("unimplemented at line %d of %s", __LINE__, __FILE__);
539 }
540
541 void
plat_tem_hide_prom_cursor(void)542 plat_tem_hide_prom_cursor(void)
543 {
544 panic("unimplemented at line %d of %s", __LINE__, __FILE__);
545 }
546
547 /*ARGSUSED*/
548 void
plat_tem_get_prom_pos(uint32_t * row,uint32_t * col)549 plat_tem_get_prom_pos(uint32_t *row, uint32_t *col)
550 {
551 panic("unimplemented at line %d of %s", __LINE__, __FILE__);
552 }
553