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