xref: /freebsd/usr.sbin/dconschat/dconschat.c (revision 75d11bfbc6d4289e4b3d91414e46a3af51f4402a)
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  */
38 
39 #include <sys/param.h>
40 #include <sys/types.h>
41 #include <sys/uio.h>
42 #include <sys/wait.h>
43 #include <unistd.h>
44 #include <fcntl.h>
45 #include <signal.h>
46 #include <stdint.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <termios.h>
50 #include <dev/dcons/dcons.h>
51 
52 #include <sys/socket.h>
53 #include <netinet/in.h>
54 #include <netdb.h>
55 #include <err.h>
56 #include <string.h>
57 #include <sys/eui64.h>
58 #include <sys/event.h>
59 #include <sys/time.h>
60 #include <arpa/telnet.h>
61 
62 #include <sys/ioccom.h>
63 #include <dev/firewire/firewire.h>
64 #include <dev/firewire/iec13213.h>
65 
66 #include <kvm.h>
67 #include <nlist.h>
68 
69 #include <sys/errno.h>
70 
71 #define	DCONS_POLL_HZ		100
72 #define	DCONS_POLL_OFFLINE	2	/* sec */
73 
74 #define RETRY 3
75 
76 #ifdef CSRVAL_VENDOR_PRIVATE
77 #define	USE_CROM 1
78 #else
79 #define	USE_CROM 0
80 #endif
81 
82 int verbose = 0;
83 int tc_set = 0;
84 int poll_hz = DCONS_POLL_HZ;
85 static u_char abreak[3] = {13 /* CR */, 126 /* ~ */, 2 /* ^B */};
86 
87 #define IS_CONSOLE(p)	((p)->port == DCONS_CON)
88 #define IS_GDB(p)	((p)->port == DCONS_GDB)
89 
90 static struct dcons_state {
91 	int fd;
92 	kvm_t *kd;
93 	int kq;
94 	off_t paddr;
95 	off_t reset;
96 #define F_READY		(1 << 1)
97 #define F_RD_ONLY	(1 << 2)
98 #define F_ALT_BREAK	(1 << 3)
99 #define F_TELNET	(1 << 4)
100 #define F_USE_CROM	(1 << 5)
101 #define F_ONE_SHOT	(1 << 6)
102 #define F_REPLAY	(1 << 7)
103 	int flags;
104 	enum {
105 		TYPE_KVM,
106 		TYPE_FW
107 	} type;
108 	int escape_state;
109 	struct dcons_port {
110 		int port;
111 		int sport;
112 		struct dcons_ch o;
113 		struct dcons_ch i;
114 		u_int32_t optr;
115 		u_int32_t iptr;
116 		int s;
117 		int infd;
118 		int outfd;
119 		struct addrinfo *res;
120 		int skip_read;
121 	} port[DCONS_NPORT];
122 	struct timespec to;
123 	struct timespec zero;
124 	struct termios tsave;
125 	struct termios traw;
126 	char escape;
127 } sc;
128 
129 static int dconschat_write_dcons(struct dcons_state *, int, char *, int);
130 
131 static int
dread(struct dcons_state * dc,void * buf,size_t n,off_t offset)132 dread(struct dcons_state *dc, void *buf, size_t n, off_t offset)
133 {
134 	switch (dc->type) {
135 	case TYPE_FW:
136 		return (pread(dc->fd, buf, n, offset));
137 	case TYPE_KVM:
138 		return (kvm_read(dc->kd, offset, buf, n));
139 	}
140 	return (-1);
141 }
142 
143 static int
dwrite(struct dcons_state * dc,void * buf,size_t n,off_t offset)144 dwrite(struct dcons_state *dc, void *buf, size_t n, off_t offset)
145 {
146 	if ((dc->flags & F_RD_ONLY) != 0)
147 		return (n);
148 
149 	switch (dc->type) {
150 	case TYPE_FW:
151 		return (pwrite(dc->fd, buf, n, offset));
152 	case TYPE_KVM:
153 		return (kvm_write(dc->kd, offset, buf, n));
154 	}
155 	return (-1);
156 }
157 
158 static void
dconschat_reset_target(struct dcons_state * dc,struct dcons_port * p)159 dconschat_reset_target(struct dcons_state *dc, struct dcons_port *p)
160 {
161 	char buf[PAGE_SIZE];
162 	if (dc->reset == 0)
163 		return;
164 
165 	snprintf(buf, PAGE_SIZE,
166 	    "\r\n[dconschat reset target(addr=0x%jx)...]\r\n",
167 	    (intmax_t)dc->reset);
168 	write(p->outfd, buf, strlen(buf));
169 	bzero(&buf[0], PAGE_SIZE);
170 	dwrite(dc, (void *)buf, PAGE_SIZE, dc->reset);
171 }
172 
173 
174 static void
dconschat_suspend(struct dcons_state * dc,struct dcons_port * p)175 dconschat_suspend(struct dcons_state *dc, struct dcons_port *p)
176 {
177 	if (p->sport != 0)
178 		return;
179 
180 	if (tc_set)
181 		tcsetattr(STDIN_FILENO, TCSADRAIN, &dc->tsave);
182 
183 	printf("\n[dconschat suspend]\n");
184 	kill(getpid(), SIGTSTP);
185 
186 	if (tc_set)
187 		tcsetattr(STDIN_FILENO, TCSADRAIN, &dc->traw);
188 }
189 
190 static void
dconschat_sigchld(int s)191 dconschat_sigchld(int s)
192 {
193 	struct kevent kev;
194 	struct dcons_port *p;
195 	char buf[256];
196 
197 	p = &sc.port[DCONS_CON];
198 
199 	snprintf(buf, 256, "\r\n[child exit]\r\n");
200 	write(p->outfd, buf, strlen(buf));
201 
202 	if (tc_set)
203 		tcsetattr(STDIN_FILENO, TCSADRAIN, &sc.traw);
204 
205 	EV_SET(&kev, p->infd, EVFILT_READ, EV_ADD, NOTE_LOWAT, 1, (void *)p);
206 	kevent(sc.kq, &kev, 1, NULL, 0, &sc.zero);
207 }
208 
209 static void
dconschat_fork_gdb(struct dcons_state * dc,struct dcons_port * p)210 dconschat_fork_gdb(struct dcons_state *dc, struct dcons_port *p)
211 {
212 	pid_t pid;
213 	char buf[256], com[256];
214 	struct kevent kev;
215 
216 	pid = fork();
217 	if (pid < 0) {
218 		snprintf(buf, 256, "\r\n[%s: fork failed]\r\n", __FUNCTION__);
219 		write(p->outfd, buf, strlen(buf));
220 	}
221 
222 
223 	if (pid == 0) {
224 		/* child */
225 		if (tc_set)
226 			tcsetattr(STDIN_FILENO, TCSADRAIN, &dc->tsave);
227 
228 		snprintf(com, sizeof(buf), "kgdb -r :%d kernel",
229 			dc->port[DCONS_GDB].sport);
230 		snprintf(buf, 256, "\n[fork %s]\n", com);
231 		write(p->outfd, buf, strlen(buf));
232 
233 		execl("/bin/sh", "/bin/sh", "-c", com, NULL);
234 
235 		snprintf(buf, 256, "\n[fork failed]\n");
236 		write(p->outfd, buf, strlen(buf));
237 
238 		if (tc_set)
239 			tcsetattr(STDIN_FILENO, TCSADRAIN, &dc->traw);
240 
241 		exit(0);
242 	} else {
243 		signal(SIGCHLD, dconschat_sigchld);
244 		EV_SET(&kev, p->infd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
245 		kevent(sc.kq, &kev, 1, NULL, 0, &sc.zero);
246 	}
247 }
248 
249 
250 static void
dconschat_cleanup(int sig)251 dconschat_cleanup(int sig)
252 {
253 	struct dcons_state *dc;
254 	int status;
255 
256 	dc = &sc;
257 	if (tc_set != 0)
258 		tcsetattr(STDIN_FILENO, TCSADRAIN, &dc->tsave);
259 
260 	if (sig > 0)
261 		printf("\n[dconschat exiting with signal %d ...]\n", sig);
262 	else
263 		printf("\n[dconschat exiting...]\n");
264 	wait(&status);
265 	exit(0);
266 }
267 
268 #if USE_CROM
269 static int
dconschat_get_crom(struct dcons_state * dc)270 dconschat_get_crom(struct dcons_state *dc)
271 {
272 	off_t addr;
273 	int i, state = 0;
274 	u_int32_t buf, hi = 0, lo = 0, reset_hi = 0, reset_lo = 0;
275 	struct csrreg *reg;
276 
277 	reg = (struct csrreg *)&buf;
278 	addr = 0xffff;
279 	addr = (addr << 32) | 0xf0000400;
280 	for (i = 20; i < 0x400; i += 4) {
281 		if (dread(dc, &buf, 4, addr + i) < 0) {
282 			if (verbose)
283 				warn("crom read failed");
284 			goto out;
285 		}
286 		buf = ntohl(buf);
287 		if (verbose)
288 			printf("%d %02x %06x\n", state, reg->key, reg->val);
289 		switch (state) {
290 		case 0:
291 			if (reg->key == CSRKEY_SPEC &&
292 					reg->val == CSRVAL_VENDOR_PRIVATE)
293 				state = 1;
294 			break;
295 		case 1:
296 			if (reg->key == CSRKEY_VER &&
297 					reg->val == DCONS_CSR_VAL_VER)
298 				state = 2;
299 			break;
300 		case 2:
301 			switch (reg->key) {
302 			case DCONS_CSR_KEY_HI:
303 				hi = reg->val;
304 				break;
305 			case DCONS_CSR_KEY_LO:
306 				lo = reg->val;
307 				break;
308 			case DCONS_CSR_KEY_RESET_HI:
309 				reset_hi = reg->val;
310 				break;
311 			case DCONS_CSR_KEY_RESET_LO:
312 				reset_lo = reg->val;
313 				goto out;
314 				break;
315 			case 0x81:
316 				break;
317 			default:
318 				state = 0;
319 			}
320 			break;
321 		}
322 	}
323 out:
324 	if (verbose)
325 		printf("addr: %06x %06x\n", hi, lo);
326 	dc->paddr = ((off_t)hi << 24) | lo;
327 	dc->reset = ((off_t)reset_hi << 24) | reset_lo;
328 	if (dc->paddr == 0)
329 		return (-1);
330 	return (0);
331 }
332 #endif
333 
334 static void
dconschat_ready(struct dcons_state * dc,int ready,char * reason)335 dconschat_ready(struct dcons_state *dc, int ready, char *reason)
336 {
337 	static char oldreason[64] = "";
338 	int old;
339 
340 	old = (dc->flags & F_READY) ? 1 : 0;
341 
342 	if (ready) {
343 		dc->flags |= F_READY;
344 		if (ready != old)
345 			printf("[dcons connected]\r\n");
346 		oldreason[0] = 0;
347 	} else {
348 		dc->flags &= ~F_READY;
349 		if (strncmp(oldreason, reason, sizeof(oldreason)) != 0) {
350 			printf("[dcons disconnected (%s)]\r\n", reason);
351 			strlcpy(oldreason, reason, sizeof(oldreason));
352 		}
353 	}
354 }
355 
356 static int
dconschat_fetch_header(struct dcons_state * dc)357 dconschat_fetch_header(struct dcons_state *dc)
358 {
359 	char ebuf[64];
360 	struct dcons_buf dbuf;
361 	int j;
362 
363 #if USE_CROM
364 	if (dc->paddr == 0 && (dc->flags & F_USE_CROM) != 0) {
365 		if (dconschat_get_crom(dc)) {
366 			dconschat_ready(dc, 0, "get crom failed");
367 			return (-1);
368 		}
369 	}
370 #endif
371 
372 	if (dread(dc, &dbuf, DCONS_HEADER_SIZE, dc->paddr) < 0) {
373 		dconschat_ready(dc, 0, "read header failed");
374 		return (-1);
375 	}
376 	if (dbuf.magic != htonl(DCONS_MAGIC)) {
377 		if ((dc->flags & F_USE_CROM) !=0)
378 			dc->paddr = 0;
379 		snprintf(ebuf, sizeof(ebuf), "wrong magic 0x%08x", dbuf.magic);
380 		dconschat_ready(dc, 0, ebuf);
381 		return (-1);
382 	}
383 	if (ntohl(dbuf.version) != DCONS_VERSION) {
384 		snprintf(ebuf, sizeof(ebuf),
385 		    "wrong version %d,%d",
386 		    ntohl(dbuf.version), DCONS_VERSION);
387 		/* XXX exit? */
388 		dconschat_ready(dc, 0, ebuf);
389 		return (-1);
390 	}
391 
392 	for (j = 0; j < DCONS_NPORT; j++) {
393 		struct dcons_ch *o, *i;
394 		off_t newbuf;
395 		int new = 0;
396 
397 		o = &dc->port[j].o;
398 		newbuf = dc->paddr + ntohl(dbuf.ooffset[j]);
399 		o->size = ntohl(dbuf.osize[j]);
400 
401 		if (newbuf != o->buf) {
402 			/* buffer address has changes */
403 			new = 1;
404 			o->gen = ntohl(dbuf.optr[j]) >> DCONS_GEN_SHIFT;
405 			o->pos = ntohl(dbuf.optr[j]) & DCONS_POS_MASK;
406 			o->buf = newbuf;
407 		}
408 
409 		i = &dc->port[j].i;
410 		i->size = ntohl(dbuf.isize[j]);
411 		i->gen = ntohl(dbuf.iptr[j]) >> DCONS_GEN_SHIFT;
412 		i->pos = ntohl(dbuf.iptr[j]) & DCONS_POS_MASK;
413 		i->buf = dc->paddr + ntohl(dbuf.ioffset[j]);
414 
415 		if (verbose) {
416 			printf("port %d   size offset   gen   pos\n", j);
417 			printf("output: %5d %6d %5d %5d\n"
418 				"input : %5d %6d %5d %5d\n",
419 			o->size, ntohl(dbuf.ooffset[j]), o->gen, o->pos,
420 			i->size, ntohl(dbuf.ioffset[j]), i->gen, i->pos);
421 		}
422 
423 		if (IS_CONSOLE(&dc->port[j]) && new &&
424 		    (dc->flags & F_REPLAY) !=0) {
425 			if (o->gen > 0)
426 				o->gen --;
427 			else
428 				o->pos = 0;
429 		}
430 	}
431 	dconschat_ready(dc, 1, NULL);
432 	return(0);
433 }
434 
435 static int
dconschat_get_ptr(struct dcons_state * dc)436 dconschat_get_ptr (struct dcons_state *dc) {
437 	int dlen, i;
438 	u_int32_t ptr[DCONS_NPORT*2+1];
439 	static int retry = RETRY;
440 	char ebuf[64];
441 
442 again:
443 	dlen = dread(dc, &ptr, sizeof(ptr),
444 		dc->paddr + __offsetof(struct dcons_buf, magic));
445 
446 	if (dlen < 0) {
447 		if (errno == ETIMEDOUT)
448 			if (retry -- > 0)
449 				goto again;
450 		dconschat_ready(dc, 0, "get ptr failed");
451 		return(-1);
452 	}
453 	if (ptr[0] != htonl(DCONS_MAGIC)) {
454 		if ((dc->flags & F_USE_CROM) !=0)
455 			dc->paddr = 0;
456 		snprintf(ebuf, sizeof(ebuf), "wrong magic 0x%08x", ptr[0]);
457 		dconschat_ready(dc, 0, ebuf);
458 		return(-1);
459 	}
460 	retry = RETRY;
461 	for (i = 0; i < DCONS_NPORT; i ++) {
462 		dc->port[i].optr = ntohl(ptr[i + 1]);
463 		dc->port[i].iptr = ntohl(ptr[DCONS_NPORT + i + 1]);
464 	}
465 	return(0);
466 }
467 
468 #define MAX_XFER 2048
469 static int
dconschat_read_dcons(struct dcons_state * dc,int port,char * buf,int len)470 dconschat_read_dcons(struct dcons_state *dc, int port, char *buf, int len)
471 {
472 	struct dcons_ch *ch;
473 	u_int32_t ptr, pos, gen, next_gen;
474 	int rlen, dlen, lost;
475 	int retry = RETRY;
476 
477 	ch = &dc->port[port].o;
478 	ptr = dc->port[port].optr;
479 	gen = ptr >> DCONS_GEN_SHIFT;
480 	pos = ptr & DCONS_POS_MASK;
481 	if (gen == ch->gen && pos == ch->pos)
482 		return (-1);
483 
484 	next_gen = DCONS_NEXT_GEN(ch->gen);
485 	/* XXX sanity check */
486 	if (gen == ch->gen) {
487 		if (pos > ch->pos)
488 			goto ok;
489 		lost = ch->size * DCONS_GEN_MASK - ch->pos;
490 		ch->pos = 0;
491 	} else if (gen == next_gen) {
492 		if (pos <= ch->pos)
493 			goto ok;
494 		lost = pos - ch->pos;
495 		ch->pos = pos;
496 	} else {
497 		lost = gen - ch->gen;
498 		if (lost < 0)
499 			lost += DCONS_GEN_MASK;
500 		if (verbose)
501 			printf("[genskip %d]", lost);
502 		lost = lost * ch->size - ch->pos;
503 		ch->pos = 0;
504 		ch->gen = gen;
505 	}
506 	/* generation skipped !! */
507 	/* XXX discard */
508 	if (verbose)
509 		printf("[lost %d]", lost);
510 ok:
511 	if (gen == ch->gen)
512 		rlen = pos - ch->pos;
513 	else
514 		rlen = ch->size - ch->pos;
515 
516 	if (rlen > MAX_XFER)
517 		rlen = MAX_XFER;
518 	if (rlen > len)
519 		rlen = len;
520 
521 #if 1
522 	if (verbose == 1) {
523 		printf("[%d]", rlen);
524 		fflush(stdout);
525 	}
526 #endif
527 
528 again:
529 	dlen = dread(dc, buf, rlen, ch->buf + ch->pos);
530 	if (dlen < 0) {
531 		if (errno == ETIMEDOUT)
532 			if (retry -- > 0)
533 				goto again;
534 		dconschat_ready(dc, 0, "read buffer failed");
535 		return(-1);
536 	}
537 	if (dlen != rlen)
538 		warnx("dlen(%d) != rlen(%d)\n", dlen, rlen);
539 	ch->pos += dlen;
540 	if (ch->pos >= ch->size) {
541 		ch->gen = next_gen;
542 		ch->pos = 0;
543 		if (verbose)
544 			printf("read_dcons: gen=%d", ch->gen);
545 	}
546 	return (dlen);
547 }
548 
549 static int
dconschat_write_dcons(struct dcons_state * dc,int port,char * buf,int blen)550 dconschat_write_dcons(struct dcons_state *dc, int port, char *buf, int blen)
551 {
552 	struct dcons_ch *ch;
553 	u_int32_t ptr;
554 	int len, wlen;
555 	int retry = RETRY;
556 
557 	ch = &dc->port[port].i;
558 	ptr = dc->port[port].iptr;
559 
560 	/* the others may advance the pointer sync with it */
561 	ch->gen = ptr >> DCONS_GEN_SHIFT;
562 	ch->pos = ptr & DCONS_POS_MASK;
563 
564 	while(blen > 0) {
565 		wlen = MIN(blen, ch->size - ch->pos);
566 		wlen = MIN(wlen, MAX_XFER);
567 		len = dwrite(dc, buf, wlen, ch->buf + ch->pos);
568 		if (len < 0) {
569 			if (errno == ETIMEDOUT)
570 				if (retry -- > 0)
571 					continue; /* try again */
572 			dconschat_ready(dc, 0, "write buffer failed");
573 			return(-1);
574 		}
575 		ch->pos += len;
576 		buf += len;
577 		blen -= len;
578 		if (ch->pos >= ch->size) {
579 			ch->gen = DCONS_NEXT_GEN(ch->gen);
580 			ch->pos = 0;
581 			if (verbose)
582 				printf("write_dcons: gen=%d", ch->gen);
583 
584 		}
585 	}
586 
587 	ptr = DCONS_MAKE_PTR(ch);
588 	dc->port[port].iptr = ptr;
589 
590 	if (verbose > 2)
591 		printf("(iptr: 0x%x)", ptr);
592 again:
593 	len = dwrite(dc, &ptr, sizeof(u_int32_t),
594 		dc->paddr + __offsetof(struct dcons_buf, iptr[port]));
595 	if (len < 0) {
596 		if (errno == ETIMEDOUT)
597 			if (retry -- > 0)
598 				goto again;
599 		dconschat_ready(dc, 0, "write ptr failed");
600 		return(-1);
601 	}
602 	return(0);
603 }
604 
605 
606 static int
dconschat_write_socket(int fd,char * buf,int len)607 dconschat_write_socket(int fd, char *buf, int len)
608 {
609 	write(fd, buf, len);
610 	if (verbose > 1) {
611 		buf[len] = 0;
612 		printf("<- %s\n", buf);
613 	}
614 	return (0);
615 }
616 
617 static void
dconschat_init_socket(struct dcons_state * dc,int port,char * host,int sport)618 dconschat_init_socket(struct dcons_state *dc, int port, char *host, int sport)
619 {
620 	struct addrinfo hints, *res;
621 	int on = 1, error;
622 	char service[10];
623 	struct kevent kev;
624 	struct dcons_port *p;
625 
626 	p = &dc->port[port];
627 	p->port = port;
628 	p->sport = sport;
629 	p->infd = p->outfd = -1;
630 
631 	if (sport < 0)
632 		return;
633 
634 	if (sport == 0) {
635 
636 		/* Use stdin and stdout */
637 		p->infd = STDIN_FILENO;
638 		p->outfd = STDOUT_FILENO;
639 		p->s = -1;
640 		if (tc_set == 0 &&
641 		    tcgetattr(STDIN_FILENO, &dc->tsave) == 0) {
642 			dc->traw = dc->tsave;
643 			cfmakeraw(&dc->traw);
644 			tcsetattr(STDIN_FILENO, TCSADRAIN, &dc->traw);
645 			tc_set = 1;
646 		}
647 		EV_SET(&kev, p->infd, EVFILT_READ, EV_ADD, NOTE_LOWAT, 1,
648 		    (void *)p);
649 		kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
650 		return;
651 	}
652 
653 	memset(&hints, 0, sizeof(hints));
654 	hints.ai_flags = AI_PASSIVE;
655 #if 1	/* gdb can talk v4 only */
656 	hints.ai_family = PF_INET;
657 #else
658 	hints.ai_family = PF_UNSPEC;
659 #endif
660 	hints.ai_socktype = SOCK_STREAM;
661 	hints.ai_protocol = 0;
662 
663 	if (verbose)
664 		printf("%s:%d for port %d\n",
665 			host == NULL ? "*" : host, sport, port);
666 	snprintf(service, sizeof(service), "%d", sport);
667 	error = getaddrinfo(host, service,  &hints, &res);
668 	if (error)
669 		errx(1, "tcp/%s: %s\n", service, gai_strerror(error));
670 	p->res = res;
671 	p->s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
672 	if (p->s < 0)
673 		err(1, "socket");
674 	setsockopt(p->s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
675 
676 	if (bind(p->s, p->res->ai_addr, p->res->ai_addrlen) < 0) {
677 		err(1, "bind");
678 	}
679 	if (listen(p->s, 1) < 0)
680 		err(1, "listen");
681 	EV_SET(&kev, p->s, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, (void *)p);
682 	error = kevent(dc->kq, &kev, 1, NULL, 0, &dc->to);
683 	if (error < 0)
684 		err(1, "kevent");
685 }
686 
687 static int
dconschat_accept_socket(struct dcons_state * dc,struct dcons_port * p)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
dconschat_read_filter(struct dcons_state * dc,struct dcons_port * p,u_char * sp,int slen,u_char * dp,int * dlen)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
dconschat_read_socket(struct dcons_state * dc,struct dcons_port * p)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
dconschat_proc_socket(struct dcons_state * dc)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
dconschat_proc_dcons(struct dcons_state * dc)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
dconschat_start_session(struct dcons_state * dc)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
usage(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
main(int argc,char ** argv)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