xref: /dragonfly/usr.sbin/dconschat/dconschat.c (revision 46ec0f38e0c4d76b1dac19910787c15eb5d9182f)
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: src/usr.sbin/dconschat/dconschat.c,v 1.6 2004/05/26 22:59:54 brooks Exp $
36  * $DragonFly: src/usr.sbin/dconschat/dconschat.c,v 1.1 2004/09/23 06:38:29 simokawa Exp $
37  */
38 
39 #include <sys/param.h>
40 #include <sys/types.h>
41 #include <sys/uio.h>
42 #include <unistd.h>
43 #include <fcntl.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <termios.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 #ifdef __DragonFly__
59 #include <dev/misc/dcons/dcons.h>
60 #include <bus/firewire/firewire.h>
61 #include <bus/firewire/iec13213.h>
62 #else
63 #include <dev/dcons/dcons.h>
64 #include <dev/firewire/firewire.h>
65 #include <dev/firewire/iec13213.h>
66 #endif
67 
68 #include <kvm.h>
69 #include <nlist.h>
70 
71 #include <sys/errno.h>
72 
73 #define   DCONS_POLL_HZ                 100
74 #define   DCONS_POLL_OFFLINE  2         /* sec */
75 
76 #define RETRY 3
77 
78 #ifdef CSRVAL_VENDOR_PRIVATE
79 #define   USE_CROM 1
80 #else
81 #define   USE_CROM 0
82 #endif
83 
84 int verbose = 0;
85 int tc_set = 0;
86 int poll_hz = DCONS_POLL_HZ;
87 
88 #define IS_CONSOLE(p)         ((p)->port == 0)
89 #define IS_GDB(p)   ((p)->port == 1)
90 
91 static struct dcons_state {
92           int fd;
93           kvm_t *kd;
94           int kq;
95           off_t paddr;
96 #define F_READY               (1 << 1)
97 #define F_RD_ONLY   (1 << 2)
98 #define F_ALT_BREAK (1 << 3)
99 #define F_TELNET    (1 << 4)
100 #define F_USE_CROM  (1 << 5)
101 #define F_ONE_SHOT  (1 << 6)
102 #define F_REPLAY    (1 << 7)
103           int flags;
104           enum {
105                     TYPE_KVM,
106                     TYPE_FW
107           } type;
108           int escape_state;
109           struct dcons_port {
110                     int port;
111                     struct dcons_ch o;
112                     struct dcons_ch i;
113                     u_int32_t optr;
114                     u_int32_t iptr;
115                     int s;
116                     int infd;
117                     int outfd;
118                     struct addrinfo *res;
119                     int skip_read;
120           } port[DCONS_NPORT];
121           struct timespec to;
122           struct timespec zero;
123           struct termios tsave;
124 } sc;
125 
126 static int
dread(struct dcons_state * dc,void * buf,size_t n,off_t offset)127 dread(struct dcons_state *dc, void *buf, size_t n, off_t offset)
128 {
129           switch (dc->type) {
130           case TYPE_FW:
131                     return (pread(dc->fd, buf, n, offset));
132           case TYPE_KVM:
133                     return (kvm_read(dc->kd, offset, buf, n));
134           }
135           return (-1);
136 }
137 
138 static int
dwrite(struct dcons_state * dc,void * buf,size_t n,off_t offset)139 dwrite(struct dcons_state *dc, void *buf, size_t n, off_t offset)
140 {
141           if ((dc->flags & F_RD_ONLY) != 0)
142                     return (n);
143 
144           switch (dc->type) {
145           case TYPE_FW:
146                     return (pwrite(dc->fd, buf, n, offset));
147           case TYPE_KVM:
148                     return (kvm_write(dc->kd, offset, buf, n));
149           }
150           return (-1);
151 }
152 
153 static void
dconschat_cleanup(int sig)154 dconschat_cleanup(int sig)
155 {
156           struct dcons_state *dc;
157 
158           dc = &sc;
159           if (tc_set != 0)
160                     tcsetattr(STDIN_FILENO, TCSADRAIN, &dc->tsave);
161 
162           if (sig > 0)
163                     printf("\n[dconschat exiting with signal %d ...]\n", sig);
164           else
165                     printf("\n[dconschat exiting...]\n");
166           exit(0);
167 }
168 
169 #if USE_CROM
170 static int
dconschat_get_crom(struct dcons_state * dc)171 dconschat_get_crom(struct dcons_state *dc)
172 {
173           off_t addr;
174           int i, state = 0;
175           u_int32_t buf, hi = 0, lo = 0;
176           struct csrreg *reg;
177 
178           reg = (struct csrreg *)&buf;
179           addr = 0xffff;
180           addr = (addr << 32) | 0xf0000400;
181           for (i = 20; i < 0x400; i += 4) {
182                     if (dread(dc, &buf, 4, addr + i) < 0) {
183                               if (verbose)
184                                         warn("crom read faild");
185                               return (-1);
186                     }
187                     buf = ntohl(buf);
188                     if (verbose)
189                               printf("%d %02x %06x\n", state, reg->key, reg->val);
190                     switch (state) {
191                     case 0:
192                               if (reg->key == CSRKEY_SPEC &&
193                                                   reg->val == CSRVAL_VENDOR_PRIVATE)
194                                         state = 1;
195                               break;
196                     case 1:
197                               if (reg->key == CSRKEY_VER &&
198                                                   reg->val == DCONS_CSR_VAL_VER)
199                                         state = 2;
200                               break;
201                     case 2:
202                               if (reg->key == DCONS_CSR_KEY_HI)
203                                         hi = reg->val;
204                               else if (reg->key == DCONS_CSR_KEY_LO) {
205                                         lo = reg->val;
206                                         goto out;
207                               }
208                               break;
209                     }
210           }
211           /* not found */
212           return (-1);
213 out:
214           if (verbose)
215                     printf("addr: %06x %06x\n", hi, lo);
216           dc->paddr = ((off_t)hi << 24) | lo;
217           return (0);
218 }
219 #endif
220 
221 static void
dconschat_ready(struct dcons_state * dc,int ready,const char * reason)222 dconschat_ready(struct dcons_state *dc, int ready, const char *reason)
223 {
224           static char oldreason[64] = "";
225           int old;
226 
227           old = (dc->flags & F_READY) ? 1 : 0;
228 
229           if (ready) {
230                     dc->flags |= F_READY;
231                     if (ready != old)
232                               printf("[dcons connected]\r\n");
233                     oldreason[0] = 0;
234           } else {
235                     dc->flags &= ~F_READY;
236                     if (strncmp(oldreason, reason, sizeof(oldreason)) != 0) {
237                               printf("[dcons disconnected (%s)]\r\n", reason);
238                               strlcpy(oldreason, reason, sizeof(oldreason));
239                     }
240           }
241 }
242 
243 static int
dconschat_fetch_header(struct dcons_state * dc)244 dconschat_fetch_header(struct dcons_state *dc)
245 {
246           char ebuf[64];
247           struct dcons_buf dbuf;
248           int j;
249 
250 #if USE_CROM
251           if (dc->paddr == 0 && (dc->flags & F_USE_CROM) != 0) {
252                     if (dconschat_get_crom(dc)) {
253                               dconschat_ready(dc, 0, "get crom failed");
254                               return (-1);
255                     }
256           }
257 #endif
258 
259           if (dread(dc, &dbuf, DCONS_HEADER_SIZE, dc->paddr) < 0) {
260                     dconschat_ready(dc, 0, "read header failed");
261                     return (-1);
262           }
263           if (dbuf.magic != htonl(DCONS_MAGIC)) {
264                     if ((dc->flags & F_USE_CROM) !=0)
265                               dc->paddr = 0;
266                     snprintf(ebuf, sizeof(ebuf), "wrong magic 0x%08x", dbuf.magic);
267                     dconschat_ready(dc, 0, ebuf);
268                     return (-1);
269           }
270           if (ntohl(dbuf.version) != DCONS_VERSION) {
271                     snprintf(ebuf, sizeof(ebuf),
272                         "wrong version %d,%d",
273                         ntohl(dbuf.version), DCONS_VERSION);
274                     /* XXX exit? */
275                     dconschat_ready(dc, 0, ebuf);
276                     return (-1);
277           }
278 
279           for (j = 0; j < DCONS_NPORT; j++) {
280                     struct dcons_ch *o, *i;
281                     off_t newbuf;
282                     int new = 0;
283 
284                     o = &dc->port[j].o;
285                     newbuf = dc->paddr + ntohl(dbuf.ooffset[j]);
286                     o->size = ntohl(dbuf.osize[j]);
287 
288                     if (newbuf != o->buf) {
289                               /* buffer address has changes */
290                               new = 1;
291                               o->gen = ntohl(dbuf.optr[j]) >> DCONS_GEN_SHIFT;
292                               o->pos = ntohl(dbuf.optr[j]) & DCONS_POS_MASK;
293                               o->buf = newbuf;
294                     }
295 
296                     i = &dc->port[j].i;
297                     i->size = ntohl(dbuf.isize[j]);
298                     i->gen = ntohl(dbuf.iptr[j]) >> DCONS_GEN_SHIFT;
299                     i->pos = ntohl(dbuf.iptr[j]) & DCONS_POS_MASK;
300                     i->buf = dc->paddr + ntohl(dbuf.ioffset[j]);
301 
302                     if (verbose) {
303                               printf("port %d   size offset   gen   pos\n", j);
304                               printf("output: %5d %6d %5d %5d\n"
305                                         "input : %5d %6d %5d %5d\n",
306                               o->size, ntohl(dbuf.ooffset[j]), o->gen, o->pos,
307                               i->size, ntohl(dbuf.ioffset[j]), i->gen, i->pos);
308                     }
309 
310                     if (IS_CONSOLE(&dc->port[j]) && new &&
311                         (dc->flags & F_REPLAY) !=0) {
312                               if (o->gen > 0)
313                                         o->gen --;
314                               else
315                                         o->pos = 0;
316                     }
317           }
318           dconschat_ready(dc, 1, NULL);
319           return(0);
320 }
321 
322 static int
dconschat_get_ptr(struct dcons_state * dc)323 dconschat_get_ptr (struct dcons_state *dc) {
324           int dlen, i;
325           u_int32_t ptr[DCONS_NPORT*2+1];
326           static int retry = RETRY;
327 
328 again:
329           dlen = dread(dc, &ptr, sizeof(ptr),
330                     dc->paddr + __offsetof(struct dcons_buf, magic));
331 
332           if (dlen < 0) {
333                     if (errno == ETIMEDOUT)
334                               if (retry -- > 0)
335                                         goto again;
336                     dconschat_ready(dc, 0, "get ptr failed");
337                     return(-1);
338           }
339           if (ptr[0] != htonl(DCONS_MAGIC)) {
340                     dconschat_ready(dc, 0, "wrong magic");
341                     return(-1);
342           }
343           retry = RETRY;
344           for (i = 0; i < DCONS_NPORT; i ++) {
345                     dc->port[i].optr = ntohl(ptr[i + 1]);
346                     dc->port[i].iptr = ntohl(ptr[DCONS_NPORT + i + 1]);
347           }
348           return(0);
349 }
350 
351 #define MAX_XFER 2048
352 static int
dconschat_read_dcons(struct dcons_state * dc,int port,char * buf,int len)353 dconschat_read_dcons(struct dcons_state *dc, int port, char *buf, int len)
354 {
355           struct dcons_ch *ch;
356           u_int32_t ptr, pos, gen, next_gen;
357           int rlen, dlen, lost;
358           int retry = RETRY;
359 
360           ch = &dc->port[port].o;
361           ptr = dc->port[port].optr;
362           gen = ptr >> DCONS_GEN_SHIFT;
363           pos = ptr & DCONS_POS_MASK;
364           if (gen == ch->gen && pos == ch->pos)
365                     return (-1);
366 
367           next_gen = DCONS_NEXT_GEN(ch->gen);
368           /* XXX sanity check */
369           if (gen == ch->gen) {
370                     if (pos > ch->pos)
371                               goto ok;
372                     lost = ch->size * DCONS_GEN_MASK - ch->pos;
373                     ch->pos = 0;
374           } else if (gen == next_gen) {
375                     if (pos <= ch->pos)
376                               goto ok;
377                     lost = pos - ch->pos;
378                     ch->pos = pos;
379           } else {
380                     lost = gen - ch->gen;
381                     if (lost < 0)
382                               lost += DCONS_GEN_MASK;
383                     if (verbose)
384                               printf("[genskip %d]", lost);
385                     lost = lost * ch->size - ch->pos;
386                     ch->pos = 0;
387                     ch->gen = gen;
388           }
389           /* generation skipped !! */
390           /* XXX discard */
391           if (verbose)
392                     printf("[lost %d]", lost);
393 ok:
394           if (gen == ch->gen)
395                     rlen = pos - ch->pos;
396           else
397                     rlen = ch->size - ch->pos;
398 
399           if (rlen > MAX_XFER)
400                     rlen = MAX_XFER;
401           if (rlen > len)
402                     rlen = len;
403 
404 #if 1
405           if (verbose) {
406                     printf("[%d]", rlen);
407                     fflush(stdout);
408           }
409 #endif
410 
411 again:
412           dlen = dread(dc, buf, rlen, ch->buf + ch->pos);
413           if (dlen < 0) {
414                     if (errno == ETIMEDOUT)
415                               if (retry -- > 0)
416                                         goto again;
417                     dconschat_ready(dc, 0, "read buffer failed");
418                     return(-1);
419           }
420           if (dlen != rlen)
421                     warnx("dlen(%d) != rlen(%d)\n", dlen, rlen);
422           ch->pos += dlen;
423           if (ch->pos >= ch->size) {
424                     ch->gen = next_gen;
425                     ch->pos = 0;
426                     if (verbose)
427                               printf("read_dcons: gen=%d", ch->gen);
428           }
429           return (dlen);
430 }
431 
432 static int
dconschat_write_dcons(struct dcons_state * dc,int port,char * buf,int blen)433 dconschat_write_dcons(struct dcons_state *dc, int port, char *buf, int blen)
434 {
435           struct dcons_ch *ch;
436           u_int32_t ptr;
437           int len, wlen;
438           int retry = RETRY;
439 
440           ch = &dc->port[port].i;
441           ptr = dc->port[port].iptr;
442 
443           /* the others may advance the pointer sync with it */
444           ch->gen = ptr >> DCONS_GEN_SHIFT;
445           ch->pos = ptr & DCONS_POS_MASK;
446 
447           while(blen > 0) {
448                     wlen = MIN(blen, (int)(ch->size - ch->pos));
449                     wlen = MIN(wlen, MAX_XFER);
450                     len = dwrite(dc, buf, wlen, ch->buf + ch->pos);
451                     if (len < 0) {
452                               if (errno == ETIMEDOUT)
453                                         if (retry -- > 0)
454                                                   continue; /* try again */
455                               dconschat_ready(dc, 0, "write buffer failed");
456                               return(-1);
457                     }
458                     ch->pos += len;
459                     buf += len;
460                     blen -= len;
461                     if (ch->pos >= ch->size) {
462                               ch->gen = DCONS_NEXT_GEN(ch->gen);
463                               ch->pos = 0;
464                               if (verbose)
465                                         printf("write_dcons: gen=%d", ch->gen);
466 
467                     }
468           }
469 
470           ptr = DCONS_MAKE_PTR(ch);
471           dc->port[port].iptr = ptr;
472 
473           if (verbose > 2)
474                     printf("(iptr: 0x%x)", ptr);
475 again:
476           len = dwrite(dc, &ptr, sizeof(u_int32_t),
477                     dc->paddr + __offsetof(struct dcons_buf, iptr[port]));
478           if (len < 0) {
479                     if (errno == ETIMEDOUT)
480                               if (retry -- > 0)
481                                         goto again;
482                     dconschat_ready(dc, 0, "write ptr failed");
483                     return(-1);
484           }
485           return(0);
486 }
487 
488 static int
dconschat_write_socket(int fd,char * buf,int len)489 dconschat_write_socket(int fd, char *buf, int len)
490 {
491           write(fd, buf, len);
492           if (verbose > 1) {
493                     buf[len] = 0;
494                     printf("[%s]", buf);
495           }
496           return (0);
497 }
498 
499 static void
dconschat_init_socket(struct dcons_state * dc,int port,char * host,int sport)500 dconschat_init_socket(struct dcons_state *dc, int port, char *host, int sport)
501 {
502           struct addrinfo hints, *res;
503           int on = 1, error;
504           char service[10];
505           struct kevent kev;
506           struct dcons_port *p;
507 
508           p = &dc->port[port];
509           p->port = port;
510           p->infd = p->outfd = -1;
511 
512           if (sport < 0)
513                     return;
514 
515           if (sport == 0) {
516 
517                     /* Use stdin and stdout */
518                     p->infd = STDIN_FILENO;
519                     p->outfd = STDOUT_FILENO;
520                     p->s = -1;
521                     if (tc_set == 0 &&
522                         tcgetattr(STDIN_FILENO, &dc->tsave) == 0) {
523                               struct termios traw;
524 
525                               traw = dc->tsave;
526                               cfmakeraw(&traw);
527                               tcsetattr(STDIN_FILENO, TCSADRAIN, &traw);
528                               tc_set = 1;
529                     }
530                     EV_SET(&kev, p->infd, EVFILT_READ, EV_ADD, NOTE_LOWAT, 1,
531                         (void *)p);
532                     kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
533                     return;
534           }
535 
536           memset(&hints, 0, sizeof(hints));
537           hints.ai_flags = AI_PASSIVE;
538 #if 1     /* gdb can talk v4 only */
539           hints.ai_family = PF_INET;
540 #else
541           hints.ai_family = PF_UNSPEC;
542 #endif
543           hints.ai_socktype = SOCK_STREAM;
544           hints.ai_protocol = 0;
545 
546           if (verbose)
547                     printf("%s:%d for port %d\n",
548                               host == NULL ? "*" : host, sport, port);
549           snprintf(service, sizeof(service), "%d", sport);
550           error = getaddrinfo(host, service,  &hints, &res);
551           if (error)
552                     errx(1, "tcp/%s: %s\n", service, gai_strerror(error));
553           p->res = res;
554           p->s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
555           if (p->s < 0)
556                     err(1, "socket");
557           setsockopt(p->s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
558 
559           if (bind(p->s, p->res->ai_addr, p->res->ai_addrlen) < 0) {
560                     err(1, "bind");
561           }
562           if (listen(p->s, 1) < 0)
563                     err(1, "listen");
564           EV_SET(&kev, p->s, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, (void *)p);
565           error = kevent(dc->kq, &kev, 1, NULL, 0, &dc->to);
566           if (error < 0)
567                     err(1, "kevent");
568           return;
569 }
570 
571 static int
dconschat_accept_socket(struct dcons_state * dc,struct dcons_port * p)572 dconschat_accept_socket(struct dcons_state *dc, struct dcons_port *p)
573 {
574           int foo, ns, flags;
575           struct kevent kev;
576 
577           /* accept connection */
578           foo = p->res->ai_addrlen;
579           ns = accept(p->s, p->res->ai_addr, &foo);
580           if (ns < 0)
581                     err(1, "accept");
582           if (verbose)
583                     printf("port%d accepted\n", p->port);
584 
585           flags = fcntl(ns, F_GETFL, 0);
586           flags |= O_NDELAY;
587           fcntl(ns, F_SETFL, flags);
588 #if 1
589           if (IS_CONSOLE(p) && (dc->flags & F_TELNET) != 0) {
590                     char sga[] = {IAC, WILL, TELOPT_SGA};
591                     char linemode[] = {IAC, DONT, TELOPT_LINEMODE};
592                     char echo[] = {IAC, WILL, TELOPT_ECHO};
593                     char bin[] = {IAC, DO, TELOPT_BINARY};
594 
595                     write(ns, sga, sizeof(sga));
596                     write(ns, linemode, sizeof(linemode));
597                     write(ns, echo, sizeof(echo));
598                     write(ns, bin, sizeof(bin));
599                     p->skip_read = 0;
600           }
601 #endif
602 
603           p->infd = p->outfd = ns;
604           EV_SET(&kev, ns, EVFILT_READ, EV_ADD, NOTE_LOWAT, 1, (void *)p);
605           kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
606           return(0);
607 }
608 
609 static int
dconschat_read_filter(struct dcons_state * dc,struct dcons_port * p,u_char * sp,int slen,u_char * dp,int * dlen)610 dconschat_read_filter(struct dcons_state *dc, struct dcons_port *p,
611     u_char *sp, int slen, u_char *dp, int *dlen)
612 {
613           static u_char abreak[3] = {13 /* CR */, 126 /* ~ */, 2 /* ^B */};
614 
615           while (slen > 0) {
616                     if (IS_CONSOLE(p)) {
617                               if ((dc->flags & F_TELNET) != 0) {
618                                         /* XXX Telent workarounds */
619                                         if (p->skip_read -- > 0) {
620                                                   sp ++;
621                                                   slen --;
622                                                   continue;
623                                         }
624                                         if (*sp == IAC) {
625                                                   if (verbose)
626                                                             printf("(IAC)");
627                                                   p->skip_read = 2;
628                                                   sp ++;
629                                                   slen --;
630                                                   continue;
631                                         }
632                                         if (*sp == 0) {
633                                                   if (verbose)
634                                                             printf("(0 stripped)");
635                                                   sp ++;
636                                                   slen --;
637                                                   continue;
638                                         }
639                               }
640                               switch (dc->escape_state) {
641                               case STATE1:
642                                         if (*sp == KEY_TILDE)
643                                                   dc->escape_state = STATE2;
644                                         else
645                                                   dc->escape_state = STATE0;
646                                         break;
647                               case STATE2:
648                                         dc->escape_state = STATE0;
649                                         if (*sp == '.')
650                                                   dconschat_cleanup(0);
651                               }
652                               if (*sp == KEY_CR)
653                                         dc->escape_state = STATE1;
654                     } else if (IS_GDB(p)) {
655                               /* GDB: ^C -> CR+~+^B */
656                               if (*sp == 0x3 && (dc->flags & F_ALT_BREAK) != 0) {
657                                         bcopy(abreak, dp, 3);
658                                         dp += 3;
659                                         sp ++;
660                                         *dlen += 3;
661                                         /* discard rest of the packet */
662                                         slen = 0;
663                                         break;
664                               }
665                     }
666                     *dp++ = *sp++;
667                     (*dlen) ++;
668                     slen --;
669           }
670           return (*dlen);
671 
672 }
673 
674 static int
dconschat_read_socket(struct dcons_state * dc,struct dcons_port * p)675 dconschat_read_socket(struct dcons_state *dc, struct dcons_port *p)
676 {
677           struct kevent kev;
678           int len, wlen;
679           char rbuf[MAX_XFER], wbuf[MAX_XFER+2];
680 
681           if ((len = read(p->infd, rbuf, sizeof(rbuf))) > 0) {
682                     wlen = 0;
683                     dconschat_read_filter(dc, p, rbuf, len, wbuf, &wlen);
684                     /* XXX discard if not ready*/
685                     if (wlen > 0 && (dc->flags & F_READY) != 0) {
686                               dconschat_write_dcons(dc, p->port, wbuf, wlen);
687                               if (verbose > 1) {
688                                         wbuf[wlen] = 0;
689                                         printf("(%s)\n", wbuf);
690                               }
691                               if (verbose) {
692                                         printf("(%d)", wlen);
693                                         fflush(stdout);
694                               }
695                     }
696           } else {
697                     if (verbose) {
698                               if (len == 0)
699                                         warnx("port%d: closed", p->port);
700                               else
701                                         warn("port%d: read", p->port);
702                     }
703                     EV_SET(&kev, p->infd, EVFILT_READ,
704                               EV_DELETE, 0, 0, NULL);
705                     kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
706                     close(p->infd);
707                     close(p->outfd);
708                     /* XXX exit for pipe case XXX */
709                     EV_SET(&kev, p->s, EVFILT_READ,
710                                         EV_ADD | EV_ONESHOT, 0, 0, (void *) p);
711                     kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
712                     p->infd = p->outfd = -1;
713           }
714           return(0);
715 }
716 #define NEVENT 5
717 static int
dconschat_proc_socket(struct dcons_state * dc)718 dconschat_proc_socket(struct dcons_state *dc)
719 {
720           struct kevent elist[NEVENT], *e;
721           int i, n;
722           struct dcons_port *p;
723 
724           n = kevent(dc->kq, NULL, 0, elist, NEVENT, &dc->to);
725           for (i = 0; i < n; i ++) {
726                     e = &elist[i];
727                     p = (struct dcons_port *)e->udata;
728                     if (e->ident == (uintptr_t)p->s) {
729                               dconschat_accept_socket(dc, p);
730                     } else {
731                               dconschat_read_socket(dc, p);
732                     }
733           }
734           return(0);
735 }
736 
737 static int
dconschat_proc_dcons(struct dcons_state * dc)738 dconschat_proc_dcons(struct dcons_state *dc)
739 {
740           int port, len, error;
741           char buf[MAX_XFER];
742           struct dcons_port *p;
743 
744           error = dconschat_get_ptr(dc);
745           if (error) {
746                     /* XXX we should stop write operation too. */
747                     return error;
748           }
749           for (port = 0; port < DCONS_NPORT; port ++) {
750                     p = &dc->port[port];
751                     if (p->infd < 0)
752                               continue;
753                     while ((len = dconschat_read_dcons(dc, port, buf,
754                         sizeof(buf))) > 0) {
755                               dconschat_write_socket(p->outfd, buf, len);
756                               dconschat_get_ptr(dc);
757                     }
758                     if ((dc->flags & F_ONE_SHOT) != 0 && len <= 0)
759                               dconschat_cleanup(0);
760           }
761           return 0;
762 }
763 
764 static int
dconschat_start_session(struct dcons_state * dc)765 dconschat_start_session(struct dcons_state *dc)
766 {
767           int counter = 0;
768 
769           while (1) {
770                     if ((dc->flags & F_READY) == 0 &&
771                               (++counter % (poll_hz * DCONS_POLL_OFFLINE)) == 0)
772                               dconschat_fetch_header(dc);
773                     if ((dc->flags & F_READY) != 0)
774                               dconschat_proc_dcons(dc);
775                     dconschat_proc_socket(dc);
776           }
777           return (0);
778 }
779 
780 static void
usage(void)781 usage(void)
782 {
783           fprintf(stderr,
784               "usage: dconschat [-brvwRT1] [-h hz] [-C port] [-G port]\n"
785               "\t\t\t[-M core] [-N system]\n"
786               "\t\t\t[-u unit] [-a address] [-t target_eui64]\n"
787               "\t-b translate ctrl-C to CR+~+ctrl-B on gdb port\n"
788               "\t-v verbose\n"
789               "\t-w listen on wildcard address rather than localhost\n"
790               "\t-r replay old buffer on connection\n"
791               "\t-R read-only\n"
792               "\t-T enable Telnet protocol workaround on console port\n"
793               "\t-1 one shot: read buffer and exit\n"
794               "\t-h polling rate\n"
795               "\t-C port number for console port\n"
796               "\t-G port number for gdb port\n"
797               "\t(for KVM)\n"
798               "\t-M core file\n"
799               "\t-N system file\n"
800               "\t(for FireWire)\n"
801               "\t-u specify unit number of the bus\n"
802               "\t-t EUI64 of target host (must be specified)\n"
803               "\t-a physical address of dcons buffer on target host\n"
804           );
805           exit(0);
806 }
807 int
main(int argc,char ** argv)808 main(int argc, char **argv)
809 {
810           struct dcons_state *dc;
811           struct fw_eui64 eui;
812           struct eui64 target;
813           char devicename[256], *core = NULL, *sysfile = NULL;
814           int i, ch, error;
815           int unit=0, wildcard=0;
816           int port[DCONS_NPORT];
817 
818           bzero(&sc, sizeof(sc));
819           dc = &sc;
820           dc->flags |= USE_CROM ? F_USE_CROM : 0;
821 
822           /* defualt ports */
823           port[0] = 0;        /* stdin/out for console */
824           port[1] = -1;       /* disable gdb port */
825 
826           while ((ch = getopt(argc, argv, "a:bh:rt:u:vwC:G:M:N:RT1")) != -1) {
827                     switch(ch) {
828                     case 'a':
829                               dc->paddr = strtoull(optarg, NULL, 0);
830                               dc->flags &= ~F_USE_CROM;
831                               break;
832                     case 'b':
833                               dc->flags |= F_ALT_BREAK;
834                               break;
835                     case 'h':
836                               poll_hz = strtoul(optarg, NULL, 0);
837                               if (poll_hz == 0)
838                                         poll_hz = DCONS_POLL_HZ;
839                               break;
840                     case 'r':
841                               dc->flags |= F_REPLAY;
842                               break;
843                     case 't':
844                               if (eui64_hostton(optarg, &target) != 0 &&
845                                   eui64_aton(optarg, &target) != 0)
846                                         errx(1, "invalid target: %s", optarg);
847                               eui.hi = ntohl(*(u_int32_t*)&(target.octet[0]));
848                               eui.lo = ntohl(*(u_int32_t*)&(target.octet[4]));
849                               dc->type = TYPE_FW;
850                               break;
851                     case 'u':
852                               unit = strtol(optarg, NULL, 0);
853                               break;
854                     case 'v':
855                               verbose ++;
856                               break;
857                     case 'w':
858                               wildcard = 1;
859                               break;
860                     case 'C':
861                               port[0] = strtol(optarg, NULL, 0);
862                               break;
863                     case 'G':
864                               port[1] = strtol(optarg, NULL, 0);
865                               break;
866                     case 'M':
867                               core = optarg;
868                               break;
869                     case 'N':
870                               sysfile = optarg;
871                               break;
872                     case 'R':
873                               dc->flags |= F_RD_ONLY;
874                               break;
875                     case 'T':
876                               dc->flags |= F_TELNET;
877                               break;
878                     case '1':
879                               dc->flags |= F_ONE_SHOT | F_REPLAY;
880                               break;
881                     default:
882                               usage();
883                     }
884           }
885           if (dc->paddr == 0 && (dc->flags & F_USE_CROM) == 0) {
886                     warnx("no address specified");
887                     usage();
888           }
889 
890           if (port[0] < 0 && port[1] < 0) {
891                     warnx("no port specified");
892                     usage();
893           }
894 
895           /* set signal handler */
896           signal(SIGHUP, dconschat_cleanup);
897           signal(SIGINT, dconschat_cleanup);
898           signal(SIGPIPE, dconschat_cleanup);
899           signal(SIGTERM, dconschat_cleanup);
900 
901           /* init firewire */
902           switch (dc->type) {
903           case TYPE_FW:
904 #define MAXDEV 10
905                     for (i = 0; i < MAXDEV; i ++) {
906                               snprintf(devicename, sizeof(devicename),
907                                   "/dev/fwmem%d.%d", unit, i);
908                               dc->fd = open(devicename, O_RDWR);
909                               if (dc->fd >= 0)
910                                         goto found;
911                     }
912                     err(1, "open");
913 found:
914                     error = ioctl(dc->fd, FW_SDEUI64, &eui);
915                     if (error)
916                               err(1, "ioctl");
917                     break;
918           case TYPE_KVM:
919           {
920                     struct nlist nl[] = {{.n_name = "dcons_buf"}, {.n_name = ""}};
921                     void *dcons_buf;
922 
923                     dc->kd = kvm_open(sysfile, core, NULL,
924                         (dc->flags & F_RD_ONLY) ? O_RDONLY : O_RDWR, "dconschat");
925                     if (dc->kd == NULL)
926                               errx(1, "kvm_open");
927 
928                     if (kvm_nlist(dc->kd, nl) < 0)
929                               errx(1, "kvm_nlist: %s", kvm_geterr(dc->kd));
930 
931                     if (kvm_read(dc->kd, nl[0].n_value, &dcons_buf,
932                         sizeof(void *)) < 0)
933                               errx(1, "kvm_read: %s", kvm_geterr(dc->kd));
934                     dc->paddr = (uintptr_t)dcons_buf;
935                     if (verbose)
936                               printf("dcons_buf: 0x%x\n", (uint)dc->paddr);
937                     break;
938           }
939           }
940           dconschat_fetch_header(dc);
941 
942           /* iniit sockets */
943           dc->kq = kqueue();
944           if (poll_hz == 1) {
945                     dc->to.tv_sec = 1;
946                     dc->to.tv_nsec = 0;
947           } else {
948                     dc->to.tv_sec = 0;
949                     dc->to.tv_nsec = 1000 * 1000 * 1000 / poll_hz;
950           }
951           dc->zero.tv_sec = 0;
952           dc->zero.tv_nsec = 0;
953           for (i = 0; i < DCONS_NPORT; i++)
954                     dconschat_init_socket(dc, i,
955                         wildcard ? NULL : __DECONST(char *, "localhost"), port[i]);
956 
957           dconschat_start_session(dc);
958 
959           for (i = 0; i < DCONS_NPORT; i++) {
960                     freeaddrinfo(dc->port[i].res);
961           }
962           return (0);
963 }
964