xref: /freebsd/usr.sbin/dconschat/dconschat.c (revision 19fae0f66023a97a9b464b3beeeabb2081f575b3)
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 failed");
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 }
685 
686 static int
687 dconschat_accept_socket(struct dcons_state *dc, struct dcons_port *p)
688 {
689 	socklen_t addrlen;
690 	int ns, flags;
691 	struct kevent kev;
692 
693 	/* accept connection */
694 	addrlen = p->res->ai_addrlen;
695 	ns = accept(p->s, p->res->ai_addr, &addrlen);
696 	if (ns < 0)
697 		err(1, "accept");
698 	if (verbose)
699 		printf("port%d accepted\n", p->port);
700 
701 	flags = fcntl(ns, F_GETFL, 0);
702 	flags |= O_NDELAY;
703 	fcntl(ns, F_SETFL, flags);
704 #if 1
705 	if (IS_CONSOLE(p) && (dc->flags & F_TELNET) != 0) {
706 		char sga[] = {IAC, WILL, TELOPT_SGA};
707 		char linemode[] = {IAC, DONT, TELOPT_LINEMODE};
708 		char echo[] = {IAC, WILL, TELOPT_ECHO};
709 		char bin[] = {IAC, DO, TELOPT_BINARY};
710 
711 		write(ns, sga, sizeof(sga));
712 		write(ns, linemode, sizeof(linemode));
713 		write(ns, echo, sizeof(echo));
714 		write(ns, bin, sizeof(bin));
715 		p->skip_read = 0;
716 	}
717 #endif
718 	/* discard backlog on GDB port */
719 	if (IS_GDB(p)) {
720 		char buf[2048];
721 		int len;
722 
723 		while ((len = dconschat_read_dcons(dc, DCONS_GDB, &buf[0],
724 				 2048)) > 0)
725 			if (verbose)
726 				printf("discard %d chars on GDB port\n", len);
727 	}
728 
729 	p->infd = p->outfd = ns;
730 	EV_SET(&kev, ns, EVFILT_READ, EV_ADD, NOTE_LOWAT, 1, (void *)p);
731 	kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
732 	return(0);
733 }
734 
735 static int
736 dconschat_read_filter(struct dcons_state *dc, struct dcons_port *p,
737     u_char *sp, int slen, u_char *dp, int *dlen)
738 {
739 	int skip;
740 	char *buf;
741 
742 	while (slen > 0) {
743 		skip = 0;
744 		if (IS_CONSOLE(p)) {
745 			if ((dc->flags & F_TELNET) != 0) {
746 				/* XXX Telnet workarounds */
747 				if (p->skip_read -- > 0) {
748 					sp ++;
749 					slen --;
750 					continue;
751 				}
752 				if (*sp == IAC) {
753 					if (verbose)
754 						printf("(IAC)");
755 					p->skip_read = 2;
756 					sp ++;
757 					slen --;
758 					continue;
759 				}
760 				if (*sp == 0) {
761 					if (verbose)
762 						printf("(0 stripped)");
763 					sp ++;
764 					slen --;
765 					continue;
766 				}
767 			}
768 			switch (dc->escape_state) {
769 			case STATE1:
770 				if (*sp == dc->escape) {
771 					skip = 1;
772 					dc->escape_state = STATE2;
773 				} else
774 					dc->escape_state = STATE0;
775 				break;
776 			case STATE2:
777 				dc->escape_state = STATE0;
778 				skip = 1;
779 				if (*sp == '.')
780 					dconschat_cleanup(0);
781 				else if (*sp == CTRL('B')) {
782 					bcopy(abreak, dp, 3);
783 					dp += 3;
784 					*dlen += 3;
785 				}
786 				else if (*sp == CTRL('G'))
787 					dconschat_fork_gdb(dc, p);
788 				else if ((*sp == CTRL('R'))
789 						&& (dc->reset != 0)) {
790 					dc->escape_state = STATE3;
791 					buf = "\r\n[Are you sure to reset target? (y/N)]";
792 					write(p->outfd, buf, strlen(buf));
793 				} else if (*sp == CTRL('Z'))
794 					dconschat_suspend(dc, p);
795 				else {
796 					skip = 0;
797 					*dp++ = dc->escape;
798 					(*dlen) ++;
799 				}
800 				break;
801 			case STATE3:
802 				dc->escape_state = STATE0;
803 				skip = 1;
804 				if (*sp == 'y')
805 					dconschat_reset_target(dc, p);
806 				else {
807 					write(p->outfd, sp, 1);
808 					write(p->outfd, "\r\n", 2);
809 				}
810 				break;
811 			}
812 			if (*sp == KEY_CR)
813 				dc->escape_state = STATE1;
814 		} else if (IS_GDB(p)) {
815 			/* GDB: ^C -> CR+~+^B */
816 			if (*sp == CTRL('C') && (dc->flags & F_ALT_BREAK) != 0) {
817 				bcopy(abreak, dp, 3);
818 				dp += 3;
819 				sp ++;
820 				*dlen += 3;
821 				/* discard rest of the packet */
822 				slen = 0;
823 				break;
824 			}
825 		}
826 		if (!skip) {
827 			*dp++ = *sp;
828 			(*dlen) ++;
829 		}
830 		sp ++;
831 		slen --;
832 	}
833 	return (*dlen);
834 
835 }
836 
837 static int
838 dconschat_read_socket(struct dcons_state *dc, struct dcons_port *p)
839 {
840 	struct kevent kev;
841 	int len, wlen;
842 	char rbuf[MAX_XFER], wbuf[MAX_XFER+2];
843 
844 	if ((len = read(p->infd, rbuf, sizeof(rbuf))) > 0) {
845 		wlen = 0;
846 		dconschat_read_filter(dc, p, rbuf, len, wbuf, &wlen);
847 		/* XXX discard if not ready*/
848 		if (wlen > 0 && (dc->flags & F_READY) != 0) {
849 			dconschat_write_dcons(dc, p->port, wbuf, wlen);
850 			if (verbose > 1) {
851 				wbuf[wlen] = 0;
852 				printf("-> %s\n", wbuf);
853 			} else if (verbose == 1) {
854 				printf("(%d)", wlen);
855 				fflush(stdout);
856 			}
857 		}
858 	} else {
859 		if (verbose) {
860 			if (len == 0)
861 				warnx("port%d: closed", p->port);
862 			else
863 				warn("port%d: read", p->port);
864 		}
865 		EV_SET(&kev, p->infd, EVFILT_READ,
866 			EV_DELETE, 0, 0, NULL);
867 		kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
868 		close(p->infd);
869 		close(p->outfd);
870 		/* XXX exit for pipe case XXX */
871 		EV_SET(&kev, p->s, EVFILT_READ,
872 				EV_ADD | EV_ONESHOT, 0, 0, (void *) p);
873 		kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
874 		p->infd = p->outfd = -1;
875 	}
876 	return(0);
877 }
878 #define NEVENT 5
879 static int
880 dconschat_proc_socket(struct dcons_state *dc)
881 {
882 	struct kevent elist[NEVENT], *e;
883 	int i, n;
884 	struct dcons_port *p;
885 
886 	n = kevent(dc->kq, NULL, 0, elist, NEVENT, &dc->to);
887 	for (i = 0; i < n; i ++) {
888 		e = &elist[i];
889 		p = (struct dcons_port *)e->udata;
890 		if (e->ident == p->s) {
891 			dconschat_accept_socket(dc, p);
892 		} else {
893 			dconschat_read_socket(dc, p);
894 		}
895 	}
896 	return(0);
897 }
898 
899 static int
900 dconschat_proc_dcons(struct dcons_state *dc)
901 {
902 	int port, len, err;
903 	char buf[MAX_XFER];
904 	struct dcons_port *p;
905 
906 	err = dconschat_get_ptr(dc);
907 	if (err) {
908 		/* XXX we should stop write operation too. */
909 		return err;
910 	}
911 	for (port = 0; port < DCONS_NPORT; port ++) {
912 		p = &dc->port[port];
913 		if (p->infd < 0)
914 			continue;
915 		while ((len = dconschat_read_dcons(dc, port, buf,
916 		    sizeof(buf))) > 0) {
917 			dconschat_write_socket(p->outfd, buf, len);
918 			if ((err = dconschat_get_ptr(dc)))
919 				return (err);
920 		}
921 		if ((dc->flags & F_ONE_SHOT) != 0 && len <= 0)
922 			dconschat_cleanup(0);
923 	}
924 	return 0;
925 }
926 
927 static int
928 dconschat_start_session(struct dcons_state *dc)
929 {
930 	int counter = 0;
931 	int retry = 0;
932 	int retry_unit_init = MAX(1, poll_hz / 10);
933 	int retry_unit_offline = poll_hz * DCONS_POLL_OFFLINE;
934 	int retry_unit = retry_unit_init;
935 	int retry_max = retry_unit_offline / retry_unit;
936 
937 	while (1) {
938 		if (((dc->flags & F_READY) == 0) && ++counter > retry_unit) {
939 			counter = 0;
940 			retry ++;
941 			if (retry > retry_max)
942 				retry_unit = retry_unit_offline;
943 			if (verbose) {
944 				printf("%d/%d ", retry, retry_max);
945 				fflush(stdout);
946 			}
947 			dconschat_fetch_header(dc);
948 		}
949 		if ((dc->flags & F_READY) != 0) {
950 			counter = 0;
951 			retry = 0;
952 			retry_unit = retry_unit_init;
953 			dconschat_proc_dcons(dc);
954 		}
955 		dconschat_proc_socket(dc);
956 	}
957 	return (0);
958 }
959 
960 static void
961 usage(void)
962 {
963 	fprintf(stderr,
964  	    "usage: dconschat [-brvwRT1] [-h hz] [-C port] [-G port]\n"
965 	    "\t\t\t[-M core] [-N system]\n"
966 	    "\t\t\t[-u unit] [-a address] [-t target_eui64]\n"
967 	    "\t-b	translate ctrl-C to CR+~+ctrl-B on gdb port\n"
968 	    "\t-v	verbose\n"
969 	    "\t-w	listen on wildcard address rather than localhost\n"
970 	    "\t-r	replay old buffer on connection\n"
971 	    "\t-R	read-only\n"
972 	    "\t-T	enable Telnet protocol workaround on console port\n"
973 	    "\t-1	one shot: read buffer and exit\n"
974 	    "\t-h	polling rate\n"
975 	    "\t-C	port number for console port\n"
976 	    "\t-G	port number for gdb port\n"
977 	    "\t(for KVM)\n"
978 	    "\t-M	core file\n"
979 	    "\t-N	system file\n"
980 	    "\t(for FireWire)\n"
981 	    "\t-u	specify unit number of the bus\n"
982 	    "\t-t	EUI64 of target host (must be specified)\n"
983 	    "\t-a	physical address of dcons buffer on target host\n"
984 	);
985 	exit(0);
986 }
987 int
988 main(int argc, char **argv)
989 {
990 	struct dcons_state *dc;
991 	struct fw_eui64 eui;
992 	struct eui64 target;
993 	char devname[256], *core = NULL, *system = NULL;
994 	int i, ch, error;
995 	int unit=0, wildcard=0;
996 	int port[DCONS_NPORT];
997 
998 	bzero(&sc, sizeof(sc));
999 	dc = &sc;
1000 	dc->flags |= USE_CROM ? F_USE_CROM : 0;
1001 
1002 	/* default ports */
1003 	port[0] = 0;	/* stdin/out for console */
1004 	port[1] = -1;	/* disable gdb port */
1005 
1006 	/* default escape char */
1007 	dc->escape = KEY_TILDE;
1008 
1009 	while ((ch = getopt(argc, argv, "a:be:h:rt:u:vwC:G:M:N:RT1")) != -1) {
1010 		switch(ch) {
1011 		case 'a':
1012 			dc->paddr = strtoull(optarg, NULL, 0);
1013 			dc->flags &= ~F_USE_CROM;
1014 			break;
1015 		case 'b':
1016 			dc->flags |= F_ALT_BREAK;
1017 			break;
1018 		case 'e':
1019 			dc->escape = optarg[0];
1020 			break;
1021 		case 'h':
1022 			poll_hz = strtoul(optarg, NULL, 0);
1023 			if (poll_hz == 0)
1024 				poll_hz = DCONS_POLL_HZ;
1025 			break;
1026 		case 'r':
1027 			dc->flags |= F_REPLAY;
1028 			break;
1029 		case 't':
1030 			if (eui64_hostton(optarg, &target) != 0 &&
1031 			    eui64_aton(optarg, &target) != 0)
1032 				errx(1, "invalid target: %s", optarg);
1033 			eui.hi = ntohl(*(u_int32_t*)&(target.octet[0]));
1034 			eui.lo = ntohl(*(u_int32_t*)&(target.octet[4]));
1035 			dc->type = TYPE_FW;
1036 			break;
1037 		case 'u':
1038 			unit = strtol(optarg, NULL, 0);
1039 			break;
1040 		case 'v':
1041 			verbose ++;
1042 			break;
1043 		case 'w':
1044 			wildcard = 1;
1045 			break;
1046 		case 'C':
1047 			port[0] = strtol(optarg, NULL, 0);
1048 			break;
1049 		case 'G':
1050 			port[1] = strtol(optarg, NULL, 0);
1051 			break;
1052 		case 'M':
1053 			core = optarg;
1054 			break;
1055 		case 'N':
1056 			system = optarg;
1057 			break;
1058 		case 'R':
1059 			dc->flags |= F_RD_ONLY;
1060 			break;
1061 		case 'T':
1062 			dc->flags |= F_TELNET;
1063 			break;
1064 		case '1':
1065 			dc->flags |= F_ONE_SHOT | F_REPLAY;
1066 			break;
1067 		default:
1068 			usage();
1069 		}
1070 	}
1071 	if (dc->paddr == 0 && (dc->flags & F_USE_CROM) == 0) {
1072 		warnx("no address specified");
1073 		usage();
1074 	}
1075 
1076 	if (port[0] < 0 && port[1] < 0) {
1077 		warnx("no port specified");
1078 		usage();
1079 	}
1080 
1081 	/* set signal handler */
1082 	signal(SIGHUP, dconschat_cleanup);
1083 	signal(SIGINT, dconschat_cleanup);
1084 	signal(SIGPIPE, dconschat_cleanup);
1085 	signal(SIGTERM, dconschat_cleanup);
1086 
1087 	/* init firewire */
1088 	switch (dc->type) {
1089 	case TYPE_FW:
1090 #define MAXDEV 10
1091 		for (i = 0; i < MAXDEV; i ++) {
1092 			snprintf(devname, sizeof(devname),
1093 			    "/dev/fwmem%d.%d", unit, i);
1094 			dc->fd = open(devname, O_RDWR);
1095 			if (dc->fd >= 0)
1096 				goto found;
1097 		}
1098 		err(1, "open");
1099 found:
1100 		error = ioctl(dc->fd, FW_SDEUI64, &eui);
1101 		if (error)
1102 			err(1, "ioctl");
1103 		break;
1104 	case TYPE_KVM:
1105 	{
1106 		struct nlist nl[] = {{"dcons_buf"}, {""}};
1107 		void *dcons_buf;
1108 
1109 		dc->kd = kvm_open(system, core, NULL,
1110 		    (dc->flags & F_RD_ONLY) ? O_RDONLY : O_RDWR, "dconschat");
1111 		if (dc->kd == NULL)
1112 			errx(1, "kvm_open");
1113 
1114 		if (kvm_nlist(dc->kd, nl) < 0)
1115 			errx(1, "kvm_nlist: %s", kvm_geterr(dc->kd));
1116 
1117 		if (kvm_read(dc->kd, nl[0].n_value, &dcons_buf,
1118 		    sizeof(void *)) < 0)
1119 			errx(1, "kvm_read: %s", kvm_geterr(dc->kd));
1120 		dc->paddr = (uintptr_t)dcons_buf;
1121 		if (verbose)
1122 			printf("dcons_buf: 0x%x\n", (uint)dc->paddr);
1123 		break;
1124 	}
1125 	}
1126 	dconschat_fetch_header(dc);
1127 
1128 	/* init sockets */
1129 	dc->kq = kqueue();
1130 	if (poll_hz == 1) {
1131 		dc->to.tv_sec = 1;
1132 		dc->to.tv_nsec = 0;
1133 	} else {
1134 		dc->to.tv_sec = 0;
1135 		dc->to.tv_nsec = 1000 * 1000 * 1000 / poll_hz;
1136 	}
1137 	dc->zero.tv_sec = 0;
1138 	dc->zero.tv_nsec = 0;
1139 	for (i = 0; i < DCONS_NPORT; i++)
1140 		dconschat_init_socket(dc, i,
1141 		    wildcard ? NULL : "localhost", port[i]);
1142 
1143 	dconschat_start_session(dc);
1144 
1145 	for (i = 0; i < DCONS_NPORT; i++) {
1146 		freeaddrinfo(dc->port[i].res);
1147 	}
1148 	return (0);
1149 }
1150