]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - usr.sbin/dconschat/dconschat.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / usr.sbin / dconschat / dconschat.c
1 /*
2  * Copyright (C) 2003
3  *      Hidetoshi Shimokawa. All rights reserved.
4  * 
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *
16  *      This product includes software developed by Hidetoshi Shimokawa.
17  *
18  * 4. Neither the name of the author nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  * 
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  * 
34  * $Id: dconschat.c,v 1.76 2003/10/23 06:21:13 simokawa Exp $
35  * $FreeBSD$
36  */
37
38 #include <sys/param.h>
39 #include <sys/types.h>
40 #include <sys/uio.h>
41 #include <unistd.h>
42 #include <fcntl.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <termios.h>
46 #include <dev/dcons/dcons.h>
47
48 #include <sys/socket.h>
49 #include <netinet/in.h>
50 #include <netdb.h>
51 #include <err.h>
52 #include <string.h>
53 #include <sys/eui64.h>
54 #include <sys/event.h>
55 #include <sys/time.h>
56 #include <arpa/telnet.h>
57
58 #include <sys/ioccom.h>
59 #include <dev/firewire/firewire.h>
60 #include <dev/firewire/iec13213.h>
61
62 #include <kvm.h>
63 #include <nlist.h>
64
65 #include <sys/errno.h>
66
67 #define DCONS_POLL_HZ           100
68 #define DCONS_POLL_OFFLINE      2       /* sec */
69
70 #define RETRY 3
71
72 #ifdef CSRVAL_VENDOR_PRIVATE
73 #define USE_CROM 1
74 #else
75 #define USE_CROM 0
76 #endif
77
78 int verbose = 0;
79 int tc_set = 0;
80 int poll_hz = DCONS_POLL_HZ;
81 static u_char abreak[3] = {13 /* CR */, 126 /* ~ */, 2 /* ^B */};
82
83 #define IS_CONSOLE(p)   ((p)->port == DCONS_CON)
84 #define IS_GDB(p)       ((p)->port == DCONS_GDB)
85
86 static struct dcons_state {
87         int fd;
88         kvm_t *kd;
89         int kq;
90         off_t paddr;
91         off_t reset;
92 #define F_READY         (1 << 1)
93 #define F_RD_ONLY       (1 << 2)
94 #define F_ALT_BREAK     (1 << 3)
95 #define F_TELNET        (1 << 4)
96 #define F_USE_CROM      (1 << 5)
97 #define F_ONE_SHOT      (1 << 6)
98 #define F_REPLAY        (1 << 7)
99         int flags;
100         enum {
101                 TYPE_KVM,
102                 TYPE_FW
103         } type;
104         int escape_state;
105         struct dcons_port {
106                 int port;
107                 int sport;
108                 struct dcons_ch o;
109                 struct dcons_ch i;
110                 u_int32_t optr;
111                 u_int32_t iptr;
112                 int s;
113                 int infd;
114                 int outfd;
115                 struct addrinfo *res;
116                 int skip_read;
117         } port[DCONS_NPORT];
118         struct timespec to;
119         struct timespec zero;
120         struct termios tsave;
121         struct termios traw;
122         char escape;
123 } sc;
124
125 static int dconschat_write_dcons(struct dcons_state *, int, char *, int);
126
127 static int
128 dread(struct dcons_state *dc, void *buf, size_t n, off_t offset)
129 {
130         switch (dc->type) {
131         case TYPE_FW:
132                 return (pread(dc->fd, buf, n, offset));
133         case TYPE_KVM:
134                 return (kvm_read(dc->kd, offset, buf, n));
135         }
136         return (-1);
137 }
138
139 static int
140 dwrite(struct dcons_state *dc, void *buf, size_t n, off_t offset)
141 {
142         if ((dc->flags & F_RD_ONLY) != 0)
143                 return (n);
144
145         switch (dc->type) {
146         case TYPE_FW:
147                 return (pwrite(dc->fd, buf, n, offset));
148         case TYPE_KVM:
149                 return (kvm_write(dc->kd, offset, buf, n));
150         }
151         return (-1);
152 }
153
154 static void
155 dconschat_reset_target(struct dcons_state *dc, struct dcons_port *p)
156 {
157         char buf[PAGE_SIZE];
158         if (dc->reset == 0)
159                 return;
160
161         snprintf(buf, PAGE_SIZE, "\r\n[dconschat reset target(addr=0x%zx)...]\r\n", dc->reset);
162         write(p->outfd, buf, strlen(buf));
163         bzero(&buf[0], PAGE_SIZE);
164         dwrite(dc, (void *)buf, PAGE_SIZE, dc->reset);
165 }
166
167
168 static void
169 dconschat_suspend(struct dcons_state *dc, struct dcons_port *p)
170 {
171         if (p->sport != 0)
172                 return;
173
174         if (tc_set)
175                 tcsetattr(STDIN_FILENO, TCSADRAIN, &dc->tsave);
176
177         printf("\n[dconschat suspend]\n");
178         kill(getpid(), SIGTSTP);
179
180         if (tc_set)
181                 tcsetattr(STDIN_FILENO, TCSADRAIN, &dc->traw);
182 }
183
184 static void
185 dconschat_sigchld(int s)
186 {
187         struct kevent kev;
188         struct dcons_port *p;
189         char buf[256];
190
191         p = &sc.port[DCONS_CON];
192
193         snprintf(buf, 256, "\r\n[child exit]\r\n");
194         write(p->outfd, buf, strlen(buf));
195
196         if (tc_set)
197                 tcsetattr(STDIN_FILENO, TCSADRAIN, &sc.traw);
198
199         EV_SET(&kev, p->infd, EVFILT_READ, EV_ADD, NOTE_LOWAT, 1, (void *)p);
200         kevent(sc.kq, &kev, 1, NULL, 0, &sc.zero);
201 }
202
203 static void
204 dconschat_fork_gdb(struct dcons_state *dc, struct dcons_port *p)
205 {
206         pid_t pid;
207         char buf[256], com[256];
208         struct kevent kev;
209
210         pid = fork();
211         if (pid < 0) {
212                 snprintf(buf, 256, "\r\n[%s: fork failed]\r\n", __FUNCTION__);
213                 write(p->outfd, buf, strlen(buf));
214         }
215
216
217         if (pid == 0) {
218                 /* child */
219                 if (tc_set)
220                         tcsetattr(STDIN_FILENO, TCSADRAIN, &dc->tsave);
221
222                 snprintf(com, sizeof(buf), "kgdb -r :%d kernel",
223                         dc->port[DCONS_GDB].sport);
224                 snprintf(buf, 256, "\n[fork %s]\n", com);
225                 write(p->outfd, buf, strlen(buf));
226
227                 execl("/bin/sh", "/bin/sh", "-c", com, 0);
228
229                 snprintf(buf, 256, "\n[fork failed]\n");
230                 write(p->outfd, buf, strlen(buf));
231
232                 if (tc_set)
233                         tcsetattr(STDIN_FILENO, TCSADRAIN, &dc->traw);
234
235                 exit(0);
236         } else {
237                 signal(SIGCHLD, dconschat_sigchld);
238                 EV_SET(&kev, p->infd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
239                 kevent(sc.kq, &kev, 1, NULL, 0, &sc.zero);
240         }
241 }
242
243
244 static void
245 dconschat_cleanup(int sig)
246 {
247         struct dcons_state *dc;
248         int status;
249
250         dc = &sc;
251         if (tc_set != 0)
252                 tcsetattr(STDIN_FILENO, TCSADRAIN, &dc->tsave);
253
254         if (sig > 0)
255                 printf("\n[dconschat exiting with signal %d ...]\n", sig);
256         else
257                 printf("\n[dconschat exiting...]\n");
258         wait(&status);
259         exit(0);
260 }
261
262 #if USE_CROM
263 static int
264 dconschat_get_crom(struct dcons_state *dc)
265 {
266         off_t addr;
267         int i, state = 0;
268         u_int32_t buf, hi = 0, lo = 0, reset_hi = 0, reset_lo = 0;
269         struct csrreg *reg; 
270
271         reg = (struct csrreg *)&buf;
272         addr = 0xffff;
273         addr = (addr << 32) | 0xf0000400;
274         for (i = 20; i < 0x400; i += 4) {
275                 if (dread(dc, &buf, 4, addr + i) < 0) {
276                         if (verbose)
277                                 warn("crom read faild");
278                         goto out;
279                 }
280                 buf = ntohl(buf);
281                 if (verbose)
282                         printf("%d %02x %06x\n", state, reg->key, reg->val);
283                 switch (state) {
284                 case 0:
285                         if (reg->key == CSRKEY_SPEC &&
286                                         reg->val == CSRVAL_VENDOR_PRIVATE)
287                                 state = 1;
288                         break;
289                 case 1:
290                         if (reg->key == CSRKEY_VER &&
291                                         reg->val == DCONS_CSR_VAL_VER)
292                                 state = 2;
293                         break;
294                 case 2:
295                         switch (reg->key) {
296                         case DCONS_CSR_KEY_HI:
297                                 hi = reg->val;
298                                 break;
299                         case DCONS_CSR_KEY_LO:
300                                 lo = reg->val;
301                                 break;
302                         case DCONS_CSR_KEY_RESET_HI:
303                                 reset_hi = reg->val;
304                                 break;
305                         case DCONS_CSR_KEY_RESET_LO:
306                                 reset_lo = reg->val;
307                                 goto out;
308                                 break;
309                         case 0x81:
310                                 break;
311                         default:
312                                 state = 0;
313                         }
314                         break;
315                 }
316         }
317 out:
318         if (verbose)
319                 printf("addr: %06x %06x\n", hi, lo); 
320         dc->paddr = ((off_t)hi << 24) | lo;
321         dc->reset = ((off_t)reset_hi << 24) | reset_lo;
322         if (dc->paddr == 0)
323                 return (-1);
324         return (0);
325 }
326 #endif
327
328 static void
329 dconschat_ready(struct dcons_state *dc, int ready, char *reason)
330 {
331         static char oldreason[64] = "";
332         int old;
333
334         old = (dc->flags & F_READY) ? 1 : 0;
335
336         if (ready) {
337                 dc->flags |= F_READY;
338                 if (ready != old)
339                         printf("[dcons connected]\r\n");
340                 oldreason[0] = 0;
341         } else {
342                 dc->flags &= ~F_READY;
343                 if (strncmp(oldreason, reason, sizeof(oldreason)) != 0) {
344                         printf("[dcons disconnected (%s)]\r\n", reason);
345                         strlcpy(oldreason, reason, sizeof(oldreason));
346                 }
347         }
348 }
349
350 static int
351 dconschat_fetch_header(struct dcons_state *dc)
352 {
353         char ebuf[64];
354         struct dcons_buf dbuf;
355         int j;
356
357 #if USE_CROM
358         if (dc->paddr == 0 && (dc->flags & F_USE_CROM) != 0) {
359                 if (dconschat_get_crom(dc)) {
360                         dconschat_ready(dc, 0, "get crom failed");
361                         return (-1);
362                 }
363         }
364 #endif
365
366         if (dread(dc, &dbuf, DCONS_HEADER_SIZE, dc->paddr) < 0) {
367                 dconschat_ready(dc, 0, "read header failed");
368                 return (-1);
369         }
370         if (dbuf.magic != htonl(DCONS_MAGIC)) {
371                 if ((dc->flags & F_USE_CROM) !=0)
372                         dc->paddr = 0;
373                 snprintf(ebuf, sizeof(ebuf), "wrong magic 0x%08x", dbuf.magic);
374                 dconschat_ready(dc, 0, ebuf);
375                 return (-1);
376         }
377         if (ntohl(dbuf.version) != DCONS_VERSION) {
378                 snprintf(ebuf, sizeof(ebuf),
379 #if __FreeBSD_version < 500000
380                     "wrong version %ld,%d",
381 #else
382                     "wrong version %d,%d",
383 #endif
384                     ntohl(dbuf.version), DCONS_VERSION);
385                 /* XXX exit? */
386                 dconschat_ready(dc, 0, ebuf);
387                 return (-1);
388         }
389
390         for (j = 0; j < DCONS_NPORT; j++) {
391                 struct dcons_ch *o, *i;
392                 off_t newbuf;
393                 int new = 0;
394
395                 o = &dc->port[j].o;
396                 newbuf = dc->paddr + ntohl(dbuf.ooffset[j]);
397                 o->size = ntohl(dbuf.osize[j]);
398
399                 if (newbuf != o->buf) {
400                         /* buffer address has changes */
401                         new = 1;
402                         o->gen = ntohl(dbuf.optr[j]) >> DCONS_GEN_SHIFT;
403                         o->pos = ntohl(dbuf.optr[j]) & DCONS_POS_MASK;
404                         o->buf = newbuf;
405                 }
406
407                 i = &dc->port[j].i;
408                 i->size = ntohl(dbuf.isize[j]);
409                 i->gen = ntohl(dbuf.iptr[j]) >> DCONS_GEN_SHIFT;
410                 i->pos = ntohl(dbuf.iptr[j]) & DCONS_POS_MASK;
411                 i->buf = dc->paddr + ntohl(dbuf.ioffset[j]);
412
413                 if (verbose) {
414                         printf("port %d   size offset   gen   pos\n", j);
415 #if __FreeBSD_version < 500000
416                         printf("output: %5d %6ld %5d %5d\n"
417                                 "input : %5d %6ld %5d %5d\n",
418 #else
419                         printf("output: %5d %6d %5d %5d\n"
420                                 "input : %5d %6d %5d %5d\n",
421 #endif
422                         o->size, ntohl(dbuf.ooffset[j]), o->gen, o->pos,
423                         i->size, ntohl(dbuf.ioffset[j]), i->gen, i->pos);
424                 }
425
426                 if (IS_CONSOLE(&dc->port[j]) && new &&
427                     (dc->flags & F_REPLAY) !=0) {
428                         if (o->gen > 0)
429                                 o->gen --;
430                         else
431                                 o->pos = 0;
432                 }
433         }
434         dconschat_ready(dc, 1, NULL);
435         return(0);
436 }
437
438 static int
439 dconschat_get_ptr (struct dcons_state *dc) {
440         int dlen, i;
441         u_int32_t ptr[DCONS_NPORT*2+1];
442         static int retry = RETRY;
443         char ebuf[64];
444
445 again:
446         dlen = dread(dc, &ptr, sizeof(ptr),
447                 dc->paddr + __offsetof(struct dcons_buf, magic));
448
449         if (dlen < 0) {
450                 if (errno == ETIMEDOUT)
451                         if (retry -- > 0)
452                                 goto again;
453                 dconschat_ready(dc, 0, "get ptr failed");
454                 return(-1);
455         }
456         if (ptr[0] != htonl(DCONS_MAGIC)) {
457                 if ((dc->flags & F_USE_CROM) !=0)
458                         dc->paddr = 0;
459                 snprintf(ebuf, sizeof(ebuf), "wrong magic 0x%08x", ptr[0]);
460                 dconschat_ready(dc, 0, ebuf);
461                 return(-1);
462         }
463         retry = RETRY;
464         for (i = 0; i < DCONS_NPORT; i ++) {
465                 dc->port[i].optr = ntohl(ptr[i + 1]);
466                 dc->port[i].iptr = ntohl(ptr[DCONS_NPORT + i + 1]);
467         }
468         return(0);
469 }
470
471 #define MAX_XFER 2048
472 static int
473 dconschat_read_dcons(struct dcons_state *dc, int port, char *buf, int len)
474 {
475         struct dcons_ch *ch;
476         u_int32_t ptr, pos, gen, next_gen;
477         int rlen, dlen, lost;
478         int retry = RETRY;
479
480         ch = &dc->port[port].o;
481         ptr = dc->port[port].optr;
482         gen = ptr >> DCONS_GEN_SHIFT;
483         pos = ptr & DCONS_POS_MASK;
484         if (gen == ch->gen && pos == ch->pos)
485                 return (-1);
486
487         next_gen = DCONS_NEXT_GEN(ch->gen);
488         /* XXX sanity check */
489         if (gen == ch->gen) {
490                 if (pos > ch->pos)
491                         goto ok;
492                 lost = ch->size * DCONS_GEN_MASK - ch->pos;
493                 ch->pos = 0;
494         } else if (gen == next_gen) {
495                 if (pos <= ch->pos)
496                         goto ok;
497                 lost = pos - ch->pos;
498                 ch->pos = pos;
499         } else {
500                 lost = gen - ch->gen;
501                 if (lost < 0)
502                         lost += DCONS_GEN_MASK;
503                 if (verbose)
504                         printf("[genskip %d]", lost);
505                 lost = lost * ch->size - ch->pos;
506                 ch->pos = 0;
507                 ch->gen = gen;
508         }
509         /* generation skipped !! */
510         /* XXX discard */
511         if (verbose)
512                 printf("[lost %d]", lost);
513 ok:
514         if (gen == ch->gen)
515                 rlen = pos - ch->pos;
516         else
517                 rlen = ch->size - ch->pos;
518
519         if (rlen > MAX_XFER)
520                 rlen = MAX_XFER;
521         if (rlen > len)
522                 rlen = len;
523
524 #if 1
525         if (verbose == 1)
526                 printf("[%d]", rlen); fflush(stdout);
527 #endif
528
529 again:
530         dlen = dread(dc, buf, rlen, ch->buf + ch->pos);
531         if (dlen < 0) {
532                 if (errno == ETIMEDOUT)
533                         if (retry -- > 0)
534                                 goto again;
535                 dconschat_ready(dc, 0, "read buffer failed");
536                 return(-1);
537         }
538         if (dlen != rlen)
539                 warnx("dlen(%d) != rlen(%d)\n", dlen, rlen);
540         ch->pos += dlen;
541         if (ch->pos >= ch->size) {
542                 ch->gen = next_gen;
543                 ch->pos = 0;
544                 if (verbose)
545                         printf("read_dcons: gen=%d", ch->gen);
546         }
547         return (dlen);
548 }
549
550 static int
551 dconschat_write_dcons(struct dcons_state *dc, int port, char *buf, int blen)
552 {
553         struct dcons_ch *ch;
554         u_int32_t ptr;
555         int len, wlen;
556         int retry = RETRY;
557
558         ch = &dc->port[port].i;
559         ptr = dc->port[port].iptr;
560
561         /* the others may advance the pointer sync with it */
562         ch->gen = ptr >> DCONS_GEN_SHIFT;
563         ch->pos = ptr & DCONS_POS_MASK;
564
565         while(blen > 0) {
566                 wlen = MIN(blen, ch->size - ch->pos);
567                 wlen = MIN(wlen, MAX_XFER);
568                 len = dwrite(dc, buf, wlen, ch->buf + ch->pos);
569                 if (len < 0) {
570                         if (errno == ETIMEDOUT)
571                                 if (retry -- > 0)
572                                         continue; /* try again */
573                         dconschat_ready(dc, 0, "write buffer failed");
574                         return(-1);
575                 }
576                 ch->pos += len;
577                 buf += len;
578                 blen -= len;
579                 if (ch->pos >= ch->size) {
580                         ch->gen = DCONS_NEXT_GEN(ch->gen);
581                         ch->pos = 0;
582                         if (verbose)
583                                 printf("write_dcons: gen=%d", ch->gen);
584                                 
585                 }
586         }
587
588         ptr = DCONS_MAKE_PTR(ch);
589         dc->port[port].iptr = ptr;
590
591         if (verbose > 2)
592                 printf("(iptr: 0x%x)", ptr);
593 again:
594         len = dwrite(dc, &ptr, sizeof(u_int32_t),
595                 dc->paddr + __offsetof(struct dcons_buf, iptr[port]));
596         if (len < 0) {
597                 if (errno == ETIMEDOUT)
598                         if (retry -- > 0)
599                                 goto again;
600                 dconschat_ready(dc, 0, "write ptr failed");
601                 return(-1);
602         }
603         return(0);
604 }
605
606
607 static int
608 dconschat_write_socket(int fd, char *buf, int len)
609 {
610         write(fd, buf, len);
611         if (verbose > 1) {
612                 buf[len] = 0;
613                 printf("<- %s\n", buf);
614         }
615         return (0);
616 }
617
618 static void
619 dconschat_init_socket(struct dcons_state *dc, int port, char *host, int sport)
620 {
621         struct addrinfo hints, *res;
622         int on = 1, error;
623         char service[10];
624         struct kevent kev;
625         struct dcons_port *p;
626
627         p = &dc->port[port];
628         p->port = port;
629         p->sport = sport;
630         p->infd = p->outfd = -1;
631
632         if (sport < 0)
633                 return;
634
635         if (sport == 0) {
636
637                 /* Use stdin and stdout */
638                 p->infd = STDIN_FILENO;
639                 p->outfd = STDOUT_FILENO;
640                 p->s = -1;
641                 if (tc_set == 0 &&
642                     tcgetattr(STDIN_FILENO, &dc->tsave) == 0) {
643                         dc->traw = dc->tsave;
644                         cfmakeraw(&dc->traw);
645                         tcsetattr(STDIN_FILENO, TCSADRAIN, &dc->traw);
646                         tc_set = 1;
647                 }
648                 EV_SET(&kev, p->infd, EVFILT_READ, EV_ADD, NOTE_LOWAT, 1,
649                     (void *)p);
650                 kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
651                 return;
652         }
653
654         memset(&hints, 0, sizeof(hints));
655         hints.ai_flags = AI_PASSIVE;
656 #if 1   /* gdb can talk v4 only */
657         hints.ai_family = PF_INET;
658 #else
659         hints.ai_family = PF_UNSPEC;
660 #endif
661         hints.ai_socktype = SOCK_STREAM;
662         hints.ai_protocol = 0;
663
664         if (verbose)
665                 printf("%s:%d for port %d\n",
666                         host == NULL ? "*" : host, sport, port);
667         snprintf(service, sizeof(service), "%d", sport);
668         error = getaddrinfo(host, service,  &hints, &res);
669         if (error)
670                 errx(1, "tcp/%s: %s\n", service, gai_strerror(error));
671         p->res = res;
672         p->s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
673         if (p->s < 0)
674                 err(1, "socket");
675         setsockopt(p->s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
676
677         if (bind(p->s, p->res->ai_addr, p->res->ai_addrlen) < 0) {
678                 err(1, "bind");
679         }
680         if (listen(p->s, 1) < 0)
681                 err(1, "listen");
682         EV_SET(&kev, p->s, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, (void *)p);
683         error = kevent(dc->kq, &kev, 1, NULL, 0, &dc->to);
684         if (error < 0)
685                 err(1, "kevent");
686         return;
687 }
688
689 static int
690 dconschat_accept_socket(struct dcons_state *dc, struct dcons_port *p)
691 {
692         socklen_t addrlen;
693         int ns, flags;
694         struct kevent kev;
695
696         /* accept connection */
697         addrlen = p->res->ai_addrlen;
698         ns = accept(p->s, p->res->ai_addr, &addrlen);
699         if (ns < 0)
700                 err(1, "accept");
701         if (verbose)
702                 printf("port%d accepted\n", p->port);
703
704         flags = fcntl(ns, F_GETFL, 0);
705         flags |= O_NDELAY;
706         fcntl(ns, F_SETFL, flags);
707 #if 1
708         if (IS_CONSOLE(p) && (dc->flags & F_TELNET) != 0) {
709                 char sga[] = {IAC, WILL, TELOPT_SGA};
710                 char linemode[] = {IAC, DONT, TELOPT_LINEMODE};
711                 char echo[] = {IAC, WILL, TELOPT_ECHO};
712                 char bin[] = {IAC, DO, TELOPT_BINARY};
713
714                 write(ns, sga, sizeof(sga));
715                 write(ns, linemode, sizeof(linemode));
716                 write(ns, echo, sizeof(echo));
717                 write(ns, bin, sizeof(bin));
718                 p->skip_read = 0;
719         }
720 #endif
721         /* discard backlog on GDB port */
722         if (IS_GDB(p)) {
723                 char buf[2048];
724                 int len;
725
726                 while ((len = dconschat_read_dcons(dc, DCONS_GDB, &buf[0],
727                                  2048)) > 0)
728                         if (verbose)
729                                 printf("discard %d chars on GDB port\n", len);
730         }
731
732         p->infd = p->outfd = ns;
733         EV_SET(&kev, ns, EVFILT_READ, EV_ADD, NOTE_LOWAT, 1, (void *)p);
734         kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
735         return(0);
736 }
737
738 static int
739 dconschat_read_filter(struct dcons_state *dc, struct dcons_port *p,
740     u_char *sp, int slen, u_char *dp, int *dlen)
741 {
742         int skip;
743         char *buf;
744
745         while (slen > 0) {
746                 skip = 0;
747                 if (IS_CONSOLE(p)) {
748                         if ((dc->flags & F_TELNET) != 0) {
749                                 /* XXX Telnet workarounds */
750                                 if (p->skip_read -- > 0) {
751                                         sp ++;
752                                         slen --;
753                                         continue;
754                                 }
755                                 if (*sp == IAC) {
756                                         if (verbose)
757                                                 printf("(IAC)");
758                                         p->skip_read = 2;
759                                         sp ++;
760                                         slen --;
761                                         continue;
762                                 }
763                                 if (*sp == 0) {
764                                         if (verbose)
765                                                 printf("(0 stripped)");
766                                         sp ++;
767                                         slen --;
768                                         continue;
769                                 }
770                         }
771                         switch (dc->escape_state) {
772                         case STATE1:
773                                 if (*sp == dc->escape) {
774                                         skip = 1;
775                                         dc->escape_state = STATE2;
776                                 } else
777                                         dc->escape_state = STATE0;
778                                 break;
779                         case STATE2:
780                                 dc->escape_state = STATE0;
781                                 skip = 1;
782                                 if (*sp == '.')
783                                         dconschat_cleanup(0);
784                                 else if (*sp == CTRL('B')) {
785                                         bcopy(abreak, dp, 3);
786                                         dp += 3;
787                                         *dlen += 3;
788                                 }
789                                 else if (*sp == CTRL('G'))
790                                         dconschat_fork_gdb(dc, p);
791                                 else if ((*sp == CTRL('R'))
792                                                 && (dc->reset != 0)) {
793                                         dc->escape_state = STATE3;
794                                         buf = "\r\n[Are you sure to reset target? (y/N)]";
795                                         write(p->outfd, buf, strlen(buf));
796                                 } else if (*sp == CTRL('Z'))
797                                         dconschat_suspend(dc, p);
798                                 else {
799                                         skip = 0;
800                                         *dp++ = dc->escape;
801                                         (*dlen) ++;
802                                 }
803                                 break;
804                         case STATE3:
805                                 dc->escape_state = STATE0;
806                                 skip = 1;
807                                 if (*sp == 'y')
808                                         dconschat_reset_target(dc, p);
809                                 else {
810                                         write(p->outfd, sp, 1);
811                                         write(p->outfd, "\r\n", 2);
812                                 }
813                                 break;
814                         }
815                         if (*sp == KEY_CR)
816                                 dc->escape_state = STATE1;
817                 } else if (IS_GDB(p)) {
818                         /* GDB: ^C -> CR+~+^B */
819                         if (*sp == CTRL('C') && (dc->flags & F_ALT_BREAK) != 0) {
820                                 bcopy(abreak, dp, 3);
821                                 dp += 3;
822                                 sp ++;
823                                 *dlen += 3;
824                                 /* discard rest of the packet */
825                                 slen = 0;
826                                 break;
827                         }
828                 }
829                 if (!skip) {
830                         *dp++ = *sp;
831                         (*dlen) ++;
832                 }
833                 sp ++;
834                 slen --;
835         }
836         return (*dlen);
837                         
838 }
839
840 static int
841 dconschat_read_socket(struct dcons_state *dc, struct dcons_port *p)
842 {
843         struct kevent kev;
844         int len, wlen;
845         char rbuf[MAX_XFER], wbuf[MAX_XFER+2];
846
847         if ((len = read(p->infd, rbuf, sizeof(rbuf))) > 0) {
848                 wlen = 0;
849                 dconschat_read_filter(dc, p, rbuf, len, wbuf, &wlen);
850                 /* XXX discard if not ready*/
851                 if (wlen > 0 && (dc->flags & F_READY) != 0) {
852                         dconschat_write_dcons(dc, p->port, wbuf, wlen);
853                         if (verbose > 1) {
854                                 wbuf[wlen] = 0;
855                                 printf("-> %s\n", wbuf);
856                         } else if (verbose == 1) {
857                                 printf("(%d)", wlen);
858                                 fflush(stdout);
859                         }
860                 }
861         } else {
862                 if (verbose) {
863                         if (len == 0)
864                                 warnx("port%d: closed", p->port);
865                         else
866                                 warn("port%d: read", p->port);
867                 }
868                 EV_SET(&kev, p->infd, EVFILT_READ,
869                         EV_DELETE, 0, 0, NULL);
870                 kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
871                 close(p->infd);
872                 close(p->outfd);
873                 /* XXX exit for pipe case XXX */
874                 EV_SET(&kev, p->s, EVFILT_READ,
875                                 EV_ADD | EV_ONESHOT, 0, 0, (void *) p);
876                 kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
877                 p->infd = p->outfd = -1;
878         }
879         return(0);
880 }
881 #define NEVENT 5
882 static int
883 dconschat_proc_socket(struct dcons_state *dc)
884 {
885         struct kevent elist[NEVENT], *e;
886         int i, n;
887         struct dcons_port *p;
888
889         n = kevent(dc->kq, NULL, 0, elist, NEVENT, &dc->to);
890         for (i = 0; i < n; i ++) {
891                 e = &elist[i];
892                 p = (struct dcons_port *)e->udata;
893                 if (e->ident == p->s) {
894                         dconschat_accept_socket(dc, p);
895                 } else {
896                         dconschat_read_socket(dc, p);
897                 }
898         }
899         return(0);
900 }
901
902 static int
903 dconschat_proc_dcons(struct dcons_state *dc)
904 {
905         int port, len, err;
906         char buf[MAX_XFER];
907         struct dcons_port *p;
908
909         err = dconschat_get_ptr(dc);
910         if (err) {
911                 /* XXX we should stop write operation too. */
912                 return err;
913         }
914         for (port = 0; port < DCONS_NPORT; port ++) {
915                 p = &dc->port[port];
916                 if (p->infd < 0)
917                         continue;
918                 while ((len = dconschat_read_dcons(dc, port, buf,
919                     sizeof(buf))) > 0) {
920                         dconschat_write_socket(p->outfd, buf, len);
921                         if ((err = dconschat_get_ptr(dc)))
922                                 return (err);
923                 }
924                 if ((dc->flags & F_ONE_SHOT) != 0 && len <= 0)
925                         dconschat_cleanup(0);
926         }
927         return 0;
928 }
929
930 static int
931 dconschat_start_session(struct dcons_state *dc)
932 {
933         int counter = 0;
934         int retry = 0;
935         int retry_unit_init = MAX(1, poll_hz / 10);
936         int retry_unit_offline = poll_hz * DCONS_POLL_OFFLINE;
937         int retry_unit = retry_unit_init;
938         int retry_max = retry_unit_offline / retry_unit;
939
940         while (1) {
941                 if (((dc->flags & F_READY) == 0) && ++counter > retry_unit) {
942                         counter = 0;
943                         retry ++;
944                         if (retry > retry_max)
945                                 retry_unit = retry_unit_offline;
946                         if (verbose) {
947                                 printf("%d/%d ", retry, retry_max);
948                                 fflush(stdout);
949                         }
950                         dconschat_fetch_header(dc);
951                 }
952                 if ((dc->flags & F_READY) != 0) {
953                         counter = 0;
954                         retry = 0;
955                         retry_unit = retry_unit_init;
956                         dconschat_proc_dcons(dc);
957                 }
958                 dconschat_proc_socket(dc);
959         }
960         return (0);
961 }
962
963 static void
964 usage(void)
965 {
966         fprintf(stderr,
967             "usage: dconschat [-brvwRT1] [-h hz] [-C port] [-G port]\n"
968             "\t\t\t[-M core] [-N system]\n"
969             "\t\t\t[-u unit] [-a address] [-t target_eui64]\n"
970             "\t-b       translate ctrl-C to CR+~+ctrl-B on gdb port\n"
971             "\t-v       verbose\n"
972             "\t-w       listen on wildcard address rather than localhost\n"
973             "\t-r       replay old buffer on connection\n"
974             "\t-R       read-only\n"
975             "\t-T       enable Telnet protocol workaround on console port\n"
976             "\t-1       one shot: read buffer and exit\n"
977             "\t-h       polling rate\n"
978             "\t-C       port number for console port\n"
979             "\t-G       port number for gdb port\n"
980             "\t(for KVM)\n"
981             "\t-M       core file\n"
982             "\t-N       system file\n"
983             "\t(for FireWire)\n"
984             "\t-u       specify unit number of the bus\n"
985             "\t-t       EUI64 of target host (must be specified)\n"
986             "\t-a       physical address of dcons buffer on target host\n"
987         );
988         exit(0);
989 }
990 int
991 main(int argc, char **argv)
992 {
993         struct dcons_state *dc;
994         struct fw_eui64 eui;
995         struct eui64 target;
996         char devname[256], *core = NULL, *system = NULL;
997         int i, ch, error;
998         int unit=0, wildcard=0;
999         int port[DCONS_NPORT];
1000
1001         bzero(&sc, sizeof(sc));
1002         dc = &sc;
1003         dc->flags |= USE_CROM ? F_USE_CROM : 0;
1004
1005         /* default ports */
1006         port[0] = 0;    /* stdin/out for console */
1007         port[1] = -1;   /* disable gdb port */
1008
1009         /* default escape char */
1010         dc->escape = KEY_TILDE;
1011
1012         while ((ch = getopt(argc, argv, "a:be:h:rt:u:vwC:G:M:N:RT1")) != -1) {
1013                 switch(ch) {
1014                 case 'a':
1015                         dc->paddr = strtoull(optarg, NULL, 0);
1016                         dc->flags &= ~F_USE_CROM;
1017                         break;
1018                 case 'b':
1019                         dc->flags |= F_ALT_BREAK;
1020                         break;
1021                 case 'e':
1022                         dc->escape = optarg[0];
1023                         break;
1024                 case 'h':
1025                         poll_hz = strtoul(optarg, NULL, 0);
1026                         if (poll_hz == 0)
1027                                 poll_hz = DCONS_POLL_HZ;
1028                         break;
1029                 case 'r':
1030                         dc->flags |= F_REPLAY;
1031                         break;
1032                 case 't':
1033                         if (eui64_hostton(optarg, &target) != 0 &&
1034                             eui64_aton(optarg, &target) != 0)
1035                                 errx(1, "invalid target: %s", optarg);
1036                         eui.hi = ntohl(*(u_int32_t*)&(target.octet[0]));
1037                         eui.lo = ntohl(*(u_int32_t*)&(target.octet[4]));
1038                         dc->type = TYPE_FW;
1039                         break;
1040                 case 'u':
1041                         unit = strtol(optarg, NULL, 0);
1042                         break;
1043                 case 'v':
1044                         verbose ++;
1045                         break;
1046                 case 'w':
1047                         wildcard = 1;
1048                         break;
1049                 case 'C':
1050                         port[0] = strtol(optarg, NULL, 0);
1051                         break;
1052                 case 'G':
1053                         port[1] = strtol(optarg, NULL, 0);
1054                         break;
1055                 case 'M':
1056                         core = optarg;
1057                         break;  
1058                 case 'N':
1059                         system = optarg;
1060                         break;  
1061                 case 'R':
1062                         dc->flags |= F_RD_ONLY;
1063                         break;
1064                 case 'T':
1065                         dc->flags |= F_TELNET;
1066                         break;
1067                 case '1':
1068                         dc->flags |= F_ONE_SHOT | F_REPLAY;
1069                         break;
1070                 default:
1071                         usage();
1072                 }
1073         }
1074         if (dc->paddr == 0 && (dc->flags & F_USE_CROM) == 0) {
1075                 warnx("no address specified");
1076                 usage();
1077         }
1078
1079         if (port[0] < 0 && port[1] < 0) {
1080                 warnx("no port specified");
1081                 usage();
1082         }
1083
1084         /* set signal handler */
1085         signal(SIGHUP, dconschat_cleanup);
1086         signal(SIGINT, dconschat_cleanup);
1087         signal(SIGPIPE, dconschat_cleanup);
1088         signal(SIGTERM, dconschat_cleanup);
1089
1090         /* init firewire */
1091         switch (dc->type) {
1092         case TYPE_FW:
1093 #define MAXDEV 10
1094                 for (i = 0; i < MAXDEV; i ++) {
1095                         snprintf(devname, sizeof(devname),
1096                             "/dev/fwmem%d.%d", unit, i);
1097                         dc->fd = open(devname, O_RDWR);
1098                         if (dc->fd >= 0)
1099                                 goto found;
1100                 }
1101                 err(1, "open");
1102 found:
1103                 error = ioctl(dc->fd, FW_SDEUI64, &eui);
1104                 if (error)
1105                         err(1, "ioctl");
1106                 break;
1107         case TYPE_KVM:
1108         {
1109                 struct nlist nl[] = {{"dcons_buf"}, {""}};
1110                 void *dcons_buf;
1111
1112                 dc->kd = kvm_open(system, core, NULL,
1113                     (dc->flags & F_RD_ONLY) ? O_RDONLY : O_RDWR, "dconschat");
1114                 if (dc->kd == NULL)
1115                         errx(1, "kvm_open");
1116
1117                 if (kvm_nlist(dc->kd, nl) < 0)
1118                         errx(1, "kvm_nlist: %s", kvm_geterr(dc->kd));
1119
1120                 if (kvm_read(dc->kd, nl[0].n_value, &dcons_buf,
1121                     sizeof(void *)) < 0)
1122                         errx(1, "kvm_read: %s", kvm_geterr(dc->kd));
1123                 dc->paddr = (uintptr_t)dcons_buf;
1124                 if (verbose)
1125                         printf("dcons_buf: 0x%x\n", (uint)dc->paddr);
1126                 break;
1127         }
1128         }
1129         dconschat_fetch_header(dc);
1130
1131         /* init sockets */
1132         dc->kq = kqueue();
1133         if (poll_hz == 1) {
1134                 dc->to.tv_sec = 1;
1135                 dc->to.tv_nsec = 0;
1136         } else {
1137                 dc->to.tv_sec = 0;
1138                 dc->to.tv_nsec = 1000 * 1000 * 1000 / poll_hz;
1139         }
1140         dc->zero.tv_sec = 0;
1141         dc->zero.tv_nsec = 0;
1142         for (i = 0; i < DCONS_NPORT; i++)
1143                 dconschat_init_socket(dc, i,
1144                     wildcard ? NULL : "localhost", port[i]);
1145
1146         dconschat_start_session(dc);
1147
1148         for (i = 0; i < DCONS_NPORT; i++) {
1149                 freeaddrinfo(dc->port[i].res);
1150         }
1151         return (0);
1152 }