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.
This commit is contained in:
simokawa 2003-10-24 15:44:10 +00:00
parent 7248844c9b
commit c96f6e4f1d
21 changed files with 2408 additions and 3 deletions

View File

@ -50,6 +50,8 @@ ttyd0 "/usr/libexec/getty std.9600" vt100 on secure
ttyd1 "/usr/libexec/getty std.9600" dialup off secure
ttyd2 "/usr/libexec/getty std.9600" dialup off secure
ttyd3 "/usr/libexec/getty std.9600" dialup off secure
# Dumb console
dcons "/usr/libexec/getty std.9600" vt100 off secure
# Pseudo terminals
ttyp0 none network
ttyp1 none network

View File

@ -48,6 +48,8 @@ ttyd0 "/usr/libexec/getty std.9600" dialup off secure
ttyd1 "/usr/libexec/getty std.9600" dialup off secure
ttyd2 "/usr/libexec/getty std.9600" dialup off secure
ttyd3 "/usr/libexec/getty std.9600" dialup off secure
# Dumb console
dcons "/usr/libexec/getty std.9600" vt100 off secure
# Pseudo terminals
ttyp0 none network
ttyp1 none network

View File

@ -48,6 +48,8 @@ ttyd0 "/usr/libexec/getty std.9600" dialup off secure
ttyd1 "/usr/libexec/getty std.9600" dialup off secure
ttyd2 "/usr/libexec/getty std.9600" dialup off secure
ttyd3 "/usr/libexec/getty std.9600" dialup off secure
# Dumb console
dcons "/usr/libexec/getty std.9600" vt100 off secure
# Pseudo terminals
ttyp0 none network
ttyp1 none network

View File

@ -48,6 +48,8 @@ ttyu0 "/usr/libexec/getty std.9600" vt100 on secure
ttyu1 "/usr/libexec/getty std.9600" dialup off secure
ttyu2 "/usr/libexec/getty std.9600" dialup off secure
ttyu3 "/usr/libexec/getty std.9600" dialup off secure
# Dumb console
dcons "/usr/libexec/getty std.9600" vt100 off secure
# Pseudo terminals.
ttyp0 none network
ttyp1 none network

View File

@ -52,6 +52,8 @@ ttyu0 "/usr/libexec/getty std.9600" vt100 on secure
ttyu1 "/usr/libexec/getty std.9600" vt100 on secure
ttyu2 "/usr/libexec/getty std.9600" vt100 off secure
ttyu3 "/usr/libexec/getty std.9600" vt100 off secure
# Dumb console
dcons "/usr/libexec/getty std.9600" vt100 off secure
# Pseudo terminals
ttyp0 none network
ttyp1 none network

View File

@ -41,6 +41,8 @@ MAN= aac.4 \
cue.4 \
da.4 \
dc.4 \
dcons.4 \
dcons_crom.4 \
ddb.4 \
de.4 \
devctl.4 \

112
share/man/man4/dcons.4 Normal file
View File

@ -0,0 +1,112 @@
.\" 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 DCONS 4
.Os
.Sh NAME
.Nm dcons
.Nd dumb console device driver
.Sh SYNOPSIS
.Cd device dcons
.Pp
.Cd options DDB
.Cd options ALT_BREAK_TO_DEBUGGER
.Pp
.Cd device firewire
.Sh DESCRIPTION
The
.Nm
device is the simple console device which just reads from and writes to
an allocated buffer for input and output respectivly.
It is no use by itself and it is supposed that the buffer is accessed
via a bus like
.Xr FireWire 4
for interaction.
.Pp
The buffer consists of 4 channels.
There are 2 ports, one for console tty and other is GDB ports then each port
has a input channel and a output channel.
The physical address of the buffer is sometimes neccesary to acess the buffer.
You can get the address by
.Xr sysctl 8
or
.Xr dmesg 8
.
.Sh EXAMPLE
If you want to run
.Xr getty 8
on dcons, insert following line into
.Xr /etc/ttys 5
and
send a HUP signal to
.Xr init 8
using
.Xr kill 1 .
.Bd -literal -offset indent
dcons "/usr/libexec/getty std.9600" vt100 on secure
.Ed
.Pp
You can use either of the following commands to obtain physical
address of the buffer.
.Bd -literal -offset indent
% sysctl kern.dcons.paddr
kern.dcons.paddr: 4732704
% dmesg | grep dcons: | tail -1
dcons: virtual 0xc0483720 physical 0x483720 quad 0x120dc8
.Ed
.Pp
In this example, the buffer is located at 4732704 in decimal
and 0x483720 in hex.
.Pp
Once
.Xr fwochi 4
device is initialized to allow physical access,
the buffer can be accessed from another host via FireWire bus using
.Xr fwchat 8
application. See
.Xr fwchat 8
for more details.
.Pp
.Sh FILES
.Bl -tag -width indent -compact
.It Pa /dev/dcons
.It Pa /dev/dconsctl
.It Pa /etc/ttys
.El
.Sh SEE ALSO
.Xr ddb 4 ,
.Xr firewire 4 ,
.Xr fwohci 4 ,
.Xr fwchat 8 ,
.Xr fwcontrol 8 ,
.Xr ttys 5
.Sh AUTHORS
.An Hidetoshi Shimokawa Aq simokawa@FreeBSD.org
.Sh BUGS
This driver is still under development.
.Pp

