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