1121468Ssimokawa/*
2121468Ssimokawa * Copyright (C) 2003
3121468Ssimokawa * 	Hidetoshi Shimokawa. All rights reserved.
4121468Ssimokawa *
5121468Ssimokawa * Redistribution and use in source and binary forms, with or without
6121468Ssimokawa * modification, are permitted provided that the following conditions
7121468Ssimokawa * are met:
8121468Ssimokawa * 1. Redistributions of source code must retain the above copyright
9121468Ssimokawa *    notice, this list of conditions and the following disclaimer.
10121468Ssimokawa * 2. Redistributions in binary form must reproduce the above copyright
11121468Ssimokawa *    notice, this list of conditions and the following disclaimer in the
12121468Ssimokawa *    documentation and/or other materials provided with the distribution.
13121468Ssimokawa * 3. All advertising materials mentioning features or use of this software
14121468Ssimokawa *    must display the following acknowledgement:
15121468Ssimokawa *
16121468Ssimokawa *	This product includes software developed by Hidetoshi Shimokawa.
17121468Ssimokawa *
18121468Ssimokawa * 4. Neither the name of the author nor the names of its contributors
19121468Ssimokawa *    may be used to endorse or promote products derived from this software
20121468Ssimokawa *    without specific prior written permission.
21121468Ssimokawa *
22121468Ssimokawa * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23121468Ssimokawa * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24121468Ssimokawa * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25121468Ssimokawa * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26121468Ssimokawa * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27121468Ssimokawa * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28121468Ssimokawa * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29121468Ssimokawa * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30121468Ssimokawa * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31121468Ssimokawa * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32121468Ssimokawa * SUCH DAMAGE.
33121468Ssimokawa *
34121468Ssimokawa * $Id: dconschat.c,v 1.76 2003/10/23 06:21:13 simokawa Exp $
35121468Ssimokawa * $FreeBSD: releng/10.3/usr.sbin/dconschat/dconschat.c 285007 2015-07-01 15:03:49Z pfg $
36121468Ssimokawa */
37121468Ssimokawa
38121468Ssimokawa#include <sys/param.h>
39121468Ssimokawa#include <sys/types.h>
40121468Ssimokawa#include <sys/uio.h>
41194187Sed#include <sys/wait.h>
42121468Ssimokawa#include <unistd.h>
43121468Ssimokawa#include <fcntl.h>
44194187Sed#include <signal.h>
45233195Sdim#include <stdint.h>
46121468Ssimokawa#include <stdio.h>
47121468Ssimokawa#include <stdlib.h>
48121468Ssimokawa#include <termios.h>
49121468Ssimokawa#include <dev/dcons/dcons.h>
50121468Ssimokawa
51121468Ssimokawa#include <sys/socket.h>
52121468Ssimokawa#include <netinet/in.h>
53121468Ssimokawa#include <netdb.h>
54121468Ssimokawa#include <err.h>
55121468Ssimokawa#include <string.h>
56129760Sbrooks#include <sys/eui64.h>
57121468Ssimokawa#include <sys/event.h>
58121468Ssimokawa#include <sys/time.h>
59121468Ssimokawa#include <arpa/telnet.h>
60121468Ssimokawa
61121468Ssimokawa#include <sys/ioccom.h>
62121468Ssimokawa#include <dev/firewire/firewire.h>
63121468Ssimokawa#include <dev/firewire/iec13213.h>
64121468Ssimokawa
65121468Ssimokawa#include <kvm.h>
66121468Ssimokawa#include <nlist.h>
67121468Ssimokawa
68121468Ssimokawa#include <sys/errno.h>
69121468Ssimokawa
70126038Ssimokawa#define	DCONS_POLL_HZ		100
71126038Ssimokawa#define	DCONS_POLL_OFFLINE	2	/* sec */
72121468Ssimokawa
73121468Ssimokawa#define RETRY 3
74121468Ssimokawa
75121468Ssimokawa#ifdef CSRVAL_VENDOR_PRIVATE
76121468Ssimokawa#define	USE_CROM 1
77121468Ssimokawa#else
78121468Ssimokawa#define	USE_CROM 0
79121468Ssimokawa#endif
80121468Ssimokawa
81121468Ssimokawaint verbose = 0;
82121468Ssimokawaint tc_set = 0;
83126038Ssimokawaint poll_hz = DCONS_POLL_HZ;
84170775Ssimokawastatic u_char abreak[3] = {13 /* CR */, 126 /* ~ */, 2 /* ^B */};
85121468Ssimokawa
86170775Ssimokawa#define IS_CONSOLE(p)	((p)->port == DCONS_CON)
87170775Ssimokawa#define IS_GDB(p)	((p)->port == DCONS_GDB)
88121468Ssimokawa
89121468Ssimokawastatic struct dcons_state {
90121468Ssimokawa	int fd;
91121468Ssimokawa	kvm_t *kd;
92121468Ssimokawa	int kq;
93121468Ssimokawa	off_t paddr;
94170422Ssimokawa	off_t reset;
95121468Ssimokawa#define F_READY		(1 << 1)
96121468Ssimokawa#define F_RD_ONLY	(1 << 2)
97121468Ssimokawa#define F_ALT_BREAK	(1 << 3)
98121468Ssimokawa#define F_TELNET	(1 << 4)
99121468Ssimokawa#define F_USE_CROM	(1 << 5)
100121468Ssimokawa#define F_ONE_SHOT	(1 << 6)
101121468Ssimokawa#define F_REPLAY	(1 << 7)
102121468Ssimokawa	int flags;
103121468Ssimokawa	enum {
104121468Ssimokawa		TYPE_KVM,
105121468Ssimokawa		TYPE_FW
106121468Ssimokawa	} type;
107121468Ssimokawa	int escape_state;
108121468Ssimokawa	struct dcons_port {
109121468Ssimokawa		int port;
110170775Ssimokawa		int sport;
111121468Ssimokawa		struct dcons_ch o;
112121468Ssimokawa		struct dcons_ch i;
113121468Ssimokawa		u_int32_t optr;
114121468Ssimokawa		u_int32_t iptr;
115121468Ssimokawa		int s;
116121468Ssimokawa		int infd;
117121468Ssimokawa		int outfd;
118121468Ssimokawa		struct addrinfo *res;
119121468Ssimokawa		int skip_read;
120121468Ssimokawa	} port[DCONS_NPORT];
121121468Ssimokawa	struct timespec to;
122121468Ssimokawa	struct timespec zero;
123121468Ssimokawa	struct termios tsave;
124170422Ssimokawa	struct termios traw;
125170775Ssimokawa	char escape;
126121468Ssimokawa} sc;
127121468Ssimokawa
128170775Ssimokawastatic int dconschat_write_dcons(struct dcons_state *, int, char *, int);
129170775Ssimokawa
130121468Ssimokawastatic int
131121468Ssimokawadread(struct dcons_state *dc, void *buf, size_t n, off_t offset)
132121468Ssimokawa{
133121468Ssimokawa	switch (dc->type) {
134121468Ssimokawa	case TYPE_FW:
135121468Ssimokawa		return (pread(dc->fd, buf, n, offset));
136121468Ssimokawa	case TYPE_KVM:
137121468Ssimokawa		return (kvm_read(dc->kd, offset, buf, n));
138121468Ssimokawa	}
139121468Ssimokawa	return (-1);
140121468Ssimokawa}
141121468Ssimokawa
142121468Ssimokawastatic int
143121468Ssimokawadwrite(struct dcons_state *dc, void *buf, size_t n, off_t offset)
144121468Ssimokawa{
145121468Ssimokawa	if ((dc->flags & F_RD_ONLY) != 0)
146121468Ssimokawa		return (n);
147121468Ssimokawa
148121468Ssimokawa	switch (dc->type) {
149121468Ssimokawa	case TYPE_FW:
150121468Ssimokawa		return (pwrite(dc->fd, buf, n, offset));
151121468Ssimokawa	case TYPE_KVM:
152121468Ssimokawa		return (kvm_write(dc->kd, offset, buf, n));
153121468Ssimokawa	}
154121468Ssimokawa	return (-1);
155121468Ssimokawa}
156121468Ssimokawa
157121468Ssimokawastatic void
158170775Ssimokawadconschat_reset_target(struct dcons_state *dc, struct dcons_port *p)
159170422Ssimokawa{
160170775Ssimokawa	char buf[PAGE_SIZE];
161170422Ssimokawa	if (dc->reset == 0)
162170422Ssimokawa		return;
163170422Ssimokawa
164233195Sdim	snprintf(buf, PAGE_SIZE,
165233195Sdim	    "\r\n[dconschat reset target(addr=0x%jx)...]\r\n",
166233195Sdim	    (intmax_t)dc->reset);
167170775Ssimokawa	write(p->outfd, buf, strlen(buf));
168170775Ssimokawa	bzero(&buf[0], PAGE_SIZE);
169170775Ssimokawa	dwrite(dc, (void *)buf, PAGE_SIZE, dc->reset);
170170422Ssimokawa}
171170422Ssimokawa
172170422Ssimokawa
173170422Ssimokawastatic void
174170775Ssimokawadconschat_suspend(struct dcons_state *dc, struct dcons_port *p)
175170422Ssimokawa{
176170775Ssimokawa	if (p->sport != 0)
177170775Ssimokawa		return;
178170775Ssimokawa
179170422Ssimokawa	if (tc_set)
180170422Ssimokawa		tcsetattr(STDIN_FILENO, TCSADRAIN, &dc->tsave);
181170422Ssimokawa
182170422Ssimokawa	printf("\n[dconschat suspend]\n");
183170422Ssimokawa	kill(getpid(), SIGTSTP);
184170422Ssimokawa
185170422Ssimokawa	if (tc_set)
186170422Ssimokawa		tcsetattr(STDIN_FILENO, TCSADRAIN, &dc->traw);
187170422Ssimokawa}
188170422Ssimokawa
189170422Ssimokawastatic void
190170775Ssimokawadconschat_sigchld(int s)
191170775Ssimokawa{
192170775Ssimokawa	struct kevent kev;
193170775Ssimokawa	struct dcons_port *p;
194170775Ssimokawa	char buf[256];
195170775Ssimokawa
196170775Ssimokawa	p = &sc.port[DCONS_CON];
197170775Ssimokawa
198170775Ssimokawa	snprintf(buf, 256, "\r\n[child exit]\r\n");
199170775Ssimokawa	write(p->outfd, buf, strlen(buf));
200170775Ssimokawa
201170775Ssimokawa	if (tc_set)
202170775Ssimokawa		tcsetattr(STDIN_FILENO, TCSADRAIN, &sc.traw);
203170775Ssimokawa
204170775Ssimokawa	EV_SET(&kev, p->infd, EVFILT_READ, EV_ADD, NOTE_LOWAT, 1, (void *)p);
205170775Ssimokawa	kevent(sc.kq, &kev, 1, NULL, 0, &sc.zero);
206170775Ssimokawa}
207170775Ssimokawa
208170775Ssimokawastatic void
209170775Ssimokawadconschat_fork_gdb(struct dcons_state *dc, struct dcons_port *p)
210170775Ssimokawa{
211170775Ssimokawa	pid_t pid;
212170775Ssimokawa	char buf[256], com[256];
213170775Ssimokawa	struct kevent kev;
214170775Ssimokawa
215170775Ssimokawa	pid = fork();
216170775Ssimokawa	if (pid < 0) {
217170775Ssimokawa		snprintf(buf, 256, "\r\n[%s: fork failed]\r\n", __FUNCTION__);
218170775Ssimokawa		write(p->outfd, buf, strlen(buf));
219170775Ssimokawa	}
220170775Ssimokawa
221170775Ssimokawa
222170775Ssimokawa	if (pid == 0) {
223170775Ssimokawa		/* child */
224170775Ssimokawa		if (tc_set)
225170775Ssimokawa			tcsetattr(STDIN_FILENO, TCSADRAIN, &dc->tsave);
226170775Ssimokawa
227170775Ssimokawa		snprintf(com, sizeof(buf), "kgdb -r :%d kernel",
228170775Ssimokawa			dc->port[DCONS_GDB].sport);
229170775Ssimokawa		snprintf(buf, 256, "\n[fork %s]\n", com);
230170775Ssimokawa		write(p->outfd, buf, strlen(buf));
231170775Ssimokawa
232285007Spfg		execl("/bin/sh", "/bin/sh", "-c", com, NULL);
233170775Ssimokawa
234170775Ssimokawa		snprintf(buf, 256, "\n[fork failed]\n");
235170775Ssimokawa		write(p->outfd, buf, strlen(buf));
236170775Ssimokawa
237170775Ssimokawa		if (tc_set)
238170775Ssimokawa			tcsetattr(STDIN_FILENO, TCSADRAIN, &dc->traw);
239170775Ssimokawa
240170775Ssimokawa		exit(0);
241170775Ssimokawa	} else {
242170775Ssimokawa		signal(SIGCHLD, dconschat_sigchld);
243170775Ssimokawa		EV_SET(&kev, p->infd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
244170775Ssimokawa		kevent(sc.kq, &kev, 1, NULL, 0, &sc.zero);
245170775Ssimokawa	}
246170775Ssimokawa}
247170775Ssimokawa
248170775Ssimokawa
249170775Ssimokawastatic void
250121468Ssimokawadconschat_cleanup(int sig)
251121468Ssimokawa{
252121468Ssimokawa	struct dcons_state *dc;
253170775Ssimokawa	int status;
254121468Ssimokawa
255121468Ssimokawa	dc = &sc;
256121468Ssimokawa	if (tc_set != 0)
257121468Ssimokawa		tcsetattr(STDIN_FILENO, TCSADRAIN, &dc->tsave);
258121468Ssimokawa
259121468Ssimokawa	if (sig > 0)
260122224Ssimokawa		printf("\n[dconschat exiting with signal %d ...]\n", sig);
261121468Ssimokawa	else
262122224Ssimokawa		printf("\n[dconschat exiting...]\n");
263170775Ssimokawa	wait(&status);
264121468Ssimokawa	exit(0);
265121468Ssimokawa}
266121468Ssimokawa
267121468Ssimokawa#if USE_CROM
268121468Ssimokawastatic int
269121468Ssimokawadconschat_get_crom(struct dcons_state *dc)
270121468Ssimokawa{
271121468Ssimokawa	off_t addr;
272121468Ssimokawa	int i, state = 0;
273170422Ssimokawa	u_int32_t buf, hi = 0, lo = 0, reset_hi = 0, reset_lo = 0;
274121468Ssimokawa	struct csrreg *reg;
275121468Ssimokawa
276121468Ssimokawa	reg = (struct csrreg *)&buf;
277121468Ssimokawa	addr = 0xffff;
278121468Ssimokawa	addr = (addr << 32) | 0xf0000400;
279121468Ssimokawa	for (i = 20; i < 0x400; i += 4) {
280121468Ssimokawa		if (dread(dc, &buf, 4, addr + i) < 0) {
281121468Ssimokawa			if (verbose)
282121468Ssimokawa				warn("crom read faild");
283170433Ssimokawa			goto out;
284121468Ssimokawa		}
285121468Ssimokawa		buf = ntohl(buf);
286121468Ssimokawa		if (verbose)
287121468Ssimokawa			printf("%d %02x %06x\n", state, reg->key, reg->val);
288121468Ssimokawa		switch (state) {
289121468Ssimokawa		case 0:
290121468Ssimokawa			if (reg->key == CSRKEY_SPEC &&
291121468Ssimokawa					reg->val == CSRVAL_VENDOR_PRIVATE)
292121468Ssimokawa				state = 1;
293121468Ssimokawa			break;
294121468Ssimokawa		case 1:
295121468Ssimokawa			if (reg->key == CSRKEY_VER &&
296121468Ssimokawa					reg->val == DCONS_CSR_VAL_VER)
297121468Ssimokawa				state = 2;
298121468Ssimokawa			break;
299121468Ssimokawa		case 2:
300170422Ssimokawa			switch (reg->key) {
301170422Ssimokawa			case DCONS_CSR_KEY_HI:
302121468Ssimokawa				hi = reg->val;
303170422Ssimokawa				break;
304170422Ssimokawa			case DCONS_CSR_KEY_LO:
305121468Ssimokawa				lo = reg->val;
306170422Ssimokawa				break;
307170422Ssimokawa			case DCONS_CSR_KEY_RESET_HI:
308170422Ssimokawa				reset_hi = reg->val;
309170422Ssimokawa				break;
310170422Ssimokawa			case DCONS_CSR_KEY_RESET_LO:
311170422Ssimokawa				reset_lo = reg->val;
312121468Ssimokawa				goto out;
313170422Ssimokawa				break;
314170422Ssimokawa			case 0x81:
315170422Ssimokawa				break;
316170422Ssimokawa			default:
317170422Ssimokawa				state = 0;
318121468Ssimokawa			}
319121468Ssimokawa			break;
320121468Ssimokawa		}
321121468Ssimokawa	}
322121468Ssimokawaout:
323121468Ssimokawa	if (verbose)
324121468Ssimokawa		printf("addr: %06x %06x\n", hi, lo);
325121468Ssimokawa	dc->paddr = ((off_t)hi << 24) | lo;
326170422Ssimokawa	dc->reset = ((off_t)reset_hi << 24) | reset_lo;
327170422Ssimokawa	if (dc->paddr == 0)
328170422Ssimokawa		return (-1);
329121468Ssimokawa	return (0);
330121468Ssimokawa}
331121468Ssimokawa#endif
332121468Ssimokawa
333121468Ssimokawastatic void
334121468Ssimokawadconschat_ready(struct dcons_state *dc, int ready, char *reason)
335121468Ssimokawa{
336121468Ssimokawa	static char oldreason[64] = "";
337121468Ssimokawa	int old;
338121468Ssimokawa
339121468Ssimokawa	old = (dc->flags & F_READY) ? 1 : 0;
340121468Ssimokawa
341121468Ssimokawa	if (ready) {
342121468Ssimokawa		dc->flags |= F_READY;
343121468Ssimokawa		if (ready != old)
344121468Ssimokawa			printf("[dcons connected]\r\n");
345121468Ssimokawa		oldreason[0] = 0;
346121468Ssimokawa	} else {
347121468Ssimokawa		dc->flags &= ~F_READY;
348121468Ssimokawa		if (strncmp(oldreason, reason, sizeof(oldreason)) != 0) {
349121468Ssimokawa			printf("[dcons disconnected (%s)]\r\n", reason);
350121468Ssimokawa			strlcpy(oldreason, reason, sizeof(oldreason));
351121468Ssimokawa		}
352121468Ssimokawa	}
353121468Ssimokawa}
354121468Ssimokawa
355121468Ssimokawastatic int
356121468Ssimokawadconschat_fetch_header(struct dcons_state *dc)
357121468Ssimokawa{
358121468Ssimokawa	char ebuf[64];
359121468Ssimokawa	struct dcons_buf dbuf;
360121468Ssimokawa	int j;
361121468Ssimokawa
362121468Ssimokawa#if USE_CROM
363121468Ssimokawa	if (dc->paddr == 0 && (dc->flags & F_USE_CROM) != 0) {
364121468Ssimokawa		if (dconschat_get_crom(dc)) {
365121468Ssimokawa			dconschat_ready(dc, 0, "get crom failed");
366121468Ssimokawa			return (-1);
367121468Ssimokawa		}
368121468Ssimokawa	}
369121468Ssimokawa#endif
370121468Ssimokawa
371121468Ssimokawa	if (dread(dc, &dbuf, DCONS_HEADER_SIZE, dc->paddr) < 0) {
372121468Ssimokawa		dconschat_ready(dc, 0, "read header failed");
373121468Ssimokawa		return (-1);
374121468Ssimokawa	}
375121468Ssimokawa	if (dbuf.magic != htonl(DCONS_MAGIC)) {
376121468Ssimokawa		if ((dc->flags & F_USE_CROM) !=0)
377121468Ssimokawa			dc->paddr = 0;
378121468Ssimokawa		snprintf(ebuf, sizeof(ebuf), "wrong magic 0x%08x", dbuf.magic);
379121468Ssimokawa		dconschat_ready(dc, 0, ebuf);
380121468Ssimokawa		return (-1);
381121468Ssimokawa	}
382121468Ssimokawa	if (ntohl(dbuf.version) != DCONS_VERSION) {
383121468Ssimokawa		snprintf(ebuf, sizeof(ebuf),
384121468Ssimokawa#if __FreeBSD_version < 500000
385121468Ssimokawa		    "wrong version %ld,%d",
386121468Ssimokawa#else
387121468Ssimokawa		    "wrong version %d,%d",
388121468Ssimokawa#endif
389121468Ssimokawa		    ntohl(dbuf.version), DCONS_VERSION);
390121468Ssimokawa		/* XXX exit? */
391121468Ssimokawa		dconschat_ready(dc, 0, ebuf);
392121468Ssimokawa		return (-1);
393121468Ssimokawa	}
394121468Ssimokawa
395121468Ssimokawa	for (j = 0; j < DCONS_NPORT; j++) {
396121468Ssimokawa		struct dcons_ch *o, *i;
397121468Ssimokawa		off_t newbuf;
398121468Ssimokawa		int new = 0;
399121468Ssimokawa
400121468Ssimokawa		o = &dc->port[j].o;
401121468Ssimokawa		newbuf = dc->paddr + ntohl(dbuf.ooffset[j]);
402121468Ssimokawa		o->size = ntohl(dbuf.osize[j]);
403121468Ssimokawa
404121468Ssimokawa		if (newbuf != o->buf) {
405121468Ssimokawa			/* buffer address has changes */
406121468Ssimokawa			new = 1;
407121468Ssimokawa			o->gen = ntohl(dbuf.optr[j]) >> DCONS_GEN_SHIFT;
408121468Ssimokawa			o->pos = ntohl(dbuf.optr[j]) & DCONS_POS_MASK;
409121468Ssimokawa			o->buf = newbuf;
410121468Ssimokawa		}
411121468Ssimokawa
412121468Ssimokawa		i = &dc->port[j].i;
413121468Ssimokawa		i->size = ntohl(dbuf.isize[j]);
414121468Ssimokawa		i->gen = ntohl(dbuf.iptr[j]) >> DCONS_GEN_SHIFT;
415121468Ssimokawa		i->pos = ntohl(dbuf.iptr[j]) & DCONS_POS_MASK;
416121468Ssimokawa		i->buf = dc->paddr + ntohl(dbuf.ioffset[j]);
417121468Ssimokawa
418121468Ssimokawa		if (verbose) {
419121468Ssimokawa			printf("port %d   size offset   gen   pos\n", j);
420121468Ssimokawa#if __FreeBSD_version < 500000
421121468Ssimokawa			printf("output: %5d %6ld %5d %5d\n"
422121468Ssimokawa				"input : %5d %6ld %5d %5d\n",
423121468Ssimokawa#else
424121468Ssimokawa			printf("output: %5d %6d %5d %5d\n"
425121468Ssimokawa				"input : %5d %6d %5d %5d\n",
426121468Ssimokawa#endif
427121468Ssimokawa			o->size, ntohl(dbuf.ooffset[j]), o->gen, o->pos,
428121468Ssimokawa			i->size, ntohl(dbuf.ioffset[j]), i->gen, i->pos);
429121468Ssimokawa		}
430121468Ssimokawa
431121468Ssimokawa		if (IS_CONSOLE(&dc->port[j]) && new &&
432121468Ssimokawa		    (dc->flags & F_REPLAY) !=0) {
433121468Ssimokawa			if (o->gen > 0)
434121468Ssimokawa				o->gen --;
435121468Ssimokawa			else
436121468Ssimokawa				o->pos = 0;
437121468Ssimokawa		}
438121468Ssimokawa	}
439121468Ssimokawa	dconschat_ready(dc, 1, NULL);
440121468Ssimokawa	return(0);
441121468Ssimokawa}
442121468Ssimokawa
443121468Ssimokawastatic int
444121468Ssimokawadconschat_get_ptr (struct dcons_state *dc) {
445121468Ssimokawa	int dlen, i;
446121468Ssimokawa	u_int32_t ptr[DCONS_NPORT*2+1];
447121468Ssimokawa	static int retry = RETRY;
448135821Ssimokawa	char ebuf[64];
449121468Ssimokawa
450121468Ssimokawaagain:
451121468Ssimokawa	dlen = dread(dc, &ptr, sizeof(ptr),
452121468Ssimokawa		dc->paddr + __offsetof(struct dcons_buf, magic));
453121468Ssimokawa
454121468Ssimokawa	if (dlen < 0) {
455121468Ssimokawa		if (errno == ETIMEDOUT)
456121468Ssimokawa			if (retry -- > 0)
457121468Ssimokawa				goto again;
458121468Ssimokawa		dconschat_ready(dc, 0, "get ptr failed");
459121468Ssimokawa		return(-1);
460121468Ssimokawa	}
461121468Ssimokawa	if (ptr[0] != htonl(DCONS_MAGIC)) {
462135821Ssimokawa		if ((dc->flags & F_USE_CROM) !=0)
463135821Ssimokawa			dc->paddr = 0;
464135821Ssimokawa		snprintf(ebuf, sizeof(ebuf), "wrong magic 0x%08x", ptr[0]);
465135821Ssimokawa		dconschat_ready(dc, 0, ebuf);
466121468Ssimokawa		return(-1);
467121468Ssimokawa	}
468121468Ssimokawa	retry = RETRY;
469121468Ssimokawa	for (i = 0; i < DCONS_NPORT; i ++) {
470121468Ssimokawa		dc->port[i].optr = ntohl(ptr[i + 1]);
471121468Ssimokawa		dc->port[i].iptr = ntohl(ptr[DCONS_NPORT + i + 1]);
472121468Ssimokawa	}
473121468Ssimokawa	return(0);
474121468Ssimokawa}
475121468Ssimokawa
476121468Ssimokawa#define MAX_XFER 2048
477121468Ssimokawastatic int
478121468Ssimokawadconschat_read_dcons(struct dcons_state *dc, int port, char *buf, int len)
479121468Ssimokawa{
480121468Ssimokawa	struct dcons_ch *ch;
481121468Ssimokawa	u_int32_t ptr, pos, gen, next_gen;
482121468Ssimokawa	int rlen, dlen, lost;
483121468Ssimokawa	int retry = RETRY;
484121468Ssimokawa
485121468Ssimokawa	ch = &dc->port[port].o;
486121468Ssimokawa	ptr = dc->port[port].optr;
487121468Ssimokawa	gen = ptr >> DCONS_GEN_SHIFT;
488121468Ssimokawa	pos = ptr & DCONS_POS_MASK;
489121468Ssimokawa	if (gen == ch->gen && pos == ch->pos)
490121468Ssimokawa		return (-1);
491121468Ssimokawa
492121468Ssimokawa	next_gen = DCONS_NEXT_GEN(ch->gen);
493121468Ssimokawa	/* XXX sanity check */
494121468Ssimokawa	if (gen == ch->gen) {
495121468Ssimokawa		if (pos > ch->pos)
496121468Ssimokawa			goto ok;
497121468Ssimokawa		lost = ch->size * DCONS_GEN_MASK - ch->pos;
498121468Ssimokawa		ch->pos = 0;
499121468Ssimokawa	} else if (gen == next_gen) {
500121468Ssimokawa		if (pos <= ch->pos)
501121468Ssimokawa			goto ok;
502121468Ssimokawa		lost = pos - ch->pos;
503121468Ssimokawa		ch->pos = pos;
504121468Ssimokawa	} else {
505121468Ssimokawa		lost = gen - ch->gen;
506121468Ssimokawa		if (lost < 0)
507121468Ssimokawa			lost += DCONS_GEN_MASK;
508121468Ssimokawa		if (verbose)
509121468Ssimokawa			printf("[genskip %d]", lost);
510121468Ssimokawa		lost = lost * ch->size - ch->pos;
511121468Ssimokawa		ch->pos = 0;
512121468Ssimokawa		ch->gen = gen;
513121468Ssimokawa	}
514121468Ssimokawa	/* generation skipped !! */
515121468Ssimokawa	/* XXX discard */
516121468Ssimokawa	if (verbose)
517121468Ssimokawa		printf("[lost %d]", lost);
518121468Ssimokawaok:
519121468Ssimokawa	if (gen == ch->gen)
520121468Ssimokawa		rlen = pos - ch->pos;
521121468Ssimokawa	else
522121468Ssimokawa		rlen = ch->size - ch->pos;
523121468Ssimokawa
524121468Ssimokawa	if (rlen > MAX_XFER)
525121468Ssimokawa		rlen = MAX_XFER;
526121468Ssimokawa	if (rlen > len)
527121468Ssimokawa		rlen = len;
528121468Ssimokawa
529121468Ssimokawa#if 1
530170775Ssimokawa	if (verbose == 1)
531121468Ssimokawa		printf("[%d]", rlen); fflush(stdout);
532121468Ssimokawa#endif
533121468Ssimokawa
534121468Ssimokawaagain:
535121468Ssimokawa	dlen = dread(dc, buf, rlen, ch->buf + ch->pos);
536121468Ssimokawa	if (dlen < 0) {
537121468Ssimokawa		if (errno == ETIMEDOUT)
538121468Ssimokawa			if (retry -- > 0)
539121468Ssimokawa				goto again;
540121468Ssimokawa		dconschat_ready(dc, 0, "read buffer failed");
541121468Ssimokawa		return(-1);
542121468Ssimokawa	}
543121468Ssimokawa	if (dlen != rlen)
544121468Ssimokawa		warnx("dlen(%d) != rlen(%d)\n", dlen, rlen);
545121468Ssimokawa	ch->pos += dlen;
546121468Ssimokawa	if (ch->pos >= ch->size) {
547121468Ssimokawa		ch->gen = next_gen;
548121468Ssimokawa		ch->pos = 0;
549121468Ssimokawa		if (verbose)
550121468Ssimokawa			printf("read_dcons: gen=%d", ch->gen);
551121468Ssimokawa	}
552121468Ssimokawa	return (dlen);
553121468Ssimokawa}
554121468Ssimokawa
555121468Ssimokawastatic int
556121468Ssimokawadconschat_write_dcons(struct dcons_state *dc, int port, char *buf, int blen)
557121468Ssimokawa{
558121468Ssimokawa	struct dcons_ch *ch;
559121468Ssimokawa	u_int32_t ptr;
560121468Ssimokawa	int len, wlen;
561121468Ssimokawa	int retry = RETRY;
562121468Ssimokawa
563121468Ssimokawa	ch = &dc->port[port].i;
564121468Ssimokawa	ptr = dc->port[port].iptr;
565121468Ssimokawa
566121468Ssimokawa	/* the others may advance the pointer sync with it */
567121468Ssimokawa	ch->gen = ptr >> DCONS_GEN_SHIFT;
568121468Ssimokawa	ch->pos = ptr & DCONS_POS_MASK;
569121468Ssimokawa
570121468Ssimokawa	while(blen > 0) {
571121468Ssimokawa		wlen = MIN(blen, ch->size - ch->pos);
572121468Ssimokawa		wlen = MIN(wlen, MAX_XFER);
573121468Ssimokawa		len = dwrite(dc, buf, wlen, ch->buf + ch->pos);
574121468Ssimokawa		if (len < 0) {
575121468Ssimokawa			if (errno == ETIMEDOUT)
576121468Ssimokawa				if (retry -- > 0)
577121468Ssimokawa					continue; /* try again */
578121468Ssimokawa			dconschat_ready(dc, 0, "write buffer failed");
579121468Ssimokawa			return(-1);
580121468Ssimokawa		}
581121468Ssimokawa		ch->pos += len;
582121468Ssimokawa		buf += len;
583121468Ssimokawa		blen -= len;
584121468Ssimokawa		if (ch->pos >= ch->size) {
585121468Ssimokawa			ch->gen = DCONS_NEXT_GEN(ch->gen);
586121468Ssimokawa			ch->pos = 0;
587121468Ssimokawa			if (verbose)
588121468Ssimokawa				printf("write_dcons: gen=%d", ch->gen);
589121468Ssimokawa
590121468Ssimokawa		}
591121468Ssimokawa	}
592121468Ssimokawa
593121468Ssimokawa	ptr = DCONS_MAKE_PTR(ch);
594121468Ssimokawa	dc->port[port].iptr = ptr;
595121468Ssimokawa
596121468Ssimokawa	if (verbose > 2)
597121468Ssimokawa		printf("(iptr: 0x%x)", ptr);
598121468Ssimokawaagain:
599121468Ssimokawa	len = dwrite(dc, &ptr, sizeof(u_int32_t),
600121468Ssimokawa		dc->paddr + __offsetof(struct dcons_buf, iptr[port]));
601121468Ssimokawa	if (len < 0) {
602121468Ssimokawa		if (errno == ETIMEDOUT)
603121468Ssimokawa			if (retry -- > 0)
604121468Ssimokawa				goto again;
605121468Ssimokawa		dconschat_ready(dc, 0, "write ptr failed");
606121468Ssimokawa		return(-1);
607121468Ssimokawa	}
608121468Ssimokawa	return(0);
609121468Ssimokawa}
610121468Ssimokawa
611170775Ssimokawa
612121468Ssimokawastatic int
613121468Ssimokawadconschat_write_socket(int fd, char *buf, int len)
614121468Ssimokawa{
615121468Ssimokawa	write(fd, buf, len);
616121468Ssimokawa	if (verbose > 1) {
617121468Ssimokawa		buf[len] = 0;
618170775Ssimokawa		printf("<- %s\n", buf);
619121468Ssimokawa	}
620121468Ssimokawa	return (0);
621121468Ssimokawa}
622121468Ssimokawa
623121468Ssimokawastatic void
624121468Ssimokawadconschat_init_socket(struct dcons_state *dc, int port, char *host, int sport)
625121468Ssimokawa{
626121468Ssimokawa	struct addrinfo hints, *res;
627121468Ssimokawa	int on = 1, error;
628121468Ssimokawa	char service[10];
629121468Ssimokawa	struct kevent kev;
630121468Ssimokawa	struct dcons_port *p;
631121468Ssimokawa
632121468Ssimokawa	p = &dc->port[port];
633121468Ssimokawa	p->port = port;
634170775Ssimokawa	p->sport = sport;
635121468Ssimokawa	p->infd = p->outfd = -1;
636121468Ssimokawa
637121468Ssimokawa	if (sport < 0)
638121468Ssimokawa		return;
639121468Ssimokawa
640121468Ssimokawa	if (sport == 0) {
641121468Ssimokawa
642121468Ssimokawa		/* Use stdin and stdout */
643121468Ssimokawa		p->infd = STDIN_FILENO;
644121468Ssimokawa		p->outfd = STDOUT_FILENO;
645121468Ssimokawa		p->s = -1;
646121468Ssimokawa		if (tc_set == 0 &&
647121468Ssimokawa		    tcgetattr(STDIN_FILENO, &dc->tsave) == 0) {
648170422Ssimokawa			dc->traw = dc->tsave;
649170422Ssimokawa			cfmakeraw(&dc->traw);
650170422Ssimokawa			tcsetattr(STDIN_FILENO, TCSADRAIN, &dc->traw);
651121468Ssimokawa			tc_set = 1;
652121468Ssimokawa		}
653121468Ssimokawa		EV_SET(&kev, p->infd, EVFILT_READ, EV_ADD, NOTE_LOWAT, 1,
654121468Ssimokawa		    (void *)p);
655121468Ssimokawa		kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
656121468Ssimokawa		return;
657121468Ssimokawa	}
658121468Ssimokawa
659121468Ssimokawa	memset(&hints, 0, sizeof(hints));
660121468Ssimokawa	hints.ai_flags = AI_PASSIVE;
661121468Ssimokawa#if 1	/* gdb can talk v4 only */
662121468Ssimokawa	hints.ai_family = PF_INET;
663121468Ssimokawa#else
664121468Ssimokawa	hints.ai_family = PF_UNSPEC;
665121468Ssimokawa#endif
666121468Ssimokawa	hints.ai_socktype = SOCK_STREAM;
667121468Ssimokawa	hints.ai_protocol = 0;
668121468Ssimokawa
669121468Ssimokawa	if (verbose)
670121468Ssimokawa		printf("%s:%d for port %d\n",
671121468Ssimokawa			host == NULL ? "*" : host, sport, port);
672121468Ssimokawa	snprintf(service, sizeof(service), "%d", sport);
673121468Ssimokawa	error = getaddrinfo(host, service,  &hints, &res);
674121468Ssimokawa	if (error)
675121468Ssimokawa		errx(1, "tcp/%s: %s\n", service, gai_strerror(error));
676121468Ssimokawa	p->res = res;
677121468Ssimokawa	p->s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
678121468Ssimokawa	if (p->s < 0)
679121468Ssimokawa		err(1, "socket");
680121468Ssimokawa	setsockopt(p->s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
681121468Ssimokawa
682121468Ssimokawa	if (bind(p->s, p->res->ai_addr, p->res->ai_addrlen) < 0) {
683121468Ssimokawa		err(1, "bind");
684121468Ssimokawa	}
685121468Ssimokawa	if (listen(p->s, 1) < 0)
686121468Ssimokawa		err(1, "listen");
687121468Ssimokawa	EV_SET(&kev, p->s, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, (void *)p);
688121468Ssimokawa	error = kevent(dc->kq, &kev, 1, NULL, 0, &dc->to);
689121468Ssimokawa	if (error < 0)
690121468Ssimokawa		err(1, "kevent");
691121468Ssimokawa	return;
692121468Ssimokawa}
693121468Ssimokawa
694121468Ssimokawastatic int
695121468Ssimokawadconschat_accept_socket(struct dcons_state *dc, struct dcons_port *p)
696121468Ssimokawa{
697143415Sstefanf	socklen_t addrlen;
698143415Sstefanf	int ns, flags;
699121468Ssimokawa	struct kevent kev;
700121468Ssimokawa
701121468Ssimokawa	/* accept connection */
702143415Sstefanf	addrlen = p->res->ai_addrlen;
703143415Sstefanf	ns = accept(p->s, p->res->ai_addr, &addrlen);
704121468Ssimokawa	if (ns < 0)
705121468Ssimokawa		err(1, "accept");
706121468Ssimokawa	if (verbose)
707121468Ssimokawa		printf("port%d accepted\n", p->port);
708121468Ssimokawa
709121468Ssimokawa	flags = fcntl(ns, F_GETFL, 0);
710121468Ssimokawa	flags |= O_NDELAY;
711121468Ssimokawa	fcntl(ns, F_SETFL, flags);
712121468Ssimokawa#if 1
713121468Ssimokawa	if (IS_CONSOLE(p) && (dc->flags & F_TELNET) != 0) {
714121468Ssimokawa		char sga[] = {IAC, WILL, TELOPT_SGA};
715121468Ssimokawa		char linemode[] = {IAC, DONT, TELOPT_LINEMODE};
716121468Ssimokawa		char echo[] = {IAC, WILL, TELOPT_ECHO};
717121468Ssimokawa		char bin[] = {IAC, DO, TELOPT_BINARY};
718121468Ssimokawa
719121468Ssimokawa		write(ns, sga, sizeof(sga));
720121468Ssimokawa		write(ns, linemode, sizeof(linemode));
721121468Ssimokawa		write(ns, echo, sizeof(echo));
722121468Ssimokawa		write(ns, bin, sizeof(bin));
723121468Ssimokawa		p->skip_read = 0;
724121468Ssimokawa	}
725121468Ssimokawa#endif
726170146Ssimokawa	/* discard backlog on GDB port */
727170146Ssimokawa	if (IS_GDB(p)) {
728170146Ssimokawa		char buf[2048];
729170146Ssimokawa		int len;
730121468Ssimokawa
731170146Ssimokawa		while ((len = dconschat_read_dcons(dc, DCONS_GDB, &buf[0],
732170146Ssimokawa				 2048)) > 0)
733170146Ssimokawa			if (verbose)
734170146Ssimokawa				printf("discard %d chars on GDB port\n", len);
735170146Ssimokawa	}
736170146Ssimokawa
737121468Ssimokawa	p->infd = p->outfd = ns;
738121468Ssimokawa	EV_SET(&kev, ns, EVFILT_READ, EV_ADD, NOTE_LOWAT, 1, (void *)p);
739121468Ssimokawa	kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
740121468Ssimokawa	return(0);
741121468Ssimokawa}
742121468Ssimokawa
743121468Ssimokawastatic int
744121468Ssimokawadconschat_read_filter(struct dcons_state *dc, struct dcons_port *p,
745121468Ssimokawa    u_char *sp, int slen, u_char *dp, int *dlen)
746121468Ssimokawa{
747170422Ssimokawa	int skip;
748170775Ssimokawa	char *buf;
749121468Ssimokawa
750121468Ssimokawa	while (slen > 0) {
751170422Ssimokawa		skip = 0;
752121468Ssimokawa		if (IS_CONSOLE(p)) {
753121468Ssimokawa			if ((dc->flags & F_TELNET) != 0) {
754143416Sstefanf				/* XXX Telnet workarounds */
755121468Ssimokawa				if (p->skip_read -- > 0) {
756121468Ssimokawa					sp ++;
757121468Ssimokawa					slen --;
758121468Ssimokawa					continue;
759121468Ssimokawa				}
760121468Ssimokawa				if (*sp == IAC) {
761121468Ssimokawa					if (verbose)
762121468Ssimokawa						printf("(IAC)");
763121468Ssimokawa					p->skip_read = 2;
764121468Ssimokawa					sp ++;
765121468Ssimokawa					slen --;
766121468Ssimokawa					continue;
767121468Ssimokawa				}
768121468Ssimokawa				if (*sp == 0) {
769121468Ssimokawa					if (verbose)
770121468Ssimokawa						printf("(0 stripped)");
771121468Ssimokawa					sp ++;
772121468Ssimokawa					slen --;
773121468Ssimokawa					continue;
774121468Ssimokawa				}
775121468Ssimokawa			}
776121468Ssimokawa			switch (dc->escape_state) {
777121468Ssimokawa			case STATE1:
778170775Ssimokawa				if (*sp == dc->escape) {
779170422Ssimokawa					skip = 1;
780121468Ssimokawa					dc->escape_state = STATE2;
781170422Ssimokawa				} else
782121468Ssimokawa					dc->escape_state = STATE0;
783121468Ssimokawa				break;
784121468Ssimokawa			case STATE2:
785121468Ssimokawa				dc->escape_state = STATE0;
786170775Ssimokawa				skip = 1;
787121468Ssimokawa				if (*sp == '.')
788121468Ssimokawa					dconschat_cleanup(0);
789170775Ssimokawa				else if (*sp == CTRL('B')) {
790170775Ssimokawa					bcopy(abreak, dp, 3);
791170775Ssimokawa					dp += 3;
792170775Ssimokawa					*dlen += 3;
793170775Ssimokawa				}
794170775Ssimokawa				else if (*sp == CTRL('G'))
795170775Ssimokawa					dconschat_fork_gdb(dc, p);
796170775Ssimokawa				else if ((*sp == CTRL('R'))
797170422Ssimokawa						&& (dc->reset != 0)) {
798170422Ssimokawa					dc->escape_state = STATE3;
799170775Ssimokawa					buf = "\r\n[Are you sure to reset target? (y/N)]";
800170775Ssimokawa					write(p->outfd, buf, strlen(buf));
801170775Ssimokawa				} else if (*sp == CTRL('Z'))
802170775Ssimokawa					dconschat_suspend(dc, p);
803170775Ssimokawa				else {
804170775Ssimokawa					skip = 0;
805170775Ssimokawa					*dp++ = dc->escape;
806170422Ssimokawa					(*dlen) ++;
807170422Ssimokawa				}
808170422Ssimokawa				break;
809170422Ssimokawa			case STATE3:
810170422Ssimokawa				dc->escape_state = STATE0;
811170422Ssimokawa				skip = 1;
812170422Ssimokawa				if (*sp == 'y')
813170775Ssimokawa					dconschat_reset_target(dc, p);
814170775Ssimokawa				else {
815170775Ssimokawa					write(p->outfd, sp, 1);
816170775Ssimokawa					write(p->outfd, "\r\n", 2);
817170775Ssimokawa				}
818170422Ssimokawa				break;
819121468Ssimokawa			}
820121468Ssimokawa			if (*sp == KEY_CR)
821121468Ssimokawa				dc->escape_state = STATE1;
822121468Ssimokawa		} else if (IS_GDB(p)) {
823121468Ssimokawa			/* GDB: ^C -> CR+~+^B */
824170775Ssimokawa			if (*sp == CTRL('C') && (dc->flags & F_ALT_BREAK) != 0) {
825121468Ssimokawa				bcopy(abreak, dp, 3);
826121468Ssimokawa				dp += 3;
827121468Ssimokawa				sp ++;
828121468Ssimokawa				*dlen += 3;
829121468Ssimokawa				/* discard rest of the packet */
830121468Ssimokawa				slen = 0;
831121468Ssimokawa				break;
832121468Ssimokawa			}
833121468Ssimokawa		}
834170422Ssimokawa		if (!skip) {
835170422Ssimokawa			*dp++ = *sp;
836170422Ssimokawa			(*dlen) ++;
837170422Ssimokawa		}
838170422Ssimokawa		sp ++;
839121468Ssimokawa		slen --;
840121468Ssimokawa	}
841121468Ssimokawa	return (*dlen);
842121468Ssimokawa
843121468Ssimokawa}
844121468Ssimokawa
845121468Ssimokawastatic int
846121468Ssimokawadconschat_read_socket(struct dcons_state *dc, struct dcons_port *p)
847121468Ssimokawa{
848121468Ssimokawa	struct kevent kev;
849121468Ssimokawa	int len, wlen;
850121468Ssimokawa	char rbuf[MAX_XFER], wbuf[MAX_XFER+2];
851121468Ssimokawa
852121468Ssimokawa	if ((len = read(p->infd, rbuf, sizeof(rbuf))) > 0) {
853121468Ssimokawa		wlen = 0;
854121468Ssimokawa		dconschat_read_filter(dc, p, rbuf, len, wbuf, &wlen);
855121468Ssimokawa		/* XXX discard if not ready*/
856121468Ssimokawa		if (wlen > 0 && (dc->flags & F_READY) != 0) {
857121468Ssimokawa			dconschat_write_dcons(dc, p->port, wbuf, wlen);
858121468Ssimokawa			if (verbose > 1) {
859121468Ssimokawa				wbuf[wlen] = 0;
860170775Ssimokawa				printf("-> %s\n", wbuf);
861170775Ssimokawa			} else if (verbose == 1) {
862121468Ssimokawa				printf("(%d)", wlen);
863121468Ssimokawa				fflush(stdout);
864121468Ssimokawa			}
865121468Ssimokawa		}
866121468Ssimokawa	} else {
867121468Ssimokawa		if (verbose) {
868121468Ssimokawa			if (len == 0)
869121468Ssimokawa				warnx("port%d: closed", p->port);
870121468Ssimokawa			else
871121468Ssimokawa				warn("port%d: read", p->port);
872121468Ssimokawa		}
873121468Ssimokawa		EV_SET(&kev, p->infd, EVFILT_READ,
874121468Ssimokawa			EV_DELETE, 0, 0, NULL);
875121468Ssimokawa		kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
876121468Ssimokawa		close(p->infd);
877121468Ssimokawa		close(p->outfd);
878121468Ssimokawa		/* XXX exit for pipe case XXX */
879121468Ssimokawa		EV_SET(&kev, p->s, EVFILT_READ,
880121468Ssimokawa				EV_ADD | EV_ONESHOT, 0, 0, (void *) p);
881121468Ssimokawa		kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
882121468Ssimokawa		p->infd = p->outfd = -1;
883121468Ssimokawa	}
884121468Ssimokawa	return(0);
885121468Ssimokawa}
886121468Ssimokawa#define NEVENT 5
887121468Ssimokawastatic int
888121468Ssimokawadconschat_proc_socket(struct dcons_state *dc)
889121468Ssimokawa{
890121468Ssimokawa	struct kevent elist[NEVENT], *e;
891121468Ssimokawa	int i, n;
892121468Ssimokawa	struct dcons_port *p;
893121468Ssimokawa
894121468Ssimokawa	n = kevent(dc->kq, NULL, 0, elist, NEVENT, &dc->to);
895121468Ssimokawa	for (i = 0; i < n; i ++) {
896121468Ssimokawa		e = &elist[i];
897121468Ssimokawa		p = (struct dcons_port *)e->udata;
898121468Ssimokawa		if (e->ident == p->s) {
899121468Ssimokawa			dconschat_accept_socket(dc, p);
900121468Ssimokawa		} else {
901121468Ssimokawa			dconschat_read_socket(dc, p);
902121468Ssimokawa		}
903121468Ssimokawa	}
904121468Ssimokawa	return(0);
905121468Ssimokawa}
906121468Ssimokawa
907121468Ssimokawastatic int
908121468Ssimokawadconschat_proc_dcons(struct dcons_state *dc)
909121468Ssimokawa{
910121468Ssimokawa	int port, len, err;
911121468Ssimokawa	char buf[MAX_XFER];
912121468Ssimokawa	struct dcons_port *p;
913121468Ssimokawa
914121468Ssimokawa	err = dconschat_get_ptr(dc);
915121468Ssimokawa	if (err) {
916121468Ssimokawa		/* XXX we should stop write operation too. */
917121468Ssimokawa		return err;
918121468Ssimokawa	}
919121468Ssimokawa	for (port = 0; port < DCONS_NPORT; port ++) {
920121468Ssimokawa		p = &dc->port[port];
921121468Ssimokawa		if (p->infd < 0)
922121468Ssimokawa			continue;
923121468Ssimokawa		while ((len = dconschat_read_dcons(dc, port, buf,
924121468Ssimokawa		    sizeof(buf))) > 0) {
925121468Ssimokawa			dconschat_write_socket(p->outfd, buf, len);
926170399Ssimokawa			if ((err = dconschat_get_ptr(dc)))
927170399Ssimokawa				return (err);
928121468Ssimokawa		}
929121468Ssimokawa		if ((dc->flags & F_ONE_SHOT) != 0 && len <= 0)
930121468Ssimokawa			dconschat_cleanup(0);
931121468Ssimokawa	}
932121468Ssimokawa	return 0;
933121468Ssimokawa}
934121468Ssimokawa
935121468Ssimokawastatic int
936121468Ssimokawadconschat_start_session(struct dcons_state *dc)
937121468Ssimokawa{
938121468Ssimokawa	int counter = 0;
939170399Ssimokawa	int retry = 0;
940170399Ssimokawa	int retry_unit_init = MAX(1, poll_hz / 10);
941170399Ssimokawa	int retry_unit_offline = poll_hz * DCONS_POLL_OFFLINE;
942170399Ssimokawa	int retry_unit = retry_unit_init;
943170399Ssimokawa	int retry_max = retry_unit_offline / retry_unit;
944121468Ssimokawa
945121468Ssimokawa	while (1) {
946170399Ssimokawa		if (((dc->flags & F_READY) == 0) && ++counter > retry_unit) {
947170399Ssimokawa			counter = 0;
948170399Ssimokawa			retry ++;
949170399Ssimokawa			if (retry > retry_max)
950170399Ssimokawa				retry_unit = retry_unit_offline;
951170399Ssimokawa			if (verbose) {
952170399Ssimokawa				printf("%d/%d ", retry, retry_max);
953170399Ssimokawa				fflush(stdout);
954170399Ssimokawa			}
955121468Ssimokawa			dconschat_fetch_header(dc);
956170399Ssimokawa		}
957170399Ssimokawa		if ((dc->flags & F_READY) != 0) {
958170399Ssimokawa			counter = 0;
959170399Ssimokawa			retry = 0;
960170399Ssimokawa			retry_unit = retry_unit_init;
961126038Ssimokawa			dconschat_proc_dcons(dc);
962170399Ssimokawa		}
963121468Ssimokawa		dconschat_proc_socket(dc);
964121468Ssimokawa	}
965121468Ssimokawa	return (0);
966121468Ssimokawa}
967121468Ssimokawa
968121468Ssimokawastatic void
969121468Ssimokawausage(void)
970121468Ssimokawa{
971121468Ssimokawa	fprintf(stderr,
972121468Ssimokawa 	    "usage: dconschat [-brvwRT1] [-h hz] [-C port] [-G port]\n"
973121468Ssimokawa	    "\t\t\t[-M core] [-N system]\n"
974121468Ssimokawa	    "\t\t\t[-u unit] [-a address] [-t target_eui64]\n"
975121468Ssimokawa	    "\t-b	translate ctrl-C to CR+~+ctrl-B on gdb port\n"
976121468Ssimokawa	    "\t-v	verbose\n"
977121468Ssimokawa	    "\t-w	listen on wildcard address rather than localhost\n"
978121468Ssimokawa	    "\t-r	replay old buffer on connection\n"
979121468Ssimokawa	    "\t-R	read-only\n"
980121468Ssimokawa	    "\t-T	enable Telnet protocol workaround on console port\n"
981121468Ssimokawa	    "\t-1	one shot: read buffer and exit\n"
982121468Ssimokawa	    "\t-h	polling rate\n"
983121468Ssimokawa	    "\t-C	port number for console port\n"
984121468Ssimokawa	    "\t-G	port number for gdb port\n"
985121468Ssimokawa	    "\t(for KVM)\n"
986121468Ssimokawa	    "\t-M	core file\n"
987121468Ssimokawa	    "\t-N	system file\n"
988121468Ssimokawa	    "\t(for FireWire)\n"
989121468Ssimokawa	    "\t-u	specify unit number of the bus\n"
990121468Ssimokawa	    "\t-t	EUI64 of target host (must be specified)\n"
991121468Ssimokawa	    "\t-a	physical address of dcons buffer on target host\n"
992121468Ssimokawa	);
993121468Ssimokawa	exit(0);
994121468Ssimokawa}
995121468Ssimokawaint
996121468Ssimokawamain(int argc, char **argv)
997121468Ssimokawa{
998121468Ssimokawa	struct dcons_state *dc;
999121468Ssimokawa	struct fw_eui64 eui;
1000129760Sbrooks	struct eui64 target;
1001121468Ssimokawa	char devname[256], *core = NULL, *system = NULL;
1002121468Ssimokawa	int i, ch, error;
1003126038Ssimokawa	int unit=0, wildcard=0;
1004121468Ssimokawa	int port[DCONS_NPORT];
1005121468Ssimokawa
1006121468Ssimokawa	bzero(&sc, sizeof(sc));
1007121468Ssimokawa	dc = &sc;
1008121468Ssimokawa	dc->flags |= USE_CROM ? F_USE_CROM : 0;
1009121468Ssimokawa
1010143416Sstefanf	/* default ports */
1011121468Ssimokawa	port[0] = 0;	/* stdin/out for console */
1012121468Ssimokawa	port[1] = -1;	/* disable gdb port */
1013121468Ssimokawa
1014171397Ssimokawa	/* default escape char */
1015171397Ssimokawa	dc->escape = KEY_TILDE;
1016171397Ssimokawa
1017170775Ssimokawa	while ((ch = getopt(argc, argv, "a:be:h:rt:u:vwC:G:M:N:RT1")) != -1) {
1018121468Ssimokawa		switch(ch) {
1019121468Ssimokawa		case 'a':
1020121468Ssimokawa			dc->paddr = strtoull(optarg, NULL, 0);
1021121468Ssimokawa			dc->flags &= ~F_USE_CROM;
1022121468Ssimokawa			break;
1023121468Ssimokawa		case 'b':
1024121468Ssimokawa			dc->flags |= F_ALT_BREAK;
1025121468Ssimokawa			break;
1026170775Ssimokawa		case 'e':
1027171397Ssimokawa			dc->escape = optarg[0];
1028170775Ssimokawa			break;
1029121468Ssimokawa		case 'h':
1030121468Ssimokawa			poll_hz = strtoul(optarg, NULL, 0);
1031121468Ssimokawa			if (poll_hz == 0)
1032121468Ssimokawa				poll_hz = DCONS_POLL_HZ;
1033121468Ssimokawa			break;
1034121468Ssimokawa		case 'r':
1035121468Ssimokawa			dc->flags |= F_REPLAY;
1036121468Ssimokawa			break;
1037121468Ssimokawa		case 't':
1038129760Sbrooks			if (eui64_hostton(optarg, &target) != 0 &&
1039129760Sbrooks			    eui64_aton(optarg, &target) != 0)
1040129760Sbrooks				errx(1, "invalid target: %s", optarg);
1041129760Sbrooks			eui.hi = ntohl(*(u_int32_t*)&(target.octet[0]));
1042129760Sbrooks			eui.lo = ntohl(*(u_int32_t*)&(target.octet[4]));
1043121468Ssimokawa			dc->type = TYPE_FW;
1044121468Ssimokawa			break;
1045121468Ssimokawa		case 'u':
1046121468Ssimokawa			unit = strtol(optarg, NULL, 0);
1047121468Ssimokawa			break;
1048121468Ssimokawa		case 'v':
1049121468Ssimokawa			verbose ++;
1050121468Ssimokawa			break;
1051121468Ssimokawa		case 'w':
1052121468Ssimokawa			wildcard = 1;
1053121468Ssimokawa			break;
1054121468Ssimokawa		case 'C':
1055121468Ssimokawa			port[0] = strtol(optarg, NULL, 0);
1056121468Ssimokawa			break;
1057121468Ssimokawa		case 'G':
1058121468Ssimokawa			port[1] = strtol(optarg, NULL, 0);
1059121468Ssimokawa			break;
1060121468Ssimokawa		case 'M':
1061121468Ssimokawa			core = optarg;
1062121468Ssimokawa			break;
1063121468Ssimokawa		case 'N':
1064121468Ssimokawa			system = optarg;
1065121468Ssimokawa			break;
1066121468Ssimokawa		case 'R':
1067121468Ssimokawa			dc->flags |= F_RD_ONLY;
1068121468Ssimokawa			break;
1069121468Ssimokawa		case 'T':
1070121468Ssimokawa			dc->flags |= F_TELNET;
1071121468Ssimokawa			break;
1072121468Ssimokawa		case '1':
1073121468Ssimokawa			dc->flags |= F_ONE_SHOT | F_REPLAY;
1074121468Ssimokawa			break;
1075121468Ssimokawa		default:
1076121468Ssimokawa			usage();
1077121468Ssimokawa		}
1078121468Ssimokawa	}
1079121468Ssimokawa	if (dc->paddr == 0 && (dc->flags & F_USE_CROM) == 0) {
1080121468Ssimokawa		warnx("no address specified");
1081121468Ssimokawa		usage();
1082121468Ssimokawa	}
1083121468Ssimokawa
1084121468Ssimokawa	if (port[0] < 0 && port[1] < 0) {
1085121468Ssimokawa		warnx("no port specified");
1086121468Ssimokawa		usage();
1087121468Ssimokawa	}
1088121468Ssimokawa
1089121468Ssimokawa	/* set signal handler */
1090121468Ssimokawa	signal(SIGHUP, dconschat_cleanup);
1091121468Ssimokawa	signal(SIGINT, dconschat_cleanup);
1092121468Ssimokawa	signal(SIGPIPE, dconschat_cleanup);
1093121468Ssimokawa	signal(SIGTERM, dconschat_cleanup);
1094121468Ssimokawa
1095121468Ssimokawa	/* init firewire */
1096121468Ssimokawa	switch (dc->type) {
1097121468Ssimokawa	case TYPE_FW:
1098122356Ssimokawa#define MAXDEV 10
1099121468Ssimokawa		for (i = 0; i < MAXDEV; i ++) {
1100121468Ssimokawa			snprintf(devname, sizeof(devname),
1101121468Ssimokawa			    "/dev/fwmem%d.%d", unit, i);
1102121468Ssimokawa			dc->fd = open(devname, O_RDWR);
1103121468Ssimokawa			if (dc->fd >= 0)
1104121468Ssimokawa				goto found;
1105121468Ssimokawa		}
1106121468Ssimokawa		err(1, "open");
1107121468Ssimokawafound:
1108121468Ssimokawa		error = ioctl(dc->fd, FW_SDEUI64, &eui);
1109121468Ssimokawa		if (error)
1110121468Ssimokawa			err(1, "ioctl");
1111121468Ssimokawa		break;
1112121468Ssimokawa	case TYPE_KVM:
1113121468Ssimokawa	{
1114121468Ssimokawa		struct nlist nl[] = {{"dcons_buf"}, {""}};
1115121468Ssimokawa		void *dcons_buf;
1116121468Ssimokawa
1117121468Ssimokawa		dc->kd = kvm_open(system, core, NULL,
1118121468Ssimokawa		    (dc->flags & F_RD_ONLY) ? O_RDONLY : O_RDWR, "dconschat");
1119121468Ssimokawa		if (dc->kd == NULL)
1120121468Ssimokawa			errx(1, "kvm_open");
1121121468Ssimokawa
1122121468Ssimokawa		if (kvm_nlist(dc->kd, nl) < 0)
1123121468Ssimokawa			errx(1, "kvm_nlist: %s", kvm_geterr(dc->kd));
1124121468Ssimokawa
1125121468Ssimokawa		if (kvm_read(dc->kd, nl[0].n_value, &dcons_buf,
1126121468Ssimokawa		    sizeof(void *)) < 0)
1127121468Ssimokawa			errx(1, "kvm_read: %s", kvm_geterr(dc->kd));
1128121468Ssimokawa		dc->paddr = (uintptr_t)dcons_buf;
1129121468Ssimokawa		if (verbose)
1130121468Ssimokawa			printf("dcons_buf: 0x%x\n", (uint)dc->paddr);
1131121468Ssimokawa		break;
1132121468Ssimokawa	}
1133121468Ssimokawa	}
1134121468Ssimokawa	dconschat_fetch_header(dc);
1135121468Ssimokawa
1136143416Sstefanf	/* init sockets */
1137121468Ssimokawa	dc->kq = kqueue();
1138121468Ssimokawa	if (poll_hz == 1) {
1139121468Ssimokawa		dc->to.tv_sec = 1;
1140121468Ssimokawa		dc->to.tv_nsec = 0;
1141121468Ssimokawa	} else {
1142121468Ssimokawa		dc->to.tv_sec = 0;
1143121468Ssimokawa		dc->to.tv_nsec = 1000 * 1000 * 1000 / poll_hz;
1144121468Ssimokawa	}
1145121468Ssimokawa	dc->zero.tv_sec = 0;
1146121468Ssimokawa	dc->zero.tv_nsec = 0;
1147121468Ssimokawa	for (i = 0; i < DCONS_NPORT; i++)
1148121468Ssimokawa		dconschat_init_socket(dc, i,
1149121468Ssimokawa		    wildcard ? NULL : "localhost", port[i]);
1150121468Ssimokawa
1151121468Ssimokawa	dconschat_start_session(dc);
1152121468Ssimokawa
1153121468Ssimokawa	for (i = 0; i < DCONS_NPORT; i++) {
1154121468Ssimokawa		freeaddrinfo(dc->port[i].res);
1155121468Ssimokawa	}
1156121468Ssimokawa	return (0);
1157121468Ssimokawa}
1158