View File

@ -0,0 +1,59 @@
.\" 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 June 16, 2003
.Dt DCONS_CROM 4
.Os
.Sh NAME
.Nm dcons_crom
.Nd Configuration ROM stub for
.Xr dcons 4
.Sh SYNOPSIS
.Cd device dcons_crom
.Cd device dcons
.Cd device firewire
.Pp
.Sh DESCRIPTION
The
.Nm
exposes buffer address of
.Xr dcons 4
via Configuration ROM which is accessed by
.Xr FireWire 4
.
.El
.Sh SEE ALSO
.Xr dcons 4 ,
.Xr firewire 4 ,
.Xr fwohci 4 ,
.Xr fwchat 8 ,
.Xr fwcontrol 8 ,
.Sh AUTHORS
.An Hidetoshi Shimokawa Aq simokawa@FreeBSD.org
.Sh BUGS
This driver is still under development.
.Pp

View File

@ -2241,11 +2241,21 @@ makeoptions UKBD_DFLT_KEYMAP=it.iso
options UVSCOM_DEFAULT_OPKTSIZE=8 # default output packet size
#####################################################################
# Firewire support
# FireWire support
device firewire # Firewire bus code
device firewire # FireWire bus code
device sbp # SCSI over Firewire (Requires scbus and da)
device fwe # Ethernet over Firewire (non-standard!)
device fwe # Ethernet over FireWire (non-standard!)
#####################################################################
# dcons support (Dumb Console Device)
device dcons # dumb console driver
device dcons_crom # FireWire attachment
options DCONS_BUF_SIZE=16384 # buffer size
options DCONS_POLL_HZ=100 # polling rate
options DCONS_FORCE_CONSOLE=0 # force to be the primary console
options DCONS_FORCE_GDB=1 # force to be the gdb device
#####################################################################
# crypto subsystem

View File

@ -351,6 +351,8 @@ dev/cs/if_cs.c optional cs
dev/cs/if_cs_isa.c optional cs isa
dev/cs/if_cs_pccard.c optional cs card
dev/cs/if_cs_pccard.c optional cs pccard
dev/dcons/dcons.c optional dcons
dev/dcons/dcons_crom.c optional dcons_crom
dev/digi/digi.c optional digi
dev/digi/digi_isa.c optional digi isa
dev/digi/digi_pci.c optional digi pci

View File

@ -673,3 +673,9 @@ AH_SUPPORT_AR5212 opt_ah.h
AH_DEBUG opt_ah.h
AH_DEBUG_ALQ opt_ah.h
AH_ASSERT opt_ah.h
# dcons options
DCONS_BUF_SIZE opt_dcons.h
DCONS_POLL_HZ opt_dcons.h
DCONS_FORCE_CONSOLE opt_dcons.h
DCONS_FORCE_GDB opt_dcons.h

648
sys/dev/dcons/dcons.c Normal file
View File

