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