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