@ -0,0 +1,648 @@
/*
* 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: dcons.c,v 1.65 2003/10/24 03:24:55 simokawa Exp $
* $FreeBSD$
*/
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/types.h>
#include <sys/conf.h>
#include <sys/cons.h>
#include <sys/consio.h>
#include <sys/tty.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/ucred.h>
#include <machine/bus.h>
#include <dev/dcons/dcons.h>
#include <ddb/ddb.h>
#include <sys/reboot.h>
#include <sys/sysctl.h>
#include "opt_ddb.h"
#include "opt_comconsole.h"
#include "opt_dcons.h"
#ifndef DCONS_POLL_HZ
#define DCONS_POLL_HZ 100
#endif
#ifndef DCONS_BUF_SIZE
#define DCONS_BUF_SIZE (16*1024)
#endif
#ifndef DCONS_FORCE_CONSOLE
#define DCONS_FORCE_CONSOLE 0 /* mostly for FreeBSD-4 */
#endif
#ifndef DCONS_FORCE_GDB
#define DCONS_FORCE_GDB 1
#endif
#if __FreeBSD_version >= 500101
#define CONS_NODEV 1 /* for latest current */
static struct consdev gdbconsdev;
#endif
#define CDEV_MAJOR 184
static d_open_t dcons_open;
static d_close_t dcons_close;
static d_ioctl_t dcons_ioctl;
static struct cdevsw dcons_cdevsw = {
#if __FreeBSD_version >= 500104
.d_open = dcons_open,
.d_close = dcons_close,
.d_read = ttyread,
.d_write = ttywrite,
.d_ioctl = dcons_ioctl,
.d_poll = ttypoll,
.d_name = "dcons",
.d_maj = CDEV_MAJOR,
#else
/* open */ dcons_open,
/* close */ dcons_close,
/* read */ ttyread,
/* write */ ttywrite,
/* ioctl */ dcons_ioctl,
/* poll */ ttypoll,
/* mmap */ nommap,
/* strategy */ nostrategy,
/* name */ "dcons",
/* major */ CDEV_MAJOR,
/* dump */ nodump,
/* psize */ nopsize,
/* flags */ 0,
#endif
};
#ifndef KLD_MODULE
static char bssbuf[DCONS_BUF_SIZE]; /* buf in bss */
#endif
struct dcons_buf *dcons_buf;
size_t dcons_bufsize;
bus_dma_tag_t dcons_dma_tag = NULL;
bus_dmamap_t dcons_dma_map = NULL;
static int poll_hz = DCONS_POLL_HZ;
SYSCTL_NODE(_kern, OID_AUTO, dcons, CTLFLAG_RD, 0, "Dumb Console");
SYSCTL_INT(_kern_dcons, OID_AUTO, poll_hz, CTLFLAG_RW, &poll_hz, 0,
"dcons polling rate");
static int drv_init = 0;
static struct callout dcons_callout;
/* per device data */
static struct dcons_softc {
dev_t dev;
struct dcons_ch o, i;
int brk_state;
#define DC_GDB 1
int flags;
} sc[DCONS_NPORT];
static void dcons_tty_start(struct tty *);
static int dcons_tty_param(struct tty *, struct termios *);
static void dcons_timeout(void *);
static int dcons_drv_init(int);
static int dcons_getc(struct dcons_softc *);
static int dcons_checkc(struct dcons_softc *);
static void dcons_putc(struct dcons_softc *, int);
static cn_probe_t dcons_cnprobe;
static cn_init_t dcons_cninit;
static cn_getc_t dcons_cngetc;
static cn_checkc_t dcons_cncheckc;
static cn_putc_t dcons_cnputc;
CONS_DRIVER(dcons, dcons_cnprobe, dcons_cninit, NULL, dcons_cngetc,
dcons_cncheckc, dcons_cnputc, NULL);
#if __FreeBSD_version < 500000
#define THREAD proc
#else
#define THREAD thread
#endif
static int
dcons_open(dev_t dev, int flag, int mode, struct THREAD *td)
{
struct tty *tp;
int unit, error, s;
unit = minor(dev);
if (unit != 0)
return (ENXIO);
tp = dev->si_tty = ttymalloc(dev->si_tty);
tp->t_oproc = dcons_tty_start;
tp->t_param = dcons_tty_param;
tp->t_stop = nottystop;
tp->t_dev = dev;
error = 0;
s = spltty();
if ((tp->t_state & TS_ISOPEN) == 0) {
tp->t_state |= TS_CARR_ON;
ttychars(tp);
tp->t_iflag = TTYDEF_IFLAG;
tp->t_oflag = TTYDEF_OFLAG;
tp->t_cflag = TTYDEF_CFLAG|CLOCAL;
tp->t_lflag = TTYDEF_LFLAG;
tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
ttsetwater(tp);
} else if ((tp->t_state & TS_XCLUDE) && suser(td)) {
splx(s);
return (EBUSY);
}
splx(s);
error = (*linesw[tp->t_line].l_open)(dev, tp);
return (error);
}
static int
dcons_close(dev_t dev, int flag, int mode, struct THREAD *td)
{
int unit;
struct tty *tp;
unit = minor(dev);
if (unit != 0)
return (ENXIO);
tp = dev->si_tty;
if (tp->t_state & TS_ISOPEN) {
(*linesw[tp->t_line].l_close)(tp, flag);
ttyclose(tp);
}
return (0);
}
static int
dcons_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct THREAD *td)
{
int unit;
struct tty *tp;
int error;
unit = minor(dev);
if (unit != 0)
return (ENXIO);
tp = dev->si_tty;
error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, td);
if (error != ENOIOCTL)
return (error);
error = ttioctl(tp, cmd, data, flag);
if (error != ENOIOCTL)
return (error);
return (ENOTTY);
}
static int
dcons_tty_param(struct tty *tp, struct termios *t)
{
tp->t_ispeed = t->c_ispeed;
tp->t_ospeed = t->c_ospeed;
tp->t_cflag = t->c_cflag;
return 0;
}
static void
dcons_tty_start(struct tty *tp)
{
struct dcons_softc *dc;
int s;
dc = (struct dcons_softc *)tp->t_dev->si_drv1;
s = spltty();
if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) {
ttwwakeup(tp);
return;
}
tp->t_state |= TS_BUSY;
while (tp->t_outq.c_cc != 0)
dcons_putc(dc, getc(&tp->t_outq));
tp->t_state &= ~TS_BUSY;
ttwwakeup(tp);
splx(s);
}
static void
dcons_timeout(void *v)
{
struct tty *tp;
struct dcons_softc *dc;
int i, c, polltime;
for (i = 0; i < DCONS_NPORT; i ++) {
dc = &sc[i];
tp = dc->dev->si_tty;
while ((c = dcons_checkc(dc)) != -1)
if (tp->t_state & TS_ISOPEN)
(*linesw[tp->t_line].l_rint)(c, tp);
}
polltime = hz / poll_hz;
if (polltime < 1)
polltime = 1;
callout_reset(&dcons_callout, polltime, dcons_timeout, tp);
}
static void
dcons_cnprobe(struct consdev *cp)
{
#if __FreeBSD_version >= 501109
sprintf(cp->cn_name, "dcons");
#else
cp->cn_dev = makedev(CDEV_MAJOR, DCONS_CON);
#endif
#if DCONS_FORCE_CONSOLE
cp->cn_pri = CN_REMOTE;
#else
cp->cn_pri = CN_NORMAL;
#endif
}
static void
dcons_cninit(struct consdev *cp)
{
dcons_drv_init(0);
#if CONS_NODEV
cp->cn_arg
#else
cp->cn_dev->si_drv1
#endif
= (void *)&sc[DCONS_CON]; /* share port0 with unit0 */
}
#if CONS_NODEV
static int
dcons_cngetc(struct consdev *cp)
{
return(dcons_getc((struct dcons_softc *)cp->cn_arg));
}
static int
dcons_cncheckc(struct consdev *cp)
{
return(dcons_checkc((struct dcons_softc *)cp->cn_arg));
}
static void
dcons_cnputc(struct consdev *cp, int c)
{
dcons_putc((struct dcons_softc *)cp->cn_arg, c);
}
#else
static int
dcons_cngetc(dev_t dev)
{
return(dcons_getc((struct dcons_softc *)dev->si_drv1));
}
static int
dcons_cncheckc(dev_t dev)
{
return(dcons_checkc((struct dcons_softc *)dev->si_drv1));
}
static void
dcons_cnputc(dev_t dev, int c)
{
dcons_putc((struct dcons_softc *)dev->si_drv1, c);
}
#endif
static int
dcons_getc(struct dcons_softc *dc)
{
int c;
while ((c = dcons_checkc(dc)) == -1);
return (c & 0xff);
}
static int
dcons_checkc(struct dcons_softc *dc)
{
unsigned char c;
u_int32_t ptr, pos, gen, next_gen;
struct dcons_ch *ch;
ch = &dc->i;
if (dcons_dma_tag != NULL)
bus_dmamap_sync(dcons_dma_tag, dcons_dma_map,
BUS_DMASYNC_POSTREAD);
ptr = ntohl(*ch->ptr);
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 && gen != next_gen)
|| (gen == ch->gen && pos < ch->pos)) {
/* generation skipped !! */
/* XXX discard */
ch->gen = gen;
ch->pos = pos;
return (-1);
}
c = ch->buf[ch->pos];
ch->pos ++;
if (ch->pos >= ch->size) {
ch->gen = next_gen;
ch->pos = 0;
}
#if DDB && ALT_BREAK_TO_DEBUGGER
switch (dc->brk_state) {
case STATE1:
if (c == KEY_TILDE)
dc->brk_state = STATE2;
else
dc->brk_state = STATE0;
break;
case STATE2:
dc->brk_state = STATE0;
if (c == KEY_CTRLB) {
#if DCONS_FORCE_GDB
if (dc->flags & DC_GDB)
boothowto |= RB_GDB;
#endif
breakpoint();
}
}
if (c == KEY_CR)
dc->brk_state = STATE1;
#endif
return (c);
}
static void
dcons_putc(struct dcons_softc *dc, int c)
{
struct dcons_ch *ch;
ch = &dc->o;
ch->buf[ch->pos] = c;
ch->pos ++;
if (ch->pos >= ch->size) {
ch->gen = DCONS_NEXT_GEN(ch->gen);
ch->pos = 0;
}
*ch->ptr = DCONS_MAKE_PTR(ch);
if (dcons_dma_tag != NULL)
bus_dmamap_sync(dcons_dma_tag, dcons_dma_map,
BUS_DMASYNC_PREWRITE);
}
static int
dcons_init_port(int port, int offset, int size)
{
int osize;
struct dcons_softc *dc;
dc = &sc[port];
osize = size * 3 / 4;
dc->o.size = osize;
dc->i.size = size - osize;
dc->o.buf = (char *)dcons_buf + offset;
dc->i.buf = dc->o.buf + osize;
dc->o.gen = dc->i.gen = 0;
dc->o.pos = dc->i.pos = 0;
dc->o.ptr = &dcons_buf->optr[port];
dc->i.ptr = &dcons_buf->iptr[port];
dc->brk_state = STATE0;
dcons_buf->osize[port] = htonl(osize);
dcons_buf->isize[port] = htonl(size - osize);
dcons_buf->ooffset[port] = htonl(offset);
dcons_buf->ioffset[port] = htonl(offset + osize);
dcons_buf->optr[port] = DCONS_MAKE_PTR(&dc->o);
dcons_buf->iptr[port] = DCONS_MAKE_PTR(&dc->i);
return(0);
}
static int
dcons_drv_init(int stage)
{
int size, size0, offset;
if (drv_init)
return(drv_init);
drv_init = -1;
dcons_bufsize = DCONS_BUF_SIZE;
#ifndef KLD_MODULE
if (stage == 0) /* XXX or cold */
/*
* DCONS_FORCE_CONSOLE == 1 and statically linked.
* called from cninit(). can't use contigmalloc yet .
*/
dcons_buf = (struct dcons_buf *) bssbuf;
else
#endif
/*
* DCONS_FORCE_CONSOLE == 0 or kernel module case.
* if the module is loaded after boot,
* dcons_buf could be non-continuous.
*/
dcons_buf = (struct dcons_buf *) contigmalloc(dcons_bufsize,
M_DEVBUF, 0, 0x10000, 0xffffffff, PAGE_SIZE, 0ul);
offset = DCONS_HEADER_SIZE;
size = (dcons_bufsize - offset);
size0 = size * 3 / 4;
dcons_init_port(0, offset, size0);
offset += size0;
dcons_init_port(1, offset, size - size0);
dcons_buf->version = htonl(DCONS_VERSION);
dcons_buf->magic = ntohl(DCONS_MAGIC);
#if DDB && DCONS_FORCE_GDB
#if CONS_NODEV
gdbconsdev.cn_arg = (void *)&sc[DCONS_GDB];
#if __FreeBSD_version >= 501109
sprintf(gdbconsdev.cn_name, "dgdb");
#endif
gdb_arg = &gdbconsdev;
#else
gdbdev = makedev(CDEV_MAJOR, DCONS_GDB);
#endif
gdb_getc = dcons_cngetc;
gdb_putc = dcons_cnputc;
#endif
drv_init = 1;
return 0;
}
static int
dcons_attach_port(int port, char *name, int flags)
{
struct dcons_softc *dc;
struct tty *tp;
dc = &sc[port];
dc->flags = flags;
dc->dev = make_dev(&dcons_cdevsw, port,
UID_ROOT, GID_WHEEL, 0600, name);
tp = ttymalloc(NULL);
dc->dev->si_drv1 = (void *)dc;
dc->dev->si_tty = tp;
tp->t_oproc = dcons_tty_start;
tp->t_param = dcons_tty_param;
tp->t_stop = nottystop;
tp->t_dev = dc->dev;
return(0);
}
static int
dcons_attach(void)
{
int polltime;
dcons_attach_port(DCONS_CON, "dcons", 0);
dcons_attach_port(DCONS_GDB, "dgdb", DC_GDB);
#if __FreeBSD_version < 500000
callout_init(&dcons_callout);
#else
callout_init(&dcons_callout, 0);
#endif
polltime = hz / poll_hz;
if (polltime < 1)
polltime = 1;
callout_reset(&dcons_callout, polltime, dcons_timeout, NULL);
return(0);
}
static int
dcons_detach(int port)
{
struct tty *tp;
struct dcons_softc *dc;
dc = &sc[port];
tp = dc->dev->si_tty;
if (tp->t_state & TS_ISOPEN) {
printf("dcons: still opened\n");
(*linesw[tp->t_line].l_close)(tp, 0);
tp->t_gen++;
ttyclose(tp);
ttwakeup(tp);
ttwwakeup(tp);
}
/* XXX
* must wait until all device are closed.
*/
tsleep((void *)dc, PWAIT, "dcodtc", hz/4);
destroy_dev(dc->dev);
return(0);
}
/* cnXXX works only for FreeBSD-5 */
static int
dcons_modevent(module_t mode, int type, void *data)
{
int err = 0, ret;
switch (type) {
case MOD_LOAD:
ret = dcons_drv_init(1);
dcons_attach();
#if __FreeBSD_version >= 500000
if (ret == 0) {
dcons_cnprobe(&dcons_consdev);
dcons_cninit(&dcons_consdev);
cnadd(&dcons_consdev);
}
#endif
break;
case MOD_UNLOAD:
printf("dcons: unload\n");
callout_stop(&dcons_callout);
#if DDB && DCONS_FORCE_GDB
#if CONS_NODEV
gdb_arg = NULL;
#else
gdbdev = NODEV;
#endif
#endif
#if __FreeBSD_version >= 500000
cnremove(&dcons_consdev);
#endif
dcons_detach(DCONS_CON);
dcons_detach(DCONS_GDB);
dcons_buf->magic = 0;
contigfree(dcons_buf, DCONS_BUF_SIZE, M_DEVBUF);
break;
case MOD_SHUTDOWN:
break;
}
return(err);
}
DEV_MODULE(dcons, dcons_modevent, NULL);
MODULE_VERSION(dcons, DCONS_VERSION);

