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