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