97
sys/dev/dcons/dcons.h Normal file
View File

@ -0,0 +1,97 @@
/*
* Copyright (C) 2002
* 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: dcons.h,v 1.15 2003/10/23 15:05:31 simokawa Exp $
* $FreeBSD$
*/
#ifdef _KERNEL
#define V volatile
#else
#define V
#endif
#define DCONS_NPORT 2
#define DCONS_CON 0
#define DCONS_GDB 1
struct dcons_buf {
#define DCONS_VERSION 2
V u_int32_t version;
V u_int32_t ooffset[DCONS_NPORT];
V u_int32_t ioffset[DCONS_NPORT];
V u_int32_t osize[DCONS_NPORT];
V u_int32_t isize[DCONS_NPORT];
#define DCONS_MAGIC 0x64636f6e /* "dcon" */
V u_int32_t magic;
#define DCONS_GEN_SHIFT (24)
#define DCONS_GEN_MASK (0xff)
#define DCONS_POS_MASK ((1<< DCONS_GEN_SHIFT) - 1)
V u_int32_t optr[DCONS_NPORT];
V u_int32_t iptr[DCONS_NPORT];
V char buf[0];
};
#define DCONS_CSR_VAL_VER 0x64636f /* "dco" */
#define DCONS_CSR_KEY_HI 0x3a
#define DCONS_CSR_KEY_LO 0x3b
#define DCONS_HEADER_SIZE sizeof(struct dcons_buf)
#define DCONS_MAKE_PTR(x) htonl(((x)->gen << DCONS_GEN_SHIFT) | (x)->pos)
#define DCONS_NEXT_GEN(x) (((x) + 1) & DCONS_GEN_MASK)
struct dcons_ch {
u_int32_t size;
u_int32_t gen;
u_int32_t pos;
#ifdef _KERNEL
V u_int32_t *ptr;
V char *buf;
#else
off_t buf;
#endif
};
#define KEY_CTRLB 2 /* ^B */
#define KEY_CR 13 /* CR '\r' */
#define KEY_TILDE 126 /* ~ */
#define STATE0 0
#define STATE1 1
#define STATE2 2
#ifdef _KERNEL
extern struct dcons_buf *dcons_buf;
extern size_t dcons_bufsize;
extern bus_dma_tag_t dcons_dma_tag;
extern bus_dmamap_t dcons_dma_map;
#endif

