xref: /titanic_41/usr/src/uts/sun4u/montecarlo/io/ttymux_dacf/ttymux_dacf.c (revision f500b19684bd0346ac05bec02a50af07f369da1a)
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  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * This is a dacf module based upon the Extensions to Device Autoconfiguration
28  * project.  See PSARC/1998/212 for more details.
29  *
30  * This module provides the dacf functions
31  * to be called after a driver has attached and before it detaches.
32  * The post attach functionality is used to autoconfigure a serial console
33  * multiplexer if the OBP console is a multiplexer.
34  */
35 
36 #include <sys/types.h>
37 #include <sys/param.h>
38 #include <sys/cmn_err.h>
39 #include <sys/user.h>
40 #include <sys/vfs.h>
41 #include <sys/vnode.h>
42 #include <sys/systm.h>
43 #include <sys/file.h>
44 #include <sys/klwp.h>
45 #include <sys/stropts.h>
46 #include <sys/stream.h>
47 #include <sys/strsubr.h>
48 
49 #include <sys/consdev.h>
50 #include <sys/kbio.h>
51 #include <sys/debug.h>
52 #include <sys/reboot.h>
53 #include <sys/termios.h>
54 #include <sys/clock.h>
55 
56 #include <sys/kstr.h>
57 #include <sys/ddi.h>
58 #include <sys/sunddi.h>
59 #include <sys/sunndi.h>
60 #include <sys/modctl.h>
61 #include <sys/ddi_impldefs.h>
62 #include <sys/ndi_impldefs.h>
63 
64 #include <sys/errno.h>
65 #include <sys/devops.h>
66 #include <sys/note.h>
67 #include <sys/open.h>
68 #include <sys/kmem.h>
69 #include <sys/dacf.h>
70 #include <sys/promif.h>
71 
72 #include "ttymux_dacf.h"
73 
74 #pragma weak	find_platform_consoles
75 extern char *find_platform_consoles(sm_mux_state_t *_m, dev_info_t *_di,
76     dev_t _d, uint_t _f);
77 
78 #define	platform_consoles(_m, _di, _d, _f)		\
79 	(find_platform_consoles != NULL		\
80 	? find_platform_consoles(_m, _di, _d, _f)	\
81 	: (nulldev(_m, _di, _d, _f), (char *)0))
82 
83 /*
84  * External functions
85  */
86 extern uintptr_t space_fetch(char *key);
87 extern int space_store(char *key, uintptr_t ptr);
88 extern void ttymux_dprintf(int l, const char *fmt, ...);
89 extern int prom_ihandle_to_path(ihandle_t, char *, uint_t);
90 extern void prom_interpret(char *, uintptr_t, uintptr_t, uintptr_t,
91     uintptr_t, uintptr_t);
92 extern ihandle_t prom_stdin_ihandle();
93 extern ihandle_t prom_stdout_ihandle();
94 
95 extern vnode_t *rconsvp;	/* redirection device */
96 
97 /*
98  * Dacf entry points
99  */
100 static int ttymux_config(dacf_infohdl_t, dacf_arghdl_t, int);
101 
102 /*
103  * Internal functions
104  */
105 static dacf_op_t ttymuxconfig_op[] = {
106 	{ DACF_OPID_POSTATTACH, ttymux_config },
107 	{ DACF_OPID_END,	NULL },
108 };
109 
110 static dacf_opset_t opsets[] = {
111 	{ "ttymux_config",	ttymuxconfig_op },
112 	{ NULL,		NULL }
113 };
114 
115 struct dacfsw dacfsw = {
116 	DACF_MODREV_1,
117 	opsets,
118 };
119 
120 struct modldacf modldacf = {
121 	&mod_dacfops,   /* Type of module */
122 	"ttymux DACF",
123 	&dacfsw
124 };
125 
126 struct modlinkage modlinkage = {
127 	MODREV_1, (void *)&modldacf, NULL
128 };
129 
130 /*LINTLIBRARY*/
131 
132 /*
133  * The following minor nodes can be linked underneath the serial
134  * console multiplexer.
135  * These are the only ones currently tested.
136  * (NOTE: Devices of device_type serial are also allowed to be used as
137  * additional consoles).
138  * Disallow plumbing of untested node types.
139  */
140 static const char * const supported_types[] = {
141 	DDI_NT_SERIAL, (char *const)NULL
142 };
143 
144 #define	OFLAGS	FREAD|FWRITE|FNOCTTY|FNONBLOCK
145 
146 #define	INPUT_ALIAS	"multiplexer-input-devices"
147 #define	OUTPUT_ALIAS	"multiplexer-output-devices"
148 #define	OBPDEV		0x100
149 #define	FORTH_STRINGLEN	1024
150 #define	MUXDEVTYPE	"SUNW,serial-multiplexer"
151 
152 static char fth_fmt[] =
153 "\" get-device-list\" "	/* ( method-str method-len ) */
154 "h# %p "		/* ( method-str method-len ihandle ) */
155 "$call-method "		/* ( ihandle_n-1 ... ihandle n ) */
156 "dup "			/* ( ihandle_n-1 ... ihandle n n ) */
157 "h# %p "		/* ( ihandle_n-1 ... ihandle n n numfound ) */
158 "l! "			/* ( ihandle_n-1 ... ihandle n ) */
159 "0 "			/* ( ihandle_n-1 ... ihandle n 0 ) */
160 "do "			/* ( ihandle_n-1 ... ihandle_i+1 ihandle_i ) */
161 "  i "			/* ( ihandle_n-1 ... ihandle_i+1 ihandle_i index ) */
162 "  h# %x "		/* ( ihandle_n-1 ... ihandle_i+1 ihandle_i index max) */
163 "  < if "		/* ( ihandle_n-1 ... ihandle_i+1 ihandle_i ) */
164 "    h# %p "		/* ( ihandle_n-1 ... ihandle_i+1 ihandle_i buf ) */
165 "    i "		/* ( ihandle_n-1 ... ihandle_i+1 ihandle_i buf index) */
166 "    4 * + "		/* ( ihandle_n-1 ... ihandle_i+1 ihandle_i buf' ) */
167 "    l! "		/* ( ihandle_n-1 ... ihandle_i+1 ) */
168 "  else "		/* */
169 "    drop "		/* ( ihandle_n-1 ... ihandle_i+1 ) */
170 "  then "		/* */
171 "loop ";		/* ( ihandle_n-1 ... ihandle_i+1 ) */
172 
173 int
_init(void)174 _init(void)
175 {
176 	return (mod_install(&modlinkage));
177 }
178 
179 int
_fini()180 _fini()
181 {
182 	return (mod_remove(&modlinkage));
183 }
184 
185 int
_info(struct modinfo * modinfop)186 _info(struct modinfo *modinfop)
187 {
188 	return (mod_info(&modlinkage, modinfop));
189 }
190 
191 static int
ioctl_cmd(vnode_t * avp,int cmd,void * data,int datasize,int * bytecnt)192 ioctl_cmd(vnode_t *avp, int cmd, void *data, int datasize, int *bytecnt)
193 {
194 	struct strioctl ios;
195 	int		rval;
196 
197 	ios.ic_timout = 0;
198 	ios.ic_cmd = cmd;
199 	ios.ic_dp = (char *)data;
200 	ios.ic_len = datasize;
201 
202 	rval = kstr_ioctl(avp, I_STR, (intptr_t)&ios);
203 	if (bytecnt)
204 		*bytecnt = ios.ic_len;
205 	return (rval);
206 }
207 
208 /*
209  * How many consoles are actually linked underneath the Solaris console
210  * multiplexer.
211  */
212 static int
usable_consoles(sm_mux_state_t * sp,uint_t * iconsoles,uint_t * oconsoles)213 usable_consoles(sm_mux_state_t *sp, uint_t *iconsoles, uint_t *oconsoles)
214 {
215 	uint_t  j, cnt, icnt = 0u, ocnt = 0u;
216 
217 	mutex_enter(&sp->sm_cons_mutex);
218 	for (j = 0, cnt = 0; j < sp->sm_cons_cnt; j++)
219 		if (sp->sm_cons_links[j].sm_muxid != 0) {
220 			sm_console_t *cn = &sp->sm_cons_links[j];
221 			if (cn->sm_mode & FORINPUT)
222 				icnt += 1;
223 			if (cn->sm_mode & FOROUTPUT)
224 				ocnt += 1;
225 			if (cn->sm_mode == FORIO)
226 				cnt += 1;
227 		}
228 	mutex_exit(&sp->sm_cons_mutex);
229 	*iconsoles = icnt;
230 	*oconsoles = ocnt;
231 	return (cnt);
232 }
233 
234 /*
235  * Before linking a device underneath a serial multiplexer check that
236  * its minor node type is supported.
237  */
238 static boolean_t
compatible_console(dev_t dev)239 compatible_console(dev_t dev)
240 {
241 	int			circ;
242 	boolean_t		compatible;
243 	char *const		*nodetype;
244 	struct ddi_minor_data	*dmdp;
245 	dev_info_t		*dip;
246 	char			devtype[32];
247 	int			len;
248 
249 	/*
250 	 * Find the node nodetype to verify that the current version of
251 	 * the code supports its use as a console
252 	 * Supported types are listed in the array supported_types
253 	 */
254 	if ((dip = e_ddi_hold_devi_by_dev(dev, 0)) == NULL) {
255 		ttymux_dprintf(DPRINT_L2, "No dip for %d:%d\n",
256 		    getmajor(dev), getminor(dev));
257 		return (B_FALSE);
258 	}
259 
260 	compatible = B_FALSE;
261 	len = sizeof (devtype);
262 
263 	ndi_devi_enter(dip, &circ);
264 	for (dmdp = DEVI(dip)->devi_minor; dmdp != NULL; dmdp = dmdp->next) {
265 		struct ddi_minor_data   *mdp = dmdp;
266 
267 		if (mdp->ddm_dev == dev) {
268 
269 			ttymux_dprintf(DPRINT_L0, "compat: matched dev\n");
270 			/*
271 			 * check the OBP device_type property first
272 			 * its a good bet that it will be compatible
273 			 * if it has the value serial.
274 			 */
275 			if (ddi_prop_op(DDI_DEV_T_ANY, dip,
276 			    PROP_LEN_AND_VAL_BUF, 0, "device_type",
277 			    (caddr_t)devtype, &len) == DDI_PROP_SUCCESS &&
278 			    strcmp(devtype, "serial") == 0) {
279 				compatible = B_TRUE;
280 			} else {
281 				for (nodetype =
282 				    (char *const *)&supported_types[0];
283 				    *nodetype != (char *const)NULL;
284 				    nodetype++) {
285 					if (strcmp(*nodetype,
286 					    mdp->ddm_node_type) == 0) {
287 						compatible = B_TRUE;
288 						break;
289 					}
290 				}
291 			}
292 			break;
293 		}
294 	}
295 	ndi_devi_exit(dip, circ);
296 	ddi_release_devi(dip);
297 
298 	/*
299 	 * The current version of the implementation has only been tested
300 	 * with a serial multiplexer.
301 	 */
302 
303 	ttymux_dprintf(DPRINT_L0, "%d:%d is %s\n", getmajor(dev),
304 	    getminor(dev), (compatible) ? "compatible" : "incompatible");
305 
306 	return (compatible);
307 }
308 
309 /*
310  * get-device-list ( -- [ihandle n-1, ... ihandle], n )
311  * Call the "get-device-list" method of an OBP device.
312  * ihdl		- ihandle of the OBP device whose method is to be called
313  * ihdls	- array of ihandles returned to the caller
314  * maxi		- length of the ihdls array
315  */
316 static int
get_device_list(ihandle_t ihdl,ihandle_t * ihdls,size_t maxi)317 get_device_list(ihandle_t ihdl, ihandle_t *ihdls, size_t maxi)
318 {
319 	int	numfound = -1;
320 	char	fstr[FORTH_STRINGLEN];
321 
322 	if (snprintf(fstr, FORTH_STRINGLEN, fth_fmt, (caddr32_t)ihdl,
323 	    &numfound, maxi, ihdls) > FORTH_STRINGLEN) {
324 		ttymux_dprintf(DPRINT_L3,
325 		    "WARNING: forth buffer size is too small.\n");
326 		return (0);
327 	}
328 
329 	prom_interpret(fstr, 0, 0, 0, 0, 0);
330 
331 	ttymux_dprintf(DPRINT_L0, "ihdl 0x%p cnt %d\n",
332 	    (caddr32_t)ihdl, numfound);
333 
334 	return (numfound);
335 }
336 
337 /*
338  * Read an OBP property and return the result in propval.
339  * The caller is responsible for freeing the memory.
340  */
341 static int
read_prop(pnode_t node,char * propname,char ** propval)342 read_prop(pnode_t node, char *propname, char **propval)
343 {
344 	int	proplen = -1;
345 
346 	if (node == OBP_BADNODE ||
347 	    (proplen = prom_getproplen(node, propname)) <= 0)
348 		return (proplen);
349 
350 	*propval = kmem_zalloc(proplen + 1, KM_SLEEP);
351 	(void) prom_getprop(node, propname, *propval);
352 
353 	return (proplen);
354 }
355 
356 /*
357  * Parse a white space separated list of tokens and call
358  * the input action with each parsed token.
359  */
360 static void
parse(sm_mux_state_t * ms,char * p,void (* action)(sm_mux_state_t *,char *,void *),void * arg)361 parse(sm_mux_state_t *ms, char *p,
362     void (*action)(sm_mux_state_t *, char *, void *), void *arg)
363 {
364 	char    *e, *tok = NULL;
365 
366 	if (p == 0 || *p == 0)
367 		return;
368 
369 	e = p + strlen(p);
370 
371 	do {
372 		switch (*p) {
373 		case ' ':
374 		case '\t':
375 			if (tok != NULL) {
376 				*p = 0;
377 				action(ms, tok, arg);
378 				tok = NULL;
379 				*p = ' ';
380 			}
381 			break;
382 		default:
383 			if (tok == NULL) {
384 				tok = p;
385 			}
386 			break;
387 		}
388 	} while (++p < e);
389 
390 	if (tok != NULL)
391 		action(ms, tok, arg);
392 }
393 
394 /*
395  * Search for a console structure matching a device path.
396  * Return a new initialized structure if one does not exist.
397  */
398 sm_console_t *
get_aconsole(sm_mux_state_t * ms,char * path)399 get_aconsole(sm_mux_state_t *ms, char *path)
400 {
401 	sm_console_t	*cn;
402 	int		j;
403 
404 	for (cn = ms->sm_cons_links, j = 0;
405 	    j < ms->sm_cons_cnt; cn++, j++) {
406 		if (cn->sm_path && strcmp(cn->sm_path, path) == 0)
407 			break;
408 	}
409 	if (j == ms->sm_cons_cnt) {
410 		if (j + 1 == TTYMUX_MAX_LINKS) {
411 			cn = NULL;
412 		} else {
413 			bzero((caddr_t)cn, sizeof (*cn));
414 			ms->sm_cons_cnt += 1;
415 		}
416 	}
417 	return (cn);
418 }
419 
420 /*
421  * Create a new console structure representing the device
422  * identified by path. The void * argument indicates which I/O
423  * mode the device will support.
424  */
425 static void
add_aconsole(sm_mux_state_t * ms,char * path,void * arg)426 add_aconsole(sm_mux_state_t *ms, char *path, void *arg)
427 {
428 	sm_console_t	*cn;
429 	char		*cpath;
430 
431 	if (*path == '/') {
432 		cpath = kmem_alloc(strlen(path) + 1, KM_SLEEP);
433 		(void) strcpy(cpath, path);
434 	} else if (read_prop(prom_alias_node(), path, &cpath) <= 0) {
435 			return;
436 	}
437 
438 	/*
439 	 * Device paths should have a minor name - if its missing assume
440 	 * it should be :a!
441 	 */
442 	if (strrchr(cpath, ':') == NULL) {
443 		char	*p;
444 		size_t	len = strlen(cpath) + 1;
445 
446 		p = kmem_zalloc(len + 2, KM_SLEEP);
447 		(void) strcpy(p, cpath);
448 		(void) strcat(p, ":a");	/* assume :a ! */
449 		kmem_free(cpath, len);
450 		cpath = p;
451 	}
452 	if ((cn = get_aconsole(ms, cpath)) != NULL) {
453 		cn->sm_obp_con = ((uint_t)(uintptr_t)arg & OBPDEV) ?
454 		    B_TRUE : B_FALSE;
455 		cn->sm_mode |= (io_mode_t)((uint_t)(uintptr_t)arg & FORIO);
456 		if (cn->sm_path != NULL)
457 			kmem_free(cn->sm_path, strlen(cn->sm_path) + 1);
458 		cn->sm_path = cpath;
459 	} else {
460 		ttymux_dprintf(DPRINT_L3, "Too many "
461 		    " consoles - ignoring %s\n", cpath);
462 		kmem_free(cpath, strlen(cpath) + 1);
463 	}
464 }
465 
466 /*
467  * Discover which consoles OBP is using.
468  */
469 static int
find_obp_consoles(sm_mux_state_t * ms,io_mode_t mode)470 find_obp_consoles(sm_mux_state_t *ms, io_mode_t mode)
471 {
472 	sm_console_t	*cn;
473 	int		i, cnt;
474 	char		*devpath;
475 	ihandle_t	ihdls[TTYMUX_MAX_LINKS];
476 	ihandle_t	stdihdl;
477 
478 	if (mode == FORINPUT)
479 		stdihdl = ms->sm_cons_stdin.sm_i_ihdl;
480 	else if (mode == FOROUTPUT)
481 		stdihdl = ms->sm_cons_stdout.sm_o_ihdl;
482 	else
483 		return (EINVAL);
484 	devpath = kmem_alloc(MAXPATHLEN+2, KM_SLEEP);
485 
486 	cnt = get_device_list(stdihdl, ihdls, TTYMUX_MAX_LINKS);
487 
488 	for (i = 0; i < cnt; i++) {
489 
490 		if (prom_ihandle_to_path(ihdls[i], devpath, MAXPATHLEN) == 0)
491 			continue;
492 		/*
493 		 * If the minor name is not part of the path and there is
494 		 * more than one minor node then ddi_pathname_to_dev_t
495 		 * can fail to resolve the path correctly (it's an OBP
496 		 * problem)!!! If there's no minor name then assume the default
497 		 * minor name (:a).
498 		 */
499 		if (strrchr(devpath, ':') == NULL)
500 			(void) strcat(devpath, ":a");	/* assume :a ! */
501 
502 		if ((cn = get_aconsole(ms, devpath)) == 0) {
503 			ttymux_dprintf(DPRINT_L3, "Too many "
504 			    " consoles - ignoring %s\n", devpath);
505 			continue;
506 		}
507 
508 		cn->sm_mode |= mode;
509 		cn->sm_obp_con = B_TRUE;
510 		if (mode == FORINPUT)
511 			cn->sm_i_ihdl = ihdls[i];
512 		else
513 			cn->sm_o_ihdl = ihdls[i];
514 		if (cn->sm_path == NULL) {
515 			cn->sm_path = kmem_alloc(strlen(devpath) + 1, KM_SLEEP);
516 			(void) strcpy(cn->sm_path, devpath);
517 		}
518 
519 	}
520 	kmem_free(devpath, MAXPATHLEN + 2);
521 
522 	return (0);
523 }
524 
525 /*
526  * Convert a file system path into a dev_t
527  */
528 static dev_t
fs_devtype(char * fspath)529 fs_devtype(char *fspath)
530 {
531 	vnode_t	*vp = NULL;
532 	dev_t	dev;
533 
534 	if (fspath == 0 ||
535 	    vn_open(fspath, UIO_SYSSPACE, FREAD, 0, &vp, 0, 0) != 0) {
536 		return (NODEV);
537 	} else {
538 		dev = vp->v_rdev;
539 		VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL);
540 		VN_RELE(vp);
541 		return (dev);
542 	}
543 }
544 
545 /*
546  * Convert a device tree path into a dev_t
547  */
548 static dev_t
di_devtype(char * path)549 di_devtype(char *path)
550 {
551 	dev_t   dev;
552 
553 	if (path == 0 || *path == 0)
554 		return (NODEV);
555 
556 	ttymux_dprintf(DPRINT_L0, "loading device %s\n", path);
557 	dev = ddi_pathname_to_dev_t(path);
558 
559 	return (dev);
560 }
561 
562 
563 static int
open_stream(vnode_t ** vp,int * fd,dev_t dev)564 open_stream(vnode_t **vp, int *fd, dev_t dev)
565 {
566 	file_t	*fp;
567 	int	rv;
568 
569 	/* create a vnode for the device and open it */
570 	*vp = makespecvp(dev, VCHR);
571 	if ((rv = VOP_OPEN(vp, FREAD+FWRITE+FNOCTTY, CRED(), NULL)) != 0) {
572 		goto out2;
573 	}
574 	/* Associate a file pointer with the vnode */
575 	if ((rv = falloc(*vp, FREAD+FWRITE+FNOCTTY, &fp, NULL)) != 0) {
576 		goto out1;
577 	}
578 	mutex_exit(&fp->f_tlock);	/* must be called single threaded */
579 	/* Allocate a file descriptor (any non-negative integer will suffice) */
580 	if ((*fd = ufalloc(0)) == -1) {
581 		rv = EMFILE;
582 		goto out1;
583 	}
584 	/* associate the file pointer with the fd */
585 	setf(*fd, fp);
586 	return (0);
587 
588 out1:
589 	VOP_CLOSE(*vp, FREAD+FWRITE+FNOCTTY, 1, (offset_t)0, CRED(), NULL);
590 out2:
591 	VN_RELE(*vp);
592 	return (rv);
593 }
594 
595 /*
596  * Plumb a device specified by the sm_console_t argument underneath the
597  * serial multiplexer indicated by the vnode_t argument.
598  */
599 static int
link_aconsole(vnode_t * mux_avp,sm_console_t * cn)600 link_aconsole(vnode_t *mux_avp, sm_console_t *cn)
601 {
602 	vnode_t		*lvp;
603 	int		lfd;
604 	int		rv, rval;
605 	ttymux_assoc_t	assoc;
606 	struct termios	tc;
607 
608 	ASSERT(cn->sm_path);
609 
610 	/* get an open vnode for the device */
611 	if ((rv = open_stream(&lvp, &lfd, cn->sm_dev)) != 0)
612 		return (rv);
613 
614 	/*
615 	 * Enable the receiver on the lower device since it will
616 	 * be used by OBP.
617 	 */
618 	if ((rv = ioctl_cmd(lvp, TCGETS, &tc, sizeof (tc), 0)) == 0) {
619 		tc.c_cflag |= CREAD;
620 		rv = ioctl_cmd(lvp, TCSETS, &tc, sizeof (tc), 0);
621 	}
622 	if (rv != 0)
623 		ttymux_dprintf(DPRINT_L3,
624 		    "DACF: Failed to enable console receiver [error %d]\n", rv);
625 
626 	/*
627 	 * Pop all the modules off the stream prior to linking it.
628 	 */
629 	do {
630 		rv = strioctl(lvp, I_POP, 0, 0, K_TO_K, CRED(), &rval);
631 	} while (rv == 0);
632 
633 	if (rv != EINVAL) {
634 		ttymux_dprintf(DPRINT_L3,
635 		    "Failed to pop all modules: error %d", rv);
636 		goto out;
637 	}
638 
639 	if ((rv = strioctl(mux_avp, I_PLINK, (intptr_t)lfd,
640 	    FREAD+FWRITE+FNOCTTY, K_TO_K, CRED(), &(cn->sm_muxid))) != 0) {
641 
642 		ttymux_dprintf(DPRINT_L3,
643 		    "Failed to link device: error %d", rv);
644 		goto out;
645 	}
646 	/* close the linked device */
647 	(void) closeandsetf(lfd, NULL);
648 	/*
649 	 * Now tell the mux to associate the new stream
650 	 */
651 	assoc.ttymux_udev = mux_avp->v_rdev;
652 	assoc.ttymux_ldev = cn->sm_dev;
653 	assoc.ttymux_linkid = cn->sm_muxid;
654 	assoc.ttymux_tag = 0;
655 	assoc.ttymux_ioflag = cn->sm_mode;
656 	if ((rv = ioctl_cmd(mux_avp, TTYMUX_ASSOC,
657 	    (void *)&assoc, sizeof (assoc), 0)) != 0) {
658 		ttymux_dprintf(DPRINT_L3,
659 		    "Failed to associate %d:%d with the console\n",
660 		    getmajor(cn->sm_dev), getminor(cn->sm_dev));
661 
662 		if (strioctl(mux_avp, I_PUNLINK, (intptr_t)cn->sm_muxid, 0,
663 		    K_TO_K, CRED(), &rval) != 0)
664 			ttymux_dprintf(DPRINT_L3,
665 			    "Can't unlink %d:%d - Closing vnode\n",
666 			    getmajor(cn->sm_dev), getminor(cn->sm_dev));
667 
668 	}
669 	return (rv);
670 
671 out:
672 	VOP_CLOSE(lvp, FREAD+FWRITE+FNOCTTY, 1, (offset_t)0, CRED(), NULL);
673 	VN_RELE(lvp);
674 	return (rv);
675 }
676 
677 static int
enable_aconsole(sm_mux_state_t * ms,sm_console_t * cn,vnode_t * muxvp)678 enable_aconsole(sm_mux_state_t *ms, sm_console_t *cn, vnode_t *muxvp)
679 {
680 	ttymux_assoc_t	assoc;
681 
682 	ASSERT(cn && cn->sm_dev != NODEV);
683 
684 	assoc.ttymux_ldev = cn->sm_dev;
685 
686 	cn->sm_muxid = (ioctl_cmd(muxvp, TTYMUX_GETLINK,
687 	    (void *)&assoc, sizeof (assoc), 0) == 0) ? assoc.ttymux_linkid : 0;
688 
689 	if (cn->sm_muxid != 0)
690 		return (0);	/* already linked */
691 	else
692 		return (link_aconsole(muxvp, cn));
693 }
694 
695 /*
696  * Enable all discovered consoles such that they can provide real I/O.
697  * The discovered list is stored in the sm_mux_state_t pointer.
698  */
699 static int
enable_all_consoles(sm_mux_state_t * ms,vnode_t * muxvp)700 enable_all_consoles(sm_mux_state_t *ms, vnode_t *muxvp)
701 {
702 	sm_console_t	*cn;
703 	uint_t		j;
704 
705 	ttymux_dprintf(DPRINT_L0, "Enable %d devices\n", ms->sm_cons_cnt);
706 	for (cn = ms->sm_cons_links, j = 0;
707 	    j < ms->sm_cons_cnt; cn++, j++) {
708 
709 		if (cn->sm_path == NULL)
710 			continue;
711 
712 		if ((strstr(cn->sm_path, "/dev") == cn->sm_path &&
713 		    (cn->sm_dev = fs_devtype(cn->sm_path)) == NODEV) ||
714 		    (cn->sm_dev = di_devtype(cn->sm_path)) == NODEV) {
715 
716 			ttymux_dprintf(DPRINT_L0,
717 			    "Cannot find a driver for device: %s\n",
718 			    cn->sm_path ? cn->sm_path : "");
719 			continue;
720 		}
721 		ttymux_dprintf(DPRINT_L0, "Enabling %d:%d\n",
722 		    getmajor(cn->sm_dev), getminor(cn->sm_dev));
723 
724 		/*
725 		 * Refuse requests to use devices as consoles which have an
726 		 * unsupported minor node type.
727 		 */
728 		if (compatible_console(cn->sm_dev) == B_FALSE)
729 			continue;
730 
731 		/*
732 		 * Enable a console device by linking the target console
733 		 * underneath the ttymux minor node that has been specified
734 		 * in a DACF reservation (see /etc/dacf.conf).
735 		 */
736 		(void) enable_aconsole(ms, cn, muxvp);
737 	}
738 	return (0);
739 }
740 
741 static int
find_consoles(sm_mux_state_t * ms,dev_info_t * dip,dev_t dev)742 find_consoles(sm_mux_state_t *ms, dev_info_t *dip, dev_t dev)
743 {
744 	int	len;
745 	char	*propval;
746 	char	devtype[32];
747 	pnode_t	node;
748 	uint_t	flags;
749 
750 	/*
751 	 * Look for target consoles based on options node properties
752 	 */
753 	node = prom_optionsnode();
754 	if ((len = read_prop(node, INPUT_ALIAS, &propval)) > 0) {
755 		parse(ms, propval, add_aconsole, (void *)FORINPUT);
756 		kmem_free(propval, len + 1);
757 	}
758 	if ((len = read_prop(node, OUTPUT_ALIAS, &propval)) > 0) {
759 		parse(ms, propval, add_aconsole, (void *)FOROUTPUT);
760 		kmem_free(propval, len + 1);
761 	}
762 
763 	/*
764 	 * Look for platform specific target consoles.
765 	 * Assume that they are OBP consoles and used for both input and output.
766 	 */
767 	flags = (uint_t)FORIO | OBPDEV;
768 	if ((propval = platform_consoles(ms, dip, dev, flags)) != NULL) {
769 		parse(ms, propval, add_aconsole, (void *)(uintptr_t)flags);
770 		kmem_free(propval, strlen(propval) + 1);
771 	}
772 
773 	/*
774 	 * Discover which consoles OBP is actually using according to
775 	 * interfaces proposed by case number FWARC/262.
776 	 */
777 	len = sizeof (devtype);
778 	if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF, 0,
779 	    "device_type", (caddr_t)devtype, &len) == DDI_PROP_SUCCESS &&
780 	    strcmp(devtype, "serial") == 0 &&
781 	    ddi_prop_exists(DDI_DEV_T_ANY, dip, 0, MUXDEVTYPE) == 1) {
782 
783 		(void) find_obp_consoles(ms, FORINPUT);
784 		(void) find_obp_consoles(ms, FOROUTPUT);
785 
786 	}
787 	ttymux_dprintf(DPRINT_L0, "%d consoles configured\n",
788 	    ms->sm_cons_cnt);
789 	return (ms->sm_cons_cnt);
790 }
791 
792 static int
validate_reservation(dacf_infohdl_t di,dev_info_t ** dip,dev_t * dev,io_mode_t * mode)793 validate_reservation(dacf_infohdl_t di, dev_info_t **dip, dev_t *dev,
794     io_mode_t *mode)
795 {
796 	char	*dname, *nname, *ipath, *opath;
797 
798 	if ((dname = (char *)dacf_driver_name(di)) == NULL)
799 		return (EINVAL);
800 
801 	if ((*dip = dacf_devinfo_node(di)) == NULL)
802 		return (EINVAL);
803 
804 	*dev = makedevice(ddi_driver_major(*dip), dacf_minor_number(di));
805 
806 	if (*dev == NODEV || *dip == NULL || strcmp(dname, TTYMUX_DRVNAME) != 0)
807 		return (EINVAL);
808 	else if (getminor(*dev) != (minor_t)0)
809 		return (EINVAL);	/* minor 0 is special */
810 	else if (rconsvp != NULL || space_fetch(TTYMUXPTR) != NULL)
811 		return (EAGAIN);	/* already configured */
812 
813 	opath = prom_stdoutpath();
814 	ipath = prom_stdinpath();
815 	nname = ddi_node_name(*dip);
816 	*mode = 0;
817 
818 	if (ipath != NULL && strstr(ipath, nname) != 0)
819 		*mode = FORINPUT;
820 	if (opath != NULL && strstr(opath, nname) != 0)
821 		*mode |= FOROUTPUT;
822 	if ((*mode & FORIO) == 0)
823 		return (EINVAL);
824 	if ((*mode & FOROUTPUT) == 0) {
825 		ttymux_dprintf(DPRINT_L3,
826 		    "Warning: multiplexer is not the output device\n");
827 	}
828 	if ((*mode & FORINPUT) == 0) {
829 		ttymux_dprintf(DPRINT_L3,
830 		    "Warning: multiplexer is not the input device\n");
831 	}
832 	return (0);
833 }
834 
835 /*
836  * This operation set is for configuring the ttymux driver for use as
837  * the system console.
838  * It must run before consconfig configures the Solaris console.
839  */
840 /*ARGSUSED*/
841 static int
ttymux_config(dacf_infohdl_t info_hdl,dacf_arghdl_t arg_hdl,int flags)842 ttymux_config(dacf_infohdl_t info_hdl, dacf_arghdl_t arg_hdl, int flags)
843 {
844 	sm_mux_state_t	*ms;
845 	io_mode_t	mode;
846 	dev_t		dev;
847 	dev_info_t	*dip;
848 	uint_t		i, icnt = 0, ocnt = 0;
849 	int		rv;
850 	vnode_t		*muxvp;
851 
852 	ttymux_dprintf(DPRINT_L0, "\n");
853 
854 	if ((rv = validate_reservation(info_hdl, &dip, &dev, &mode)) != 0) {
855 		ttymux_dprintf(DPRINT_L0, "reservation ignored (%d)\n", rv);
856 		return (DACF_SUCCESS);
857 	}
858 
859 	ms = kmem_zalloc(sizeof (*ms), KM_SLEEP);
860 
861 	mutex_init(&ms->sm_cons_mutex, NULL, MUTEX_DRIVER, NULL);
862 
863 	for (i = 0; i < TTYMUX_MAX_LINKS; i++)
864 		ms->sm_cons_links[i].sm_dev = NODEV;
865 
866 	ms->sm_cons_stdin.sm_dev = ms->sm_cons_stdout.sm_dev = NODEV;
867 	if (mode & FORINPUT)
868 		ms->sm_cons_stdin.sm_dev = dev;
869 	if (mode & FOROUTPUT)
870 		ms->sm_cons_stdout.sm_dev = dev;
871 	ms->sm_cons_stdin.sm_i_ihdl = prom_stdin_ihandle();
872 	ms->sm_cons_stdout.sm_o_ihdl = prom_stdout_ihandle();
873 
874 	if (prom_is_openprom()) {
875 		pnode_t	node = prom_optionsnode();
876 
877 		if (prom_getproplen(node, INPUT_ALIAS) > 0) {
878 			ms->sm_ialias = kmem_alloc(
879 			    strlen(INPUT_ALIAS) + 1, KM_SLEEP);
880 			(void) strcpy(ms->sm_ialias, INPUT_ALIAS);
881 		}
882 		if (prom_getproplen(node, OUTPUT_ALIAS) > 0) {
883 			ms->sm_oalias = kmem_alloc(
884 			    strlen(OUTPUT_ALIAS) + 1, KM_SLEEP);
885 			(void) strcpy(ms->sm_oalias, OUTPUT_ALIAS);
886 		}
887 	}
888 
889 	(void) find_consoles(ms, dip, dev);
890 	/* Store the console list for use by the ttymux driver */
891 	if (space_store(TTYMUXPTR, (uintptr_t)ms) != 0) {
892 		ttymux_dprintf(DPRINT_L3, "Named pointer error\n");
893 		if (ms->sm_ialias)
894 			kmem_free(ms->sm_ialias, strlen(ms->sm_ialias) + 1);
895 		if (ms->sm_oalias)
896 			kmem_free(ms->sm_oalias, strlen(ms->sm_oalias) + 1);
897 		for (i = 0; i < ms->sm_cons_cnt; i++) {
898 			sm_console_t *cn = &ms->sm_cons_links[i];
899 			if (cn->sm_path)
900 				kmem_free(cn->sm_path, strlen(cn->sm_path) + 1);
901 		}
902 		kmem_free(ms, sizeof (*ms));
903 		return (DACF_FAILURE);
904 	}
905 
906 	muxvp = dacf_makevp(info_hdl);
907 
908 	if ((rv = VOP_OPEN(&muxvp, OFLAGS, CRED(), NULL)) == 0) {
909 
910 		(void) enable_all_consoles(ms, muxvp);
911 		(void) usable_consoles(ms, &icnt, &ocnt);
912 
913 		VOP_CLOSE(muxvp, OFLAGS, 1, (offset_t)0, CRED(), NULL);
914 		VN_RELE(muxvp);
915 	} else {
916 		ttymux_dprintf(DPRINT_L3,
917 		    "Error %d opening the console device\n", rv);
918 		VN_RELE(muxvp);
919 		return (DACF_FAILURE);
920 	}
921 
922 	if (icnt == 0 && (mode & FORINPUT))
923 		ttymux_dprintf(DPRINT_L3, "No input consoles configured.\n");
924 	if (ocnt == 0 && (mode & FOROUTPUT))
925 		ttymux_dprintf(DPRINT_L3, "No output consoles configured.\n");
926 
927 	ttymux_dprintf(DPRINT_L0, "mux config complete\n");
928 
929 	return (DACF_SUCCESS);
930 }
931