xref: /titanic_52/usr/src/uts/sun4u/starfire/cvc/cvc.c (revision 3c112a2b34403220c06c3e2fcac403358cfba168)
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 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 
28 /*
29  * MT STREAMS Virtual Console Device Driver
30  */
31 
32 #include <sys/types.h>
33 #include <sys/sysmacros.h>
34 #include <sys/processor.h>
35 #include <sys/cpuvar.h>
36 #include <sys/open.h>
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/signal.h>
40 #include <sys/cred.h>
41 #include <sys/user.h>
42 #include <sys/proc.h>
43 #include <sys/vnode.h>
44 #include <sys/uio.h>
45 #include <sys/buf.h>
46 #include <sys/file.h>
47 #include <sys/kmem.h>
48 #include <sys/vmem.h>
49 #include <sys/stat.h>
50 #include <sys/stream.h>
51 #include <sys/stropts.h>
52 #include <sys/strsubr.h>
53 #include <sys/strsun.h>
54 #include <sys/tty.h>
55 #include <sys/ptyvar.h>
56 #include <sys/poll.h>
57 #include <sys/debug.h>
58 #include <sys/conf.h>
59 
60 #include <sys/starfire.h>
61 #include <sys/mman.h>
62 #include <vm/seg_kmem.h>
63 
64 #include <sys/ddi.h>
65 #include <sys/sunddi.h>
66 #include <sys/errno.h>
67 #include <sys/modctl.h>
68 #include <sys/cpu_sgnblk_defs.h>
69 #include <sys/cvc.h>
70 #include <sys/cpu_sgn.h>
71 
72 extern void	prom_printf(char *fmt, ...);
73 
74 static int	cvc_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
75 static int	cvc_attach(dev_info_t *, ddi_attach_cmd_t);
76 static int	cvc_detach(dev_info_t *, ddi_detach_cmd_t);
77 static int	cvc_open(register queue_t *, dev_t *, int, int, cred_t *);
78 static int	cvc_close(queue_t *, int, cred_t *);
79 static int	cvc_wput(queue_t *, mblk_t *);
80 static int	cvc_wsrv(queue_t *);
81 static void	cvc_ioctl(queue_t *, mblk_t *);
82 static void	cvc_ack(mblk_t *, mblk_t *, uint_t);
83 static void	cvc_reioctl(void *);
84 static void	cvc_input_daemon(void);
85 static void	cvc_putc(register int);
86 static void	cvc_flush_buf(void *);
87 static void	cvc_bbsram_ops(volatile uchar_t *);
88 
89 static caddr_t	cvc_iobuf_mapin(processorid_t);
90 static void	cvc_iobuf_mapout(processorid_t);
91 	void	cvc_assign_iocpu(processorid_t);
92 
93 /*
94  * Private copy of devinfo pointer; cvc_info uses it.
95  */
96 static dev_info_t	*cvcdip;
97 
98 /*
99  * This buffer is used to manage mapping in the I/O buffer that CVC
100  * uses when communicating with the SSP Client (netcon_server) via bbsram.
101  */
102 static caddr_t	cvc_iobufp[NCPU];
103 
104 typedef struct cvc_s {
105 	bufcall_id_t	cvc_wbufcid;
106 	tty_common_t	cvc_tty;
107 } cvc_t;
108 
109 cvc_t	cvc_common_tty;
110 
111 static struct module_info cvcm_info = {
112 	1313,		/* mi_idnum Bad luck number  ;-) */
113 	"cvc",		/* mi_idname */
114 	0,		/* mi_minpsz */
115 	INFPSZ,		/* mi_maxpsz */
116 	2048,		/* mi_hiwat */
117 	2048		/* mi_lowat */
118 };
119 
120 static struct qinit cvcrinit = {
121 	NULL,		/* qi_putp */
122 	NULL,		/* qi_srvp */
123 	cvc_open,	/* qi_qopen */
124 	cvc_close,	/* qi_qclose */
125 	NULL,		/* qi_qadmin */
126 	&cvcm_info,	/* qi_minfo */
127 	NULL		/* qi_mstat */
128 };
129 
130 static struct qinit cvcwinit = {
131 	cvc_wput,	/* qi_putp */
132 	cvc_wsrv,	/* qi_srvp */
133 	cvc_open,	/* qi_qopen */
134 	cvc_close,	/* qi_qclose */
135 	NULL,		/* qi_qadmin */
136 	&cvcm_info,	/* qi_minfo */
137 	NULL		/* qi_mstat */
138 };
139 
140 struct streamtab	cvcinfo = {
141 	&cvcrinit,	/* st_rdinit */
142 	&cvcwinit,	/* st_wrinit */
143 	NULL,		/* st_muxrinit */
144 	NULL		/* st_muxwrinit */
145 };
146 
147 #define	TIMEOUT_DELAY		100000
148 
149 #define	BBSRAM_INPUT_BUF	((volatile char *)(cvc_iobufp[cvc_iocpu] \
150 					+ BBSRAM_INPUT_COUNT_OFF))
151 
152 #define	BBSRAM_OUTPUT_BUF	((volatile char *)(cvc_iobufp[cvc_iocpu] \
153 					+ BBSRAM_OUTPUT_COUNT_OFF))
154 
155 #define	BBSRAM_INPUT_COUNT	(*((volatile short *)BBSRAM_INPUT_BUF))
156 
157 #define	BBSRAM_OUTPUT_COUNT	(*((volatile short *)BBSRAM_OUTPUT_BUF))
158 
159 #define	CVC_OUT_MAXSPIN	1024
160 
161 /* The bbsram control reg is located at the end of the I/O buffers */
162 #define	BBSRAM_CONTROL_REG	((volatile uchar_t *)(cvc_iobufp[cvc_iocpu] \
163 					+ CVC_IN_SIZE + CVC_OUT_SIZE))
164 
165 static krwlock_t	cvclock;	/* lock protecting everything here */
166 static queue_t		*cvcinput_q;	/* queue for console input */
167 static queue_t		*cvcoutput_q;	/* queue for console output */
168 static int		cvc_instance = -1;
169 static int		cvc_stopped = 0;
170 static int		cvc_suspended = 0;
171 static int		cvc_hangup_ok = 0;
172 
173 static kthread_id_t	cvc_input_daemon_thread;
174 static kmutex_t		cvcmutex;	/* protects input */
175 static kmutex_t		cvc_buf_mutex;	/* protects internal output buffer */
176 static kmutex_t		cvc_bbsram_input_mutex; /* protects BBSRAM inp buff */
177 static int		input_ok = 0;	/* true when stream is valid */
178 static int		stop_bbsram = 1; /* true when BBSRAM is not usable */
179 static int		stop_timeout = 0;
180 static uchar_t		cvc_output_buffer[MAX_XFER_OUTPUT]; /* output buffer */
181 static ushort_t		cvc_output_count = 0;
182 static int		via_bbsram = 0; /* toggle switch */
183 static timeout_id_t	cvc_timeout_id = (timeout_id_t)-1;
184 static processorid_t	cvc_iocpu = -1;	/* cpu id of cpu zero */
185 
186 /*
187  * Module linkage information for the kernel.
188  */
189 
190 DDI_DEFINE_STREAM_OPS(cvcops, nulldev, nulldev, cvc_attach, cvc_detach,
191 		    nodev, cvc_info, (D_MTPERQ | D_MP), &cvcinfo,
192 		    ddi_quiesce_not_supported);
193 
194 static struct modldrv modldrv = {
195 	&mod_driverops, /* Type of module.  This one is a pseudo driver */
196 	"CVC driver 'cvc'",
197 	&cvcops,	/* driver ops */
198 };
199 
200 static struct modlinkage modlinkage = {
201 	MODREV_1,
202 	&modldrv,
203 	NULL
204 };
205 
206 int
207 _init(void)
208 {
209 	int	status;
210 
211 	status = mod_install(&modlinkage);
212 	if (status == 0) {
213 		mutex_init(&cvcmutex, NULL, MUTEX_DEFAULT, NULL);
214 	}
215 	return (status);
216 }
217 
218 int
219 _fini(void)
220 {
221 	return (EBUSY);
222 }
223 
224 int
225 _info(struct modinfo *modinfop)
226 {
227 	return (mod_info(&modlinkage, modinfop));
228 }
229 
230 /*
231  * DDI glue routines.
232  */
233 
234 /* ARGSUSED */
235 static int
236 cvc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
237 {
238 	static char	been_here = 0;
239 
240 	if (cmd == DDI_RESUME) {
241 		cvc_suspended = 0;
242 		return (DDI_SUCCESS);
243 	}
244 
245 	mutex_enter(&cvcmutex);
246 	if (!been_here) {
247 		been_here = 1;
248 		mutex_init(&cvc_buf_mutex, NULL, MUTEX_DEFAULT, NULL);
249 		mutex_init(&cvc_bbsram_input_mutex, NULL, MUTEX_DEFAULT, NULL);
250 		rw_init(&cvclock, NULL, RW_DRIVER, NULL);
251 		rw_enter(&cvclock, RW_WRITER);
252 		cvc_timeout_id = timeout(cvc_flush_buf, NULL,
253 		    drv_usectohz(TIMEOUT_DELAY));
254 		rw_exit(&cvclock);
255 		cvc_instance = ddi_get_instance(devi);
256 	} else {
257 #if defined(DEBUG)
258 		cmn_err(CE_NOTE,
259 		    "cvc_attach: called multiple times!! (instance = %d)",
260 		    ddi_get_instance(devi));
261 #endif /* DEBUG */
262 		return (DDI_SUCCESS);
263 	}
264 	mutex_exit(&cvcmutex);
265 
266 	if (ddi_create_minor_node(devi, "cvc", S_IFCHR,
267 	    0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
268 		ddi_remove_minor_node(devi, NULL);
269 		return (-1);
270 	}
271 	cvcdip = devi;
272 	cvcinput_q = NULL;
273 	cvcoutput_q = NULL;
274 	return (DDI_SUCCESS);
275 }
276 
277 static int
278 cvc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
279 {
280 	if (cmd == DDI_SUSPEND) {
281 		cvc_suspended = 1;
282 	} else {
283 		if (cmd != DDI_DETACH) {
284 			return (DDI_FAILURE);
285 		}
286 		/*
287 		 * XXX this doesn't even begin to address the detach
288 		 * issues - it doesn't terminate the outstanding thread,
289 		 * it doesn't clean up mutexes, kill the timeout routine
290 		 * etc.
291 		 */
292 		if (cvc_instance == ddi_get_instance(dip)) {
293 			ddi_remove_minor_node(dip, NULL);
294 		}
295 	}
296 	return (DDI_SUCCESS);
297 }
298 
299 /* ARGSUSED */
300 static int
301 cvc_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
302 {
303 	register int error;
304 
305 	switch (infocmd) {
306 	case DDI_INFO_DEVT2DEVINFO:
307 		if (cvcdip == NULL) {
308 			error = DDI_FAILURE;
309 		} else {
310 			*result = (void *)cvcdip;
311 			error = DDI_SUCCESS;
312 		}
313 		break;
314 	case DDI_INFO_DEVT2INSTANCE:
315 		*result = (void *)0;
316 		error = DDI_SUCCESS;
317 		break;
318 	default:
319 		error = DDI_FAILURE;
320 	}
321 	return (error);
322 }
323 
324 /* ARGSUSED */
325 static int
326 cvc_open(register queue_t *q, dev_t *devp, int flag, int sflag, cred_t *crp)
327 {
328 	register int		unit = getminor(*devp);
329 	register int		err = 0;
330 	tty_common_t		*tty;
331 	cvc_t			*cp;
332 	static int		input_daemon_started;
333 
334 	if (unit != 0)
335 		return (ENXIO);
336 
337 	if (q->q_ptr)
338 		return (0);
339 
340 	cp = (cvc_t *)&cvc_common_tty;
341 	bzero((caddr_t)cp, sizeof (cvc_t));
342 	cp->cvc_wbufcid = 0;
343 	tty = &cp->cvc_tty;
344 	tty->t_readq = q;
345 	tty->t_writeq = WR(q);
346 	WR(q)->q_ptr = q->q_ptr = (caddr_t)cp;
347 	cvcinput_q = RD(q);		/* save for cvc_redir */
348 	qprocson(q);
349 	mutex_enter(&cvcmutex);
350 	input_ok = 1;
351 	if (!input_daemon_started) {
352 		extern struct cpu	*SIGBCPU;	/* bugid4141050 */
353 		extern cpu_sgnblk_t	*cpu_sgnblkp[];
354 
355 		input_daemon_started = 1;
356 		mutex_exit(&cvcmutex);
357 
358 		ASSERT(cpu_sgnblkp[SIGBCPU->cpu_id] != NULL);
359 		cvc_assign_iocpu(SIGBCPU->cpu_id);
360 
361 		cvc_input_daemon_thread = thread_create(NULL, 0,
362 		    cvc_input_daemon, NULL, 0, &p0, TS_RUN, minclsyspri);
363 	} else {
364 		mutex_exit(&cvcmutex);
365 	}
366 #ifdef lint
367 	cvc_input_daemon_thread = cvc_input_daemon_thread;
368 #endif
369 	return (err);
370 }
371 
372 /* ARGSUSED */
373 static int
374 cvc_close(queue_t *q, int flag, cred_t *crp)
375 {
376 	register int		err = 0;
377 	register cvc_t		*cp;
378 
379 	mutex_enter(&cvcmutex);
380 	input_ok = 0;
381 	mutex_exit(&cvcmutex);
382 
383 	cp = q->q_ptr;
384 	if (cp->cvc_wbufcid != 0) {
385 		unbufcall(cp->cvc_wbufcid);
386 	}
387 	ttycommon_close(&cp->cvc_tty);
388 	WR(q)->q_ptr = q->q_ptr = NULL;
389 	cvcinput_q = NULL;
390 	bzero((caddr_t)cp, sizeof (cvc_t));
391 	qprocsoff(q);
392 	return (err);
393 }
394 
395 
396 /*
397  * cvc_wput()
398  *	cn driver does a strwrite of console output data to rconsvp which
399  *	has been set by consconfig. The data enters the cvc stream at the
400  *	streamhead and flows thru ttycompat and ldterm which have been
401  *	pushed on the stream.  Console output data gets sent out either
402  *	by cvcredir (if there is a cvcd running) or bbsram (if there
403  *	isn't).
404  *	Data is sent to the cvcredir via it's read q which is cvcoutput_q
405  *	and was set in cvc_register().
406  */
407 static int
408 cvc_wput(register queue_t *q, register mblk_t *mp)
409 {
410 	int		error = 0;
411 
412 	rw_enter(&cvclock, RW_READER);
413 	switch (mp->b_datap->db_type) {
414 
415 		case M_IOCTL:
416 		case M_CTL:
417 			cvc_ioctl(q, mp);
418 			break;
419 
420 		case M_FLUSH:
421 			if (*mp->b_rptr & FLUSHW) {
422 				/*
423 				 * Flush our write queue.
424 				 */
425 				flushq(q, FLUSHDATA);
426 				*mp->b_rptr &= ~FLUSHW;
427 			}
428 			if (*mp->b_rptr & FLUSHR) {
429 				flushq(RD(q), FLUSHDATA);
430 				qreply(q, mp);
431 			} else
432 				freemsg(mp);
433 			break;
434 
435 		case M_STOP:
436 			cvc_stopped = 1;
437 			freemsg(mp);
438 			break;
439 
440 		case M_START:
441 			cvc_stopped = 0;
442 			freemsg(mp);
443 			qenable(q);  /* Start up delayed messages */
444 			break;
445 
446 		case M_READ:
447 			/*
448 			 * ldterm handles this (VMIN/VTIME processing).
449 			 */
450 			freemsg(mp);
451 			break;
452 		default:
453 			cmn_err(CE_WARN, "cvc_wput: illegal mblk = 0x%p",
454 			    (void *)mp);
455 			cmn_err(CE_WARN, "cvc_wput: type = 0x%x",
456 			    mp->b_datap->db_type);
457 			/* FALLTHROUGH */
458 #ifdef lint
459 			break;
460 #endif
461 
462 		case M_DATA:
463 			if (cvc_stopped == 1 || cvc_suspended == 1) {
464 				(void) putq(q, mp);
465 				break;
466 			}
467 			if (cvcoutput_q != NULL && !via_bbsram) {
468 				/*
469 				 * Send it up past cvcredir module.
470 				 */
471 				putnext(cvcoutput_q, mp);
472 			} else {
473 				char	*msgp, c;
474 				mblk_t	*mp2 = mp;
475 				int count;
476 
477 				while (mp2 != NULL) {
478 					count = mp2->b_wptr - mp2->b_rptr;
479 					msgp = (char *)mp2->b_rptr;
480 					while (count > 0) {
481 						count--;
482 						if ((c = *msgp++) != '\0') {
483 							/* don't print NULs */
484 							cvc_putc(c);
485 						}
486 					}
487 					mp2 = mp2->b_cont;
488 				}
489 				freemsg(mp);
490 			}
491 			break;
492 
493 	}
494 	rw_exit(&cvclock);
495 	return (error);
496 }
497 
498 static int cvc_wsrv_count = 0;
499 
500 static int
501 cvc_wsrv(queue_t *q)
502 {
503 	register mblk_t *mp;
504 
505 	cvc_wsrv_count++;
506 
507 	if (cvc_stopped == 1 || cvc_suspended == 1) {
508 		return (0);
509 	}
510 
511 	rw_enter(&cvclock, RW_READER);
512 	while ((mp = getq(q)) != NULL) {
513 		if (cvcoutput_q != NULL && !via_bbsram) {
514 			/*
515 			 * Send it up past cvcredir module.
516 			 */
517 			putnext(cvcoutput_q, mp);
518 		} else {
519 			char    *msgp, c;
520 			mblk_t  *mp2 = mp;
521 			int count;
522 
523 			while (mp2 != NULL) {
524 				count = mp2->b_wptr - mp2->b_rptr;
525 				msgp = (char *)mp2->b_rptr;
526 				while (count > 0) {
527 					count--;
528 					if ((c = *msgp++) != '\0') {
529 						/* don't print NULs */
530 						cvc_putc(c);
531 					}
532 				}
533 				mp2 = mp2->b_cont;
534 			}
535 			freemsg(mp);
536 		}
537 	}
538 	rw_exit(&cvclock);
539 	return (0);
540 }
541 
542 
543 /*
544  * cvc_ioctl()
545  *	handle normal console ioctls.
546  */
547 static void
548 cvc_ioctl(register queue_t *q, register mblk_t *mp)
549 {
550 	register struct iocblk		*iocp;
551 	register tty_common_t		*tty;
552 	register cvc_t			*cp;
553 	int				datasize;
554 	int				error = 0;
555 	mblk_t				*tmp;
556 
557 	cp = q->q_ptr;
558 	tty = &cp->cvc_tty;
559 	if (tty->t_iocpending != NULL) {
560 		freemsg(tty->t_iocpending);
561 		tty->t_iocpending = NULL;
562 	}
563 	datasize = ttycommon_ioctl(tty, q, mp, &error);
564 	if (datasize != 0) {
565 		if (cp->cvc_wbufcid)
566 			unbufcall(cp->cvc_wbufcid);
567 		cp->cvc_wbufcid = bufcall(datasize, BPRI_HI, cvc_reioctl, cp);
568 		return;
569 	}
570 	if (error < 0) {
571 		iocp = (struct iocblk *)mp->b_rptr;
572 		/*
573 		 * "ttycommon_ioctl" didn't do anything; we process it here.
574 		 */
575 		error = 0;
576 		switch (iocp->ioc_cmd) {
577 
578 		/*
579 		 *  Set modem bit ioctls.  These are NOPs for us, since we
580 		 * dont control any hardware.
581 		 */
582 		case TCSBRK:
583 		case TIOCSBRK:
584 		case TIOCCBRK:
585 		case TIOCMSET:
586 		case TIOCMBIS:
587 		case TIOCMBIC:
588 			if (iocp->ioc_count != TRANSPARENT) {
589 				mioc2ack(mp, NULL, 0, 0);
590 			} else {
591 				mcopyin(mp, NULL, sizeof (int), NULL);
592 			}
593 			/* qreply done below */
594 			break;
595 
596 		/*
597 		 *  Get modem bits, we return 0 in mblk.
598 		 */
599 		case TIOCMGET:
600 			tmp = allocb(sizeof (int), BPRI_MED);
601 			if (tmp == NULL) {
602 				miocnak(q, mp, 0, EAGAIN);
603 				return;
604 			}
605 			*(int *)tmp->b_rptr = 0;
606 
607 			if (iocp->ioc_count != TRANSPARENT)
608 				mioc2ack(mp, tmp, sizeof (int), 0);
609 			else
610 				mcopyout(mp, NULL, sizeof (int), NULL, tmp);
611 			/* qreply done below */
612 			break;
613 
614 		default:
615 			/*
616 			 * If we don't understand it, it's an error. NAK it.
617 			 */
618 			error = EINVAL;
619 			break;
620 		}
621 	}
622 	if (error != 0) {
623 		iocp->ioc_error = error;
624 		mp->b_datap->db_type = M_IOCNAK;
625 	}
626 	qreply(q, mp);
627 
628 }
629 
630 
631 /*
632  * cvc_redir()
633  *	called from cvcredir:cvcr_wput() to handle console input
634  *	data. This routine puts the cvcredir write (downstream) data
635  *	onto the cvc read (upstream) queues.  Note that if `mp' is
636  *	an M_IOCTL, then it may be reused by the caller to send back
637  *	an M_IOCACK or M_IOCNAK.
638  */
639 int
640 cvc_redir(mblk_t *mp)
641 {
642 	register struct iocblk	*iocp;
643 	register tty_common_t	*tty;
644 	register cvc_t		*cp;
645 	struct winsize		*ws;
646 	int			error;
647 
648 	if (cvcinput_q == NULL) {
649 		cmn_err(CE_WARN, "cvc_redir: cvcinput_q NULL!");
650 		return (EINVAL);
651 	}
652 
653 	if (DB_TYPE(mp) != M_IOCTL) {
654 		putnext(cvcinput_q, mp);
655 		return (0);
656 	}
657 
658 	iocp = (struct iocblk *)mp->b_rptr;
659 	if (iocp->ioc_cmd == TIOCSWINSZ) {
660 		error = miocpullup(mp, sizeof (struct winsize));
661 		if (error != 0)
662 			return (error);
663 
664 		ws = (struct winsize *)mp->b_cont->b_rptr;
665 		cp = cvcinput_q->q_ptr;
666 		tty = &cp->cvc_tty;
667 		mutex_enter(&tty->t_excl);
668 		if (bcmp(&tty->t_size, ws, sizeof (struct winsize)) != 0) {
669 			tty->t_size = *ws;
670 			mutex_exit(&tty->t_excl);
671 			(void) putnextctl1(cvcinput_q, M_PCSIG, SIGWINCH);
672 		} else
673 			mutex_exit(&tty->t_excl);
674 	} else {
675 		/*
676 		 * It must be a CVC_DISCONNECT, send hangup.
677 		 */
678 		ASSERT(iocp->ioc_cmd == CVC_DISCONNECT);
679 		if (cvc_hangup_ok)
680 			(void) putnextctl(cvcinput_q, M_HANGUP);
681 	}
682 
683 	return (0);
684 }
685 
686 
687 /*
688  * cvc_register()
689  *	called from cvcredir to register it's queues.  cvc
690  *	receives data from cn via the streamhead and sends it to cvcredir
691  *	via pointers to cvcredir's queues.
692  */
693 int
694 cvc_register(queue_t *q)
695 {
696 	int error = -1;
697 
698 	if (cvcinput_q == NULL)
699 		cmn_err(CE_WARN, "cvc_register: register w/ no console open!");
700 	rw_enter(&cvclock, RW_WRITER);
701 	if (cvcoutput_q == NULL) {
702 		cvcoutput_q = RD(q);  /* Make sure its the upstream q */
703 		qprocson(cvcoutput_q);	/* must be done within cvclock */
704 		error = 0;
705 	} else {
706 		/*
707 		 * cmn_err will call us, so release lock.
708 		 */
709 		rw_exit(&cvclock);
710 		if (cvcoutput_q == q)
711 			cmn_err(CE_WARN, "cvc_register: duplicate q!");
712 		else
713 			cmn_err(CE_WARN, "cvc_register: nondup q = 0x%p",
714 			    (void *)q);
715 		return (error);
716 	}
717 
718 	/*
719 	 * Unless "via_bbsram" is set, i/o will be going through cvcd, so
720 	 * stop flushing output to BBSRAM.
721 	 */
722 	if ((cvc_timeout_id != (timeout_id_t)-1) && (!via_bbsram)) {
723 		stop_timeout = 1;
724 		(void) untimeout(cvc_timeout_id);
725 		cvc_timeout_id = (timeout_id_t)-1;
726 		cvc_hangup_ok = 1;
727 	}
728 	rw_exit(&cvclock);
729 	return (error);
730 }
731 
732 
733 /*
734  * cvc_unregister()
735  *	called from cvcredir to clear pointers to its queues.
736  *	cvcredir no longer wants to send or receive data.
737  */
738 void
739 cvc_unregister(queue_t *q)
740 {
741 	rw_enter(&cvclock, RW_WRITER);
742 	if (q == cvcoutput_q) {
743 		qprocsoff(cvcoutput_q);	/* must be done within cvclock */
744 		cvcoutput_q = NULL;
745 	} else {
746 		rw_exit(&cvclock);
747 		cmn_err(CE_WARN, "cvc_unregister: q = 0x%p not registered",
748 		    (void *)q);
749 		return;
750 	}
751 
752 	/*
753 	 * i/o will not be going through cvcd, start flushing output to
754 	 * BBSRAM
755 	 */
756 	if (cvc_timeout_id == (timeout_id_t)-1) {
757 		stop_timeout = 0;
758 		cvc_timeout_id = timeout(cvc_flush_buf, NULL,
759 		    drv_usectohz(TIMEOUT_DELAY));
760 	}
761 	rw_exit(&cvclock);
762 }
763 
764 /*
765  * cvc_reioctl()
766  *	Retry an "ioctl", now that "bufcall" claims we may be able
767  *	to allocate the buffer we need.
768  */
769 static void
770 cvc_reioctl(void *unit)
771 {
772 	register queue_t	*q;
773 	register mblk_t		*mp;
774 	register cvc_t		*cp = (cvc_t *)unit;
775 
776 	/*
777 	 * The bufcall is no longer pending.
778 	 */
779 	if (!cp->cvc_wbufcid) {
780 		return;
781 	}
782 	cp->cvc_wbufcid = 0;
783 	if ((q = cp->cvc_tty.t_writeq) == NULL) {
784 		return;
785 	}
786 	if ((mp = cp->cvc_tty.t_iocpending) != NULL) {
787 		/* not pending any more */
788 		cp->cvc_tty.t_iocpending = NULL;
789 		cvc_ioctl(q, mp);
790 	}
791 }
792 
793 
794 /*
795  * cvc_bbsram_ops()
796  *	Process commands sent to cvc from netcon_server via BBSRAM
797  */
798 static void
799 cvc_bbsram_ops(volatile unsigned char *op_reg)
800 {
801 	uchar_t	 op;
802 
803 	if ((op = *op_reg) == 0)
804 		return;
805 
806 	ASSERT(MUTEX_HELD(&cvc_bbsram_input_mutex));
807 
808 	switch (op) {
809 	case CVC_BBSRAM_BREAK:		/* A console break (L1-A) */
810 		abort_sequence_enter((char *)NULL);
811 		break;
812 	case CVC_BBSRAM_DISCONNECT:	/* Break connection, hang up */
813 		if (cvcinput_q && cvc_hangup_ok)
814 			(void) putnextctl(cvcinput_q, M_HANGUP);
815 		break;
816 	case CVC_BBSRAM_VIA_NET:	/* console via network */
817 		via_bbsram = 0;
818 		/*
819 		 * stop periodic flushing of output to BBSRAM
820 		 * only if cvcredir/cvcd are present
821 		 */
822 		rw_enter(&cvclock, RW_WRITER);
823 		if (cvcoutput_q != NULL) {
824 			stop_timeout = 1;
825 			if (cvc_timeout_id != (timeout_id_t)-1) {
826 				(void) untimeout(cvc_timeout_id);
827 				cvc_timeout_id = (timeout_id_t)-1;
828 			}
829 		}
830 		rw_exit(&cvclock);
831 		break;
832 	case CVC_BBSRAM_VIA_BBSRAM:	/* console via bbsram */
833 		via_bbsram = 1;
834 		/* start periodic flushing of ouput to BBSRAM */
835 		rw_enter(&cvclock, RW_WRITER);
836 		if (cvc_timeout_id == (timeout_id_t)-1) {
837 			stop_timeout = 0;
838 			cvc_timeout_id = timeout(cvc_flush_buf,
839 			    NULL, drv_usectohz(TIMEOUT_DELAY));
840 		}
841 		rw_exit(&cvclock);
842 		break;
843 	case CVC_BBSRAM_CLOSE_NET:
844 		/*
845 		 * Send a hangup control message upstream to cvcd
846 		 * thru cvcredir.  This is an attempt to close
847 		 * out any existing network connection(if any).
848 		 * cvcoutput_q should point to the cvcredir's read
849 		 * queue.
850 		 */
851 		rw_enter(&cvclock, RW_READER);
852 		if (cvcoutput_q != NULL) {
853 			(void) putnextctl(cvcoutput_q, M_HANGUP);
854 		}
855 		rw_exit(&cvclock);
856 		break;
857 	default:
858 		cmn_err(CE_WARN, "cvc: unknown BBSRAM opcode %d\n",
859 		    (unsigned int)op);
860 		break;
861 	}
862 	*op_reg = 0;
863 }
864 
865 
866 /*
867  * cvc_putc()
868  *	Put a single character out to BBSRAM if space available.
869  */
870 static void
871 cvc_putc(register int c)
872 {
873 	static int	output_lost = 0;
874 
875 	if (c == '\n')
876 		cvc_putc('\r');
877 
878 	mutex_enter(&cvc_buf_mutex);
879 	/*
880 	 * Just exit if the buffer is already full.
881 	 * It will be up to cvc_flush_buf() to flush the buffer.
882 	 */
883 	if (cvc_output_count == MAX_XFER_OUTPUT) {
884 		output_lost = 1;
885 		mutex_exit(&cvc_buf_mutex);
886 		return;
887 	}
888 	if (output_lost)
889 		prom_printf("WARNING: overflow of cvc output buffer, "
890 		    "output lost!");
891 	output_lost = 0;
892 	cvc_output_buffer[cvc_output_count] = (unsigned char)c;
893 	cvc_output_count++;
894 	if ((cvc_output_count == MAX_XFER_OUTPUT) || (c == '\n')) {
895 		/* flush cvc's internal output buffer to BBSRAM */
896 
897 		/*
898 		 * Wait for the BBSRAM output buffer to be emptied.
899 		 * This may hang if netcon_server isn't running on the SSP
900 		 */
901 		int maxspin = CVC_OUT_MAXSPIN;
902 		while ((BBSRAM_OUTPUT_COUNT != 0) && --maxspin) {
903 			if (stop_bbsram) {
904 				mutex_exit(&cvc_buf_mutex);
905 				return;
906 			}
907 			DELAY(1000);
908 		}
909 		bcopy((caddr_t)cvc_output_buffer,
910 		    (caddr_t)(BBSRAM_OUTPUT_BUF - cvc_output_count),
911 		    cvc_output_count);
912 
913 		BBSRAM_OUTPUT_COUNT = cvc_output_count;
914 		cvc_output_count = 0;
915 	}
916 	mutex_exit(&cvc_buf_mutex);
917 }
918 
919 
920 /*
921  * cvc_flush_buf()
922  *	Flush cvc's internal output buffer to BBSRAM at regular intervals.
923  *	This should only be done if cvcd is not running or the user (via the cvc
924  *	application on the SSP) has requested that i/o go through BBSRAM.
925  */
926 /* ARGSUSED */
927 static void
928 cvc_flush_buf(void *notused)
929 {
930 	if (stop_timeout)
931 		return;
932 
933 	mutex_enter(&cvc_buf_mutex);
934 	if (cvc_output_count != 0) {
935 		/*
936 		 * Wait for the BBSRAM output buffer to be emptied.
937 		 * This may hang if netcon_server isn't running on the SSP.
938 		 */
939 		int maxspin = CVC_OUT_MAXSPIN;
940 		while ((BBSRAM_OUTPUT_COUNT != 0) && --maxspin) {
941 			if (stop_bbsram)
942 				goto exit;
943 			DELAY(1000);
944 		}
945 
946 		bcopy((caddr_t)cvc_output_buffer,
947 		    (caddr_t)BBSRAM_OUTPUT_BUF - cvc_output_count,
948 		    cvc_output_count);
949 
950 		BBSRAM_OUTPUT_COUNT = cvc_output_count;
951 		cvc_output_count = 0;
952 	}
953 exit:
954 	mutex_exit(&cvc_buf_mutex);
955 	/* rw_enter(&cvclock, RW_WRITER); */
956 	cvc_timeout_id = timeout(cvc_flush_buf, NULL,
957 	    drv_usectohz(TIMEOUT_DELAY));
958 	/* rw_exit(&cvclock); */
959 }
960 
961 
962 /*
963  * cvc_getstr()
964  *	Poll BBSRAM for console input while available.
965  */
966 static void
967 cvc_getstr(char *cp)
968 {
969 	short		count;
970 	volatile char	*lp;
971 
972 	mutex_enter(&cvc_bbsram_input_mutex);
973 	/* Poll BBSRAM for input */
974 	do {
975 		if (stop_bbsram) {
976 			*cp = '\0';	/* set string to zero-length */
977 			mutex_exit(&cvc_bbsram_input_mutex);
978 			return;
979 		}
980 		/*
981 		 * Use a smaller delay between checks of BBSRAM for input
982 		 * when cvcd/cvcredir are not running or "via_bbsram" has
983 		 * been set.
984 		 * We don't go away completely when i/o is going through the
985 		 * network via cvcd since a command may be sent via BBSRAM
986 		 * to switch if the network is down or hung.
987 		 */
988 		if ((cvcoutput_q == NULL) || (via_bbsram))
989 			delay(drv_usectohz(100000));
990 		else
991 			delay(drv_usectohz(1000000));
992 		cvc_bbsram_ops(BBSRAM_CONTROL_REG);
993 		count = BBSRAM_INPUT_COUNT;
994 	} while (count == 0);
995 
996 	lp = BBSRAM_INPUT_BUF - count;
997 
998 	while (count--) {
999 		*cp++ = *lp++;
1000 	}
1001 	*cp = '\0';
1002 
1003 	BBSRAM_INPUT_COUNT = 0;
1004 	mutex_exit(&cvc_bbsram_input_mutex);
1005 }
1006 
1007 
1008 /*
1009  * cvc_input_daemon()
1010  *	this function runs as a separate kernel thread and polls BBSRAM for
1011  *	input, and possibly put it on read stream for the console.
1012  *	There are two poll rates (implemented in cvc_getstr):
1013  *		 100 000 uS (10 Hz) - no cvcd communications || via_bbsram
1014  *		1000 000 uS ( 1 Hz) - cvcd communications
1015  * 	This continues to run even if there are network console communications
1016  *	in order to handle out-of-band signaling.
1017  */
1018 static void
1019 cvc_input_daemon(void)
1020 {
1021 	char		linebuf[MAX_XFER_INPUT];
1022 	char		*cp;
1023 	mblk_t		*mbp;
1024 	int		c;
1025 	int		dropped_read = 0;
1026 
1027 	for (;;) {
1028 		cvc_getstr(linebuf);
1029 
1030 		mbp = allocb(strlen(linebuf), BPRI_MED);
1031 		if (mbp == NULL) {	/* drop it & go on if no buffer */
1032 			if (!dropped_read) {
1033 				cmn_err(CE_WARN,
1034 				    "cvc_input_daemon: "
1035 				    "dropping BBSRAM reads\n");
1036 			}
1037 			dropped_read++;
1038 			continue;
1039 		}
1040 		if (dropped_read) {
1041 			cmn_err(CE_WARN,
1042 			    "cvc_input_daemon: dropped %d BBSRAM reads\n",
1043 			    dropped_read);
1044 			dropped_read = 0;
1045 		}
1046 
1047 		for (cp = linebuf; *cp != '\0'; cp++) {
1048 			c = (int)*cp;
1049 			if (c == '\r')
1050 				c = '\n';
1051 			c &= 0177;
1052 			*mbp->b_wptr = (char)c;
1053 			mbp->b_wptr++;
1054 		}
1055 		mutex_enter(&cvcmutex);
1056 		if (input_ok) {
1057 			if (cvcinput_q == NULL) {
1058 				cmn_err(CE_WARN,
1059 				    "cvc_input_daemon: cvcinput_q is NULL!");
1060 			} else {
1061 				putnext(cvcinput_q, mbp);
1062 			}
1063 		} else {
1064 			freemsg(mbp);
1065 		}
1066 		mutex_exit(&cvcmutex);
1067 	}
1068 
1069 	/* NOTREACHED */
1070 }
1071 
1072 
1073 /*
1074  * cvc_bbsram_stop()
1075  *	Prevents accesses to BBSRAM. used by cvc_assign_iocpu() when
1076  *	mapping in BBSRAM to a virtual address.
1077  */
1078 static void
1079 cvc_bbsram_stop(void)
1080 {
1081 	stop_bbsram = 1;
1082 	mutex_enter(&cvc_bbsram_input_mutex);
1083 	mutex_enter(&cvc_buf_mutex);
1084 }
1085 
1086 
1087 /*
1088  * cvc_bbsram_start()
1089  *	Allow accesses to BBSRAM, used by cvc_assign_iocpu() after
1090  *	BBSRAM has been mapped to a virtual address.
1091  */
1092 static void
1093 cvc_bbsram_start(void)
1094 {
1095 	stop_bbsram = 0;
1096 	mutex_exit(&cvc_buf_mutex);
1097 	mutex_exit(&cvc_bbsram_input_mutex);
1098 }
1099 
1100 
1101 /*
1102  * cvc_assign_iocpu()
1103  *	Map in BBSRAM to a virtual address
1104  *	This called by the kernel with the cpu id of cpu zero.
1105  */
1106 void
1107 cvc_assign_iocpu(processorid_t newcpu)
1108 {
1109 	processorid_t	oldcpu = cvc_iocpu;
1110 
1111 	if (newcpu == oldcpu)
1112 		return;
1113 
1114 	cvc_iobufp[newcpu] = cvc_iobuf_mapin(newcpu);
1115 
1116 	cvc_bbsram_stop();
1117 
1118 	cvc_iocpu = newcpu;
1119 
1120 	cvc_bbsram_start();
1121 
1122 	if (oldcpu != -1)
1123 		cvc_iobuf_mapout(oldcpu);
1124 }
1125 
1126 
1127 /*
1128  * cvc_iobuf_mapin()
1129  *	Map in the cvc bbsram i/o buffer into kernel space.
1130  */
1131 static caddr_t
1132 cvc_iobuf_mapin(processorid_t cpu_id)
1133 {
1134 	caddr_t	cvaddr;
1135 	uint64_t cvc_iobuf_physaddr;
1136 	pfn_t pfn;
1137 	uint_t num_pages;
1138 	extern cpu_sgnblk_t *cpu_sgnblkp[];
1139 
1140 	ASSERT(cpu_sgnblkp[cpu_id] != NULL);
1141 
1142 	/*
1143 	 * First construct the physical base address of the bbsram
1144 	 * in Starfire PSI space associated with this cpu in question.
1145 	 */
1146 	cvc_iobuf_physaddr = STARFIRE_UPAID2UPS(cpu_id) | STARFIRE_PSI_BASE;
1147 
1148 	/*
1149 	 * Next add the cvc i/o buffer offset obtained from the
1150 	 * sigblock to get cvc iobuf physical address
1151 	 */
1152 	cvc_iobuf_physaddr += cpu_sgnblkp[cpu_id]->sigb_cvc_off;
1153 
1154 	/* Get the page frame number */
1155 	pfn = (cvc_iobuf_physaddr >> MMU_PAGESHIFT);
1156 
1157 	/* Calculate how many pages we need to map in */
1158 	num_pages = mmu_btopr(((uint_t)(cvc_iobuf_physaddr
1159 	    & MMU_PAGEOFFSET) + sizeof (sigb_cvc_t)));
1160 
1161 	/*
1162 	 * Map in the cvc iobuf
1163 	 */
1164 	cvaddr = vmem_alloc(heap_arena, ptob(num_pages), VM_SLEEP);
1165 
1166 	hat_devload(kas.a_hat, cvaddr, mmu_ptob(num_pages), pfn,
1167 	    PROT_READ | PROT_WRITE, HAT_LOAD_LOCK);
1168 
1169 	return ((caddr_t)(cvaddr + (uint_t)(cvc_iobuf_physaddr
1170 	    & MMU_PAGEOFFSET)));
1171 }
1172 
1173 
1174 /*
1175  * cvc_iobuf_mapout()
1176  *	Map out the cvc iobuf from kernel space
1177  */
1178 static void
1179 cvc_iobuf_mapout(processorid_t cpu_id)
1180 {
1181 	caddr_t	cvaddr;
1182 	size_t	num_pages;
1183 
1184 	if ((cvaddr = cvc_iobufp[cpu_id]) == 0) {
1185 		/* already unmapped - return */
1186 		return;
1187 	}
1188 
1189 	/* Calculate how many pages we need to map out */
1190 	num_pages = mmu_btopr(((size_t)((uint64_t)cvaddr & MMU_PAGEOFFSET) +
1191 	    sizeof (sigb_cvc_t)));
1192 
1193 	/* Get cvaddr to the start of the page boundary */
1194 	cvaddr = (caddr_t)(((uint64_t)cvaddr & MMU_PAGEMASK));
1195 
1196 	hat_unload(kas.a_hat, cvaddr, mmu_ptob(num_pages), HAT_UNLOAD_UNLOCK);
1197 	vmem_free(heap_arena, cvaddr, ptob(num_pages));
1198 
1199 	cvc_iobufp[cpu_id] = NULL;
1200 }
1201