xref: /titanic_44/usr/src/uts/sun4u/starfire/cvc/cvc.c (revision 5151fb1220e0ceafdc172203863c73da4285c170)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * MT STREAMS Virtual Console Device Driver
31  */
32 
33 #include <sys/types.h>
34 #include <sys/sysmacros.h>
35 #include <sys/processor.h>
36 #include <sys/cpuvar.h>
37 #include <sys/open.h>
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/signal.h>
41 #include <sys/cred.h>
42 #include <sys/user.h>
43 #include <sys/proc.h>
44 #include <sys/vnode.h>
45 #include <sys/uio.h>
46 #include <sys/buf.h>
47 #include <sys/file.h>
48 #include <sys/kmem.h>
49 #include <sys/vmem.h>
50 #include <sys/stat.h>
51 #include <sys/stream.h>
52 #include <sys/stropts.h>
53 #include <sys/strsubr.h>
54 #include <sys/strsun.h>
55 #include <sys/tty.h>
56 #include <sys/ptyvar.h>
57 #include <sys/poll.h>
58 #include <sys/debug.h>
59 #include <sys/conf.h>
60 
61 #include <sys/starfire.h>
62 #include <sys/mman.h>
63 #include <vm/seg_kmem.h>
64 
65 #include <sys/ddi.h>
66 #include <sys/sunddi.h>
67 #include <sys/errno.h>
68 #include <sys/modctl.h>
69 #include <sys/cpu_sgnblk_defs.h>
70 #include <sys/cvc.h>
71 #include <sys/cpu_sgn.h>
72 
73 extern void	prom_printf(char *fmt, ...);
74 
75 static int	cvc_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
76 static int	cvc_attach(dev_info_t *, ddi_attach_cmd_t);
77 static int	cvc_detach(dev_info_t *, ddi_detach_cmd_t);
78 static int	cvc_open(register queue_t *, dev_t *, int, int, cred_t *);
79 static int	cvc_close(queue_t *, int, cred_t *);
80 static int	cvc_wput(queue_t *, mblk_t *);
81 static int	cvc_wsrv(queue_t *);
82 static void	cvc_ioctl(queue_t *, mblk_t *);
83 static void	cvc_ack(mblk_t *, mblk_t *, uint_t);
84 static void	cvc_reioctl(void *);
85 static void	cvc_input_daemon(void);
86 static void	cvc_putc(register int);
87 static void	cvc_flush_buf(void *);
88 static void	cvc_bbsram_ops(volatile uchar_t *);
89 
90 static caddr_t	cvc_iobuf_mapin(processorid_t);
91 static void	cvc_iobuf_mapout(processorid_t);
92 	void	cvc_assign_iocpu(processorid_t);
93 
94 /*
95  * Private copy of devinfo pointer; cvc_info uses it.
96  */
97 static dev_info_t	*cvcdip;
98 
99 /*
100  * This buffer is used to manage mapping in the I/O buffer that CVC
101  * uses when communicating with the SSP Client (netcon_server) via bbsram.
102  */
103 static caddr_t	cvc_iobufp[NCPU];
104 
105 typedef struct cvc_s {
106 	bufcall_id_t	cvc_wbufcid;
107 	tty_common_t	cvc_tty;
108 } cvc_t;
109 
110 cvc_t	cvc_common_tty;
111 
112 static struct module_info cvcm_info = {
113 	1313,		/* mi_idnum Bad luck number  ;-) */
114 	"cvc",		/* mi_idname */
115 	0,		/* mi_minpsz */
116 	INFPSZ,		/* mi_maxpsz */
117 	2048,		/* mi_hiwat */
118 	2048		/* mi_lowat */
119 };
120 
121 static struct qinit cvcrinit = {
122 	NULL,		/* qi_putp */
123 	NULL,		/* qi_srvp */
124 	cvc_open,	/* qi_qopen */
125 	cvc_close,	/* qi_qclose */
126 	NULL,		/* qi_qadmin */
127 	&cvcm_info,	/* qi_minfo */
128 	NULL		/* qi_mstat */
129 };
130 
131 static struct qinit cvcwinit = {
132 	cvc_wput,	/* qi_putp */
133 	cvc_wsrv,	/* qi_srvp */
134 	cvc_open,	/* qi_qopen */
135 	cvc_close,	/* qi_qclose */
136 	NULL,		/* qi_qadmin */
137 	&cvcm_info,	/* qi_minfo */
138 	NULL		/* qi_mstat */
139 };
140 
141 struct streamtab	cvcinfo = {
142 	&cvcrinit,	/* st_rdinit */
143 	&cvcwinit,	/* st_wrinit */
144 	NULL,		/* st_muxrinit */
145 	NULL		/* st_muxwrinit */
146 };
147 
148 #define	TIMEOUT_DELAY		100000
149 
150 #define	BBSRAM_INPUT_BUF	((volatile char *)(cvc_iobufp[cvc_iocpu] \
151 					+ BBSRAM_INPUT_COUNT_OFF))
152 
153 #define	BBSRAM_OUTPUT_BUF	((volatile char *)(cvc_iobufp[cvc_iocpu] \
154 					+ BBSRAM_OUTPUT_COUNT_OFF))
155 
156 #define	BBSRAM_INPUT_COUNT	(*((volatile short *)BBSRAM_INPUT_BUF))
157 
158 #define	BBSRAM_OUTPUT_COUNT	(*((volatile short *)BBSRAM_OUTPUT_BUF))
159 
160 #define	CVC_OUT_MAXSPIN	1024
161 
162 /* The bbsram control reg is located at the end of the I/O buffers */
163 #define	BBSRAM_CONTROL_REG	((volatile uchar_t *)(cvc_iobufp[cvc_iocpu] \
164 					+ CVC_IN_SIZE + CVC_OUT_SIZE))
165 
166 static krwlock_t	cvclock;	/* lock protecting everything here */
167 static queue_t		*cvcinput_q;	/* queue for console input */
168 static queue_t		*cvcoutput_q;	/* queue for console output */
169 static int		cvc_instance = -1;
170 static int		cvc_stopped = 0;
171 static int		cvc_suspended = 0;
172 static int		cvc_hangup_ok = 0;
173 
174 static kthread_id_t	cvc_input_daemon_thread;
175 static kmutex_t		cvcmutex;	/* protects input */
176 static kmutex_t		cvc_buf_mutex;	/* protects internal output buffer */
177 static kmutex_t		cvc_bbsram_input_mutex; /* protects BBSRAM inp buff */
178 static int		input_ok = 0;	/* true when stream is valid */
179 static int		stop_bbsram = 1; /* true when BBSRAM is not usable */
180 static int		stop_timeout = 0;
181 static uchar_t		cvc_output_buffer[MAX_XFER_OUTPUT]; /* output buffer */
182 static ushort_t		cvc_output_count = 0;
183 static int		via_bbsram = 0; /* toggle switch */
184 static timeout_id_t	cvc_timeout_id = (timeout_id_t)-1;
185 static processorid_t	cvc_iocpu = -1;	/* cpu id of cpu zero */
186 
187 /*
188  * Module linkage information for the kernel.
189  */
190 
191 DDI_DEFINE_STREAM_OPS(cvcops, nulldev, nulldev, cvc_attach, cvc_detach,
192 			nodev, cvc_info, (D_MTPERQ | D_MP), &cvcinfo);
193 
194 static struct modldrv modldrv = {
195 	&mod_driverops, /* Type of module.  This one is a pseudo driver */
196 	"CVC driver 'cvc' v%I%",
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", mp);
454 			cmn_err(CE_WARN, "cvc_wput: type = 0x%x",
455 				mp->b_datap->db_type);
456 			/* FALLTHROUGH */
457 #ifdef lint
458 			break;
459 #endif
460 
461 		case M_DATA:
462 			if (cvc_stopped == 1 || cvc_suspended == 1) {
463 				(void) putq(q, mp);
464 				break;
465 			}
466 			if (cvcoutput_q != NULL && !via_bbsram) {
467 				/*
468 				 * Send it up past cvcredir module.
469 				 */
470 				putnext(cvcoutput_q, mp);
471 			} else {
472 				char	*msgp, c;
473 				mblk_t	*mp2 = mp;
474 				int count;
475 
476 				while (mp2 != NULL) {
477 					count = mp2->b_wptr - mp2->b_rptr;
478 					msgp = (char *)mp2->b_rptr;
479 					while (count > 0) {
480 						count--;
481 						if ((c = *msgp++) != '\0') {
482 							/* don't print NULs */
483 							cvc_putc(c);
484 						}
485 					}
486 					mp2 = mp2->b_cont;
487 				}
488 				freemsg(mp);
489 			}
490 			break;
491 
492 	}
493 	rw_exit(&cvclock);
494 	return (error);
495 }
496 
497 static int cvc_wsrv_count = 0;
498 
499 static int
500 cvc_wsrv(queue_t *q)
501 {
502 	register mblk_t *mp;
503 
504 	cvc_wsrv_count++;
505 
506 	if (cvc_stopped == 1 || cvc_suspended == 1) {
507 		return (0);
508 	}
509 
510 	rw_enter(&cvclock, RW_READER);
511 	while ((mp = getq(q)) != NULL) {
512 		if (cvcoutput_q != NULL && !via_bbsram) {
513 			/*
514 			 * Send it up past cvcredir module.
515 			 */
516 			putnext(cvcoutput_q, mp);
517 		} else {
518 			char    *msgp, c;
519 			mblk_t  *mp2 = mp;
520 			int count;
521 
522 			while (mp2 != NULL) {
523 				count = mp2->b_wptr - mp2->b_rptr;
524 				msgp = (char *)mp2->b_rptr;
525 				while (count > 0) {
526 					count--;
527 					if ((c = *msgp++) != '\0') {
528 						/* don't print NULs */
529 						cvc_putc(c);
530 					}
531 				}
532 				mp2 = mp2->b_cont;
533 			}
534 			freemsg(mp);
535 		}
536 	}
537 	rw_exit(&cvclock);
538 	return (0);
539 }
540 
541 
542 /*
543  * cvc_ioctl()
544  *	handle normal console ioctls.
545  */
546 static void
547 cvc_ioctl(register queue_t *q, register mblk_t *mp)
548 {
549 	register struct iocblk		*iocp;
550 	register tty_common_t		*tty;
551 	register cvc_t			*cp;
552 	int				datasize;
553 	int				error = 0;
554 	mblk_t				*tmp;
555 
556 	cp = q->q_ptr;
557 	tty = &cp->cvc_tty;
558 	if (tty->t_iocpending != NULL) {
559 		freemsg(tty->t_iocpending);
560 		tty->t_iocpending = NULL;
561 	}
562 	datasize = ttycommon_ioctl(tty, q, mp, &error);
563 	if (datasize != 0) {
564 		if (cp->cvc_wbufcid)
565 			unbufcall(cp->cvc_wbufcid);
566 		cp->cvc_wbufcid = bufcall(datasize, BPRI_HI, cvc_reioctl, cp);
567 		return;
568 	}
569 	if (error < 0) {
570 		iocp = (struct iocblk *)mp->b_rptr;
571 		/*
572 		 * "ttycommon_ioctl" didn't do anything; we process it here.
573 		 */
574 		error = 0;
575 		switch (iocp->ioc_cmd) {
576 
577 		/*
578 		 *  Set modem bit ioctls.  These are NOPs for us, since we
579 		 * dont control any hardware.
580 		 */
581 		case TCSBRK:
582 		case TIOCSBRK:
583 		case TIOCCBRK:
584 		case TIOCMSET:
585 		case TIOCMBIS:
586 		case TIOCMBIC:
587 			if (iocp->ioc_count != TRANSPARENT) {
588 				mioc2ack(mp, NULL, 0, 0);
589 			} else {
590 				mcopyin(mp, NULL, sizeof (int), NULL);
591 			}
592 			/* qreply done below */
593 			break;
594 
595 		/*
596 		 *  Get modem bits, we return 0 in mblk.
597 		 */
598 		case TIOCMGET:
599 			tmp = allocb(sizeof (int), BPRI_MED);
600 			if (tmp == NULL) {
601 				miocnak(q, mp, 0, EAGAIN);
602 				return;
603 			}
604 			*(int *)tmp->b_rptr = 0;
605 
606 			if (iocp->ioc_count != TRANSPARENT)
607 				mioc2ack(mp, tmp, sizeof (int), 0);
608 			else
609 				mcopyout(mp, NULL, sizeof (int), NULL, tmp);
610 			/* qreply done below */
611 			break;
612 
613 		default:
614 			/*
615 			 * If we don't understand it, it's an error. NAK it.
616 			 */
617 			error = EINVAL;
618 			break;
619 		}
620 	}
621 	if (error != 0) {
622 		iocp->ioc_error = error;
623 		mp->b_datap->db_type = M_IOCNAK;
624 	}
625 	qreply(q, mp);
626 
627 }
628 
629 
630 /*
631  * cvc_redir()
632  *	called from cvcredir:cvcr_wput() to handle console input
633  *	data. This routine puts the cvcredir write (downstream) data
634  *	onto the cvc read (upstream) queues.  Note that if `mp' is
635  *	an M_IOCTL, then it may be reused by the caller to send back
636  *	an M_IOCACK or M_IOCNAK.
637  */
638 int
639 cvc_redir(mblk_t *mp)
640 {
641 	register struct iocblk	*iocp;
642 	register tty_common_t	*tty;
643 	register cvc_t		*cp;
644 	struct winsize		*ws;
645 	int			error;
646 
647 	if (cvcinput_q == NULL) {
648 		cmn_err(CE_WARN, "cvc_redir: cvcinput_q NULL!");
649 		return (EINVAL);
650 	}
651 
652 	if (DB_TYPE(mp) != M_IOCTL) {
653 		putnext(cvcinput_q, mp);
654 		return (0);
655 	}
656 
657 	iocp = (struct iocblk *)mp->b_rptr;
658 	if (iocp->ioc_cmd == TIOCSWINSZ) {
659 		error = miocpullup(mp, sizeof (struct winsize));
660 		if (error != 0)
661 			return (error);
662 
663 		ws = (struct winsize *)mp->b_cont->b_rptr;
664 		cp = cvcinput_q->q_ptr;
665 		tty = &cp->cvc_tty;
666 		mutex_enter(&tty->t_excl);
667 		if (bcmp(&tty->t_size, ws, sizeof (struct winsize)) != 0) {
668 			tty->t_size = *ws;
669 			mutex_exit(&tty->t_excl);
670 			(void) putnextctl1(cvcinput_q, M_PCSIG, SIGWINCH);
671 		} else
672 			mutex_exit(&tty->t_excl);
673 	} else {
674 		/*
675 		 * It must be a CVC_DISCONNECT, send hangup.
676 		 */
677 		ASSERT(iocp->ioc_cmd == CVC_DISCONNECT);
678 		if (cvc_hangup_ok)
679 			(void) putnextctl(cvcinput_q, M_HANGUP);
680 	}
681 
682 	return (0);
683 }
684 
685 
686 /*
687  * cvc_register()
688  *	called from cvcredir to register it's queues.  cvc
689  *	receives data from cn via the streamhead and sends it to cvcredir
690  *	via pointers to cvcredir's queues.
691  */
692 int
693 cvc_register(queue_t *q)
694 {
695 	int error = -1;
696 
697 	if (cvcinput_q == NULL)
698 		cmn_err(CE_WARN, "cvc_register: register w/ no console open!");
699 	rw_enter(&cvclock, RW_WRITER);
700 	if (cvcoutput_q == NULL) {
701 		cvcoutput_q = RD(q);  /* Make sure its the upstream q */
702 		qprocson(cvcoutput_q);	/* must be done within cvclock */
703 		error = 0;
704 	} else {
705 		/*
706 		 * cmn_err will call us, so release lock.
707 		 */
708 		rw_exit(&cvclock);
709 		if (cvcoutput_q == q)
710 			cmn_err(CE_WARN, "cvc_register: duplicate q!");
711 		else
712 			cmn_err(CE_WARN, "cvc_register: nondup q = 0x%p",
713 				q);
714 		return (error);
715 	}
716 
717 	/*
718 	 * Unless "via_bbsram" is set, i/o will be going through cvcd, so
719 	 * stop flushing output to BBSRAM.
720 	 */
721 	if ((cvc_timeout_id != (timeout_id_t)-1) && (!via_bbsram)) {
722 		stop_timeout = 1;
723 		(void) untimeout(cvc_timeout_id);
724 		cvc_timeout_id = (timeout_id_t)-1;
725 		cvc_hangup_ok = 1;
726 	}
727 	rw_exit(&cvclock);
728 	return (error);
729 }
730 
731 
732 /*
733  * cvc_unregister()
734  *	called from cvcredir to clear pointers to its queues.
735  *	cvcredir no longer wants to send or receive data.
736  */
737 void
738 cvc_unregister(queue_t *q)
739 {
740 	rw_enter(&cvclock, RW_WRITER);
741 	if (q == cvcoutput_q) {
742 		qprocsoff(cvcoutput_q);	/* must be done within cvclock */
743 		cvcoutput_q = NULL;
744 	} else {
745 		rw_exit(&cvclock);
746 		cmn_err(CE_WARN, "cvc_unregister: q = 0x%p not registered", q);
747 		return;
748 	}
749 
750 	/*
751 	 * i/o will not be going through cvcd, start flushing output to
752 	 * BBSRAM
753 	 */
754 	if (cvc_timeout_id == (timeout_id_t)-1) {
755 		stop_timeout = 0;
756 		cvc_timeout_id = timeout(cvc_flush_buf, NULL,
757 			drv_usectohz(TIMEOUT_DELAY));
758 	}
759 	rw_exit(&cvclock);
760 }
761 
762 /*
763  * cvc_reioctl()
764  *	Retry an "ioctl", now that "bufcall" claims we may be able
765  *	to allocate the buffer we need.
766  */
767 static void
768 cvc_reioctl(void *unit)
769 {
770 	register queue_t	*q;
771 	register mblk_t		*mp;
772 	register cvc_t		*cp = (cvc_t *)unit;
773 
774 	/*
775 	 * The bufcall is no longer pending.
776 	 */
777 	if (!cp->cvc_wbufcid) {
778 		return;
779 	}
780 	cp->cvc_wbufcid = 0;
781 	if ((q = cp->cvc_tty.t_writeq) == NULL) {
782 		return;
783 	}
784 	if ((mp = cp->cvc_tty.t_iocpending) != NULL) {
785 		/* not pending any more */
786 		cp->cvc_tty.t_iocpending = NULL;
787 		cvc_ioctl(q, mp);
788 	}
789 }
790 
791 
792 /*
793  * cvc_bbsram_ops()
794  *	Process commands sent to cvc from netcon_server via BBSRAM
795  */
796 static void
797 cvc_bbsram_ops(volatile unsigned char *op_reg)
798 {
799 	uchar_t	 op;
800 
801 	if ((op = *op_reg) == 0)
802 		return;
803 
804 	ASSERT(MUTEX_HELD(&cvc_bbsram_input_mutex));
805 
806 	switch (op) {
807 	case CVC_BBSRAM_BREAK:		/* A console break (L1-A) */
808 		abort_sequence_enter((char *)NULL);
809 		break;
810 	case CVC_BBSRAM_DISCONNECT:	/* Break connection, hang up */
811 		if (cvcinput_q && cvc_hangup_ok)
812 			(void) putnextctl(cvcinput_q, M_HANGUP);
813 		break;
814 	case CVC_BBSRAM_VIA_NET:	/* console via network */
815 		via_bbsram = 0;
816 		/*
817 		 * stop periodic flushing of output to BBSRAM
818 		 * only if cvcredir/cvcd are present
819 		 */
820 		rw_enter(&cvclock, RW_WRITER);
821 		if (cvcoutput_q != NULL) {
822 			stop_timeout = 1;
823 			if (cvc_timeout_id != (timeout_id_t)-1) {
824 				(void) untimeout(cvc_timeout_id);
825 				cvc_timeout_id = (timeout_id_t)-1;
826 			}
827 		}
828 		rw_exit(&cvclock);
829 		break;
830 	case CVC_BBSRAM_VIA_BBSRAM:	/* console via bbsram */
831 		via_bbsram = 1;
832 		/* start periodic flushing of ouput to BBSRAM */
833 		rw_enter(&cvclock, RW_WRITER);
834 		if (cvc_timeout_id == (timeout_id_t)-1) {
835 			stop_timeout = 0;
836 			cvc_timeout_id = timeout(cvc_flush_buf,
837 			    NULL, drv_usectohz(TIMEOUT_DELAY));
838 		}
839 		rw_exit(&cvclock);
840 		break;
841 	case CVC_BBSRAM_CLOSE_NET:
842 		/*
843 		 * Send a hangup control message upstream to cvcd
844 		 * thru cvcredir.  This is an attempt to close
845 		 * out any existing network connection(if any).
846 		 * cvcoutput_q should point to the cvcredir's read
847 		 * queue.
848 		 */
849 		rw_enter(&cvclock, RW_READER);
850 		if (cvcoutput_q != NULL) {
851 			(void) putnextctl(cvcoutput_q, M_HANGUP);
852 		}
853 		rw_exit(&cvclock);
854 		break;
855 	default:
856 		cmn_err(CE_WARN, "cvc: unknown BBSRAM opcode %d\n",
857 			(unsigned int)op);
858 		break;
859 	}
860 	*op_reg = 0;
861 }
862 
863 
864 /*
865  * cvc_putc()
866  *	Put a single character out to BBSRAM if space available.
867  */
868 static void
869 cvc_putc(register int c)
870 {
871 	static int	output_lost = 0;
872 
873 	if (c == '\n')
874 		cvc_putc('\r');
875 
876 	mutex_enter(&cvc_buf_mutex);
877 	/*
878 	 * Just exit if the buffer is already full.
879 	 * It will be up to cvc_flush_buf() to flush the buffer.
880 	 */
881 	if (cvc_output_count == MAX_XFER_OUTPUT) {
882 		output_lost = 1;
883 		mutex_exit(&cvc_buf_mutex);
884 		return;
885 	}
886 	if (output_lost)
887 		prom_printf("WARNING: overflow of cvc output buffer, "
888 		    "output lost!");
889 	output_lost = 0;
890 	cvc_output_buffer[cvc_output_count] = (unsigned char)c;
891 	cvc_output_count++;
892 	if ((cvc_output_count == MAX_XFER_OUTPUT) || (c == '\n')) {
893 		/* flush cvc's internal output buffer to BBSRAM */
894 
895 		/*
896 		 * Wait for the BBSRAM output buffer to be emptied.
897 		 * This may hang if netcon_server isn't running on the SSP
898 		 */
899 		int maxspin = CVC_OUT_MAXSPIN;
900 		while ((BBSRAM_OUTPUT_COUNT != 0) && --maxspin) {
901 			if (stop_bbsram) {
902 				mutex_exit(&cvc_buf_mutex);
903 				return;
904 			}
905 			DELAY(1000);
906 		}
907 		bcopy((caddr_t)cvc_output_buffer,
908 			(caddr_t)(BBSRAM_OUTPUT_BUF - cvc_output_count),
909 			cvc_output_count);
910 
911 		BBSRAM_OUTPUT_COUNT = cvc_output_count;
912 		cvc_output_count = 0;
913 	}
914 	mutex_exit(&cvc_buf_mutex);
915 }
916 
917 
918 /*
919  * cvc_flush_buf()
920  *	Flush cvc's internal output buffer to BBSRAM at regular intervals.
921  *	This should only be done if cvcd is not running or the user (via the cvc
922  *	application on the SSP) has requested that i/o go through BBSRAM.
923  */
924 /* ARGSUSED */
925 static void
926 cvc_flush_buf(void *notused)
927 {
928 	if (stop_timeout)
929 		return;
930 
931 	mutex_enter(&cvc_buf_mutex);
932 	if (cvc_output_count != 0) {
933 		/*
934 		 * Wait for the BBSRAM output buffer to be emptied.
935 		 * This may hang if netcon_server isn't running on the SSP.
936 		 */
937 		int maxspin = CVC_OUT_MAXSPIN;
938 		while ((BBSRAM_OUTPUT_COUNT != 0) && --maxspin) {
939 			if (stop_bbsram)
940 				goto exit;
941 			DELAY(1000);
942 		}
943 
944 		bcopy((caddr_t)cvc_output_buffer,
945 			(caddr_t)BBSRAM_OUTPUT_BUF - cvc_output_count,
946 			cvc_output_count);
947 
948 		BBSRAM_OUTPUT_COUNT = cvc_output_count;
949 		cvc_output_count = 0;
950 	}
951 exit:
952 	mutex_exit(&cvc_buf_mutex);
953 	/* rw_enter(&cvclock, RW_WRITER); */
954 	cvc_timeout_id = timeout(cvc_flush_buf, NULL,
955 		drv_usectohz(TIMEOUT_DELAY));
956 	/* rw_exit(&cvclock); */
957 }
958 
959 
960 /*
961  * cvc_getstr()
962  *	Poll BBSRAM for console input while available.
963  */
964 static void
965 cvc_getstr(char *cp)
966 {
967 	short		count;
968 	volatile char	*lp;
969 
970 	mutex_enter(&cvc_bbsram_input_mutex);
971 	/* Poll BBSRAM for input */
972 	do {
973 		if (stop_bbsram) {
974 			*cp = '\0';	/* set string to zero-length */
975 			mutex_exit(&cvc_bbsram_input_mutex);
976 			return;
977 		}
978 		/*
979 		 * Use a smaller delay between checks of BBSRAM for input
980 		 * when cvcd/cvcredir are not running or "via_bbsram" has
981 		 * been set.
982 		 * We don't go away completely when i/o is going through the
983 		 * network via cvcd since a command may be sent via BBSRAM
984 		 * to switch if the network is down or hung.
985 		 */
986 		if ((cvcoutput_q == NULL) || (via_bbsram))
987 			delay(drv_usectohz(100000));
988 		else
989 			delay(drv_usectohz(1000000));
990 		cvc_bbsram_ops(BBSRAM_CONTROL_REG);
991 		count = BBSRAM_INPUT_COUNT;
992 	} while (count == 0);
993 
994 	lp = BBSRAM_INPUT_BUF - count;
995 
996 	while (count--) {
997 		*cp++ = *lp++;
998 	}
999 	*cp = '\0';
1000 
1001 	BBSRAM_INPUT_COUNT = 0;
1002 	mutex_exit(&cvc_bbsram_input_mutex);
1003 }
1004 
1005 
1006 /*
1007  * cvc_input_daemon()
1008  *	this function runs as a separate kernel thread and polls BBSRAM for
1009  *	input, and possibly put it on read stream for the console.
1010  *	There are two poll rates (implemented in cvc_getstr):
1011  *		 100 000 uS (10 Hz) - no cvcd communications || via_bbsram
1012  *		1000 000 uS ( 1 Hz) - cvcd communications
1013  * 	This continues to run even if there are network console communications
1014  *	in order to handle out-of-band signaling.
1015  */
1016 static void
1017 cvc_input_daemon(void)
1018 {
1019 	char		linebuf[MAX_XFER_INPUT];
1020 	char		*cp;
1021 	mblk_t		*mbp;
1022 	int		c;
1023 	int		dropped_read = 0;
1024 
1025 	for (;;) {
1026 		cvc_getstr(linebuf);
1027 
1028 		mbp = allocb(strlen(linebuf), BPRI_MED);
1029 		if (mbp == NULL) {	/* drop it & go on if no buffer */
1030 			if (!dropped_read) {
1031 				cmn_err(CE_WARN,
1032 				    "cvc_input_daemon: "
1033 				    "dropping BBSRAM reads\n");
1034 			}
1035 			dropped_read++;
1036 			continue;
1037 		}
1038 		if (dropped_read) {
1039 			cmn_err(CE_WARN,
1040 			    "cvc_input_daemon: dropped %d BBSRAM reads\n",
1041 			    dropped_read);
1042 			dropped_read = 0;
1043 		}
1044 
1045 		for (cp = linebuf; *cp != '\0'; cp++) {
1046 			c = (int)*cp;
1047 			if (c == '\r')
1048 				c = '\n';
1049 			c &= 0177;
1050 			*mbp->b_wptr = (char)c;
1051 			mbp->b_wptr++;
1052 		}
1053 		mutex_enter(&cvcmutex);
1054 		if (input_ok) {
1055 			if (cvcinput_q == NULL) {
1056 				cmn_err(CE_WARN,
1057 				    "cvc_input_daemon: cvcinput_q is NULL!");
1058 			} else {
1059 				putnext(cvcinput_q, mbp);
1060 			}
1061 		} else {
1062 			freemsg(mbp);
1063 		}
1064 		mutex_exit(&cvcmutex);
1065 	}
1066 
1067 	/* NOTREACHED */
1068 }
1069 
1070 
1071 /*
1072  * cvc_bbsram_stop()
1073  *	Prevents accesses to BBSRAM. used by cvc_assign_iocpu() when
1074  *	mapping in BBSRAM to a virtual address.
1075  */
1076 static void
1077 cvc_bbsram_stop(void)
1078 {
1079 	stop_bbsram = 1;
1080 	mutex_enter(&cvc_bbsram_input_mutex);
1081 	mutex_enter(&cvc_buf_mutex);
1082 }
1083 
1084 
1085 /*
1086  * cvc_bbsram_start()
1087  *	Allow accesses to BBSRAM, used by cvc_assign_iocpu() after
1088  *	BBSRAM has been mapped to a virtual address.
1089  */
1090 static void
1091 cvc_bbsram_start(void)
1092 {
1093 	stop_bbsram = 0;
1094 	mutex_exit(&cvc_buf_mutex);
1095 	mutex_exit(&cvc_bbsram_input_mutex);
1096 }
1097 
1098 
1099 /*
1100  * cvc_assign_iocpu()
1101  *	Map in BBSRAM to a virtual address
1102  *	This called by the kernel with the cpu id of cpu zero.
1103  */
1104 void
1105 cvc_assign_iocpu(processorid_t newcpu)
1106 {
1107 	processorid_t	oldcpu = cvc_iocpu;
1108 
1109 	if (newcpu == oldcpu)
1110 		return;
1111 
1112 	cvc_iobufp[newcpu] = cvc_iobuf_mapin(newcpu);
1113 
1114 	cvc_bbsram_stop();
1115 
1116 	cvc_iocpu = newcpu;
1117 
1118 	cvc_bbsram_start();
1119 
1120 	if (oldcpu != -1)
1121 		cvc_iobuf_mapout(oldcpu);
1122 }
1123 
1124 
1125 /*
1126  * cvc_iobuf_mapin()
1127  *	Map in the cvc bbsram i/o buffer into kernel space.
1128  */
1129 static caddr_t
1130 cvc_iobuf_mapin(processorid_t cpu_id)
1131 {
1132 	caddr_t	cvaddr;
1133 	uint64_t cvc_iobuf_physaddr;
1134 	pfn_t pfn;
1135 	uint_t num_pages;
1136 	extern cpu_sgnblk_t *cpu_sgnblkp[];
1137 
1138 	ASSERT(cpu_sgnblkp[cpu_id] != NULL);
1139 
1140 	/*
1141 	 * First construct the physical base address of the bbsram
1142 	 * in Starfire PSI space associated with this cpu in question.
1143 	 */
1144 	cvc_iobuf_physaddr = STARFIRE_UPAID2UPS(cpu_id) | STARFIRE_PSI_BASE;
1145 
1146 	/*
1147 	 * Next add the cvc i/o buffer offset obtained from the
1148 	 * sigblock to get cvc iobuf physical address
1149 	 */
1150 	cvc_iobuf_physaddr += cpu_sgnblkp[cpu_id]->sigb_cvc_off;
1151 
1152 	/* Get the page frame number */
1153 	pfn = (cvc_iobuf_physaddr >> MMU_PAGESHIFT);
1154 
1155 	/* Calculate how many pages we need to map in */
1156 	num_pages = mmu_btopr(((uint_t)(cvc_iobuf_physaddr
1157 		& MMU_PAGEOFFSET) + sizeof (sigb_cvc_t)));
1158 
1159 	/*
1160 	 * Map in the cvc iobuf
1161 	 */
1162 	cvaddr = vmem_alloc(heap_arena, ptob(num_pages), VM_SLEEP);
1163 
1164 	hat_devload(kas.a_hat, cvaddr, mmu_ptob(num_pages), pfn,
1165 	    PROT_READ | PROT_WRITE, HAT_LOAD_LOCK);
1166 
1167 	return ((caddr_t)(cvaddr + (uint_t)(cvc_iobuf_physaddr
1168 		& MMU_PAGEOFFSET)));
1169 }
1170 
1171 
1172 /*
1173  * cvc_iobuf_mapout()
1174  *	Map out the cvc iobuf from kernel space
1175  */
1176 static void
1177 cvc_iobuf_mapout(processorid_t cpu_id)
1178 {
1179 	caddr_t	cvaddr;
1180 	size_t	num_pages;
1181 
1182 	if ((cvaddr = cvc_iobufp[cpu_id]) == 0) {
1183 		/* already unmapped - return */
1184 		return;
1185 	}
1186 
1187 	/* Calculate how many pages we need to map out */
1188 	num_pages = mmu_btopr(((size_t)((uint64_t)cvaddr & MMU_PAGEOFFSET) +
1189 		sizeof (sigb_cvc_t)));
1190 
1191 	/* Get cvaddr to the start of the page boundary */
1192 	cvaddr = (caddr_t)(((uint64_t)cvaddr & MMU_PAGEMASK));
1193 
1194 	hat_unload(kas.a_hat, cvaddr, mmu_ptob(num_pages), HAT_UNLOAD_UNLOCK);
1195 	vmem_free(heap_arena, cvaddr, ptob(num_pages));
1196 
1197 	cvc_iobufp[cpu_id] = NULL;
1198 }
1199