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
72extern void	prom_printf(char *fmt, ...);
73
74static int	cvc_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
75static int	cvc_attach(dev_info_t *, ddi_attach_cmd_t);
76static int	cvc_detach(dev_info_t *, ddi_detach_cmd_t);
77static int	cvc_open(register queue_t *, dev_t *, int, int, cred_t *);
78static int	cvc_close(queue_t *, int, cred_t *);
79static int	cvc_wput(queue_t *, mblk_t *);
80static int	cvc_wsrv(queue_t *);
81static void	cvc_ioctl(queue_t *, mblk_t *);
82static void	cvc_ack(mblk_t *, mblk_t *, uint_t);
83static void	cvc_reioctl(void *);
84static void	cvc_input_daemon(void);
85static void	cvc_putc(register int);
86static void	cvc_flush_buf(void *);
87static void	cvc_bbsram_ops(volatile uchar_t *);
88
89static caddr_t	cvc_iobuf_mapin(processorid_t);
90static 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 */
96static 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 */
102static caddr_t	cvc_iobufp[NCPU];
103
104typedef struct cvc_s {
105	bufcall_id_t	cvc_wbufcid;
106	tty_common_t	cvc_tty;
107} cvc_t;
108
109cvc_t	cvc_common_tty;
110
111static 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
120static 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
130static 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
140struct 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
165static krwlock_t	cvclock;	/* lock protecting everything here */
166static queue_t		*cvcinput_q;	/* queue for console input */
167static queue_t		*cvcoutput_q;	/* queue for console output */
168static int		cvc_instance = -1;
169static int		cvc_stopped = 0;
170static int		cvc_suspended = 0;
171static int		cvc_hangup_ok = 0;
172
173static kthread_id_t	cvc_input_daemon_thread;
174static kmutex_t		cvcmutex;	/* protects input */
175static kmutex_t		cvc_buf_mutex;	/* protects internal output buffer */
176static kmutex_t		cvc_bbsram_input_mutex; /* protects BBSRAM inp buff */
177static int		input_ok = 0;	/* true when stream is valid */
178static int		stop_bbsram = 1; /* true when BBSRAM is not usable */
179static int		stop_timeout = 0;
180static uchar_t		cvc_output_buffer[MAX_XFER_OUTPUT]; /* output buffer */
181static ushort_t		cvc_output_count = 0;
182static int		via_bbsram = 0; /* toggle switch */
183static timeout_id_t	cvc_timeout_id = (timeout_id_t)-1;
184static processorid_t	cvc_iocpu = -1;	/* cpu id of cpu zero */
185
186/*
187 * Module linkage information for the kernel.
188 */
189
190DDI_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
194static 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
200static struct modlinkage modlinkage = {
201	MODREV_1,
202	&modldrv,
203	NULL
204};
205
206int
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
218int
219_fini(void)
220{
221	return (EBUSY);
222}
223
224int
225_info(struct modinfo *modinfop)
226{
227	return (mod_info(&modlinkage, modinfop));
228}
229
230/*
231 * DDI glue routines.
232 */
233
234/* ARGSUSED */
235static int
236cvc_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
277static int
278cvc_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 */
300static int
301cvc_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 */
325static int
326cvc_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 */
373static int
374cvc_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 */
407static int
408cvc_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
498static int cvc_wsrv_count = 0;
499
500static int
501cvc_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 */
547static void
548cvc_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 */
639int
640cvc_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 */
693int
694cvc_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 */
738void
739cvc_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 */
769static void
770cvc_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 */
798static void
799cvc_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 */
870static void
871cvc_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 */
927static void
928cvc_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	}
953exit:
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 */
966static void
967cvc_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 */
1018static void
1019cvc_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 */
1078static void
1079cvc_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 */
1092static void
1093cvc_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 */
1106void
1107cvc_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 */
1131static caddr_t
1132cvc_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 */
1178static void
1179cvc_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