xref: /freebsd/usr.sbin/dconschat/dconschat.c (revision 1669d8afc64812c8d2d1d147ae1fd42ff441e1b1)
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 }
1153