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 * Serengeti console driver, see sys/sgcn.h for more information 30 * This driver uses the QPAIR form of STREAMS Perimeters to serialize access 31 * to the read and write STREAMS queues. 32 */ 33 34 #include <sys/errno.h> 35 #include <sys/stat.h> 36 #include <sys/kmem.h> 37 #include <sys/conf.h> 38 #include <sys/termios.h> 39 #include <sys/modctl.h> 40 #include <sys/kbio.h> 41 #include <sys/stropts.h> 42 #include <sys/stream.h> 43 #include <sys/strsun.h> 44 #include <sys/sysmacros.h> 45 #include <sys/promif.h> 46 #include <sys/prom_plat.h> 47 #include <sys/sgsbbc.h> 48 #include <sys/sgsbbc_iosram.h> 49 #include <sys/sgcn.h> 50 #include <sys/serengeti.h> 51 #include <sys/ddi.h> 52 #include <sys/sunddi.h> 53 #include <sys/strsubr.h> 54 55 /* 56 * Here we define several macros for accessing console IOSRAM 57 */ 58 59 #define POINTER(base, field) ((caddr_t)&base.field) 60 #define OFFSETOF(base, field) ((caddr_t)&base.field - (caddr_t)&base) 61 62 #define RW_CONSOLE_READ 0xAAAA 63 #define RW_CONSOLE_WRITE 0xBBBB 64 65 #define CONSOLE_READ(buf, len) sgcn_rw(RW_CONSOLE_READ, buf, len) 66 #define CONSOLE_WRITE(buf, len) sgcn_rw(RW_CONSOLE_WRITE, buf, len) 67 68 #define SGCN_MI_IDNUM 0xABCD 69 #define SGCN_MI_HIWAT 2048*2048 70 #define SGCN_MI_LOWAT 128 71 72 /* dev_ops and cb_ops for device driver */ 73 static int sgcn_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 74 static int sgcn_attach(dev_info_t *, ddi_attach_cmd_t); 75 static int sgcn_detach(dev_info_t *, ddi_detach_cmd_t); 76 static int sgcn_open(queue_t *, dev_t *, int, int, cred_t *); 77 static int sgcn_close(queue_t *, int, cred_t *); 78 static int sgcn_wput(queue_t *, mblk_t *); 79 static int sgcn_wsrv(queue_t *); 80 static int sgcn_rsrv(queue_t *); 81 82 /* interrupt handlers */ 83 static void sgcn_data_in_handler(caddr_t); 84 static void sgcn_space_2_out_handler(caddr_t); 85 static void sgcn_break_handler(caddr_t); 86 87 /* other internal sgcn routines */ 88 static void sgcn_ioctl(queue_t *, mblk_t *); 89 static void sgcn_reioctl(void *); 90 static void sgcn_start(void); 91 static int sgcn_transmit(queue_t *, mblk_t *); 92 static void sgcn_flush(void); 93 static int sgcn_read_header(int, cnsram_header *); 94 static int sgcn_rw(int, caddr_t, int); 95 static void sgcn_log_error(int, int); 96 97 /* circular buffer routines */ 98 static int circular_buffer_write(int, int, int, int, caddr_t, int); 99 static int circular_buffer_read(int, int, int, int, caddr_t, int); 100 101 static boolean_t abort_charseq_recognize(uchar_t); 102 static void sg_abort_seq_handler(char *); 103 104 static sgcn_t *sgcn_state; 105 static uchar_t sgcn_stopped = FALSE; 106 static int sgcn_timeout_period = 20; /* time out in seconds */ 107 108 /* streams structures */ 109 static struct module_info minfo = { 110 SGCN_MI_IDNUM, /* mi_idnum */ 111 "sgcn", /* mi_idname */ 112 0, /* mi_minpsz */ 113 INFPSZ, /* mi_maxpsz */ 114 SGCN_MI_HIWAT, /* mi_hiwat */ 115 SGCN_MI_LOWAT /* mi_lowat */ 116 }; 117 118 static struct qinit rinit = { 119 putq, /* qi_putp */ 120 sgcn_rsrv, /* qi_srvp */ 121 sgcn_open, /* qi_qopen */ 122 sgcn_close, /* qi_qclose */ 123 NULL, /* qi_qadmin */ 124 &minfo, /* qi_minfo */ 125 NULL /* qi_mstat */ 126 }; 127 128 static struct qinit winit = { 129 sgcn_wput, /* qi_putp */ 130 sgcn_wsrv, /* qi_srvp */ 131 sgcn_open, /* qi_qopen */ 132 sgcn_close, /* qi_qclose */ 133 NULL, /* qi_qadmin */ 134 &minfo, /* qi_minfo */ 135 NULL /* qi_mstat */ 136 }; 137 138 static struct streamtab sgcnstrinfo = { 139 &rinit, 140 &winit, 141 NULL, 142 NULL 143 }; 144 145 /* standard device driver structures */ 146 static struct cb_ops sgcn_cb_ops = { 147 nulldev, /* open() */ 148 nulldev, /* close() */ 149 nodev, /* strategy() */ 150 nodev, /* print() */ 151 nodev, /* dump() */ 152 nodev, /* read() */ 153 nodev, /* write() */ 154 nodev, /* ioctl() */ 155 nodev, /* devmap() */ 156 nodev, /* mmap() */ 157 nodev, /* segmap() */ 158 nochpoll, /* poll() */ 159 ddi_prop_op, /* prop_op() */ 160 &sgcnstrinfo, /* cb_str */ 161 D_MP | D_MTQPAIR /* cb_flag */ 162 }; 163 164 static struct dev_ops sgcn_ops = { 165 DEVO_REV, 166 0, /* refcnt */ 167 sgcn_getinfo, /* getinfo() */ 168 nulldev, /* identify() */ 169 nulldev, /* probe() */ 170 sgcn_attach, /* attach() */ 171 sgcn_detach, /* detach() */ 172 nodev, /* reset() */ 173 &sgcn_cb_ops, /* cb_ops */ 174 (struct bus_ops *)NULL, /* bus_ops */ 175 NULL, /* power() */ 176 ddi_quiesce_not_supported, /* quiesce */ 177 }; 178 179 static struct modldrv modldrv = { 180 &mod_driverops, 181 "Serengeti console driver", 182 &sgcn_ops 183 }; 184 185 static struct modlinkage modlinkage = { 186 MODREV_1, 187 (void*)&modldrv, 188 NULL 189 }; 190 191 192 /* driver configuration routines */ 193 int 194 _init(void) 195 { 196 int error; 197 198 sgcn_state = kmem_zalloc(sizeof (sgcn_t), KM_SLEEP); 199 200 error = mod_install(&modlinkage); 201 202 if (error == 0) { 203 mutex_init(&sgcn_state->sgcn_lock, NULL, MUTEX_DRIVER, NULL); 204 } else { 205 kmem_free(sgcn_state, sizeof (sgcn_t)); 206 } 207 208 return (error); 209 } 210 211 int 212 _fini(void) 213 { 214 /* can't remove console driver */ 215 return (EBUSY); 216 } 217 218 int 219 _info(struct modinfo *modinfop) 220 { 221 return (mod_info(&modlinkage, modinfop)); 222 } 223 224 /* 225 * sgcn_attach is called at startup time. 226 * There is only once instance of this driver. 227 */ 228 static int 229 sgcn_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 230 { 231 extern int ddi_create_internal_pathname( 232 dev_info_t *, char *, int, minor_t); 233 cnsram_header header; 234 int rv; 235 236 if (cmd != DDI_ATTACH) 237 return (DDI_FAILURE); 238 239 if (ddi_create_internal_pathname(dip, "sgcn", S_IFCHR, 0) 240 != DDI_SUCCESS) 241 return (DDI_FAILURE); 242 243 /* prepare some data structures in soft state */ 244 mutex_enter(&sgcn_state->sgcn_lock); 245 246 sgcn_state->sgcn_dip = dip; 247 248 mutex_exit(&sgcn_state->sgcn_lock); 249 250 /* 251 * We need to verify IOSRAM is intact at startup time. If by 252 * any chance IOSRAM is corrupted, that means SC is not ready. 253 * All we can do is stopping. 254 */ 255 rv = iosram_read(SBBC_CONSOLE_KEY, 0, (caddr_t)&header, 256 sizeof (cnsram_header)); 257 if (rv != 0) 258 cmn_err(CE_PANIC, "sgcn_attach(): Reading from IOSRAM failed"); 259 if (header.cnsram_magic != CNSRAM_MAGIC) 260 cmn_err(CE_PANIC, "sgcn_attach(): Wrong IOSRAM console buffer"); 261 if (!header.cnsram_in_end && !header.cnsram_in_begin) 262 cmn_err(CE_PANIC, "sgcn_attach(): Wrong IOSRAM input buffer"); 263 if (!header.cnsram_out_end && !header.cnsram_out_begin) 264 cmn_err(CE_PANIC, "sgcn_attach(): Wrong IOSRAM output buffer"); 265 /* 266 * XXX need to add extra check for version no. 267 */ 268 269 /* Allocate console input buffer */ 270 sgcn_state->sgcn_inbuf_size = 271 header.cnsram_in_end - header.cnsram_in_begin; 272 sgcn_state->sgcn_inbuf = 273 kmem_alloc(sgcn_state->sgcn_inbuf_size, KM_SLEEP); 274 #ifdef SGCN_DEBUG 275 prom_printf("Allocated %d(0x%X) bytes for console\n", 276 sgcn_state->sgcn_inbuf_size); 277 #endif 278 279 (void) prom_serengeti_set_console_input(SGCN_CLNT_STR); 280 281 abort_seq_handler = sg_abort_seq_handler; 282 283 #ifdef SGCN_DEBUG 284 prom_printf("sgcn_attach(): SGCN driver attached\n"); 285 #endif 286 return (DDI_SUCCESS); 287 288 } 289 290 /* ARGSUSED */ 291 static int 292 sgcn_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 293 { 294 295 if (cmd == DDI_DETACH) 296 return (DDI_FAILURE); 297 298 #ifdef SGCN_DEBUG 299 prom_printf("sgcn_detach(): SGCN driver detached\n"); 300 #endif 301 return (DDI_SUCCESS); 302 } 303 304 /* ARGSUSED */ 305 static int 306 sgcn_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 307 { 308 int error = DDI_FAILURE; 309 310 switch (infocmd) { 311 case DDI_INFO_DEVT2DEVINFO: 312 if (sgcn_state) { 313 *result = (void *) sgcn_state->sgcn_dip; 314 error = DDI_SUCCESS; 315 } 316 break; 317 318 case DDI_INFO_DEVT2INSTANCE: 319 if (getminor((dev_t)arg) == 0) { 320 *result = (void *)0; 321 error = DDI_SUCCESS; 322 } 323 break; 324 } 325 326 return (error); 327 } 328 329 /* streams open & close */ 330 /* ARGSUSED */ 331 static int 332 sgcn_open(queue_t *q, dev_t *devp, int oflag, int sflag, cred_t *credp) 333 { 334 tty_common_t *tty; 335 int unit = getminor(*devp); 336 337 if (unit != 0) 338 return (ENXIO); 339 340 /* stream already open */ 341 if (q->q_ptr) { 342 return (DDI_SUCCESS); 343 } 344 345 if (!sgcn_state) { 346 cmn_err(CE_WARN, "sgcn_open(): sgcn is not configured by\ 347 autoconfig\n"); 348 return (ENXIO); 349 } 350 351 mutex_enter(&sgcn_state->sgcn_lock); 352 tty = &(sgcn_state->sgcn_tty); 353 354 tty->t_readq = q; 355 tty->t_writeq = WR(q); 356 357 /* Link the RD and WR Q's */ 358 359 q->q_ptr = WR(q)->q_ptr = (caddr_t)sgcn_state; 360 sgcn_state->sgcn_readq = RD(q); 361 sgcn_state->sgcn_writeq = WR(q); 362 qprocson(q); 363 364 mutex_exit(&sgcn_state->sgcn_lock); 365 366 /* initialize interrupt handler */ 367 (void) iosram_reg_intr(SBBC_CONSOLE_IN, 368 (sbbc_intrfunc_t)sgcn_data_in_handler, NULL, 369 &sgcn_state->sgcn_sbbc_in_state, 370 &sgcn_state->sgcn_sbbc_in_lock); 371 (void) iosram_reg_intr(SBBC_CONSOLE_SPACE_OUT, 372 (sbbc_intrfunc_t)sgcn_space_2_out_handler, NULL, 373 &sgcn_state->sgcn_sbbc_outspace_state, 374 &sgcn_state->sgcn_sbbc_outspace_lock); 375 (void) iosram_reg_intr(SBBC_CONSOLE_BRK, 376 (sbbc_intrfunc_t)sgcn_break_handler, NULL, 377 &sgcn_state->sgcn_sbbc_brk_state, 378 &sgcn_state->sgcn_sbbc_brk_lock); 379 380 return (DDI_SUCCESS); 381 } 382 383 /* ARGSUSED */ 384 static int 385 sgcn_close(queue_t *q, int flag, cred_t *credp) 386 { 387 int ret; 388 389 ASSERT(sgcn_state == q->q_ptr); 390 391 if (sgcn_state->sgcn_wbufcid != 0) { 392 unbufcall(sgcn_state->sgcn_wbufcid); 393 } 394 395 ret = iosram_unreg_intr(SBBC_CONSOLE_BRK); 396 ASSERT(ret == 0); 397 398 ret = iosram_unreg_intr(SBBC_CONSOLE_SPACE_OUT); 399 ASSERT(ret == 0); 400 401 ret = iosram_unreg_intr(SBBC_CONSOLE_IN); 402 ASSERT(ret == 0); 403 404 ttycommon_close(&sgcn_state->sgcn_tty); 405 406 qprocsoff(q); 407 q->q_ptr = WR(q)->q_ptr = NULL; 408 sgcn_state->sgcn_readq = NULL; 409 sgcn_state->sgcn_writeq = NULL; 410 411 return (DDI_SUCCESS); 412 } 413 414 /* 415 * Put procedure for write queue. 416 * Respond to M_IOCTL, M_DATA and M_FLUSH messages here; 417 * It put's the data onto internal sgcn_output_q. 418 */ 419 static int 420 sgcn_wput(queue_t *q, mblk_t *mp) 421 { 422 423 #ifdef SGCN_DEBUG 424 struct iocblk *iocp; 425 int i; 426 #endif 427 428 ASSERT(sgcn_state == q->q_ptr); 429 430 if (!mp->b_datap) { 431 cmn_err(CE_PANIC, "sgcn_wput(): null datap"); 432 } 433 434 #ifdef SGCN_DEBUG 435 prom_printf("sgcn_wput(): SGCN wput q=%X mp=%X rd=%X wr=%X type=%X\n", 436 q, mp, mp->b_rptr, mp->b_wptr, mp->b_datap->db_type); 437 #endif 438 439 switch (mp->b_datap->db_type) { 440 case M_IOCTL: 441 case M_CTL: 442 #ifdef SGCN_DEBUG 443 iocp = (struct iocblk *)mp->b_rptr; 444 prom_printf("sgcn_wput(): M_IOCTL cmd=%X TIOC=%X\n", 445 iocp->ioc_cmd, TIOC); 446 #endif 447 switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) { 448 case TCSETSW: 449 case TCSETSF: 450 case TCSETAW: 451 case TCSETAF: 452 case TCSBRK: 453 /* 454 * The change do not take effect until all 455 * output queued before them is drained. 456 * Put this message on the queue, so that 457 * "sgcn_start" will see it when it's done 458 * with the output before it. Poke the start 459 * routine, just in case. 460 */ 461 (void) putq(q, mp); 462 sgcn_start(); 463 break; 464 default: 465 sgcn_ioctl(q, mp); 466 } 467 break; 468 469 case M_FLUSH: 470 if (*mp->b_rptr & FLUSHW) { 471 flushq(q, FLUSHDATA); 472 *mp->b_rptr &= ~FLUSHW; 473 } 474 if (*mp->b_rptr & FLUSHR) { 475 flushq(RD(q), FLUSHDATA); 476 qreply(q, mp); 477 } else { 478 freemsg(mp); 479 } 480 break; 481 482 case M_STOP: 483 sgcn_stopped = TRUE; 484 freemsg(mp); 485 break; 486 487 case M_START: 488 sgcn_stopped = FALSE; 489 freemsg(mp); 490 qenable(q); /* Start up delayed messages */ 491 break; 492 493 case M_DATA: 494 /* 495 * Queue the message up to be transmitted, 496 * and poke the start routine. 497 */ 498 #ifdef SGCN_DEBUG 499 if (mp->b_rptr < mp->b_wptr) { 500 prom_printf("sgcn_wput(): DATA q=%X mp=%X rd=%X wr=%X\n", 501 q, mp, mp->b_rptr, mp->b_wptr); 502 prom_printf("sgcn_wput(): [[[[["); 503 for (i = 0; i < mp->b_wptr-mp->b_rptr; i++) { 504 prom_printf("%c", *(mp->b_rptr+i)); 505 } 506 prom_printf("]]]]]\n"); 507 } 508 #endif /* SGCN_DEBUG */ 509 (void) putq(q, mp); 510 sgcn_start(); 511 break; 512 513 default: 514 freemsg(mp); 515 } 516 517 return (0); 518 } 519 520 /* 521 * Process an "ioctl" message sent down to us. 522 */ 523 static void 524 sgcn_ioctl(queue_t *q, mblk_t *mp) 525 { 526 struct iocblk *iocp; 527 tty_common_t *tty; 528 mblk_t *datamp; 529 int data_size; 530 int error = 0; 531 532 #ifdef SGCN_DEBUG 533 prom_printf("sgcn_ioctl(): q=%X mp=%X\n", q, mp); 534 #endif 535 iocp = (struct iocblk *)mp->b_rptr; 536 tty = &(sgcn_state->sgcn_tty); 537 538 if (tty->t_iocpending != NULL) { 539 freemsg(tty->t_iocpending); 540 tty->t_iocpending = NULL; 541 } 542 data_size = ttycommon_ioctl(tty, q, mp, &error); 543 if (data_size != 0) { 544 if (sgcn_state->sgcn_wbufcid) 545 unbufcall(sgcn_state->sgcn_wbufcid); 546 /* call sgcn_reioctl() */ 547 sgcn_state->sgcn_wbufcid = 548 bufcall(data_size, BPRI_HI, sgcn_reioctl, sgcn_state); 549 return; 550 } 551 552 if (error < 0) { 553 iocp = (struct iocblk *)mp->b_rptr; 554 /* 555 * "ttycommon_ioctl" didn't do anything; we process it here. 556 */ 557 error = 0; 558 switch (iocp->ioc_cmd) { 559 case TCSBRK: 560 case TIOCSBRK: 561 case TIOCCBRK: 562 case TIOCMSET: 563 case TIOCMBIS: 564 case TIOCMBIC: 565 if (iocp->ioc_count != TRANSPARENT) 566 mioc2ack(mp, NULL, 0, 0); 567 else 568 mcopyin(mp, NULL, sizeof (int), NULL); 569 break; 570 571 case TIOCMGET: 572 datamp = allocb(sizeof (int), BPRI_MED); 573 if (datamp == NULL) { 574 error = EAGAIN; 575 break; 576 } 577 578 *(int *)datamp->b_rptr = 0; 579 580 if (iocp->ioc_count != TRANSPARENT) 581 mioc2ack(mp, datamp, sizeof (int), 0); 582 else 583 mcopyout(mp, NULL, sizeof (int), NULL, datamp); 584 break; 585 586 default: 587 error = EINVAL; 588 break; 589 } 590 } 591 if (error != 0) { 592 iocp->ioc_count = 0; 593 iocp->ioc_error = error; 594 mp->b_datap->db_type = M_IOCNAK; 595 } 596 qreply(q, mp); 597 } 598 599 static void 600 sgcn_reioctl(void *unit) 601 { 602 queue_t *q; 603 mblk_t *mp; 604 sgcn_t *sgcnp = (sgcn_t *)unit; 605 606 if (!sgcnp->sgcn_wbufcid) { 607 return; 608 } 609 sgcnp->sgcn_wbufcid = 0; 610 if ((q = sgcnp->sgcn_tty.t_writeq) == NULL) { 611 return; 612 } 613 614 if ((mp = sgcnp->sgcn_tty.t_iocpending) != NULL) { 615 sgcnp->sgcn_tty.t_iocpending = NULL; 616 sgcn_ioctl(q, mp); 617 } 618 } 619 620 static void 621 sgcn_start() 622 { 623 624 queue_t *q; 625 mblk_t *mp; 626 int retval; 627 628 /* 629 * read stream queue and remove data from the queue and 630 * transmit them if possible 631 */ 632 q = sgcn_state->sgcn_writeq; 633 ASSERT(q != NULL); 634 while (mp = getq(q)) { 635 switch (mp->b_datap->db_type) { 636 case M_IOCTL: 637 /* 638 * These are those IOCTLs queued up 639 * do it now 640 */ 641 sgcn_ioctl(q, mp); 642 continue; 643 default: 644 /* 645 * M_DATA 646 * Copy it from stream queue buffer to 647 * sgcn buffer 648 */ 649 retval = sgcn_transmit(q, mp); 650 651 if (retval == EBUSY) { 652 /* 653 * Console output buffer is full for 654 * sgcn_timeout_period seconds, assume 655 * SC is dead, drop all console output 656 * data from stream queue. 657 */ 658 if (sgcn_state->sgcn_sc_active < 659 gethrestime_sec() - sgcn_timeout_period) 660 sgcn_flush(); 661 return; 662 } else if (retval == EAGAIN) { 663 /* 664 * Console output just became full 665 * return 666 */ 667 mutex_enter(&sgcn_state->sgcn_lock); 668 sgcn_state->sgcn_sc_active = gethrestime_sec(); 669 mutex_exit(&sgcn_state->sgcn_lock); 670 return; 671 } else { 672 /* send more console output */ 673 mutex_enter(&sgcn_state->sgcn_lock); 674 sgcn_state->sgcn_sc_active = gethrestime_sec(); 675 mutex_exit(&sgcn_state->sgcn_lock); 676 } 677 } /* switch */ 678 } 679 680 } 681 682 static int 683 sgcn_transmit(queue_t *q, mblk_t *mp) 684 { 685 caddr_t buf; 686 mblk_t *bp; 687 int len, oldlen; 688 689 #ifdef SGCN_DEBUG 690 prom_printf("sgcn_transmit(): q=%X mp=%X\n", q, mp); 691 #endif 692 do { 693 bp = mp; 694 oldlen = len = bp->b_wptr - bp->b_rptr; 695 buf = (caddr_t)bp->b_rptr; 696 len = CONSOLE_WRITE(buf, len); 697 if (len > 0) 698 (void) iosram_send_intr(SBBC_CONSOLE_OUT); 699 if (len >= 0 && len < oldlen) { 700 /* IOSRAM is full, we are not done with mp yet */ 701 bp->b_rptr += len; 702 (void) putbq(q, mp); 703 if (len) 704 return (EAGAIN); 705 else 706 return (EBUSY); 707 } 708 mp = bp->b_cont; 709 freeb(bp); 710 } while (mp); 711 712 return (0); 713 } 714 715 /* 716 * called when SC first establishes console connection 717 * drop all the data on the output queue 718 */ 719 static void 720 sgcn_flush() 721 { 722 queue_t *q; 723 mblk_t *mp; 724 725 q = sgcn_state->sgcn_writeq; 726 727 prom_printf("sgcn_flush(): WARNING console output is dropped " 728 "time=%lX\n", gethrestime_sec()); 729 while (mp = getq(q)) { 730 freemsg(mp); 731 } 732 733 } 734 735 uint64_t sgcn_input_dropped; 736 737 /* 738 * Interrupt handlers 739 * All handlers register with SBBC driver and must follow SBBC interrupt 740 * delivery conventions. 741 */ 742 /* 743 * SC sends an interrupt when new data comes in 744 */ 745 /* ARGSUSED */ 746 void 747 sgcn_data_in_handler(caddr_t arg) 748 { 749 caddr_t buf = sgcn_state->sgcn_inbuf; 750 int i, len; 751 mblk_t *mp; 752 753 /* 754 * change interrupt state so that SBBC won't trigger 755 * another one. 756 */ 757 mutex_enter(&sgcn_state->sgcn_sbbc_in_lock); 758 sgcn_state->sgcn_sbbc_in_state = SBBC_INTR_RUNNING; 759 mutex_exit(&sgcn_state->sgcn_sbbc_in_lock); 760 761 /* update sgcn_state for SC activity information */ 762 mutex_enter(&sgcn_state->sgcn_lock); 763 sgcn_state->sgcn_sc_active = gethrestime_sec(); 764 mutex_exit(&sgcn_state->sgcn_lock); 765 766 /* enter our perimeter */ 767 entersq(sgcn_state->sgcn_readq->q_syncq, SQ_CALLBACK); 768 769 for (;;) { 770 771 /* read from console input IOSRAM */ 772 len = CONSOLE_READ(buf, sgcn_state->sgcn_inbuf_size); 773 774 if (len <= 0) { 775 776 mutex_enter(&sgcn_state->sgcn_sbbc_in_lock); 777 778 len = CONSOLE_READ(buf, sgcn_state->sgcn_inbuf_size); 779 780 if (len <= 0) { 781 sgcn_state->sgcn_sbbc_in_state = SBBC_INTR_IDLE; 782 mutex_exit(&sgcn_state->sgcn_sbbc_in_lock); 783 784 /* leave our perimeter */ 785 leavesq(sgcn_state->sgcn_readq->q_syncq, 786 SQ_CALLBACK); 787 return; 788 } else { 789 mutex_exit(&sgcn_state->sgcn_sbbc_in_lock); 790 } 791 792 } 793 794 (void) iosram_send_intr(SBBC_CONSOLE_SPACE_IN); 795 796 if (abort_enable == KIOCABORTALTERNATE) { 797 for (i = 0; i < len; i ++) { 798 if (abort_charseq_recognize(buf[i])) 799 abort_sequence_enter((char *)NULL); 800 } 801 } 802 803 /* put console input onto stream */ 804 if (sgcn_state->sgcn_readq) { 805 if ((mp = allocb(len, BPRI_MED)) == (mblk_t *)NULL) { 806 sgcn_input_dropped += len; 807 cmn_err(CE_WARN, 808 "sgcn_data_in_handler(): allocb failed" 809 " (console input dropped.)"); 810 } else { 811 bcopy(buf, mp->b_wptr, len); 812 mp->b_wptr += len; 813 putnext(sgcn_state->sgcn_readq, mp); 814 } 815 } 816 } 817 818 } 819 820 /* 821 * SC sends an interrupt when it takes output data 822 * from a full IOSRAM 823 */ 824 /* ARGSUSED */ 825 void 826 sgcn_space_2_out_handler(caddr_t arg) 827 { 828 /* 829 * change interrupt state so that SBBC won't trigger 830 * another one. 831 */ 832 mutex_enter(&sgcn_state->sgcn_sbbc_outspace_lock); 833 sgcn_state->sgcn_sbbc_outspace_state = SBBC_INTR_RUNNING; 834 mutex_exit(&sgcn_state->sgcn_sbbc_outspace_lock); 835 836 mutex_enter(&sgcn_state->sgcn_lock); 837 sgcn_state->sgcn_sc_active = gethrestime_sec(); 838 mutex_exit(&sgcn_state->sgcn_lock); 839 840 if (sgcn_state->sgcn_writeq != NULL) 841 qenable(sgcn_state->sgcn_writeq); 842 843 /* restore interrupt state */ 844 mutex_enter(&sgcn_state->sgcn_sbbc_outspace_lock); 845 sgcn_state->sgcn_sbbc_outspace_state = SBBC_INTR_IDLE; 846 mutex_exit(&sgcn_state->sgcn_sbbc_outspace_lock); 847 } 848 849 /* 850 * SC sends an interrupt when it detects BREAK sequence 851 */ 852 /* ARGSUSED */ 853 void 854 sgcn_break_handler(caddr_t arg) 855 { 856 /* 857 * change interrupt state so that SBBC won't trigger 858 * another one. 859 */ 860 mutex_enter(&sgcn_state->sgcn_sbbc_brk_lock); 861 sgcn_state->sgcn_sbbc_brk_state = SBBC_INTR_RUNNING; 862 mutex_exit(&sgcn_state->sgcn_sbbc_brk_lock); 863 864 if (abort_enable != KIOCABORTALTERNATE) 865 abort_sequence_enter((char *)NULL); 866 867 /* restore interrupt state */ 868 mutex_enter(&sgcn_state->sgcn_sbbc_brk_lock); 869 sgcn_state->sgcn_sbbc_brk_state = SBBC_INTR_IDLE; 870 mutex_exit(&sgcn_state->sgcn_sbbc_brk_lock); 871 } 872 873 /* 874 * reporting errors in console driver sgcn. 875 * since we can not trust console driver at this time, we need to 876 * log errors in other system logs 877 * error codes: 878 * EIO - iosram interface failed 879 * EPROTO - IOSRAM is corrupted 880 * EINVAL - invalid argument 881 */ 882 #define SGCN_MAX_ERROR 100 883 static void 884 sgcn_log_error(int when, int what) 885 { 886 char error_msg[256], error_code[256]; 887 static uint_t error_counter = 0; 888 889 error_counter ++; 890 891 if (error_counter > SGCN_MAX_ERROR) { 892 error_counter = 0; 893 (void) strcpy(error_msg, "!Too many sgcn errors"); 894 } else { 895 (void) sprintf(error_code, "Error %d", what); 896 897 (void) sprintf(error_msg, "!%s at %s", 898 (what == EIO) ? "IOSRAM interface failed" : 899 (what == EPROTO) ? "IOSRAM corrupted" : 900 (what == EINVAL) ? "Invalid argument" : 901 error_code, 902 (when == RW_CONSOLE_READ) ? "console input" : 903 (when == RW_CONSOLE_WRITE) ? "console output, dropped" : 904 "console I/O"); 905 } 906 907 cmn_err(CE_WARN, error_msg); 908 } 909 910 static int 911 sgcn_read_header(int rw, cnsram_header *header) 912 { 913 int rv; 914 915 /* check IOSRAM contents and read pointers */ 916 rv = iosram_read(SBBC_CONSOLE_KEY, 0, (caddr_t)header, 917 sizeof (cnsram_header)); 918 if (rv != 0) { 919 return (-1); 920 } 921 922 /* 923 * Since the header is read in a byte-by-byte fashion 924 * using ddi_rep_get8, we need to re-read the producer 925 * or consumer pointer as integer in case it has changed 926 * after part of the previous value has been read. 927 */ 928 if (rw == RW_CONSOLE_READ) { 929 rv = iosram_read(SBBC_CONSOLE_KEY, 930 OFFSETOF((*header), cnsram_in_wrptr), 931 POINTER((*header), cnsram_in_wrptr), 932 sizeof (header->cnsram_in_wrptr)); 933 } else if (rw == RW_CONSOLE_WRITE) { 934 rv = iosram_read(SBBC_CONSOLE_KEY, 935 OFFSETOF((*header), cnsram_out_rdptr), 936 POINTER((*header), cnsram_out_rdptr), 937 sizeof (header->cnsram_out_rdptr)); 938 } else 939 rv = -1; 940 941 return (rv); 942 } 943 944 static int 945 sgcn_rw(int rw, caddr_t buf, int len) 946 { 947 cnsram_header header; 948 int rv, size, nbytes; 949 950 #ifdef SGCN_DEBUG 951 prom_printf("sgcn_rw() rw = %X buf = %p len = %d\n", 952 rw, buf, len); 953 #endif /* SGCN_DEBUG */ 954 if (len == 0) 955 return (0); 956 957 /* sanity check */ 958 if (buf == NULL || len < 0) { 959 sgcn_log_error(rw, EINVAL); 960 return (-1); 961 } 962 963 /* check IOSRAM contents and read pointers */ 964 rv = sgcn_read_header(rw, &header); 965 if (rv != 0) { 966 sgcn_log_error(rw, EIO); 967 return (-1); 968 } 969 if (header.cnsram_magic != CNSRAM_MAGIC) { 970 sgcn_log_error(rw, EPROTO); 971 return (-1); 972 } 973 974 if (rw == RW_CONSOLE_READ) 975 size = header.cnsram_in_end - header.cnsram_in_begin; 976 else if (rw == RW_CONSOLE_WRITE) 977 size = header.cnsram_out_end - header.cnsram_out_begin; 978 if (size < 0) { 979 sgcn_log_error(rw, EPROTO); 980 return (-1); 981 } 982 983 if (rw == RW_CONSOLE_READ) 984 nbytes = circular_buffer_read( 985 header.cnsram_in_begin, 986 header.cnsram_in_end, 987 header.cnsram_in_rdptr, 988 header.cnsram_in_wrptr, buf, len); 989 else if (rw == RW_CONSOLE_WRITE) 990 nbytes = circular_buffer_write( 991 header.cnsram_out_begin, 992 header.cnsram_out_end, 993 header.cnsram_out_rdptr, 994 header.cnsram_out_wrptr, buf, len); 995 996 /* 997 * error log was done in circular buffer routines, 998 * no need to call sgcn_log_error() here 999 */ 1000 if (nbytes < 0) 1001 return (-1); 1002 1003 if (nbytes == 0) 1004 return (0); 1005 1006 if (rw == RW_CONSOLE_READ) { 1007 header.cnsram_in_rdptr = 1008 (header.cnsram_in_rdptr - header.cnsram_in_begin 1009 + nbytes) 1010 % size + header.cnsram_in_begin; 1011 rv = iosram_write(SBBC_CONSOLE_KEY, 1012 OFFSETOF(header, cnsram_in_rdptr), 1013 POINTER(header, cnsram_in_rdptr), 1014 sizeof (header.cnsram_in_rdptr)); 1015 } else if (rw == RW_CONSOLE_WRITE) { 1016 header.cnsram_out_wrptr = 1017 (header.cnsram_out_wrptr - header.cnsram_out_begin 1018 + nbytes) 1019 % size + header.cnsram_out_begin; 1020 rv = iosram_write(SBBC_CONSOLE_KEY, 1021 OFFSETOF(header, cnsram_out_wrptr), 1022 POINTER(header, cnsram_out_wrptr), 1023 sizeof (header.cnsram_out_wrptr)); 1024 } 1025 if (rv != 0) { 1026 sgcn_log_error(rw, EIO); 1027 return (-1); 1028 } 1029 1030 return (nbytes); 1031 } 1032 1033 /* 1034 * Circular buffer interfaces 1035 * 1036 * See sgcn.h for circular buffer structure 1037 * 1038 * The circular buffer is empty when read ptr == write ptr 1039 * and is full when read ptr is one ahead of write ptr 1040 */ 1041 /* 1042 * Write to circular buffer in IOSRAM 1043 * input: 1044 * buf buffer in main memory, contains data to be written 1045 * len length of data in bytes 1046 * begin, end, rd, wr buffer pointers 1047 * return value: 1048 * actual bytes written. 1049 */ 1050 static int 1051 circular_buffer_write(int begin, int end, int rd, int wr, caddr_t buf, int len) 1052 { 1053 int size, space, space_at_end; 1054 int rv = 0; 1055 1056 size = end - begin; 1057 if (size <= 0) { 1058 rv = EINVAL; 1059 goto out; 1060 } 1061 1062 if ((len = ((len >= size) ? (size-1) : len)) == 0) 1063 return (0); /* The buffer's full, so just return 0 now. */ 1064 1065 space = (rd - wr + size - 1) % size; 1066 len = min(len, space); 1067 space_at_end = end - wr; 1068 1069 if (rd > wr || rd <= wr && space_at_end >= len) { /* one piece */ 1070 /* write console data */ 1071 rv = iosram_write(SBBC_CONSOLE_KEY, wr, buf, len); 1072 if (rv != 0) goto out; 1073 } else { /* break into two pieces because of circular buffer */ 1074 /* write console data */ 1075 if (space_at_end) { 1076 rv = iosram_write(SBBC_CONSOLE_KEY, 1077 wr, buf, space_at_end); 1078 if (rv != 0) goto out; 1079 } 1080 if (len - space_at_end) { 1081 rv = iosram_write(SBBC_CONSOLE_KEY, 1082 begin, buf+space_at_end, len-space_at_end); 1083 if (rv != 0) goto out; 1084 } 1085 } 1086 return (len); 1087 out: 1088 sgcn_log_error(RW_CONSOLE_WRITE, rv); 1089 return (-1); 1090 } 1091 1092 /* 1093 * Read from circular buffer in IOSRAM 1094 * input: 1095 * buf preallocated buffer in memory 1096 * len size of buf 1097 * begin, end, rd, wr buffer pointers 1098 * return value: 1099 * actual bytes read 1100 */ 1101 /* ARGSUSED */ 1102 static int 1103 circular_buffer_read(int begin, int end, int rd, int wr, caddr_t buf, int len) 1104 { 1105 int size, nbytes, nbytes_at_end; 1106 int rv = 0; 1107 1108 size = end - begin; 1109 if (size <= 0) { 1110 rv = EINVAL; 1111 goto out; 1112 } 1113 nbytes = (wr - rd + size) % size; 1114 1115 nbytes = min(nbytes, len); 1116 1117 if (wr > rd) { /* one piece */ 1118 rv = iosram_read(SBBC_CONSOLE_KEY, rd, buf, nbytes); 1119 if (rv != 0) goto out; 1120 } else { /* break into two pieces because of circular buffer */ 1121 nbytes_at_end = min(nbytes, end - rd); 1122 /* read console data */ 1123 if (nbytes_at_end) { 1124 rv = iosram_read(SBBC_CONSOLE_KEY, 1125 rd, buf, nbytes_at_end); 1126 if (rv != 0) goto out; 1127 } 1128 if (nbytes-nbytes_at_end) { 1129 rv = iosram_read(SBBC_CONSOLE_KEY, 1130 begin, buf+nbytes_at_end, nbytes-nbytes_at_end); 1131 if (rv != 0) goto out; 1132 } 1133 } 1134 return (nbytes); 1135 out: 1136 sgcn_log_error(RW_CONSOLE_READ, rv); 1137 return (-1); 1138 } 1139 1140 /* 1141 * Check for abort character sequence, copied from zs_async.c 1142 */ 1143 #define CNTRL(c) ((c)&037) 1144 1145 static boolean_t 1146 abort_charseq_recognize(uchar_t ch) 1147 { 1148 static int state = 0; 1149 static char sequence[] = { '\r', '~', CNTRL('b') }; 1150 1151 if (ch == sequence[state]) { 1152 if (++state >= sizeof (sequence)) { 1153 state = 0; 1154 return (B_TRUE); 1155 } 1156 } else { 1157 state = (ch == sequence[0]) ? 1 : 0; 1158 } 1159 return (B_FALSE); 1160 } 1161 1162 static void 1163 sg_abort_seq_handler(char *msg) 1164 { 1165 char key_switch; 1166 int rv; 1167 1168 /* read virtual keyswitch position from IOSRAM */ 1169 rv = iosram_read(SBBC_KEYSWITCH_KEY, 0, &key_switch, 1); 1170 if (rv != 0) { 1171 /* default to not secure if read failed */ 1172 cmn_err(CE_NOTE, "!Read keyswitch failed (%d)", rv); 1173 key_switch = 0; 1174 } 1175 if (key_switch & SG_KEYSWITCH_POSN_SECURE) { 1176 cmn_err(CE_NOTE, "!Keyswitch is in secure mode"); 1177 } else { 1178 debug_enter(msg); 1179 } 1180 } 1181 1182 static int 1183 sgcn_rsrv(queue_t *q) 1184 { 1185 mblk_t *mp; 1186 1187 if (sgcn_stopped == TRUE) { 1188 return (0); 1189 } 1190 1191 mutex_enter(&sgcn_state->sgcn_lock); 1192 sgcn_state->sgcn_sc_active = gethrestime_sec(); 1193 mutex_exit(&sgcn_state->sgcn_lock); 1194 1195 while ((mp = getq(q)) != NULL) { 1196 if (canputnext(q)) { 1197 putnext(q, mp); 1198 } else if (mp->b_datap->db_type >= QPCTL) { 1199 (void) putbq(q, mp); 1200 } 1201 } 1202 1203 return (0); 1204 } 1205 1206 /* ARGSUSED */ 1207 static int 1208 sgcn_wsrv(queue_t *q) 1209 { 1210 if (sgcn_stopped == TRUE) 1211 return (0); 1212 1213 mutex_enter(&sgcn_state->sgcn_lock); 1214 sgcn_state->sgcn_sc_active = gethrestime_sec(); 1215 mutex_exit(&sgcn_state->sgcn_lock); 1216 1217 if (sgcn_state->sgcn_writeq != NULL) 1218 sgcn_start(); 1219 1220 return (0); 1221 } 1222