summaryrefslogtreecommitdiffstats
path: root/usr.sbin/dconschat
diff options
context:
space:
mode:
authorsimokawa <simokawa@FreeBSD.org>2003-10-24 15:44:10 +0000
committersimokawa <simokawa@FreeBSD.org>2003-10-24 15:44:10 +0000
commitc96f6e4f1d827076592526a3f1f3e7887a9732a4 (patch)
tree2bed02c82f165be07e877314c53ec276a84990ec /usr.sbin/dconschat
parent7248844c9b0757577411885916b1314dfedc3cae (diff)
downloadFreeBSD-src-c96f6e4f1d827076592526a3f1f3e7887a9732a4.zip
FreeBSD-src-c96f6e4f1d827076592526a3f1f3e7887a9732a4.tar.gz
Add dumb console driver and related bits.
dcons(4): very simple console and gdb port driver dcons_crom(4): FireWire attachment dconschat(8): User interface to dcons Tested with: i386, i386-PAE, and sparc64.
Diffstat (limited to 'usr.sbin/dconschat')
-rw-r--r--usr.sbin/dconschat/Makefile12
-rw-r--r--usr.sbin/dconschat/dconschat.8229
-rw-r--r--usr.sbin/dconschat/dconschat.c959
3 files changed, 1200 insertions, 0 deletions
diff --git a/usr.sbin/dconschat/Makefile b/usr.sbin/dconschat/Makefile
new file mode 100644
index 0000000..9993ece
--- /dev/null
+++ b/usr.sbin/dconschat/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+PROG= dconschat
+SRCS= dconschat.c
+MAN= dconschat.8
+
+CFLAGS+= -I${.CURDIR}/../../sys
+
+DPADD= ${LIBKVM}
+LDADD= -lkvm
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/dconschat/dconschat.8 b/usr.sbin/dconschat/dconschat.8
new file mode 100644
index 0000000..4ed9d44
--- /dev/null
+++ b/usr.sbin/dconschat/dconschat.8
@@ -0,0 +1,229 @@
+.\" Copyright (c) 2003 Hidetoshi Shimokawa
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+.\" DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+.\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\"
+.Dd February 11, 2003
+.Dt DCONSCHAT 8
+.Os
+.Sh NAME
+.Nm dconschat
+.Nd user interface to dcons
+.Sh SYNOPSIS
+.Nm
+.Op Fl brvwRT1
+.Op Fl h Ar hz
+.Op Fl C Ar console_port
+.Op Fl G Ar gdb_port
+.Op Fl M Ar core
+.Op Fl N Ar system
+.Nm
+.Op Fl brvwR1
+.Op Fl h Ar hz
+.Op Fl C Ar console_port
+.Op Fl G Ar gdb_port
+.Op Fl a Ar address
+.Op Fl u Ar bus_num
+.Fl t Ar target_eui64
+.Sh DESCRIPTION
+The
+.Nm
+utility is designed to provide a way for users to access
+.Xr dcons 4
+(dumb console device) on a local or remote system.
+The
+.Nm
+interacts with
+.Xr dcons 4
+using
+.Xr kvm 3
+or
+.Xr firewire 4
+and interact with a user over tty or TCP/IP.
+To access remote
+.Xr dcons 4
+using
+.Xr firewire 4 ,
+you have to specify target EUI64 address by
+.Fl t
+option.
+.Pp
+The
+.Nm
+and
+.Xr dcons 4
+communicate using 2 port, one for console port and the other for
+remote gdb port.
+Users are supposed to access
+.Nm
+using tty, telnet and gdb.
+You can specify listen ports for console and gdb port by
+.Fl C
+and
+.Fl G
+options respectively. The port number 0 has special meaning that
+current tty(stdin/out) is used instead of TCP/IP.
+A negative port number will disable the port.
+To quit dconschat, send a CR + '~' + '.' sequence to the console port
+or send signal to the process.
+.Pp
+By analogy with
+.Xr pty 4
+device, the
+.Xr dcons 4
+acts as a slave device and
+.Nm
+acts as a master device with
+.Xr telnetd 8 .
+.Pp
+.Bl -tag -width indent
+.It Fl b
+Translate Ctrl-C to ALT_BREAK(CR + '~' + Ctrl-B) on gdb port.
+.It Fl r
+Replay old buffer on connection.
+.It Fl v
+Verbose debug output. Multiple '-v' increase verbosity.
+.It Fl w
+Listen on wildcard address rather than localhost.
+.It Fl R
+Read-only. Don't write anything on dcons buffer.
+.It Fl T
+Enable ad hoc workaround for telnet protocol to
+remove unnecessary byte sequences.
+It should be set when you access dconschat using telnet.
+.It Fl 1
+One-Shot. Read available buffer then exit. This implies
+.Fl r
+option.
+.It Fl h Ar hz
+Specify polling rate. The default value is 100.
+.It Fl C Ar console_port
+Specify console port. The default value is 0(stdin/stdout).
+.It Fl G Ar gdb_port
+Specify gdb port.. The default value is -1(disabled).
+.It Fl M Ar core
+Specify core file.
+.It Fl N Ar system
+Specify system file such as /boot/kernel/kernel.
+.It Fl t Ar target_eui64
+Specify the 64bit extended unique identifier of the target and use FireWire to access remote
+.Xr dcons 4 .
+.It Fl a Ar address
+Specify the physical/IO address of the dcons buffer. See
+.Xr dcons 4
+for details.
+If this option is not specified,
+.Nm
+tries to get the address from the Configuration ROM on the target.
+You are supposed to enable
+.Xr dcons_crom 4
+on the target to omit this option.
+.It Fl u Ar bus_num
+Specify FireWire bus number. The default is 0.
+.El
+.Sh EXAMPLE
+To use
+.Nm
+with FireWire for remote
+.Xr dcons ,
+you have to specify the eui64 of the target.
+You can obtain EUI64 by running
+.Xr fwcontorl 4
+without options.
+The first EUI64 is of the host running fwcontrol and others on the
+bus follow.
+.Bd -literal -offset indent
+# fwcontrol
+2 devices (info_len=2)
+node EUI64 status
+ 1 0x7766554433221100 0
+ 0 0x0011223344556677 1
+.Ed
+.Pp
+The EUI64 doesn't change unless you change the hardware
+as the ethernet address.
+.Pp
+Now we can run the
+.Nm .
+.Bd -literal -offset indent
+# dconschat -br -G 12345 -t 0x00112233445566677
+.Ed
+.Pp
+You'll get console output of the target and login prompt if a getty is
+running on dcons. You can break to DDB with ALT_BREAK (CR + '~' + Ctrl-B)
+if DDB and ALT_BREAK_TO_DEBUGGER is enabled in the target kernel.
+To quit the session, type CR + '~' + '.' in the console port.
+.Pp
+Using gdb port is almost the same as remote gdb over serial line except
+using TCP/IP instead of /dev/cu*. See
+"On-line Kernel Debugging Using Remote GDB"
+section of The FreeBSD Developers Handbook.
+.Bd -literal -offset indent
+% gdb -k kernel.debug
+(kgdb) target remote :12345
+.Ed
+.Pp
+Once gdb is attached and you specified '-b' option to dconschat,
+typing "Ctrl-C" on gdb causes break to debugger.
+.Pp
+The following command get console log from crash dump:
+.Bd -literal -offset indent
+# dconschat -1 -M vmcore.0 -N kernel.0
+.Ed
+.Pp
+If you want access to the console using telnet, try the following:
+.Bd -literal -offset indent
+# dconschat -rTC 5555 &
+# telnet localhost 5555
+.Ed
+.Pp
+You may want to keep logging console output of several machines. Conserve-com
+in the ports collection should help you. Insert the following lines
+in the conserver.cf
+.Bd -literal -offset indent
+local:|/usr/sbin/dconschat -rh 25::&:
+remote:|/usr/sbin/dconschat -rh 25 -t 0x00112233445566677::&:
+.Ed
+.Sh FILES
+.Bl -tag -width indent
+.It Pa /dev/fwmem0.0
+.It Pa /dev/mem
+.It Pa /dev/kmem
+.El
+.Sh SEE ALSO
+.Xr gdb 1 ,
+.Xr telnet 1 ,
+.Xr kvm 3 ,
+.Xr dcons 4 ,
+.Xr dcons_crom 4 ,
+.Xr ddb 4 ,
+.Xr firewire 4 ,
+.Xr fwohci 4 ,
+.Xr fwcontrol 8
+.Sh AUTHORS
+.An Hidetoshi Shimokawa Aq simokawa@FreeBSD.org
+.Sh BUGS
+This utility is still under development.
+.Pp
diff --git a/usr.sbin/dconschat/dconschat.c b/usr.sbin/dconschat/dconschat.c
new file mode 100644
index 0000000..2a982ab
--- /dev/null
+++ b/usr.sbin/dconschat/dconschat.c
@@ -0,0 +1,959 @@
+/*
+ * Copyright (C) 2003
+ * Hidetoshi Shimokawa. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ *
+ * This product includes software developed by Hidetoshi Shimokawa.
+ *
+ * 4. Neither the name of the author nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: dconschat.c,v 1.76 2003/10/23 06:21:13 simokawa Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <dev/dcons/dcons.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <err.h>
+#include <string.h>
+#include <sys/event.h>
+#include <sys/time.h>
+#include <arpa/telnet.h>
+
+#include <sys/ioccom.h>
+#include <dev/firewire/firewire.h>
+#include <dev/firewire/iec13213.h>
+
+#include <kvm.h>
+#include <nlist.h>
+
+#include <sys/errno.h>
+
+#define DCONS_POLL_HZ 100
+
+#define RETRY 3
+
+#ifdef CSRVAL_VENDOR_PRIVATE
+#define USE_CROM 1
+#else
+#define USE_CROM 0
+#endif
+
+int verbose = 0;
+int tc_set = 0;
+
+#define IS_CONSOLE(p) ((p)->port == 0)
+#define IS_GDB(p) ((p)->port == 1)
+
+static struct dcons_state {
+ int fd;
+ kvm_t *kd;
+ int kq;
+ off_t paddr;
+#define F_READY (1 << 1)
+#define F_RD_ONLY (1 << 2)
+#define F_ALT_BREAK (1 << 3)
+#define F_TELNET (1 << 4)
+#define F_USE_CROM (1 << 5)
+#define F_ONE_SHOT (1 << 6)
+#define F_REPLAY (1 << 7)
+ int flags;
+ enum {
+ TYPE_KVM,
+ TYPE_FW
+ } type;
+ int escape_state;
+ struct dcons_port {
+ int port;
+ struct dcons_ch o;
+ struct dcons_ch i;
+ u_int32_t optr;
+ u_int32_t iptr;
+ int s;
+ int infd;
+ int outfd;
+ struct addrinfo *res;
+ int skip_read;
+ } port[DCONS_NPORT];
+ struct timespec to;
+ struct timespec zero;
+ struct termios tsave;
+} sc;
+
+static int
+dread(struct dcons_state *dc, void *buf, size_t n, off_t offset)
+{
+ switch (dc->type) {
+ case TYPE_FW:
+ return (pread(dc->fd, buf, n, offset));
+ case TYPE_KVM:
+ return (kvm_read(dc->kd, offset, buf, n));
+ }
+ return (-1);
+}
+
+static int
+dwrite(struct dcons_state *dc, void *buf, size_t n, off_t offset)
+{
+ if ((dc->flags & F_RD_ONLY) != 0)
+ return (n);
+
+ switch (dc->type) {
+ case TYPE_FW:
+ return (pwrite(dc->fd, buf, n, offset));
+ case TYPE_KVM:
+ return (kvm_write(dc->kd, offset, buf, n));
+ }
+ return (-1);
+}
+
+static void
+dconschat_cleanup(int sig)
+{
+ struct dcons_state *dc;
+
+ dc = &sc;
+ if (tc_set != 0)
+ tcsetattr(STDIN_FILENO, TCSADRAIN, &dc->tsave);
+
+ if (sig > 0)
+ printf("\ndconschat exiting with signal %d ...\n", sig);
+ else
+ printf("\ndconschat exiting...\n");
+ exit(0);
+}
+
+#if USE_CROM
+static int
+dconschat_get_crom(struct dcons_state *dc)
+{
+ off_t addr;
+ int i, state = 0;
+ u_int32_t buf, hi = 0, lo = 0;
+ struct csrreg *reg;
+
+ reg = (struct csrreg *)&buf;
+ addr = 0xffff;
+ addr = (addr << 32) | 0xf0000400;
+ for (i = 20; i < 0x400; i += 4) {
+ if (dread(dc, &buf, 4, addr + i) < 0) {
+ if (verbose)
+ warn("crom read faild");
+ return (-1);
+ }
+ buf = ntohl(buf);
+ if (verbose)
+ printf("%d %02x %06x\n", state, reg->key, reg->val);
+ switch (state) {
+ case 0:
+ if (reg->key == CSRKEY_SPEC &&
+ reg->val == CSRVAL_VENDOR_PRIVATE)
+ state = 1;
+ break;
+ case 1:
+ if (reg->key == CSRKEY_VER &&
+ reg->val == DCONS_CSR_VAL_VER)
+ state = 2;
+ break;
+ case 2:
+ if (reg->key == DCONS_CSR_KEY_HI)
+ hi = reg->val;
+ else if (reg->key == DCONS_CSR_KEY_LO) {
+ lo = reg->val;
+ goto out;
+ }
+ break;
+ }
+ }
+ /* not found */
+ return (-1);
+out:
+ if (verbose)
+ printf("addr: %06x %06x\n", hi, lo);
+ dc->paddr = ((off_t)hi << 24) | lo;
+ return (0);
+}
+#endif
+
+static void
+dconschat_ready(struct dcons_state *dc, int ready, char *reason)
+{
+ static char oldreason[64] = "";
+ int old;
+
+ old = (dc->flags & F_READY) ? 1 : 0;
+
+ if (ready) {
+ dc->flags |= F_READY;
+ if (ready != old)
+ printf("[dcons connected]\r\n");
+ oldreason[0] = 0;
+ } else {
+ dc->flags &= ~F_READY;
+ if (strncmp(oldreason, reason, sizeof(oldreason)) != 0) {
+ printf("[dcons disconnected (%s)]\r\n", reason);
+ strlcpy(oldreason, reason, sizeof(oldreason));
+ }
+ }
+}
+
+static int
+dconschat_fetch_header(struct dcons_state *dc)
+{
+ char ebuf[64];
+ struct dcons_buf dbuf;
+ int j;
+
+#if USE_CROM
+ if (dc->paddr == 0 && (dc->flags & F_USE_CROM) != 0) {
+ if (dconschat_get_crom(dc)) {
+ dconschat_ready(dc, 0, "get crom failed");
+ return (-1);
+ }
+ }
+#endif
+
+ if (dread(dc, &dbuf, DCONS_HEADER_SIZE, dc->paddr) < 0) {
+ dconschat_ready(dc, 0, "read header failed");
+ return (-1);
+ }
+ if (dbuf.magic != htonl(DCONS_MAGIC)) {
+ if ((dc->flags & F_USE_CROM) !=0)
+ dc->paddr = 0;
+ snprintf(ebuf, sizeof(ebuf), "wrong magic 0x%08x", dbuf.magic);
+ dconschat_ready(dc, 0, ebuf);
+ return (-1);
+ }
+ if (ntohl(dbuf.version) != DCONS_VERSION) {
+ snprintf(ebuf, sizeof(ebuf),
+#if __FreeBSD_version < 500000
+ "wrong version %ld,%d",
+#else
+ "wrong version %d,%d",
+#endif
+ ntohl(dbuf.version), DCONS_VERSION);
+ /* XXX exit? */
+ dconschat_ready(dc, 0, ebuf);
+ return (-1);
+ }
+
+ for (j = 0; j < DCONS_NPORT; j++) {
+ struct dcons_ch *o, *i;
+ off_t newbuf;
+ int new = 0;
+
+ o = &dc->port[j].o;
+ newbuf = dc->paddr + ntohl(dbuf.ooffset[j]);
+ o->size = ntohl(dbuf.osize[j]);
+
+ if (newbuf != o->buf) {
+ /* buffer address has changes */
+ new = 1;
+ o->gen = ntohl(dbuf.optr[j]) >> DCONS_GEN_SHIFT;
+ o->pos = ntohl(dbuf.optr[j]) & DCONS_POS_MASK;
+ o->buf = newbuf;
+ }
+
+ i = &dc->port[j].i;
+ i->size = ntohl(dbuf.isize[j]);
+ i->gen = ntohl(dbuf.iptr[j]) >> DCONS_GEN_SHIFT;
+ i->pos = ntohl(dbuf.iptr[j]) & DCONS_POS_MASK;
+ i->buf = dc->paddr + ntohl(dbuf.ioffset[j]);
+
+ if (verbose) {
+ printf("port %d size offset gen pos\n", j);
+#if __FreeBSD_version < 500000
+ printf("output: %5d %6ld %5d %5d\n"
+ "input : %5d %6ld %5d %5d\n",
+#else
+ printf("output: %5d %6d %5d %5d\n"
+ "input : %5d %6d %5d %5d\n",
+#endif
+ o->size, ntohl(dbuf.ooffset[j]), o->gen, o->pos,
+ i->size, ntohl(dbuf.ioffset[j]), i->gen, i->pos);
+ }
+
+ if (IS_CONSOLE(&dc->port[j]) && new &&
+ (dc->flags & F_REPLAY) !=0) {
+ if (o->gen > 0)
+ o->gen --;
+ else
+ o->pos = 0;
+ }
+ }
+ dconschat_ready(dc, 1, NULL);
+ return(0);
+}
+
+static int
+dconschat_get_ptr (struct dcons_state *dc) {
+ int dlen, i;
+ u_int32_t ptr[DCONS_NPORT*2+1];
+ static int retry = RETRY;
+
+again:
+ dlen = dread(dc, &ptr, sizeof(ptr),
+ dc->paddr + __offsetof(struct dcons_buf, magic));
+
+ if (dlen < 0) {
+ if (errno == ETIMEDOUT)
+ if (retry -- > 0)
+ goto again;
+ dconschat_ready(dc, 0, "get ptr failed");
+ return(-1);
+ }
+ if (ptr[0] != htonl(DCONS_MAGIC)) {
+ dconschat_ready(dc, 0, "wrong magic");
+ return(-1);
+ }
+ retry = RETRY;
+ for (i = 0; i < DCONS_NPORT; i ++) {
+ dc->port[i].optr = ntohl(ptr[i + 1]);
+ dc->port[i].iptr = ntohl(ptr[DCONS_NPORT + i + 1]);
+ }
+ return(0);
+}
+
+#define MAX_XFER 2048
+static int
+dconschat_read_dcons(struct dcons_state *dc, int port, char *buf, int len)
+{
+ struct dcons_ch *ch;
+ u_int32_t ptr, pos, gen, next_gen;
+ int rlen, dlen, lost;
+ int retry = RETRY;
+
+ ch = &dc->port[port].o;
+ ptr = dc->port[port].optr;
+ gen = ptr >> DCONS_GEN_SHIFT;
+ pos = ptr & DCONS_POS_MASK;
+ if (gen == ch->gen && pos == ch->pos)
+ return (-1);
+
+ next_gen = DCONS_NEXT_GEN(ch->gen);
+ /* XXX sanity check */
+ if (gen == ch->gen) {
+ if (pos > ch->pos)
+ goto ok;
+ lost = ch->size * DCONS_GEN_MASK - ch->pos;
+ ch->pos = 0;
+ } else if (gen == next_gen) {
+ if (pos <= ch->pos)
+ goto ok;
+ lost = pos - ch->pos;
+ ch->pos = pos;
+ } else {
+ lost = gen - ch->gen;
+ if (lost < 0)
+ lost += DCONS_GEN_MASK;
+ if (verbose)
+ printf("[genskip %d]", lost);
+ lost = lost * ch->size - ch->pos;
+ ch->pos = 0;
+ ch->gen = gen;
+ }
+ /* generation skipped !! */
+ /* XXX discard */
+ if (verbose)
+ printf("[lost %d]", lost);
+ok:
+ if (gen == ch->gen)
+ rlen = pos - ch->pos;
+ else
+ rlen = ch->size - ch->pos;
+
+ if (rlen > MAX_XFER)
+ rlen = MAX_XFER;
+ if (rlen > len)
+ rlen = len;
+
+#if 1
+ if (verbose)
+ printf("[%d]", rlen); fflush(stdout);
+#endif
+
+again:
+ dlen = dread(dc, buf, rlen, ch->buf + ch->pos);
+ if (dlen < 0) {
+ if (errno == ETIMEDOUT)
+ if (retry -- > 0)
+ goto again;
+ dconschat_ready(dc, 0, "read buffer failed");
+ return(-1);
+ }
+ if (dlen != rlen)
+ warnx("dlen(%d) != rlen(%d)\n", dlen, rlen);
+ ch->pos += dlen;
+ if (ch->pos >= ch->size) {
+ ch->gen = next_gen;
+ ch->pos = 0;
+ if (verbose)
+ printf("read_dcons: gen=%d", ch->gen);
+ }
+ return (dlen);
+}
+
+static int
+dconschat_write_dcons(struct dcons_state *dc, int port, char *buf, int blen)
+{
+ struct dcons_ch *ch;
+ u_int32_t ptr;
+ int len, wlen;
+ int retry = RETRY;
+
+ ch = &dc->port[port].i;
+ ptr = dc->port[port].iptr;
+
+ /* the others may advance the pointer sync with it */
+ ch->gen = ptr >> DCONS_GEN_SHIFT;
+ ch->pos = ptr & DCONS_POS_MASK;
+
+ while(blen > 0) {
+ wlen = MIN(blen, ch->size - ch->pos);
+ wlen = MIN(wlen, MAX_XFER);
+ len = dwrite(dc, buf, wlen, ch->buf + ch->pos);
+ if (len < 0) {
+ if (errno == ETIMEDOUT)
+ if (retry -- > 0)
+ continue; /* try again */
+ dconschat_ready(dc, 0, "write buffer failed");
+ return(-1);
+ }
+ ch->pos += len;
+ buf += len;
+ blen -= len;
+ if (ch->pos >= ch->size) {
+ ch->gen = DCONS_NEXT_GEN(ch->gen);
+ ch->pos = 0;
+ if (verbose)
+ printf("write_dcons: gen=%d", ch->gen);
+
+ }
+ }
+
+ ptr = DCONS_MAKE_PTR(ch);
+ dc->port[port].iptr = ptr;
+
+ if (verbose > 2)
+ printf("(iptr: 0x%x)", ptr);
+again:
+ len = dwrite(dc, &ptr, sizeof(u_int32_t),
+ dc->paddr + __offsetof(struct dcons_buf, iptr[port]));
+ if (len < 0) {
+ if (errno == ETIMEDOUT)
+ if (retry -- > 0)
+ goto again;
+ dconschat_ready(dc, 0, "write ptr failed");
+ return(-1);
+ }
+ return(0);
+}
+
+static int
+dconschat_write_socket(int fd, char *buf, int len)
+{
+ write(fd, buf, len);
+ if (verbose > 1) {
+ buf[len] = 0;
+ printf("[%s]", buf);
+ }
+ return (0);
+}
+
+static void
+dconschat_init_socket(struct dcons_state *dc, int port, char *host, int sport)
+{
+ struct addrinfo hints, *res;
+ int on = 1, error;
+ char service[10];
+ struct kevent kev;
+ struct dcons_port *p;
+
+ p = &dc->port[port];
+ p->port = port;
+ p->infd = p->outfd = -1;
+
+ if (sport < 0)
+ return;
+
+ if (sport == 0) {
+
+ /* Use stdin and stdout */
+ p->infd = STDIN_FILENO;
+ p->outfd = STDOUT_FILENO;
+ p->s = -1;
+ if (tc_set == 0 &&
+ tcgetattr(STDIN_FILENO, &dc->tsave) == 0) {
+ struct termios traw;
+
+ traw = dc->tsave;
+ cfmakeraw(&traw);
+ tcsetattr(STDIN_FILENO, TCSADRAIN, &traw);
+ tc_set = 1;
+ }
+ EV_SET(&kev, p->infd, EVFILT_READ, EV_ADD, NOTE_LOWAT, 1,
+ (void *)p);
+ kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
+ return;
+ }
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_PASSIVE;
+#if 1 /* gdb can talk v4 only */
+ hints.ai_family = PF_INET;
+#else
+ hints.ai_family = PF_UNSPEC;
+#endif
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = 0;
+
+ if (verbose)
+ printf("%s:%d for port %d\n",
+ host == NULL ? "*" : host, sport, port);
+ snprintf(service, sizeof(service), "%d", sport);
+ error = getaddrinfo(host, service, &hints, &res);
+ if (error)
+ errx(1, "tcp/%s: %s\n", service, gai_strerror(error));
+ p->res = res;
+ p->s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (p->s < 0)
+ err(1, "socket");
+ setsockopt(p->s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+
+ if (bind(p->s, p->res->ai_addr, p->res->ai_addrlen) < 0) {
+ err(1, "bind");
+ }
+ if (listen(p->s, 1) < 0)
+ err(1, "listen");
+ EV_SET(&kev, p->s, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, (void *)p);
+ error = kevent(dc->kq, &kev, 1, NULL, 0, &dc->to);
+ if (error < 0)
+ err(1, "kevent");
+ return;
+}
+
+static int
+dconschat_accept_socket(struct dcons_state *dc, struct dcons_port *p)
+{
+ int foo, ns, flags;
+ struct kevent kev;
+
+ /* accept connection */
+ foo = p->res->ai_addrlen;
+ ns = accept(p->s, p->res->ai_addr, &foo);
+ if (ns < 0)
+ err(1, "accept");
+ if (verbose)
+ printf("port%d accepted\n", p->port);
+
+ flags = fcntl(ns, F_GETFL, 0);
+ flags |= O_NDELAY;
+ fcntl(ns, F_SETFL, flags);
+#if 1
+ if (IS_CONSOLE(p) && (dc->flags & F_TELNET) != 0) {
+ char sga[] = {IAC, WILL, TELOPT_SGA};
+ char linemode[] = {IAC, DONT, TELOPT_LINEMODE};
+ char echo[] = {IAC, WILL, TELOPT_ECHO};
+ char bin[] = {IAC, DO, TELOPT_BINARY};
+
+ write(ns, sga, sizeof(sga));
+ write(ns, linemode, sizeof(linemode));
+ write(ns, echo, sizeof(echo));
+ write(ns, bin, sizeof(bin));
+ p->skip_read = 0;
+ }
+#endif
+
+ p->infd = p->outfd = ns;
+ EV_SET(&kev, ns, EVFILT_READ, EV_ADD, NOTE_LOWAT, 1, (void *)p);
+ kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
+ return(0);
+}
+
+static int
+dconschat_read_filter(struct dcons_state *dc, struct dcons_port *p,
+ u_char *sp, int slen, u_char *dp, int *dlen)
+{
+ static u_char abreak[3] = {13 /* CR */, 126 /* ~ */, 2 /* ^B */};
+
+ while (slen > 0) {
+ if (IS_CONSOLE(p)) {
+ if ((dc->flags & F_TELNET) != 0) {
+ /* XXX Telent workarounds */
+ if (p->skip_read -- > 0) {
+ sp ++;
+ slen --;
+ continue;
+ }
+ if (*sp == IAC) {
+ if (verbose)
+ printf("(IAC)");
+ p->skip_read = 2;
+ sp ++;
+ slen --;
+ continue;
+ }
+ if (*sp == 0) {
+ if (verbose)
+ printf("(0 stripped)");
+ sp ++;
+ slen --;
+ continue;
+ }
+ }
+ switch (dc->escape_state) {
+ case STATE1:
+ if (*sp == KEY_TILDE)
+ dc->escape_state = STATE2;
+ else
+ dc->escape_state = STATE0;
+ break;
+ case STATE2:
+ dc->escape_state = STATE0;
+ if (*sp == '.')
+ dconschat_cleanup(0);
+ }
+ if (*sp == KEY_CR)
+ dc->escape_state = STATE1;
+ } else if (IS_GDB(p)) {
+ /* GDB: ^C -> CR+~+^B */
+ if (*sp == 0x3 && (dc->flags & F_ALT_BREAK) != 0) {
+ bcopy(abreak, dp, 3);
+ dp += 3;
+ sp ++;
+ *dlen += 3;
+ /* discard rest of the packet */
+ slen = 0;
+ break;
+ }
+ }
+ *dp++ = *sp++;
+ (*dlen) ++;
+ slen --;
+ }
+ return (*dlen);
+
+}
+
+static int
+dconschat_read_socket(struct dcons_state *dc, struct dcons_port *p)
+{
+ struct kevent kev;
+ int len, wlen;
+ char rbuf[MAX_XFER], wbuf[MAX_XFER+2];
+
+ if ((len = read(p->infd, rbuf, sizeof(rbuf))) > 0) {
+ wlen = 0;
+ dconschat_read_filter(dc, p, rbuf, len, wbuf, &wlen);
+ /* XXX discard if not ready*/
+ if (wlen > 0 && (dc->flags & F_READY) != 0) {
+ dconschat_write_dcons(dc, p->port, wbuf, wlen);
+ if (verbose > 1) {
+ wbuf[wlen] = 0;
+ printf("(%s)\n", wbuf);
+ }
+ if (verbose) {
+ printf("(%d)", wlen);
+ fflush(stdout);
+ }
+ }
+ } else {
+ if (verbose) {
+ if (len == 0)
+ warnx("port%d: closed", p->port);
+ else
+ warn("port%d: read", p->port);
+ }
+ EV_SET(&kev, p->infd, EVFILT_READ,
+ EV_DELETE, 0, 0, NULL);
+ kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
+ close(p->infd);
+ close(p->outfd);
+ /* XXX exit for pipe case XXX */
+ EV_SET(&kev, p->s, EVFILT_READ,
+ EV_ADD | EV_ONESHOT, 0, 0, (void *) p);
+ kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
+ p->infd = p->outfd = -1;
+ }
+ return(0);
+}
+#define NEVENT 5
+static int
+dconschat_proc_socket(struct dcons_state *dc)
+{
+ struct kevent elist[NEVENT], *e;
+ int i, n;
+ struct dcons_port *p;
+
+ n = kevent(dc->kq, NULL, 0, elist, NEVENT, &dc->to);
+ for (i = 0; i < n; i ++) {
+ e = &elist[i];
+ p = (struct dcons_port *)e->udata;
+ if (e->ident == p->s) {
+ dconschat_accept_socket(dc, p);
+ } else {
+ dconschat_read_socket(dc, p);
+ }
+ }
+ return(0);
+}
+
+static int
+dconschat_proc_dcons(struct dcons_state *dc)
+{
+ int port, len, err;
+ char buf[MAX_XFER];
+ struct dcons_port *p;
+
+ err = dconschat_get_ptr(dc);
+ if (err) {
+ /* XXX we should stop write operation too. */
+ return err;
+ }
+ for (port = 0; port < DCONS_NPORT; port ++) {
+ p = &dc->port[port];
+ if (p->infd < 0)
+ continue;
+ while ((len = dconschat_read_dcons(dc, port, buf,
+ sizeof(buf))) > 0) {
+ dconschat_write_socket(p->outfd, buf, len);
+ dconschat_get_ptr(dc);
+ }
+ if ((dc->flags & F_ONE_SHOT) != 0 && len <= 0)
+ dconschat_cleanup(0);
+ }
+ return 0;
+}
+
+static int
+dconschat_start_session(struct dcons_state *dc)
+{
+ int counter = 0;
+
+ while (1) {
+ if ((dc->flags & F_READY) == 0 && (++counter % 200) == 0)
+ dconschat_fetch_header(dc);
+ if ((dc->flags & F_READY) != 0)
+ dconschat_proc_dcons(dc);
+ dconschat_proc_socket(dc);
+ }
+ return (0);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: dconschat [-brvwRT1] [-h hz] [-C port] [-G port]\n"
+ "\t\t\t[-M core] [-N system]\n"
+ "\t\t\t[-u unit] [-a address] [-t target_eui64]\n"
+ "\t-b translate ctrl-C to CR+~+ctrl-B on gdb port\n"
+ "\t-v verbose\n"
+ "\t-w listen on wildcard address rather than localhost\n"
+ "\t-r replay old buffer on connection\n"
+ "\t-R read-only\n"
+ "\t-T enable Telnet protocol workaround on console port\n"
+ "\t-1 one shot: read buffer and exit\n"
+ "\t-h polling rate\n"
+ "\t-C port number for console port\n"
+ "\t-G port number for gdb port\n"
+ "\t(for KVM)\n"
+ "\t-M core file\n"
+ "\t-N system file\n"
+ "\t(for FireWire)\n"
+ "\t-u specify unit number of the bus\n"
+ "\t-t EUI64 of target host (must be specified)\n"
+ "\t-a physical address of dcons buffer on target host\n"
+ );
+ exit(0);
+}
+int
+main(int argc, char **argv)
+{
+ struct dcons_state *dc;
+ struct fw_eui64 eui;
+ char devname[256], *core = NULL, *system = NULL;
+ int i, ch, error;
+ int unit=0, wildcard=0, poll_hz = DCONS_POLL_HZ;
+ int port[DCONS_NPORT];
+ u_int64_t target = 0;
+
+ bzero(&sc, sizeof(sc));
+ dc = &sc;
+ dc->flags |= USE_CROM ? F_USE_CROM : 0;
+
+ /* defualt ports */
+ port[0] = 0; /* stdin/out for console */
+ port[1] = -1; /* disable gdb port */
+
+ while ((ch = getopt(argc, argv, "a:bh:rt:u:vwC:G:M:N:RT1")) != -1) {
+ switch(ch) {
+ case 'a':
+ dc->paddr = strtoull(optarg, NULL, 0);
+ dc->flags &= ~F_USE_CROM;
+ break;
+ case 'b':
+ dc->flags |= F_ALT_BREAK;
+ break;
+ case 'h':
+ poll_hz = strtoul(optarg, NULL, 0);
+ if (poll_hz == 0)
+ poll_hz = DCONS_POLL_HZ;
+ printf("poll_hz = %d\n", poll_hz);
+ break;
+ case 'r':
+ dc->flags |= F_REPLAY;
+ break;
+ case 't':
+ target = strtoull(optarg, NULL, 0);
+ eui.hi = target >> 32;
+ eui.lo = target & (((u_int64_t)1 << 32) - 1);
+ dc->type = TYPE_FW;
+ break;
+ case 'u':
+ unit = strtol(optarg, NULL, 0);
+ break;
+ case 'v':
+ verbose ++;
+ break;
+ case 'w':
+ wildcard = 1;
+ break;
+ case 'C':
+ port[0] = strtol(optarg, NULL, 0);
+ break;
+ case 'G':
+ port[1] = strtol(optarg, NULL, 0);
+ break;
+ case 'M':
+ core = optarg;
+ break;
+ case 'N':
+ system = optarg;
+ break;
+ case 'R':
+ dc->flags |= F_RD_ONLY;
+ break;
+ case 'T':
+ dc->flags |= F_TELNET;
+ break;
+ case '1':
+ dc->flags |= F_ONE_SHOT | F_REPLAY;
+ break;
+ default:
+ usage();
+ }
+ }
+ if (dc->paddr == 0 && (dc->flags & F_USE_CROM) == 0) {
+ warnx("no address specified");
+ usage();
+ }
+
+ if (port[0] < 0 && port[1] < 0) {
+ warnx("no port specified");
+ usage();
+ }
+
+ /* set signal handler */
+ signal(SIGHUP, dconschat_cleanup);
+ signal(SIGINT, dconschat_cleanup);
+ signal(SIGPIPE, dconschat_cleanup);
+ signal(SIGTERM, dconschat_cleanup);
+
+ /* init firewire */
+ switch (dc->type) {
+ case TYPE_FW:
+#define MAXDEV 4
+ for (i = 0; i < MAXDEV; i ++) {
+ snprintf(devname, sizeof(devname),
+ "/dev/fwmem%d.%d", unit, i);
+ dc->fd = open(devname, O_RDWR);
+ if (dc->fd >= 0)
+ goto found;
+ }
+ err(1, "open");
+found:
+ error = ioctl(dc->fd, FW_SDEUI64, &eui);
+ if (error)
+ err(1, "ioctl");
+ break;
+ case TYPE_KVM:
+ {
+ struct nlist nl[] = {{"dcons_buf"}, {""}};
+ void *dcons_buf;
+
+ dc->kd = kvm_open(system, core, NULL,
+ (dc->flags & F_RD_ONLY) ? O_RDONLY : O_RDWR, "dconschat");
+ if (dc->kd == NULL)
+ errx(1, "kvm_open");
+
+ if (kvm_nlist(dc->kd, nl) < 0)
+ errx(1, "kvm_nlist: %s", kvm_geterr(dc->kd));
+
+ if (kvm_read(dc->kd, nl[0].n_value, &dcons_buf,
+ sizeof(void *)) < 0)
+ errx(1, "kvm_read: %s", kvm_geterr(dc->kd));
+ dc->paddr = (uintptr_t)dcons_buf;
+ if (verbose)
+ printf("dcons_buf: 0x%x\n", (uint)dc->paddr);
+ break;
+ }
+ }
+ dconschat_fetch_header(dc);
+
+ /* iniit sockets */
+ dc->kq = kqueue();
+ if (poll_hz == 1) {
+ dc->to.tv_sec = 1;
+ dc->to.tv_nsec = 0;
+ } else {
+ dc->to.tv_sec = 0;
+ dc->to.tv_nsec = 1000 * 1000 * 1000 / poll_hz;
+ }
+ dc->zero.tv_sec = 0;
+ dc->zero.tv_nsec = 0;
+ for (i = 0; i < DCONS_NPORT; i++)
+ dconschat_init_socket(dc, i,
+ wildcard ? NULL : "localhost", port[i]);
+
+ dconschat_start_session(dc);
+
+ for (i = 0; i < DCONS_NPORT; i++) {
+ freeaddrinfo(dc->port[i].res);
+ }
+ return (0);
+}
OpenPOWER on IntegriCloud