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 } 1201