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