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