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