xref: /illumos-gate/usr/src/uts/i86pc/io/consplat.c (revision 5f82aa32fbc5dc2c59bca6ff315f44a4c4c9ea86)
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, &param);
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, &param);
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