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