xref: /illumos-gate/usr/src/uts/i86pc/io/consplat.c (revision e4d060fb4c00d44cd578713eb9a921f594b733b8)
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
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 /*
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
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 static int
222 find_fb_dev(dev_info_t *dip, void *found_dip)
223 {
224 	char *dev_type;
225 	dev_info_t *pdip;
226 	char *parent_type;
227 
228 	if (dip == ddi_root_node())
229 		return (DDI_WALK_CONTINUE);
230 
231 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
232 	    "device_type", &dev_type) != DDI_SUCCESS)
233 		return (DDI_WALK_PRUNECHILD);
234 
235 	if ((strcmp(dev_type, "isa") == 0) || (strcmp(dev_type, "eisa") == 0)) {
236 		ddi_prop_free(dev_type);
237 		return (DDI_WALK_CONTINUE);
238 	}
239 
240 	if ((strcmp(dev_type, "pci") == 0) ||
241 	    (strcmp(dev_type, "pciex") == 0)) {
242 		ddi_acc_handle_t pci_conf;
243 		uint16_t data16;
244 		char *nodename;
245 
246 		ddi_prop_free(dev_type);
247 
248 		nodename = ddi_node_name(dip);
249 
250 		/*
251 		 * If the node is not a PCI-to-PCI bridge, continue traversing
252 		 * (it could be the root node), otherwise, check for the
253 		 * VGAEnable bit to be set in the Bridge Control Register.
254 		 */
255 		if (strcmp(nodename, "pci") == 0) {
256 			if (is_pci_bridge(dip) == B_FALSE)
257 				return (DDI_WALK_CONTINUE);
258 		}
259 
260 		if (i_ddi_attach_node_hierarchy(dip) != DDI_SUCCESS)
261 			return (DDI_WALK_PRUNECHILD);
262 
263 		if (pci_config_setup(dip, &pci_conf) != DDI_SUCCESS)
264 			return (DDI_WALK_PRUNECHILD);
265 
266 		data16 = pci_config_get16(pci_conf, PCI_BCNF_BCNTRL);
267 		pci_config_teardown(&pci_conf);
268 
269 		if (data16 & PCI_BCNF_BCNTRL_VGA_ENABLE)
270 			return (DDI_WALK_CONTINUE);
271 
272 		return (DDI_WALK_PRUNECHILD);
273 	}
274 
275 	if (strcmp(dev_type, "display") != 0) {
276 		ddi_prop_free(dev_type);
277 		return (DDI_WALK_CONTINUE);
278 	}
279 
280 	ddi_prop_free(dev_type);
281 
282 	if ((pdip = ddi_get_parent(dip)) == NULL)
283 		return (DDI_WALK_PRUNECHILD);
284 
285 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip, DDI_PROP_DONTPASS,
286 	    "device_type", &parent_type) != DDI_SUCCESS)
287 		return (DDI_WALK_PRUNECHILD);
288 
289 	if ((strcmp(parent_type, "isa") == 0) ||
290 	    (strcmp(parent_type, "eisa") == 0)) {
291 		*(dev_info_t **)found_dip = dip;
292 		ddi_prop_free(parent_type);
293 		return (DDI_WALK_TERMINATE);
294 	}
295 
296 	if ((strcmp(parent_type, "pci") == 0) ||
297 	    (strcmp(parent_type, "pciex") == 0)) {
298 		ddi_acc_handle_t pci_conf;
299 		uint16_t data16;
300 
301 		ddi_prop_free(parent_type);
302 
303 		if (i_ddi_attach_node_hierarchy(dip) != DDI_SUCCESS)
304 			return (DDI_WALK_PRUNECHILD);
305 
306 		if (pci_config_setup(dip, &pci_conf) != DDI_SUCCESS)
307 			return (DDI_WALK_PRUNECHILD);
308 
309 		data16 = pci_config_get16(pci_conf, PCI_CONF_COMM);
310 		pci_config_teardown(&pci_conf);
311 
312 		if (!(data16 & PCI_COMM_IO))
313 			return (DDI_WALK_PRUNECHILD);
314 
315 		*(dev_info_t **)found_dip = dip;
316 		return (DDI_WALK_TERMINATE);
317 	}
318 
319 	ddi_prop_free(parent_type);
320 	return (DDI_WALK_PRUNECHILD);
321 }
322 
323 /*
324  * Conduct a width-first traverse searching for a display device which
325  * has either:
326  * 1) a VGA device.
327  * 2) a PCI VGA compatible device whose IO space is enabled
328  *    and the VGA Enable bit of any PCI-PCI bridge above it is set.
329  *
330  * Return the device path as the console fb path.
331  */
332 char *
333 plat_fbpath(void)
334 {
335 	dev_info_t *fb_dip = NULL;
336 	static char *fbpath = NULL;
337 	static char fbpath_buf[MAXPATHLEN];
338 
339 	ddi_walk_devs(ddi_root_node(), find_fb_dev, &fb_dip);
340 
341 	if (fb_dip == NULL)
342 		return (NULL);
343 
344 	(void) ddi_pathname(fb_dip, fbpath_buf);
345 	fbpath = fbpath_buf;
346 
347 	return (fbpath);
348 }
349 
350 char *
351 plat_mousepath(void)
352 {
353 	static char mpath[MAXPATHLEN];
354 
355 	/*
356 	 * Hardcode to isa mouse path
357 	 * XXX make it settable via bootprop?
358 	 */
359 	if (pseudo_isa)
360 		return ("/isa/i8042@1,60/mouse@1");
361 
362 	if (plat_devpath("mouse8042", mpath) == NULL)
363 		return (NULL);
364 
365 	return (mpath);
366 }
367 
368 /* return path of first usb serial device */
369 static char *
370 plat_usbser_path(void)
371 {
372 	extern dev_info_t *usbser_first_device(void);
373 
374 	dev_info_t *us_dip;
375 	static char *us_path = NULL;
376 
377 	if (us_path)
378 		return (us_path);
379 
380 	us_dip = usbser_first_device();
381 	if (us_dip == NULL)
382 		return (NULL);
383 
384 	us_path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
385 	(void) ddi_pathname(us_dip, us_path);
386 	ndi_rele_devi(us_dip);	/* held from usbser_first_device */
387 	return (us_path);
388 }
389 
390 static char *
391 plat_ttypath(int inum)
392 {
393 	static char *defaultpath[] = {
394 	    "/isa/asy@1,3f8:a",
395 	    "/isa/asy@1,2f8:b"
396 	};
397 	static char path[MAXPATHLEN];
398 	char *bp;
399 	major_t major;
400 	dev_info_t *dip;
401 
402 	if (pseudo_isa)
403 		return (defaultpath[inum]);
404 
405 	if ((major = ddi_name_to_major("asy")) == (major_t)-1)
406 		return (NULL);
407 
408 	if ((dip = devnamesp[major].dn_head) == NULL)
409 		return (NULL);
410 
411 	for (; dip != NULL; dip = ddi_get_next(dip)) {
412 		if (i_ddi_attach_node_hierarchy(dip) != DDI_SUCCESS)
413 			return (NULL);
414 
415 		if (DEVI(dip)->devi_minor->ddm_name[0] == ('a' + (char)inum))
416 			break;
417 	}
418 	if (dip == NULL)
419 		return (NULL);
420 
421 	(void) ddi_pathname(dip, path);
422 	bp = path + strlen(path);
423 	(void) snprintf(bp, 3, ":%s", DEVI(dip)->devi_minor->ddm_name);
424 
425 	return (path);
426 }
427 
428 /*
429  * Lacking support for com2 and com3, if that matters.
430  * Another possible enhancement could be to use properties
431  * for the port mapping rather than simply hard-code them.
432  */
433 char *
434 plat_stdinpath(void)
435 {
436 	switch (console_type()) {
437 #if defined(__xpv)
438 	case CONS_HYPERVISOR:
439 		return ("/xpvd/xencons@0");
440 #endif /* __xpv */
441 	case CONS_TTYA:
442 		return (plat_ttypath(0));
443 	case CONS_TTYB:
444 		return (plat_ttypath(1));
445 	case CONS_USBSER:
446 		return (plat_usbser_path());
447 	case CONS_SCREEN:
448 	default:
449 		break;
450 	};
451 	return (plat_kbdpath());
452 }
453 
454 char *
455 plat_stdoutpath(void)
456 {
457 	switch (console_type()) {
458 #if defined(__xpv)
459 	case CONS_HYPERVISOR:
460 		return ("/xpvd/xencons@0");
461 #endif /* __xpv */
462 	case CONS_TTYA:
463 		return (plat_ttypath(0));
464 	case CONS_TTYB:
465 		return (plat_ttypath(1));
466 	case CONS_USBSER:
467 		return (plat_usbser_path());
468 	case CONS_SCREEN:
469 	default:
470 		break;
471 	};
472 	return (plat_fbpath());
473 }
474 
475 /*
476  * If VIS_PIXEL mode will be implemented on x86, these following
477  * functions should be re-considered. Now these functions are
478  * unused on x86.
479  */
480 void
481 plat_tem_get_inverses(int *inverse, int *inverse_screen)
482 {
483 	*inverse = 0;
484 	*inverse_screen = 0;
485 }
486 
487 void
488 plat_tem_get_prom_font_size(int *charheight, int *windowtop)
489 {
490 	*charheight = 0;
491 	*windowtop = 0;
492 }
493 
494 /*ARGSUSED*/
495 void
496 plat_tem_get_prom_size(size_t *height, size_t *width)
497 {
498 	panic("unimplemented at line %d of %s", __LINE__, __FILE__);
499 }
500 
501 void
502 plat_tem_hide_prom_cursor(void)
503 {
504 	panic("unimplemented at line %d of %s", __LINE__, __FILE__);
505 }
506 
507 /*ARGSUSED*/
508 void
509 plat_tem_get_prom_pos(uint32_t *row, uint32_t *col)
510 {
511 	panic("unimplemented at line %d of %s", __LINE__, __FILE__);
512 }
513