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