xref: /freebsd/usr.sbin/dconschat/dconschat.c (revision d4eeb02986980bf33dd56c41ceb9fc5f180c0d47)
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 		    "wrong version %d,%d",
387 		    ntohl(dbuf.version), DCONS_VERSION);
388 		/* XXX exit? */
389 		dconschat_ready(dc, 0, ebuf);
390 		return (-1);
391 	}
392 
393 	for (j = 0; j < DCONS_NPORT; j++) {
394 		struct dcons_ch *o, *i;
395 		off_t newbuf;
396 		int new = 0;
397 
398 		o = &dc->port[j].o;
399 		newbuf = dc->paddr + ntohl(dbuf.ooffset[j]);
400 		o->size = ntohl(dbuf.osize[j]);
401 
402 		if (newbuf != o->buf) {
403 			/* buffer address has changes */
404 			new = 1;
405 			o->gen = ntohl(dbuf.optr[j]) >> DCONS_GEN_SHIFT;
406 			o->pos = ntohl(dbuf.optr[j]) & DCONS_POS_MASK;
407 			o->buf = newbuf;
408 		}
409 
410 		i = &dc->port[j].i;
411 		i->size = ntohl(dbuf.isize[j]);
412 		i->gen = ntohl(dbuf.iptr[j]) >> DCONS_GEN_SHIFT;
413 		i->pos = ntohl(dbuf.iptr[j]) & DCONS_POS_MASK;
414 		i->buf = dc->paddr + ntohl(dbuf.ioffset[j]);
415 
416 		if (verbose) {
417 			printf("port %d   size offset   gen   pos\n", j);
418 			printf("output: %5d %6d %5d %5d\n"
419 				"input : %5d %6d %5d %5d\n",
420 			o->size, ntohl(dbuf.ooffset[j]), o->gen, o->pos,
421 			i->size, ntohl(dbuf.ioffset[j]), i->gen, i->pos);
422 		}
423 
424 		if (IS_CONSOLE(&dc->port[j]) && new &&
425 		    (dc->flags & F_REPLAY) !=0) {
426 			if (o->gen > 0)
427 				o->gen --;
428 			else
429 				o->pos = 0;
430 		}
431 	}
432 	dconschat_ready(dc, 1, NULL);
433 	return(0);
434 }
435 
436 static int
437 dconschat_get_ptr (struct dcons_state *dc) {
438 	int dlen, i;
439 	u_int32_t ptr[DCONS_NPORT*2+1];
440 	static int retry = RETRY;
441 	char ebuf[64];
442 
443 again:
444 	dlen = dread(dc, &ptr, sizeof(ptr),
445 		dc->paddr + __offsetof(struct dcons_buf, magic));
446 
447 	if (dlen < 0) {
448 		if (errno == ETIMEDOUT)
449 			if (retry -- > 0)
450 				goto again;
451 		dconschat_ready(dc, 0, "get ptr failed");
452 		return(-1);
453 	}
454 	if (ptr[0] != htonl(DCONS_MAGIC)) {
455 		if ((dc->flags & F_USE_CROM) !=0)
456 			dc->paddr = 0;
457 		snprintf(ebuf, sizeof(ebuf), "wrong magic 0x%08x", ptr[0]);
458 		dconschat_ready(dc, 0, ebuf);
459 		return(-1);
460 	}
461 	retry = RETRY;
462 	for (i = 0; i < DCONS_NPORT; i ++) {
463 		dc->port[i].optr = ntohl(ptr[i + 1]);
464 		dc->port[i].iptr = ntohl(ptr[DCONS_NPORT + i + 1]);
465 	}
466 	return(0);
467 }
468 
469 #define MAX_XFER 2048
470 static int
471 dconschat_read_dcons(struct dcons_state *dc, int port, char *buf, int len)
472 {
473 	struct dcons_ch *ch;
474 	u_int32_t ptr, pos, gen, next_gen;
475 	int rlen, dlen, lost;
476 	int retry = RETRY;
477 
478 	ch = &dc->port[port].o;
479 	ptr = dc->port[port].optr;
480 	gen = ptr >> DCONS_GEN_SHIFT;
481 	pos = ptr & DCONS_POS_MASK;
482 	if (gen == ch->gen && pos == ch->pos)
483 		return (-1);
484 
485 	next_gen = DCONS_NEXT_GEN(ch->gen);
486 	/* XXX sanity check */
487 	if (gen == ch->gen) {
488 		if (pos > ch->pos)
489 			goto ok;
490 		lost = ch->size * DCONS_GEN_MASK - ch->pos;
491 		ch->pos = 0;
492 	} else if (gen == next_gen) {
493 		if (pos <= ch->pos)
494 			goto ok;
495 		lost = pos - ch->pos;
496 		ch->pos = pos;
497 	} else {
498 		lost = gen - ch->gen;
499 		if (lost < 0)
500 			lost += DCONS_GEN_MASK;
501 		if (verbose)
502 			printf("[genskip %d]", lost);
503 		lost = lost * ch->size - ch->pos;
504 		ch->pos = 0;
505 		ch->gen = gen;
506 	}
507 	/* generation skipped !! */
508 	/* XXX discard */
509 	if (verbose)
510 		printf("[lost %d]", lost);
511 ok:
512 	if (gen == ch->gen)
513 		rlen = pos - ch->pos;
514 	else
515 		rlen = ch->size - ch->pos;
516 
517 	if (rlen > MAX_XFER)
518 		rlen = MAX_XFER;
519 	if (rlen > len)
520 		rlen = len;
521 
522 #if 1
523 	if (verbose == 1)
524 		printf("[%d]", rlen); fflush(stdout);
525 #endif
526 
527 again:
528 	dlen = dread(dc, buf, rlen, ch->buf + ch->pos);
529 	if (dlen < 0) {
530 		if (errno == ETIMEDOUT)
531 			if (retry -- > 0)
532 				goto again;
533 		dconschat_ready(dc, 0, "read buffer failed");
534 		return(-1);
535 	}
536 	if (dlen != rlen)
537 		warnx("dlen(%d) != rlen(%d)\n", dlen, rlen);
538 	ch->pos += dlen;
539 	if (ch->pos >= ch->size) {
540 		ch->gen = next_gen;
541 		ch->pos = 0;
542 		if (verbose)
543 			printf("read_dcons: gen=%d", ch->gen);
544 	}
545 	return (dlen);
546 }
547 
548 static int
549 dconschat_write_dcons(struct dcons_state *dc, int port, char *buf, int blen)
550 {
551 	struct dcons_ch *ch;
552 	u_int32_t ptr;
553 	int len, wlen;
554 	int retry = RETRY;
555 
556 	ch = &dc->port[port].i;
557 	ptr = dc->port[port].iptr;
558 
559 	/* the others may advance the pointer sync with it */
560 	ch->gen = ptr >> DCONS_GEN_SHIFT;
561 	ch->pos = ptr & DCONS_POS_MASK;
562 
563 	while(blen > 0) {
564 		wlen = MIN(blen, ch->size - ch->pos);
565 		wlen = MIN(wlen, MAX_XFER);
566 		len = dwrite(dc, buf, wlen, ch->buf + ch->pos);
567 		if (len < 0) {
568 			if (errno == ETIMEDOUT)
569 				if (retry -- > 0)
570 					continue; /* try again */
571 			dconschat_ready(dc, 0, "write buffer failed");
572 			return(-1);
573 		}
574 		ch->pos += len;
575 		buf += len;
576 		blen -= len;
577 		if (ch->pos >= ch->size) {
578 			ch->gen = DCONS_NEXT_GEN(ch->gen);
579 			ch->pos = 0;
580 			if (verbose)
581 				printf("write_dcons: gen=%d", ch->gen);
582 
583 		}
584 	}
585 
586 	ptr = DCONS_MAKE_PTR(ch);
587 	dc->port[port].iptr = ptr;
588 
589 	if (verbose > 2)
590 		printf("(iptr: 0x%x)", ptr);
591 again:
592 	len = dwrite(dc, &ptr, sizeof(u_int32_t),
593 		dc->paddr + __offsetof(struct dcons_buf, iptr[port]));
594 	if (len < 0) {
595 		if (errno == ETIMEDOUT)
596 			if (retry -- > 0)
597 				goto again;
598 		dconschat_ready(dc, 0, "write ptr failed");
599 		return(-1);
600 	}
601 	return(0);
602 }
603 
604 
605 static int
606 dconschat_write_socket(int fd, char *buf, int len)
607 {
608 	write(fd, buf, len);
609 	if (verbose > 1) {
610 		buf[len] = 0;
611 		printf("<- %s\n", buf);
612 	}
613 	return (0);
614 }
615 
616 static void
617 dconschat_init_socket(struct dcons_state *dc, int port, char *host, int sport)
618 {
619 	struct addrinfo hints, *res;
620 	int on = 1, error;
621 	char service[10];
622 	struct kevent kev;
623 	struct dcons_port *p;
624 
625 	p = &dc->port[port];
626 	p->port = port;
627 	p->sport = sport;
628 	p->infd = p->outfd = -1;
629 
630 	if (sport < 0)
631 		return;
632 
633 	if (sport == 0) {
634 
635 		/* Use stdin and stdout */
636 		p->infd = STDIN_FILENO;
637 		p->outfd = STDOUT_FILENO;
638 		p->s = -1;
639 		if (tc_set == 0 &&
640 		    tcgetattr(STDIN_FILENO, &dc->tsave) == 0) {
641 			dc->traw = dc->tsave;
642 			cfmakeraw(&dc->traw);
643 			tcsetattr(STDIN_FILENO, TCSADRAIN, &dc->traw);
644 			tc_set = 1;
645 		}
646 		EV_SET(&kev, p->infd, EVFILT_READ, EV_ADD, NOTE_LOWAT, 1,
647 		    (void *)p);
648 		kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
649 		return;
650 	}
651 
652 	memset(&hints, 0, sizeof(hints));
653 	hints.ai_flags = AI_PASSIVE;
654 #if 1	/* gdb can talk v4 only */
655 	hints.ai_family = PF_INET;
656 #else
657 	hints.ai_family = PF_UNSPEC;
658 #endif
659 	hints.ai_socktype = SOCK_STREAM;
660 	hints.ai_protocol = 0;
661 
662 	if (verbose)
663 		printf("%s:%d for port %d\n",
664 			host == NULL ? "*" : host, sport, port);
665 	snprintf(service, sizeof(service), "%d", sport);
666 	error = getaddrinfo(host, service,  &hints, &res);
667 	if (error)
668 		errx(1, "tcp/%s: %s\n", service, gai_strerror(error));
669 	p->res = res;
670 	p->s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
671 	if (p->s < 0)
672 		err(1, "socket");
673 	setsockopt(p->s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
674 
675 	if (bind(p->s, p->res->ai_addr, p->res->ai_addrlen) < 0) {
676 		err(1, "bind");
677 	}
678 	if (listen(p->s, 1) < 0)
679 		err(1, "listen");
680 	EV_SET(&kev, p->s, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, (void *)p);
681 	error = kevent(dc->kq, &kev, 1, NULL, 0, &dc->to);
682 	if (error < 0)
683 		err(1, "kevent");
684 	return;
685 }
686 
687 static int
688 dconschat_accept_socket(struct dcons_state *dc, struct dcons_port *p)
689 {
690 	socklen_t addrlen;
691 	int ns, flags;
692 	struct kevent kev;
693 
694 	/* accept connection */
695 	addrlen = p->res->ai_addrlen;
696 	ns = accept(p->s, p->res->ai_addr, &addrlen);
697 	if (ns < 0)
698 		err(1, "accept");
699 	if (verbose)
700 		printf("port%d accepted\n", p->port);
701 
702 	flags = fcntl(ns, F_GETFL, 0);
703 	flags |= O_NDELAY;
704 	fcntl(ns, F_SETFL, flags);
705 #if 1
706 	if (IS_CONSOLE(p) && (dc->flags & F_TELNET) != 0) {
707 		char sga[] = {IAC, WILL, TELOPT_SGA};
708 		char linemode[] = {IAC, DONT, TELOPT_LINEMODE};
709 		char echo[] = {IAC, WILL, TELOPT_ECHO};
710 		char bin[] = {IAC, DO, TELOPT_BINARY};
711 
712 		write(ns, sga, sizeof(sga));
713 		write(ns, linemode, sizeof(linemode));
714 		write(ns, echo, sizeof(echo));
715 		write(ns, bin, sizeof(bin));
716 		p->skip_read = 0;
717 	}
718 #endif
719 	/* discard backlog on GDB port */
720 	if (IS_GDB(p)) {
721 		char buf[2048];
722 		int len;
723 
724 		while ((len = dconschat_read_dcons(dc, DCONS_GDB, &buf[0],
725 				 2048)) > 0)
726 			if (verbose)
727 				printf("discard %d chars on GDB port\n", len);
728 	}
729 
730 	p->infd = p->outfd = ns;
731 	EV_SET(&kev, ns, EVFILT_READ, EV_ADD, NOTE_LOWAT, 1, (void *)p);
732 	kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
733 	return(0);
734 }
735 
736 static int
737 dconschat_read_filter(struct dcons_state *dc, struct dcons_port *p,
738     u_char *sp, int slen, u_char *dp, int *dlen)
739 {
740 	int skip;
741 	char *buf;
742 
743 	while (slen > 0) {
744 		skip = 0;
745 		if (IS_CONSOLE(p)) {
746 			if ((dc->flags & F_TELNET) != 0) {
747 				/* XXX Telnet workarounds */
748 				if (p->skip_read -- > 0) {
749 					sp ++;
750 					slen --;
751 					continue;
752 				}
753 				if (*sp == IAC) {
754 					if (verbose)
755 						printf("(IAC)");
756 					p->skip_read = 2;
757 					sp ++;
758 					slen --;
759 					continue;
760 				}
761 				if (*sp == 0) {
762 					if (verbose)
763 						printf("(0 stripped)");
764 					sp ++;
765 					slen --;
766 					continue;
767 				}
768 			}
769 			switch (dc->escape_state) {
770 			case STATE1:
771 				if (*sp == dc->escape) {
772 					skip = 1;
773 					dc->escape_state = STATE2;
774 				} else
775 					dc->escape_state = STATE0;
776 				break;
777 			case STATE2:
778 				dc->escape_state = STATE0;
779 				skip = 1;
780 				if (*sp == '.')
781 					dconschat_cleanup(0);
782 				else if (*sp == CTRL('B')) {
783 					bcopy(abreak, dp, 3);
784 					dp += 3;
785 					*dlen += 3;
786 				}
787 				else if (*sp == CTRL('G'))
788 					dconschat_fork_gdb(dc, p);
789 				else if ((*sp == CTRL('R'))
790 						&& (dc->reset != 0)) {
791 					dc->escape_state = STATE3;
792 					buf = "\r\n[Are you sure to reset target? (y/N)]";
793 					write(p->outfd, buf, strlen(buf));
794 				} else if (*sp == CTRL('Z'))
795 					dconschat_suspend(dc, p);
796 				else {
797 					skip = 0;
798 					*dp++ = dc->escape;
799 					(*dlen) ++;
800 				}
801 				break;
802 			case STATE3:
803 				dc->escape_state = STATE0;
804 				skip = 1;
805 				if (*sp == 'y')
806 					dconschat_reset_target(dc, p);
807 				else {
808 					write(p->outfd, sp, 1);
809 					write(p->outfd, "\r\n", 2);
810 				}
811 				break;
812 			}
813 			if (*sp == KEY_CR)
814 				dc->escape_state = STATE1;
815 		} else if (IS_GDB(p)) {
816 			/* GDB: ^C -> CR+~+^B */
817 			if (*sp == CTRL('C') && (dc->flags & F_ALT_BREAK) != 0) {
818 				bcopy(abreak, dp, 3);
819 				dp += 3;
820 				sp ++;
821 				*dlen += 3;
822 				/* discard rest of the packet */
823 				slen = 0;
824 				break;
825 			}
826 		}
827 		if (!skip) {
828 			*dp++ = *sp;
829 			(*dlen) ++;
830 		}
831 		sp ++;
832 		slen --;
833 	}
834 	return (*dlen);
835 
836 }
837 
838 static int
839 dconschat_read_socket(struct dcons_state *dc, struct dcons_port *p)
840 {
841 	struct kevent kev;
842 	int len, wlen;
843 	char rbuf[MAX_XFER], wbuf[MAX_XFER+2];
844 
845 	if ((len = read(p->infd, rbuf, sizeof(rbuf))) > 0) {
846 		wlen = 0;
847 		dconschat_read_filter(dc, p, rbuf, len, wbuf, &wlen);
848 		/* XXX discard if not ready*/
849 		if (wlen > 0 && (dc->flags & F_READY) != 0) {
850 			dconschat_write_dcons(dc, p->port, wbuf, wlen);
851 			if (verbose > 1) {
852 				wbuf[wlen] = 0;
853 				printf("-> %s\n", wbuf);
854 			} else if (verbose == 1) {
855 				printf("(%d)", wlen);
856 				fflush(stdout);
857 			}
858 		}
859 	} else {
860 		if (verbose) {
861 			if (len == 0)
862 				warnx("port%d: closed", p->port);
863 			else
864 				warn("port%d: read", p->port);
865 		}
866 		EV_SET(&kev, p->infd, EVFILT_READ,
867 			EV_DELETE, 0, 0, NULL);
868 		kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
869 		close(p->infd);
870 		close(p->outfd);
871 		/* XXX exit for pipe case XXX */
872 		EV_SET(&kev, p->s, EVFILT_READ,
873 				EV_ADD | EV_ONESHOT, 0, 0, (void *) p);
874 		kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
875 		p->infd = p->outfd = -1;
876 	}
877 	return(0);
878 }
879 #define NEVENT 5
880 static int
881 dconschat_proc_socket(struct dcons_state *dc)
882 {
883 	struct kevent elist[NEVENT], *e;
884 	int i, n;
885 	struct dcons_port *p;
886 
887 	n = kevent(dc->kq, NULL, 0, elist, NEVENT, &dc->to);
888 	for (i = 0; i < n; i ++) {
889 		e = &elist[i];
890 		p = (struct dcons_port *)e->udata;
891 		if (e->ident == p->s) {
892 			dconschat_accept_socket(dc, p);
893 		} else {
894 			dconschat_read_socket(dc, p);
895 		}
896 	}
897 	return(0);
898 }
899 
900 static int
901 dconschat_proc_dcons(struct dcons_state *dc)
902 {
903 	int port, len, err;
904 	char buf[MAX_XFER];
905 	struct dcons_port *p;
906 
907 	err = dconschat_get_ptr(dc);
908 	if (err) {
909 		/* XXX we should stop write operation too. */
910 		return err;
911 	}
912 	for (port = 0; port < DCONS_NPORT; port ++) {
913 		p = &dc->port[port];
914 		if (p->infd < 0)
915 			continue;
916 		while ((len = dconschat_read_dcons(dc, port, buf,
917 		    sizeof(buf))) > 0) {
918 			dconschat_write_socket(p->outfd, buf, len);
919 			if ((err = dconschat_get_ptr(dc)))
920 				return (err);
921 		}
922 		if ((dc->flags & F_ONE_SHOT) != 0 && len <= 0)
923 			dconschat_cleanup(0);
924 	}
925 	return 0;
926 }
927 
928 static int
929 dconschat_start_session(struct dcons_state *dc)
930 {
931 	int counter = 0;
932 	int retry = 0;
933 	int retry_unit_init = MAX(1, poll_hz / 10);
934 	int retry_unit_offline = poll_hz * DCONS_POLL_OFFLINE;
935 	int retry_unit = retry_unit_init;
936 	int retry_max = retry_unit_offline / retry_unit;
937 
938 	while (1) {
939 		if (((dc->flags & F_READY) == 0) && ++counter > retry_unit) {
940 			counter = 0;
941 			retry ++;
942 			if (retry > retry_max)
943 				retry_unit = retry_unit_offline;
944 			if (verbose) {
945 				printf("%d/%d ", retry, retry_max);
946 				fflush(stdout);
947 			}
948 			dconschat_fetch_header(dc);
949 		}
950 		if ((dc->flags & F_READY) != 0) {
951 			counter = 0;
952 			retry = 0;
953 			retry_unit = retry_unit_init;
954 			dconschat_proc_dcons(dc);
955 		}
956 		dconschat_proc_socket(dc);
957 	}
958 	return (0);
959 }
960 
961 static void
962 usage(void)
963 {
964 	fprintf(stderr,
965  	    "usage: dconschat [-brvwRT1] [-h hz] [-C port] [-G port]\n"
966 	    "\t\t\t[-M core] [-N system]\n"
967 	    "\t\t\t[-u unit] [-a address] [-t target_eui64]\n"
968 	    "\t-b	translate ctrl-C to CR+~+ctrl-B on gdb port\n"
969 	    "\t-v	verbose\n"
970 	    "\t-w	listen on wildcard address rather than localhost\n"
971 	    "\t-r	replay old buffer on connection\n"
972 	    "\t-R	read-only\n"
973 	    "\t-T	enable Telnet protocol workaround on console port\n"
974 	    "\t-1	one shot: read buffer and exit\n"
975 	    "\t-h	polling rate\n"
976 	    "\t-C	port number for console port\n"
977 	    "\t-G	port number for gdb port\n"
978 	    "\t(for KVM)\n"
979 	    "\t-M	core file\n"
980 	    "\t-N	system file\n"
981 	    "\t(for FireWire)\n"
982 	    "\t-u	specify unit number of the bus\n"
983 	    "\t-t	EUI64 of target host (must be specified)\n"
984 	    "\t-a	physical address of dcons buffer on target host\n"
985 	);
986 	exit(0);
987 }
988 int
989 main(int argc, char **argv)
990 {
991 	struct dcons_state *dc;
992 	struct fw_eui64 eui;
993 	struct eui64 target;
994 	char devname[256], *core = NULL, *system = NULL;
995 	int i, ch, error;
996 	int unit=0, wildcard=0;
997 	int port[DCONS_NPORT];
998 
999 	bzero(&sc, sizeof(sc));
1000 	dc = &sc;
1001 	dc->flags |= USE_CROM ? F_USE_CROM : 0;
1002 
1003 	/* default ports */
1004 	port[0] = 0;	/* stdin/out for console */
1005 	port[1] = -1;	/* disable gdb port */
1006 
1007 	/* default escape char */
1008 	dc->escape = KEY_TILDE;
1009 
1010 	while ((ch = getopt(argc, argv, "a:be:h:rt:u:vwC:G:M:N:RT1")) != -1) {
1011 		switch(ch) {
1012 		case 'a':
1013 			dc->paddr = strtoull(optarg, NULL, 0);
1014 			dc->flags &= ~F_USE_CROM;
1015 			break;
1016 		case 'b':
1017 			dc->flags |= F_ALT_BREAK;
1018 			break;
1019 		case 'e':
1020 			dc->escape = optarg[0];
1021 			break;
1022 		case 'h':
1023 			poll_hz = strtoul(optarg, NULL, 0);
1024 			if (poll_hz == 0)
1025 				poll_hz = DCONS_POLL_HZ;
1026 			break;
1027 		case 'r':
1028 			dc->flags |= F_REPLAY;
1029 			break;
1030 		case 't':
1031 			if (eui64_hostton(optarg, &target) != 0 &&
1032 			    eui64_aton(optarg, &target) != 0)
1033 				errx(1, "invalid target: %s", optarg);
1034 			eui.hi = ntohl(*(u_int32_t*)&(target.octet[0]));
1035 			eui.lo = ntohl(*(u_int32_t*)&(target.octet[4]));
1036 			dc->type = TYPE_FW;
1037 			break;
1038 		case 'u':
1039 			unit = strtol(optarg, NULL, 0);
1040 			break;
1041 		case 'v':
1042 			verbose ++;
1043 			break;
1044 		case 'w':
1045 			wildcard = 1;
1046 			break;
1047 		case 'C':
1048 			port[0] = strtol(optarg, NULL, 0);
1049 			break;
1050 		case 'G':
1051 			port[1] = strtol(optarg, NULL, 0);
1052 			break;
1053 		case 'M':
1054 			core = optarg;
1055 			break;
1056 		case 'N':
1057 			system = optarg;
1058 			break;
1059 		case 'R':
1060 			dc->flags |= F_RD_ONLY;
1061 			break;
1062 		case 'T':
1063 			dc->flags |= F_TELNET;
1064 			break;
1065 		case '1':
1066 			dc->flags |= F_ONE_SHOT | F_REPLAY;
1067 			break;
1068 		default:
1069 			usage();
1070 		}
1071 	}
1072 	if (dc->paddr == 0 && (dc->flags & F_USE_CROM) == 0) {
1073 		warnx("no address specified");
1074 		usage();
1075 	}
1076 
1077 	if (port[0] < 0 && port[1] < 0) {
1078 		warnx("no port specified");
1079 		usage();
1080 	}
1081 
1082 	/* set signal handler */
1083 	signal(SIGHUP, dconschat_cleanup);
1084 	signal(SIGINT, dconschat_cleanup);
1085 	signal(SIGPIPE, dconschat_cleanup);
1086 	signal(SIGTERM, dconschat_cleanup);
1087 
1088 	/* init firewire */
1089 	switch (dc->type) {
1090 	case TYPE_FW:
1091 #define MAXDEV 10
1092 		for (i = 0; i < MAXDEV; i ++) {
1093 			snprintf(devname, sizeof(devname),
1094 			    "/dev/fwmem%d.%d", unit, i);
1095 			dc->fd = open(devname, O_RDWR);
1096 			if (dc->fd >= 0)
1097 				goto found;
1098 		}
1099 		err(1, "open");
1100 found:
1101 		error = ioctl(dc->fd, FW_SDEUI64, &eui);
1102 		if (error)
1103 			err(1, "ioctl");
1104 		break;
1105 	case TYPE_KVM:
1106 	{
1107 		struct nlist nl[] = {{"dcons_buf"}, {""}};
1108 		void *dcons_buf;
1109 
1110 		dc->kd = kvm_open(system, core, NULL,
1111 		    (dc->flags & F_RD_ONLY) ? O_RDONLY : O_RDWR, "dconschat");
1112 		if (dc->kd == NULL)
1113 			errx(1, "kvm_open");
1114 
1115 		if (kvm_nlist(dc->kd, nl) < 0)
1116 			errx(1, "kvm_nlist: %s", kvm_geterr(dc->kd));
1117 
1118 		if (kvm_read(dc->kd, nl[0].n_value, &dcons_buf,
1119 		    sizeof(void *)) < 0)
1120 			errx(1, "kvm_read: %s", kvm_geterr(dc->kd));
1121 		dc->paddr = (uintptr_t)dcons_buf;
1122 		if (verbose)
1123 			printf("dcons_buf: 0x%x\n", (uint)dc->paddr);
1124 		break;
1125 	}
1126 	}
1127 	dconschat_fetch_header(dc);
1128 
1129 	/* init sockets */
1130 	dc->kq = kqueue();
1131 	if (poll_hz == 1) {
1132 		dc->to.tv_sec = 1;
1133 		dc->to.tv_nsec = 0;
1134 	} else {
1135 		dc->to.tv_sec = 0;
1136 		dc->to.tv_nsec = 1000 * 1000 * 1000 / poll_hz;
1137 	}
1138 	dc->zero.tv_sec = 0;
1139 	dc->zero.tv_nsec = 0;
1140 	for (i = 0; i < DCONS_NPORT; i++)
1141 		dconschat_init_socket(dc, i,
1142 		    wildcard ? NULL : "localhost", port[i]);
1143 
1144 	dconschat_start_session(dc);
1145 
1146 	for (i = 0; i < DCONS_NPORT; i++) {
1147 		freeaddrinfo(dc->port[i].res);
1148 	}
1149 	return (0);
1150 }
1151