224
sys/dev/dcons/dcons_crom.c Normal file
View File

@ -0,0 +1,224 @@
/*
* 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: dcons_crom.c,v 1.8 2003/10/23 15:47:21 simokawa Exp $
* $FreeBSD$
*/
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/types.h>
#include <sys/conf.h>
#include <sys/malloc.h>
#include <sys/bus.h>
#include <machine/bus.h>
#include <dev/firewire/firewire.h>
#include <dev/firewire/firewirereg.h>
#include <dev/firewire/iec13213.h>
#include <dev/dcons/dcons.h>
static bus_addr_t dcons_paddr;
#ifndef CSRVAL_VENDOR_PRIVATE
#define NEED_NEW_DRIVER
#endif
#define ADDR_HI(x) (((x) >> 24) & 0xffffff)
#define ADDR_LO(x) ((x) & 0xffffff)
struct dcons_crom_softc {
struct firewire_dev_comm fd;
struct crom_chunk unit;
struct crom_chunk spec;
struct crom_chunk ver;
bus_dma_tag_t dma_tag;
bus_dmamap_t dma_map;
bus_addr_t bus_addr;
};
static void
dcons_crom_identify(driver_t *driver, device_t parent)
{
BUS_ADD_CHILD(parent, 0, "dcons_crom", device_get_unit(parent));
}
static int
dcons_crom_probe(device_t dev)
{
device_t pa;
pa = device_get_parent(dev);
if(device_get_unit(dev) != device_get_unit(pa)){
return(ENXIO);
}
device_set_desc(dev, "dcons configuration ROM");
return (0);
}
#ifndef NEED_NEW_DRIVER
static void
dcons_crom_post_busreset(void *arg)
{
struct dcons_crom_softc *sc;
struct crom_src *src;
struct crom_chunk *root;
sc = (struct dcons_crom_softc *) arg;
src = sc->fd.fc->crom_src;
root = sc->fd.fc->crom_root;
bzero(&sc->unit, sizeof(struct crom_chunk));
crom_add_chunk(src, root, &sc->unit, CROM_UDIR);
crom_add_entry(&sc->unit, CSRKEY_SPEC, CSRVAL_VENDOR_PRIVATE);
crom_add_simple_text(src, &sc->unit, &sc->spec, "FreeBSD");
crom_add_entry(&sc->unit, CSRKEY_VER, DCONS_CSR_VAL_VER);
crom_add_simple_text(src, &sc->unit, &sc->ver, "dcons");
crom_add_entry(&sc->unit, DCONS_CSR_KEY_HI, ADDR_HI(dcons_paddr));
crom_add_entry(&sc->unit, DCONS_CSR_KEY_LO, ADDR_LO(dcons_paddr));
}
#endif
static void
dmamap_cb(void *arg, bus_dma_segment_t *segments, int seg, int error)
{
bus_addr_t *ptr;
if (error)
printf("dcons_dmamap_cb: error=%d\n", error);
ptr = (bus_addr_t *) arg;
*ptr = segments[0].ds_addr;
}
static int
dcons_crom_attach(device_t dev)
{
#ifdef NEED_NEW_DRIVER
printf("dcons_crom: you need newer firewire driver\n");
return (-1);
#else
struct dcons_crom_softc *sc;
sc = (struct dcons_crom_softc *) device_get_softc(dev);
sc->fd.fc = device_get_ivars(dev);
sc->fd.dev = dev;
sc->fd.post_explore = NULL;
sc->fd.post_busreset = (void *) dcons_crom_post_busreset;
/* map dcons buffer */
bus_dma_tag_create(
/*parent*/ sc->fd.fc->dmat,
/*alignment*/ sizeof(u_int32_t),
/*boundary*/ 0,
/*lowaddr*/ BUS_SPACE_MAXADDR,
/*highaddr*/ BUS_SPACE_MAXADDR,
/*filter*/NULL, /*filterarg*/NULL,
/*maxsize*/ dcons_bufsize,
/*nsegments*/ 1,
/*maxsegsz*/ BUS_SPACE_MAXSIZE_32BIT,
/*flags*/ BUS_DMA_ALLOCNOW,
#if __FreeBSD_version >= 501102
/*lockfunc*/busdma_lock_mutex,
/*lockarg*/&Giant,
#endif
&sc->dma_tag);
bus_dmamap_create(sc->dma_tag, 0, &sc->dma_map);
bus_dmamap_load(sc->dma_tag, sc->dma_map,
(void *)dcons_buf, dcons_bufsize,
dmamap_cb, &sc->bus_addr, 0);
bus_dmamap_sync(sc->dma_tag, sc->dma_map, BUS_DMASYNC_PREWRITE);
device_printf(sc->fd.dev,
#if __FreeBSD_version < 500000
"bus_addr 0x%x\n", sc->bus_addr);
#else
"bus_addr 0x%jx\n", (uintmax_t)sc->bus_addr);
#endif
if (dcons_paddr != 0) {
/* XXX */
device_printf(sc->fd.dev, "dcons_paddr is already set\n");
return (0);
}
dcons_dma_tag = sc->dma_tag;
dcons_dma_map = sc->dma_map;
dcons_paddr = sc->bus_addr;
return (0);
#endif
}
static int
dcons_crom_detach(device_t dev)
{
struct dcons_crom_softc *sc;
sc = (struct dcons_crom_softc *) device_get_softc(dev);
sc->fd.post_busreset = NULL;
/* XXX */
if (dcons_dma_tag == sc->dma_tag)
dcons_dma_tag = NULL;
bus_dmamap_unload(sc->dma_tag, sc->dma_map);
bus_dmamap_destroy(sc->dma_tag, sc->dma_map);
bus_dma_tag_destroy(sc->dma_tag);
return 0;
}
static devclass_t dcons_crom_devclass;
static device_method_t dcons_crom_methods[] = {
/* device interface */
DEVMETHOD(device_identify, dcons_crom_identify),
DEVMETHOD(device_probe, dcons_crom_probe),
DEVMETHOD(device_attach, dcons_crom_attach),
DEVMETHOD(device_detach, dcons_crom_detach),
{ 0, 0 }
};
static driver_t dcons_crom_driver = {
"dcons_crom",
dcons_crom_methods,
sizeof(struct dcons_crom_softc),
};
DRIVER_MODULE(dcons_crom, firewire, dcons_crom_driver,
dcons_crom_devclass, 0, 0);
MODULE_VERSION(dcons_crom, 1);
MODULE_DEPEND(dcons_crom, dcons,
DCONS_VERSION, DCONS_VERSION, DCONS_VERSION);
MODULE_DEPEND(dcons_crom, firewire, 1, 1, 1);

