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
_init(void)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
_fini(void)212 _fini(void)
213 {
214 /* can't remove console driver */
215 return (EBUSY);
216 }
217
218 int
_info(struct modinfo * modinfop)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
sgcn_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)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
sgcn_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)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
sgcn_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)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
sgcn_open(queue_t * q,dev_t * devp,int oflag,int sflag,cred_t * credp)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
sgcn_close(queue_t * q,int flag,cred_t * credp)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
sgcn_wput(queue_t * q,mblk_t * mp)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
sgcn_ioctl(queue_t * q,mblk_t * mp)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
sgcn_reioctl(void * unit)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
sgcn_start()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
sgcn_transmit(queue_t * q,mblk_t * mp)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
sgcn_flush()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
sgcn_data_in_handler(caddr_t arg)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
sgcn_space_2_out_handler(caddr_t arg)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
sgcn_break_handler(caddr_t arg)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
sgcn_log_error(int when,int what)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
sgcn_read_header(int rw,cnsram_header * header)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
sgcn_rw(int rw,caddr_t buf,int len)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
circular_buffer_write(int begin,int end,int rd,int wr,caddr_t buf,int len)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
circular_buffer_read(int begin,int end,int rd,int wr,caddr_t buf,int len)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
abort_charseq_recognize(uchar_t ch)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
sg_abort_seq_handler(char * msg)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
sgcn_rsrv(queue_t * q)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
sgcn_wsrv(queue_t * q)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