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