View File

@ -32,6 +32,8 @@ SUBDIR= accf_data \
${_cryptodev} \
cue \
dc \
dcons \
dcons_crom \
de \
digi \
dummynet \

View File

@ -0,0 +1,18 @@
# $Id: Makefile,v 1.6 2003/10/24 15:41:26 simokawa Exp $
# $FreeBSD$
.PATH: ${.CURDIR}/../../dev/dcons
KMOD = dcons
SRCS = dcons.c dcons.h \
opt_dcons.h opt_ddb.h opt_comconsole.h
opt_ddb.h:
echo "#define DDB 1" > $@
opt_comconsole.h:
echo "#define ALT_BREAK_TO_DEBUGGER 1" > $@
CFLAGS+= -I${.CURDIR}/../..
.include <bsd.kmod.mk>

View File

@ -0,0 +1,14 @@
# $Id: Makefile,v 1.6 2003/10/24 15:43:24 simokawa Exp $
# $FreeBSD$
.PATH: ${.CURDIR}/../../dev/dcons
KMOD = dcons_crom
SRCS = dcons_crom.c dcons.h \
bus_if.h device_if.h
#KMODDEPS = firewire dcons
CFLAGS+= -I${.CURDIR}/../..
.include <bsd.kmod.mk>

View File

@ -25,6 +25,7 @@ SUBDIR= IPXrouted \
crunch \
ctm \
daemon \
dconschat \
devinfo \
digictl \
diskinfo \

View File

@ -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>

View File

@ -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

View File

@ -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);
}