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