xref: /illumos-gate/usr/src/uts/i86pc/io/consplat.c (revision 99ea293e719ac006d413e4fde6ac0d5cd4dd6c59)
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 #include <sys/framebuffer.h>
49 #include <sys/boot_console.h>
50 #if defined(__xpv)
51 #include <sys/hypervisor.h>
52 #endif
53 
54 extern int pseudo_isa;
55 
56 int
57 plat_use_polled_debug()
58 {
59 	return (0);
60 }
61 
62 int
63 plat_support_serial_kbd_and_ms()
64 {
65 	return (0);
66 }
67 
68 #define	A_CNT(arr)	(sizeof (arr) / sizeof (arr[0]))
69 
70 #ifndef	CONS_INVALID
71 #define	CONS_INVALID		-1
72 #define	CONS_SCREEN_TEXT	0
73 #define	CONS_TTY		1
74 #define	CONS_XXX		2	/* Unused */
75 #define	CONS_USBSER		3
76 #define	CONS_HYPERVISOR		4
77 #define	CONS_SCREEN_GRAPHICS	5
78 #endif	/* CONS_INVALID */
79 
80 char *plat_fbpath(void);
81 
82 static int
83 console_type(int *tnum)
84 {
85 	static int boot_console = CONS_INVALID;
86 	static int tty_num = 0;
87 
88 	char *cons;
89 	dev_info_t *root;
90 
91 	/* If we already have determined the console, just return it. */
92 	if (boot_console != CONS_INVALID) {
93 		if (tnum != NULL)
94 			*tnum = tty_num;
95 		return (boot_console);
96 	}
97 
98 #if defined(__xpv)
99 	if (!DOMAIN_IS_INITDOMAIN(xen_info) || bcons_hypervisor_redirect()) {
100 		boot_console = CONS_HYPERVISOR;
101 		if (tnum != NULL)
102 			*tnum = tty_num;
103 		return (boot_console);
104 	}
105 #endif /* __xpv */
106 
107 	/*
108 	 * console is defined by "console" property, with
109 	 * fallback on the old "input-device" property.
110 	 * If "input-device" is not defined either, also check "output-device".
111 	 */
112 	boot_console = CONS_SCREEN_TEXT;	/* default is screen/kb */
113 	root = ddi_root_node();
114 	if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, root,
115 	    DDI_PROP_DONTPASS, "console", &cons) == DDI_SUCCESS) ||
116 	    (ddi_prop_lookup_string(DDI_DEV_T_ANY, root,
117 	    DDI_PROP_DONTPASS, "input-device", &cons) == DDI_SUCCESS) ||
118 	    (ddi_prop_lookup_string(DDI_DEV_T_ANY, root,
119 	    DDI_PROP_DONTPASS, "output-device", &cons) == DDI_SUCCESS)) {
120 		if (strlen(cons) == 4 && strncmp(cons, "tty", 3) == 0 &&
121 		    cons[3] >= 'a' && cons[3] <= 'd') {
122 			boot_console = CONS_TTY;
123 			tty_num = cons[3] - 'a';
124 		} else if (strcmp(cons, "usb-serial") == 0) {
125 			(void) i_ddi_attach_hw_nodes("xhci");
126 			(void) i_ddi_attach_hw_nodes("ehci");
127 			(void) i_ddi_attach_hw_nodes("uhci");
128 			(void) i_ddi_attach_hw_nodes("ohci");
129 			/*
130 			 * USB device enumerate asynchronously.
131 			 * Wait 2 seconds for USB serial devices to attach.
132 			 */
133 			delay(drv_usectohz(2000000));
134 			boot_console = CONS_USBSER;
135 #if defined(__xpv)
136 		} else if (strcmp(cons, "hypervisor") == 0) {
137 			boot_console = CONS_HYPERVISOR;
138 #endif /* __xpv */
139 		}
140 		ddi_prop_free(cons);
141 	}
142 
143 	/*
144 	 * If the console is configured to use a framebuffer but none
145 	 * could be found, fallback to "ttya" since it's likely to exist
146 	 * and it matches longstanding behavior on SPARC.
147 	 */
148 	if (boot_console == CONS_SCREEN_TEXT && plat_fbpath() == NULL) {
149 		boot_console = CONS_TTY;
150 		tty_num = 0;
151 	}
152 
153 	if (tnum != NULL)
154 		*tnum = tty_num;
155 	return (boot_console);
156 }
157 
158 int
159 plat_stdin_is_keyboard(void)
160 {
161 	return (console_type(NULL) == CONS_SCREEN_TEXT);
162 }
163 
164 int
165 plat_stdout_is_framebuffer(void)
166 {
167 	return (console_type(NULL) == CONS_SCREEN_TEXT);
168 }
169 
170 static char *
171 plat_devpath(char *name, char *path)
172 {
173 	major_t major;
174 	dev_info_t *dip, *pdip;
175 
176 	if ((major = ddi_name_to_major(name)) == (major_t)-1)
177 		return (NULL);
178 
179 	if ((dip = devnamesp[major].dn_head) == NULL)
180 		return (NULL);
181 
182 	pdip = ddi_get_parent(dip);
183 	if (i_ddi_attach_node_hierarchy(pdip) != DDI_SUCCESS)
184 		return (NULL);
185 	if (ddi_initchild(pdip, dip) != DDI_SUCCESS)
186 		return (NULL);
187 
188 	(void) ddi_pathname(dip, path);
189 
190 	return (path);
191 }
192 
193 /*
194  * Return generic path to keyboard device from the alias.
195  */
196 char *
197 plat_kbdpath(void)
198 {
199 	static char kbpath[MAXPATHLEN];
200 
201 	/*
202 	 * Hardcode to isa keyboard path
203 	 * XXX make it settable via bootprop?
204 	 */
205 	if (pseudo_isa)
206 		return ("/isa/i8042@1,60/keyboard@0");
207 
208 	if (plat_devpath("kb8042", kbpath) == NULL)
209 		return (NULL);
210 
211 	return (kbpath);
212 }
213 
214 /*
215  * NOTE: this function is duplicated here and in gfx_private/vgatext while
216  *       we work on a set of commitable interfaces to sunpci.c.
217  *
218  * Use the class code to determine if the device is a PCI-to-PCI bridge.
219  * Returns:  B_TRUE  if the device is a bridge.
220  *           B_FALSE if the device is not a bridge or the property cannot be
221  *		     retrieved.
222  */
223 static boolean_t
224 is_pci_bridge(dev_info_t *dip)
225 {
226 	uint32_t class_code;
227 
228 	class_code = (uint32_t)ddi_prop_get_int(DDI_DEV_T_ANY, dip,
229 	    DDI_PROP_DONTPASS, "class-code", 0xffffffff);
230 
231 	if (class_code == 0xffffffff || class_code == DDI_PROP_NOT_FOUND)
232 		return (B_FALSE);
233 
234 	class_code &= 0x00ffff00;
235 	if (class_code == ((PCI_CLASS_BRIDGE << 16) | (PCI_BRIDGE_PCI << 8)))
236 		return (B_TRUE);
237 
238 	return (B_FALSE);
239 }
240 
241 /*
242  * Type for the parameter of find_fb_dev()
243  */
244 struct find_fb_dev_param
245 {
246 	dev_info_t *found_dip;	/* dip found for VGA console */
247 	int vga_enable;		/* check PCI_BCNF_BCNTRL_VGA_ENABLE or not */
248 };
249 
250 /*
251  * The VGA device could be under a subtractive PCI bridge on some systems.
252  * Though the PCI_BCNF_BCNTRL_VGA_ENABLE bit is not set on such subtractive
253  * PCI bridge, the subtractive PCI bridge can forward VGA access if no other
254  * agent claims the access.
255  * The vga_enable element in param acts as a flag, if not set, ignore the
256  * checking for the PCI_BCNF_BCNTRL_VGA_ENABLE bit of the PCI bridge during
257  * the search.
258  */
259 static int
260 find_fb_dev(dev_info_t *dip, void *param)
261 {
262 	struct find_fb_dev_param *p = param;
263 	char *dev_type;
264 	dev_info_t *pdip;
265 	char *parent_type;
266 
267 	if (dip == ddi_root_node())
268 		return (DDI_WALK_CONTINUE);
269 
270 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
271 	    "device_type", &dev_type) != DDI_SUCCESS)
272 		return (DDI_WALK_PRUNECHILD);
273 
274 	if ((strcmp(dev_type, "isa") == 0) || (strcmp(dev_type, "eisa") == 0)) {
275 		ddi_prop_free(dev_type);
276 		return (DDI_WALK_CONTINUE);
277 	}
278 
279 	if ((strcmp(dev_type, "pci") == 0) ||
280 	    (strcmp(dev_type, "pciex") == 0)) {
281 		ddi_acc_handle_t pci_conf;
282 		uint16_t data16;
283 		char *nodename;
284 
285 		ddi_prop_free(dev_type);
286 
287 		if (!p->vga_enable)
288 			return (DDI_WALK_CONTINUE);
289 
290 		nodename = ddi_node_name(dip);
291 
292 		/*
293 		 * If the node is not a PCI-to-PCI bridge, continue traversing
294 		 * (it could be the root node), otherwise, check for the
295 		 * VGAEnable bit to be set in the Bridge Control Register.
296 		 */
297 		if (strcmp(nodename, "pci") == 0) {
298 			if (is_pci_bridge(dip) == B_FALSE)
299 				return (DDI_WALK_CONTINUE);
300 		}
301 
302 		if (i_ddi_attach_node_hierarchy(dip) != DDI_SUCCESS)
303 			return (DDI_WALK_PRUNECHILD);
304 
305 		if (pci_config_setup(dip, &pci_conf) != DDI_SUCCESS)
306 			return (DDI_WALK_PRUNECHILD);
307 
308 		data16 = pci_config_get16(pci_conf, PCI_BCNF_BCNTRL);
309 		pci_config_teardown(&pci_conf);
310 
311 		if (data16 & PCI_BCNF_BCNTRL_VGA_ENABLE)
312 			return (DDI_WALK_CONTINUE);
313 
314 		return (DDI_WALK_PRUNECHILD);
315 	}
316 
317 	if (strcmp(dev_type, "display") != 0) {
318 		ddi_prop_free(dev_type);
319 		return (DDI_WALK_CONTINUE);
320 	}
321 
322 	ddi_prop_free(dev_type);
323 
324 	if ((pdip = ddi_get_parent(dip)) == NULL)
325 		return (DDI_WALK_PRUNECHILD);
326 
327 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip, DDI_PROP_DONTPASS,
328 	    "device_type", &parent_type) != DDI_SUCCESS)
329 		return (DDI_WALK_PRUNECHILD);
330 
331 	if ((strcmp(parent_type, "isa") == 0) ||
332 	    (strcmp(parent_type, "eisa") == 0)) {
333 		p->found_dip = dip;
334 		ddi_prop_free(parent_type);
335 		return (DDI_WALK_TERMINATE);
336 	}
337 
338 	if ((strcmp(parent_type, "pci") == 0) ||
339 	    (strcmp(parent_type, "pciex") == 0)) {
340 		ddi_acc_handle_t pci_conf;
341 		uint16_t data16;
342 
343 		ddi_prop_free(parent_type);
344 
345 		if (i_ddi_attach_node_hierarchy(dip) != DDI_SUCCESS)
346 			return (DDI_WALK_PRUNECHILD);
347 
348 		if (pci_config_setup(dip, &pci_conf) != DDI_SUCCESS)
349 			return (DDI_WALK_PRUNECHILD);
350 
351 		data16 = pci_config_get16(pci_conf, PCI_CONF_COMM);
352 		pci_config_teardown(&pci_conf);
353 
354 		if (!(data16 & PCI_COMM_IO))
355 			return (DDI_WALK_PRUNECHILD);
356 
357 		p->found_dip = dip;
358 		return (DDI_WALK_TERMINATE);
359 	}
360 
361 	ddi_prop_free(parent_type);
362 	return (DDI_WALK_PRUNECHILD);
363 }
364 
365 /*
366  * The first round search is to find:
367  * 1) a VGA device.
368  * 2) a PCI VGA compatible device whose IO space is enabled
369  *    and the VGA Enable bit of any PCI-PCI bridge above it is set.
370  * If the first round search succeeds, prune the second round search.
371  *
372  * The second round seach does not check the VGA Enable bit.
373  *
374  * Return the device path as the console fb path.
375  */
376 char *
377 plat_fbpath(void)
378 {
379 	struct find_fb_dev_param param;
380 	static char *fbpath = NULL;
381 	static char fbpath_buf[MAXPATHLEN];
382 
383 	/* first round search */
384 	param.found_dip = NULL;
385 	param.vga_enable = 1;
386 	ddi_walk_devs(ddi_root_node(), find_fb_dev, &param);
387 
388 	if (param.found_dip != NULL) {
389 		(void) ddi_pathname(param.found_dip, fbpath_buf);
390 		fbpath = fbpath_buf;
391 		return (fbpath);
392 	}
393 
394 	/*
395 	 * second round search, do not check the
396 	 * PCI_BCNF_BCNTRL_VGA_ENABLE bit
397 	 */
398 	param.found_dip = NULL;
399 	param.vga_enable = 0;
400 	ddi_walk_devs(ddi_root_node(), find_fb_dev, &param);
401 
402 	if (param.found_dip == NULL)
403 		return (NULL);
404 
405 	(void) ddi_pathname(param.found_dip, fbpath_buf);
406 	fbpath = fbpath_buf;
407 	return (fbpath);
408 }
409 
410 char *
411 plat_mousepath(void)
412 {
413 	static char mpath[MAXPATHLEN];
414 
415 	/*
416 	 * Hardcode to isa mouse path
417 	 * XXX make it settable via bootprop?
418 	 */
419 	if (pseudo_isa)
420 		return ("/isa/i8042@1,60/mouse@1");
421 
422 	if (plat_devpath("mouse8042", mpath) == NULL)
423 		return (NULL);
424 
425 	return (mpath);
426 }
427 
428 /* return path of first usb serial device */
429 static char *
430 plat_usbser_path(void)
431 {
432 	extern dev_info_t *usbser_first_device(void);
433 
434 	dev_info_t *us_dip;
435 	static char *us_path = NULL;
436 
437 	if (us_path)
438 		return (us_path);
439 
440 	us_dip = usbser_first_device();
441 	if (us_dip == NULL)
442 		return (NULL);
443 
444 	us_path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
445 	(void) ddi_pathname(us_dip, us_path);
446 	ndi_rele_devi(us_dip);	/* held from usbser_first_device */
447 	return (us_path);
448 }
449 
450 static char *
451 plat_ttypath(int inum)
452 {
453 	static char *defaultpath[] = {
454 	    "/isa/asy@1,3f8:a",
455 	    "/isa/asy@1,2f8:b",
456 	    "/isa/asy@1,3e8:c",
457 	    "/isa/asy@1,2e8:d"
458 	};
459 	static char path[MAXPATHLEN];
460 	char *bp;
461 	major_t major;
462 	dev_info_t *dip;
463 
464 	if (pseudo_isa)
465 		return (defaultpath[inum]);
466 
467 	if ((major = ddi_name_to_major("asy")) == (major_t)-1)
468 		return (NULL);
469 
470 	if ((dip = devnamesp[major].dn_head) == NULL)
471 		return (NULL);
472 
473 	for (; dip != NULL; dip = ddi_get_next(dip)) {
474 		if (i_ddi_attach_node_hierarchy(dip) != DDI_SUCCESS)
475 			return (NULL);
476 
477 		if (DEVI(dip)->devi_minor->ddm_name[0] == ('a' + (char)inum))
478 			break;
479 	}
480 	if (dip == NULL)
481 		return (NULL);
482 
483 	(void) ddi_pathname(dip, path);
484 	bp = path + strlen(path);
485 	(void) snprintf(bp, 3, ":%s", DEVI(dip)->devi_minor->ddm_name);
486 
487 	return (path);
488 }
489 
490 /*
491  * Another possible enhancement could be to use properties
492  * for the port mapping rather than simply hard-code them.
493  */
494 char *
495 plat_stdinpath(void)
496 {
497 	int tty_num = 0;
498 
499 	switch (console_type(&tty_num)) {
500 #if defined(__xpv)
501 	case CONS_HYPERVISOR:
502 		return ("/xpvd/xencons@0");
503 #endif /* __xpv */
504 	case CONS_TTY:
505 		return (plat_ttypath(tty_num));
506 	case CONS_USBSER:
507 		return (plat_usbser_path());
508 	case CONS_SCREEN_TEXT:
509 	default:
510 		break;
511 	};
512 	return (plat_kbdpath());
513 }
514 
515 char *
516 plat_stdoutpath(void)
517 {
518 	int tty_num = 0;
519 
520 	switch (console_type(&tty_num)) {
521 #if defined(__xpv)
522 	case CONS_HYPERVISOR:
523 		return ("/xpvd/xencons@0");
524 #endif /* __xpv */
525 	case CONS_TTY:
526 		return (plat_ttypath(tty_num));
527 	case CONS_USBSER:
528 		return (plat_usbser_path());
529 	case CONS_SCREEN_TEXT:
530 	default:
531 		break;
532 	};
533 	return (plat_fbpath());
534 }
535 
536 char *
537 plat_diagpath(void)
538 {
539 	dev_info_t *root;
540 	char *diag;
541 	int tty_num = -1;
542 
543 	root = ddi_root_node();
544 
545 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, root, DDI_PROP_DONTPASS,
546 	    "diag-device", &diag) == DDI_SUCCESS) {
547 		if (strlen(diag) == 4 && strncmp(diag, "tty", 3) == 0 &&
548 		    diag[3] >= 'a' && diag[3] <= 'd') {
549 			tty_num = diag[3] - 'a';
550 		}
551 		ddi_prop_free(diag);
552 	}
553 
554 	if (tty_num != -1)
555 		return (plat_ttypath(tty_num));
556 	return (NULL);
557 }
558 
559 /*
560  * If VIS_PIXEL mode will be implemented on x86, these following
561  * functions should be re-considered. Now these functions are
562  * unused on x86.
563  */
564 void
565 plat_tem_get_colors(uint8_t *fg, uint8_t *bg)
566 {
567 	*fg = fb_info.fg_color;
568 	*bg = fb_info.bg_color;
569 }
570 
571 void
572 plat_tem_get_inverses(int *inverse, int *inverse_screen)
573 {
574 	*inverse = fb_info.inverse == B_TRUE? 1 : 0;
575 	*inverse_screen = fb_info.inverse_screen == B_TRUE? 1 : 0;
576 }
577 
578 void
579 plat_tem_get_prom_font_size(int *charheight, int *windowtop)
580 {
581 	*charheight = fb_info.font_height;
582 	*windowtop = fb_info.terminal_origin.y;
583 }
584 
585 /*ARGSUSED*/
586 void
587 plat_tem_get_prom_size(size_t *height, size_t *width)
588 {
589 	*height = fb_info.terminal.y;
590 	*width = fb_info.terminal.x;
591 }
592 
593 /* this gets called once at boot time and only in case of VIS_PIXEL */
594 void
595 plat_tem_hide_prom_cursor(void)
596 {
597 	if (boot_console_type(NULL) == CONS_FRAMEBUFFER)
598 		boot_fb_cursor(B_FALSE);
599 }
600 
601 /*ARGSUSED*/
602 void
603 plat_tem_get_prom_pos(uint32_t *row, uint32_t *col)
604 {
605 	*row = fb_info.cursor.pos.y;
606 	*col = fb_info.cursor.pos.x;
607 }
608