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