3 * Hidetoshi Shimokawa. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
16 * This product includes software developed by Hidetoshi Shimokawa.
18 * 4. Neither the name of the author nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * $Id: dconschat.c,v 1.76 2003/10/23 06:21:13 simokawa Exp $
38 #include <sys/param.h>
39 #include <sys/types.h>
46 #include <dev/dcons/dcons.h>
48 #include <sys/socket.h>
49 #include <netinet/in.h>
53 #include <sys/eui64.h>
54 #include <sys/event.h>
56 #include <arpa/telnet.h>
58 #include <sys/ioccom.h>
59 #include <dev/firewire/firewire.h>
60 #include <dev/firewire/iec13213.h>
65 #include <sys/errno.h>
67 #define DCONS_POLL_HZ 100
68 #define DCONS_POLL_OFFLINE 2 /* sec */
72 #ifdef CSRVAL_VENDOR_PRIVATE
80 int poll_hz = DCONS_POLL_HZ;
82 #define IS_CONSOLE(p) ((p)->port == 0)
83 #define IS_GDB(p) ((p)->port == 1)
85 static struct dcons_state {
90 #define F_READY (1 << 1)
91 #define F_RD_ONLY (1 << 2)
92 #define F_ALT_BREAK (1 << 3)
93 #define F_TELNET (1 << 4)
94 #define F_USE_CROM (1 << 5)
95 #define F_ONE_SHOT (1 << 6)
96 #define F_REPLAY (1 << 7)
112 struct addrinfo *res;
116 struct timespec zero;
117 struct termios tsave;
121 dread(struct dcons_state *dc, void *buf, size_t n, off_t offset)
125 return (pread(dc->fd, buf, n, offset));
127 return (kvm_read(dc->kd, offset, buf, n));
133 dwrite(struct dcons_state *dc, void *buf, size_t n, off_t offset)
135 if ((dc->flags & F_RD_ONLY) != 0)
140 return (pwrite(dc->fd, buf, n, offset));
142 return (kvm_write(dc->kd, offset, buf, n));
148 dconschat_cleanup(int sig)
150 struct dcons_state *dc;
154 tcsetattr(STDIN_FILENO, TCSADRAIN, &dc->tsave);
157 printf("\n[dconschat exiting with signal %d ...]\n", sig);
159 printf("\n[dconschat exiting...]\n");
165 dconschat_get_crom(struct dcons_state *dc)
169 u_int32_t buf, hi = 0, lo = 0;
172 reg = (struct csrreg *)&buf;
174 addr = (addr << 32) | 0xf0000400;
175 for (i = 20; i < 0x400; i += 4) {
176 if (dread(dc, &buf, 4, addr + i) < 0) {
178 warn("crom read faild");
183 printf("%d %02x %06x\n", state, reg->key, reg->val);
186 if (reg->key == CSRKEY_SPEC &&
187 reg->val == CSRVAL_VENDOR_PRIVATE)
191 if (reg->key == CSRKEY_VER &&
192 reg->val == DCONS_CSR_VAL_VER)
196 if (reg->key == DCONS_CSR_KEY_HI)
198 else if (reg->key == DCONS_CSR_KEY_LO) {
209 printf("addr: %06x %06x\n", hi, lo);
210 dc->paddr = ((off_t)hi << 24) | lo;
216 dconschat_ready(struct dcons_state *dc, int ready, char *reason)
218 static char oldreason[64] = "";
221 old = (dc->flags & F_READY) ? 1 : 0;
224 dc->flags |= F_READY;
226 printf("[dcons connected]\r\n");
229 dc->flags &= ~F_READY;
230 if (strncmp(oldreason, reason, sizeof(oldreason)) != 0) {
231 printf("[dcons disconnected (%s)]\r\n", reason);
232 strlcpy(oldreason, reason, sizeof(oldreason));
238 dconschat_fetch_header(struct dcons_state *dc)
241 struct dcons_buf dbuf;
245 if (dc->paddr == 0 && (dc->flags & F_USE_CROM) != 0) {
246 if (dconschat_get_crom(dc)) {
247 dconschat_ready(dc, 0, "get crom failed");
253 if (dread(dc, &dbuf, DCONS_HEADER_SIZE, dc->paddr) < 0) {
254 dconschat_ready(dc, 0, "read header failed");
257 if (dbuf.magic != htonl(DCONS_MAGIC)) {
258 if ((dc->flags & F_USE_CROM) !=0)
260 snprintf(ebuf, sizeof(ebuf), "wrong magic 0x%08x", dbuf.magic);
261 dconschat_ready(dc, 0, ebuf);
264 if (ntohl(dbuf.version) != DCONS_VERSION) {
265 snprintf(ebuf, sizeof(ebuf),
266 #if __FreeBSD_version < 500000
267 "wrong version %ld,%d",
269 "wrong version %d,%d",
271 ntohl(dbuf.version), DCONS_VERSION);
273 dconschat_ready(dc, 0, ebuf);
277 for (j = 0; j < DCONS_NPORT; j++) {
278 struct dcons_ch *o, *i;
283 newbuf = dc->paddr + ntohl(dbuf.ooffset[j]);
284 o->size = ntohl(dbuf.osize[j]);
286 if (newbuf != o->buf) {
287 /* buffer address has changes */
289 o->gen = ntohl(dbuf.optr[j]) >> DCONS_GEN_SHIFT;
290 o->pos = ntohl(dbuf.optr[j]) & DCONS_POS_MASK;
295 i->size = ntohl(dbuf.isize[j]);
296 i->gen = ntohl(dbuf.iptr[j]) >> DCONS_GEN_SHIFT;
297 i->pos = ntohl(dbuf.iptr[j]) & DCONS_POS_MASK;
298 i->buf = dc->paddr + ntohl(dbuf.ioffset[j]);
301 printf("port %d size offset gen pos\n", j);
302 #if __FreeBSD_version < 500000
303 printf("output: %5d %6ld %5d %5d\n"
304 "input : %5d %6ld %5d %5d\n",
306 printf("output: %5d %6d %5d %5d\n"
307 "input : %5d %6d %5d %5d\n",
309 o->size, ntohl(dbuf.ooffset[j]), o->gen, o->pos,
310 i->size, ntohl(dbuf.ioffset[j]), i->gen, i->pos);
313 if (IS_CONSOLE(&dc->port[j]) && new &&
314 (dc->flags & F_REPLAY) !=0) {
321 dconschat_ready(dc, 1, NULL);
326 dconschat_get_ptr (struct dcons_state *dc) {
328 u_int32_t ptr[DCONS_NPORT*2+1];
329 static int retry = RETRY;
333 dlen = dread(dc, &ptr, sizeof(ptr),
334 dc->paddr + __offsetof(struct dcons_buf, magic));
337 if (errno == ETIMEDOUT)
340 dconschat_ready(dc, 0, "get ptr failed");
343 if (ptr[0] != htonl(DCONS_MAGIC)) {
344 if ((dc->flags & F_USE_CROM) !=0)
346 snprintf(ebuf, sizeof(ebuf), "wrong magic 0x%08x", ptr[0]);
347 dconschat_ready(dc, 0, ebuf);
351 for (i = 0; i < DCONS_NPORT; i ++) {
352 dc->port[i].optr = ntohl(ptr[i + 1]);
353 dc->port[i].iptr = ntohl(ptr[DCONS_NPORT + i + 1]);
358 #define MAX_XFER 2048
360 dconschat_read_dcons(struct dcons_state *dc, int port, char *buf, int len)
363 u_int32_t ptr, pos, gen, next_gen;
364 int rlen, dlen, lost;
367 ch = &dc->port[port].o;
368 ptr = dc->port[port].optr;
369 gen = ptr >> DCONS_GEN_SHIFT;
370 pos = ptr & DCONS_POS_MASK;
371 if (gen == ch->gen && pos == ch->pos)
374 next_gen = DCONS_NEXT_GEN(ch->gen);
375 /* XXX sanity check */
376 if (gen == ch->gen) {
379 lost = ch->size * DCONS_GEN_MASK - ch->pos;
381 } else if (gen == next_gen) {
384 lost = pos - ch->pos;
387 lost = gen - ch->gen;
389 lost += DCONS_GEN_MASK;
391 printf("[genskip %d]", lost);
392 lost = lost * ch->size - ch->pos;
396 /* generation skipped !! */
399 printf("[lost %d]", lost);
402 rlen = pos - ch->pos;
404 rlen = ch->size - ch->pos;
413 printf("[%d]", rlen); fflush(stdout);
417 dlen = dread(dc, buf, rlen, ch->buf + ch->pos);
419 if (errno == ETIMEDOUT)
422 dconschat_ready(dc, 0, "read buffer failed");
426 warnx("dlen(%d) != rlen(%d)\n", dlen, rlen);
428 if (ch->pos >= ch->size) {
432 printf("read_dcons: gen=%d", ch->gen);
438 dconschat_write_dcons(struct dcons_state *dc, int port, char *buf, int blen)
445 ch = &dc->port[port].i;
446 ptr = dc->port[port].iptr;
448 /* the others may advance the pointer sync with it */
449 ch->gen = ptr >> DCONS_GEN_SHIFT;
450 ch->pos = ptr & DCONS_POS_MASK;
453 wlen = MIN(blen, ch->size - ch->pos);
454 wlen = MIN(wlen, MAX_XFER);
455 len = dwrite(dc, buf, wlen, ch->buf + ch->pos);
457 if (errno == ETIMEDOUT)
459 continue; /* try again */
460 dconschat_ready(dc, 0, "write buffer failed");
466 if (ch->pos >= ch->size) {
467 ch->gen = DCONS_NEXT_GEN(ch->gen);
470 printf("write_dcons: gen=%d", ch->gen);
475 ptr = DCONS_MAKE_PTR(ch);
476 dc->port[port].iptr = ptr;
479 printf("(iptr: 0x%x)", ptr);
481 len = dwrite(dc, &ptr, sizeof(u_int32_t),
482 dc->paddr + __offsetof(struct dcons_buf, iptr[port]));
484 if (errno == ETIMEDOUT)
487 dconschat_ready(dc, 0, "write ptr failed");
494 dconschat_write_socket(int fd, char *buf, int len)
505 dconschat_init_socket(struct dcons_state *dc, int port, char *host, int sport)
507 struct addrinfo hints, *res;
511 struct dcons_port *p;
515 p->infd = p->outfd = -1;
522 /* Use stdin and stdout */
523 p->infd = STDIN_FILENO;
524 p->outfd = STDOUT_FILENO;
527 tcgetattr(STDIN_FILENO, &dc->tsave) == 0) {
532 tcsetattr(STDIN_FILENO, TCSADRAIN, &traw);
535 EV_SET(&kev, p->infd, EVFILT_READ, EV_ADD, NOTE_LOWAT, 1,
537 kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
541 memset(&hints, 0, sizeof(hints));
542 hints.ai_flags = AI_PASSIVE;
543 #if 1 /* gdb can talk v4 only */
544 hints.ai_family = PF_INET;
546 hints.ai_family = PF_UNSPEC;
548 hints.ai_socktype = SOCK_STREAM;
549 hints.ai_protocol = 0;
552 printf("%s:%d for port %d\n",
553 host == NULL ? "*" : host, sport, port);
554 snprintf(service, sizeof(service), "%d", sport);
555 error = getaddrinfo(host, service, &hints, &res);
557 errx(1, "tcp/%s: %s\n", service, gai_strerror(error));
559 p->s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
562 setsockopt(p->s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
564 if (bind(p->s, p->res->ai_addr, p->res->ai_addrlen) < 0) {
567 if (listen(p->s, 1) < 0)
569 EV_SET(&kev, p->s, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, (void *)p);
570 error = kevent(dc->kq, &kev, 1, NULL, 0, &dc->to);
577 dconschat_accept_socket(struct dcons_state *dc, struct dcons_port *p)
583 /* accept connection */
584 addrlen = p->res->ai_addrlen;
585 ns = accept(p->s, p->res->ai_addr, &addrlen);
589 printf("port%d accepted\n", p->port);
591 flags = fcntl(ns, F_GETFL, 0);
593 fcntl(ns, F_SETFL, flags);
595 if (IS_CONSOLE(p) && (dc->flags & F_TELNET) != 0) {
596 char sga[] = {IAC, WILL, TELOPT_SGA};
597 char linemode[] = {IAC, DONT, TELOPT_LINEMODE};
598 char echo[] = {IAC, WILL, TELOPT_ECHO};
599 char bin[] = {IAC, DO, TELOPT_BINARY};
601 write(ns, sga, sizeof(sga));
602 write(ns, linemode, sizeof(linemode));
603 write(ns, echo, sizeof(echo));
604 write(ns, bin, sizeof(bin));
609 p->infd = p->outfd = ns;
610 EV_SET(&kev, ns, EVFILT_READ, EV_ADD, NOTE_LOWAT, 1, (void *)p);
611 kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
616 dconschat_read_filter(struct dcons_state *dc, struct dcons_port *p,
617 u_char *sp, int slen, u_char *dp, int *dlen)
619 static u_char abreak[3] = {13 /* CR */, 126 /* ~ */, 2 /* ^B */};
623 if ((dc->flags & F_TELNET) != 0) {
624 /* XXX Telnet workarounds */
625 if (p->skip_read -- > 0) {
640 printf("(0 stripped)");
646 switch (dc->escape_state) {
648 if (*sp == KEY_TILDE)
649 dc->escape_state = STATE2;
651 dc->escape_state = STATE0;
654 dc->escape_state = STATE0;
656 dconschat_cleanup(0);
659 dc->escape_state = STATE1;
660 } else if (IS_GDB(p)) {
661 /* GDB: ^C -> CR+~+^B */
662 if (*sp == 0x3 && (dc->flags & F_ALT_BREAK) != 0) {
663 bcopy(abreak, dp, 3);
667 /* discard rest of the packet */
681 dconschat_read_socket(struct dcons_state *dc, struct dcons_port *p)
685 char rbuf[MAX_XFER], wbuf[MAX_XFER+2];
687 if ((len = read(p->infd, rbuf, sizeof(rbuf))) > 0) {
689 dconschat_read_filter(dc, p, rbuf, len, wbuf, &wlen);
690 /* XXX discard if not ready*/
691 if (wlen > 0 && (dc->flags & F_READY) != 0) {
692 dconschat_write_dcons(dc, p->port, wbuf, wlen);
695 printf("(%s)\n", wbuf);
698 printf("(%d)", wlen);
705 warnx("port%d: closed", p->port);
707 warn("port%d: read", p->port);
709 EV_SET(&kev, p->infd, EVFILT_READ,
710 EV_DELETE, 0, 0, NULL);
711 kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
714 /* XXX exit for pipe case XXX */
715 EV_SET(&kev, p->s, EVFILT_READ,
716 EV_ADD | EV_ONESHOT, 0, 0, (void *) p);
717 kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
718 p->infd = p->outfd = -1;
724 dconschat_proc_socket(struct dcons_state *dc)
726 struct kevent elist[NEVENT], *e;
728 struct dcons_port *p;
730 n = kevent(dc->kq, NULL, 0, elist, NEVENT, &dc->to);
731 for (i = 0; i < n; i ++) {
733 p = (struct dcons_port *)e->udata;
734 if (e->ident == p->s) {
735 dconschat_accept_socket(dc, p);
737 dconschat_read_socket(dc, p);
744 dconschat_proc_dcons(struct dcons_state *dc)
748 struct dcons_port *p;
750 err = dconschat_get_ptr(dc);
752 /* XXX we should stop write operation too. */
755 for (port = 0; port < DCONS_NPORT; port ++) {
759 while ((len = dconschat_read_dcons(dc, port, buf,
761 dconschat_write_socket(p->outfd, buf, len);
762 dconschat_get_ptr(dc);
764 if ((dc->flags & F_ONE_SHOT) != 0 && len <= 0)
765 dconschat_cleanup(0);
771 dconschat_start_session(struct dcons_state *dc)
776 if ((dc->flags & F_READY) == 0 &&
777 (++counter % (poll_hz * DCONS_POLL_OFFLINE)) == 0)
778 dconschat_fetch_header(dc);
779 if ((dc->flags & F_READY) != 0)
780 dconschat_proc_dcons(dc);
781 dconschat_proc_socket(dc);
790 "usage: dconschat [-brvwRT1] [-h hz] [-C port] [-G port]\n"
791 "\t\t\t[-M core] [-N system]\n"
792 "\t\t\t[-u unit] [-a address] [-t target_eui64]\n"
793 "\t-b translate ctrl-C to CR+~+ctrl-B on gdb port\n"
795 "\t-w listen on wildcard address rather than localhost\n"
796 "\t-r replay old buffer on connection\n"
798 "\t-T enable Telnet protocol workaround on console port\n"
799 "\t-1 one shot: read buffer and exit\n"
800 "\t-h polling rate\n"
801 "\t-C port number for console port\n"
802 "\t-G port number for gdb port\n"
807 "\t-u specify unit number of the bus\n"
808 "\t-t EUI64 of target host (must be specified)\n"
809 "\t-a physical address of dcons buffer on target host\n"
814 main(int argc, char **argv)
816 struct dcons_state *dc;
819 char devname[256], *core = NULL, *system = NULL;
821 int unit=0, wildcard=0;
822 int port[DCONS_NPORT];
824 bzero(&sc, sizeof(sc));
826 dc->flags |= USE_CROM ? F_USE_CROM : 0;
829 port[0] = 0; /* stdin/out for console */
830 port[1] = -1; /* disable gdb port */
832 while ((ch = getopt(argc, argv, "a:bh:rt:u:vwC:G:M:N:RT1")) != -1) {
835 dc->paddr = strtoull(optarg, NULL, 0);
836 dc->flags &= ~F_USE_CROM;
839 dc->flags |= F_ALT_BREAK;
842 poll_hz = strtoul(optarg, NULL, 0);
844 poll_hz = DCONS_POLL_HZ;
847 dc->flags |= F_REPLAY;
850 if (eui64_hostton(optarg, &target) != 0 &&
851 eui64_aton(optarg, &target) != 0)
852 errx(1, "invalid target: %s", optarg);
853 eui.hi = ntohl(*(u_int32_t*)&(target.octet[0]));
854 eui.lo = ntohl(*(u_int32_t*)&(target.octet[4]));
858 unit = strtol(optarg, NULL, 0);
867 port[0] = strtol(optarg, NULL, 0);
870 port[1] = strtol(optarg, NULL, 0);
879 dc->flags |= F_RD_ONLY;
882 dc->flags |= F_TELNET;
885 dc->flags |= F_ONE_SHOT | F_REPLAY;
891 if (dc->paddr == 0 && (dc->flags & F_USE_CROM) == 0) {
892 warnx("no address specified");
896 if (port[0] < 0 && port[1] < 0) {
897 warnx("no port specified");
901 /* set signal handler */
902 signal(SIGHUP, dconschat_cleanup);
903 signal(SIGINT, dconschat_cleanup);
904 signal(SIGPIPE, dconschat_cleanup);
905 signal(SIGTERM, dconschat_cleanup);
911 for (i = 0; i < MAXDEV; i ++) {
912 snprintf(devname, sizeof(devname),
913 "/dev/fwmem%d.%d", unit, i);
914 dc->fd = open(devname, O_RDWR);
920 error = ioctl(dc->fd, FW_SDEUI64, &eui);
926 struct nlist nl[] = {{"dcons_buf"}, {""}};
929 dc->kd = kvm_open(system, core, NULL,
930 (dc->flags & F_RD_ONLY) ? O_RDONLY : O_RDWR, "dconschat");
934 if (kvm_nlist(dc->kd, nl) < 0)
935 errx(1, "kvm_nlist: %s", kvm_geterr(dc->kd));
937 if (kvm_read(dc->kd, nl[0].n_value, &dcons_buf,
939 errx(1, "kvm_read: %s", kvm_geterr(dc->kd));
940 dc->paddr = (uintptr_t)dcons_buf;
942 printf("dcons_buf: 0x%x\n", (uint)dc->paddr);
946 dconschat_fetch_header(dc);
955 dc->to.tv_nsec = 1000 * 1000 * 1000 / poll_hz;
958 dc->zero.tv_nsec = 0;
959 for (i = 0; i < DCONS_NPORT; i++)
960 dconschat_init_socket(dc, i,
961 wildcard ? NULL : "localhost", port[i]);
963 dconschat_start_session(dc);
965 for (i = 0; i < DCONS_NPORT; i++) {
966 freeaddrinfo(dc->port[i].res);