1 /* 2 * Copyright (C) 2003 3 * Hidetoshi Shimokawa. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 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: 15 * 16 * This product includes software developed by Hidetoshi Shimokawa. 17 * 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. 21 * 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 32 * SUCH DAMAGE. 33 * 34 * $Id: dconschat.c,v 1.76 2003/10/23 06:21:13 simokawa Exp $ 35 * $FreeBSD: src/usr.sbin/dconschat/dconschat.c,v 1.6 2004/05/26 22:59:54 brooks Exp $ 36 * $DragonFly: src/usr.sbin/dconschat/dconschat.c,v 1.1 2004/09/23 06:38:29 simokawa Exp $ 37 */ 38 39 #include <sys/param.h> 40 #include <sys/types.h> 41 #include <sys/uio.h> 42 #include <unistd.h> 43 #include <fcntl.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <termios.h> 47 48 #include <sys/socket.h> 49 #include <netinet/in.h> 50 #include <netdb.h> 51 #include <err.h> 52 #include <string.h> 53 #include <sys/eui64.h> 54 #include <sys/event.h> 55 #include <sys/time.h> 56 #include <arpa/telnet.h> 57 58 #include <sys/ioccom.h> 59 #ifdef __DragonFly__ 60 #include <dev/misc/dcons/dcons.h> 61 #include <bus/firewire/firewire.h> 62 #include <bus/firewire/iec13213.h> 63 #else 64 #include <dev/dcons/dcons.h> 65 #include <dev/firewire/firewire.h> 66 #include <dev/firewire/iec13213.h> 67 #endif 68 69 #include <kvm.h> 70 #include <nlist.h> 71 72 #include <sys/errno.h> 73 74 #define DCONS_POLL_HZ 100 75 #define DCONS_POLL_OFFLINE 2 /* sec */ 76 77 #define RETRY 3 78 79 #ifdef CSRVAL_VENDOR_PRIVATE 80 #define USE_CROM 1 81 #else 82 #define USE_CROM 0 83 #endif 84 85 int verbose = 0; 86 int tc_set = 0; 87 int poll_hz = DCONS_POLL_HZ; 88 89 #define IS_CONSOLE(p) ((p)->port == 0) 90 #define IS_GDB(p) ((p)->port == 1) 91 92 static struct dcons_state { 93 int fd; 94 kvm_t *kd; 95 int kq; 96 off_t paddr; 97 #define F_READY (1 << 1) 98 #define F_RD_ONLY (1 << 2) 99 #define F_ALT_BREAK (1 << 3) 100 #define F_TELNET (1 << 4) 101 #define F_USE_CROM (1 << 5) 102 #define F_ONE_SHOT (1 << 6) 103 #define F_REPLAY (1 << 7) 104 int flags; 105 enum { 106 TYPE_KVM, 107 TYPE_FW 108 } type; 109 int escape_state; 110 struct dcons_port { 111 int port; 112 struct dcons_ch o; 113 struct dcons_ch i; 114 u_int32_t optr; 115 u_int32_t iptr; 116 int s; 117 int infd; 118 int outfd; 119 struct addrinfo *res; 120 int skip_read; 121 } port[DCONS_NPORT]; 122 struct timespec to; 123 struct timespec zero; 124 struct termios tsave; 125 } sc; 126 127 static int 128 dread(struct dcons_state *dc, void *buf, size_t n, off_t offset) 129 { 130 switch (dc->type) { 131 case TYPE_FW: 132 return (pread(dc->fd, buf, n, offset)); 133 case TYPE_KVM: 134 return (kvm_read(dc->kd, offset, buf, n)); 135 } 136 return (-1); 137 } 138 139 static int 140 dwrite(struct dcons_state *dc, void *buf, size_t n, off_t offset) 141 { 142 if ((dc->flags & F_RD_ONLY) != 0) 143 return (n); 144 145 switch (dc->type) { 146 case TYPE_FW: 147 return (pwrite(dc->fd, buf, n, offset)); 148 case TYPE_KVM: 149 return (kvm_write(dc->kd, offset, buf, n)); 150 } 151 return (-1); 152 } 153 154 static void 155 dconschat_cleanup(int sig) 156 { 157 struct dcons_state *dc; 158 159 dc = ≻ 160 if (tc_set != 0) 161 tcsetattr(STDIN_FILENO, TCSADRAIN, &dc->tsave); 162 163 if (sig > 0) 164 printf("\n[dconschat exiting with signal %d ...]\n", sig); 165 else 166 printf("\n[dconschat exiting...]\n"); 167 exit(0); 168 } 169 170 #if USE_CROM 171 static int 172 dconschat_get_crom(struct dcons_state *dc) 173 { 174 off_t addr; 175 int i, state = 0; 176 u_int32_t buf, hi = 0, lo = 0; 177 struct csrreg *reg; 178 179 reg = (struct csrreg *)&buf; 180 addr = 0xffff; 181 addr = (addr << 32) | 0xf0000400; 182 for (i = 20; i < 0x400; i += 4) { 183 if (dread(dc, &buf, 4, addr + i) < 0) { 184 if (verbose) 185 warn("crom read faild"); 186 return (-1); 187 } 188 buf = ntohl(buf); 189 if (verbose) 190 printf("%d %02x %06x\n", state, reg->key, reg->val); 191 switch (state) { 192 case 0: 193 if (reg->key == CSRKEY_SPEC && 194 reg->val == CSRVAL_VENDOR_PRIVATE) 195 state = 1; 196 break; 197 case 1: 198 if (reg->key == CSRKEY_VER && 199 reg->val == DCONS_CSR_VAL_VER) 200 state = 2; 201 break; 202 case 2: 203 if (reg->key == DCONS_CSR_KEY_HI) 204 hi = reg->val; 205 else if (reg->key == DCONS_CSR_KEY_LO) { 206 lo = reg->val; 207 goto out; 208 } 209 break; 210 } 211 } 212 /* not found */ 213 return (-1); 214 out: 215 if (verbose) 216 printf("addr: %06x %06x\n", hi, lo); 217 dc->paddr = ((off_t)hi << 24) | lo; 218 return (0); 219 } 220 #endif 221 222 static void 223 dconschat_ready(struct dcons_state *dc, int ready, char *reason) 224 { 225 static char oldreason[64] = ""; 226 int old; 227 228 old = (dc->flags & F_READY) ? 1 : 0; 229 230 if (ready) { 231 dc->flags |= F_READY; 232 if (ready != old) 233 printf("[dcons connected]\r\n"); 234 oldreason[0] = 0; 235 } else { 236 dc->flags &= ~F_READY; 237 if (strncmp(oldreason, reason, sizeof(oldreason)) != 0) { 238 printf("[dcons disconnected (%s)]\r\n", reason); 239 strlcpy(oldreason, reason, sizeof(oldreason)); 240 } 241 } 242 } 243 244 static int 245 dconschat_fetch_header(struct dcons_state *dc) 246 { 247 char ebuf[64]; 248 struct dcons_buf dbuf; 249 int j; 250 251 #if USE_CROM 252 if (dc->paddr == 0 && (dc->flags & F_USE_CROM) != 0) { 253 if (dconschat_get_crom(dc)) { 254 dconschat_ready(dc, 0, "get crom failed"); 255 return (-1); 256 } 257 } 258 #endif 259 260 if (dread(dc, &dbuf, DCONS_HEADER_SIZE, dc->paddr) < 0) { 261 dconschat_ready(dc, 0, "read header failed"); 262 return (-1); 263 } 264 if (dbuf.magic != htonl(DCONS_MAGIC)) { 265 if ((dc->flags & F_USE_CROM) !=0) 266 dc->paddr = 0; 267 snprintf(ebuf, sizeof(ebuf), "wrong magic 0x%08x", dbuf.magic); 268 dconschat_ready(dc, 0, ebuf); 269 return (-1); 270 } 271 if (ntohl(dbuf.version) != DCONS_VERSION) { 272 snprintf(ebuf, sizeof(ebuf), 273 #if __FreeBSD_version < 500000 274 "wrong version %ld,%d", 275 #else 276 "wrong version %d,%d", 277 #endif 278 ntohl(dbuf.version), DCONS_VERSION); 279 /* XXX exit? */ 280 dconschat_ready(dc, 0, ebuf); 281 return (-1); 282 } 283 284 for (j = 0; j < DCONS_NPORT; j++) { 285 struct dcons_ch *o, *i; 286 off_t newbuf; 287 int new = 0; 288 289 o = &dc->port[j].o; 290 newbuf = dc->paddr + ntohl(dbuf.ooffset[j]); 291 o->size = ntohl(dbuf.osize[j]); 292 293 if (newbuf != o->buf) { 294 /* buffer address has changes */ 295 new = 1; 296 o->gen = ntohl(dbuf.optr[j]) >> DCONS_GEN_SHIFT; 297 o->pos = ntohl(dbuf.optr[j]) & DCONS_POS_MASK; 298 o->buf = newbuf; 299 } 300 301 i = &dc->port[j].i; 302 i->size = ntohl(dbuf.isize[j]); 303 i->gen = ntohl(dbuf.iptr[j]) >> DCONS_GEN_SHIFT; 304 i->pos = ntohl(dbuf.iptr[j]) & DCONS_POS_MASK; 305 i->buf = dc->paddr + ntohl(dbuf.ioffset[j]); 306 307 if (verbose) { 308 printf("port %d size offset gen pos\n", j); 309 #if __FreeBSD_version < 500000 310 printf("output: %5d %6ld %5d %5d\n" 311 "input : %5d %6ld %5d %5d\n", 312 #else 313 printf("output: %5d %6d %5d %5d\n" 314 "input : %5d %6d %5d %5d\n", 315 #endif 316 o->size, ntohl(dbuf.ooffset[j]), o->gen, o->pos, 317 i->size, ntohl(dbuf.ioffset[j]), i->gen, i->pos); 318 } 319 320 if (IS_CONSOLE(&dc->port[j]) && new && 321 (dc->flags & F_REPLAY) !=0) { 322 if (o->gen > 0) 323 o->gen --; 324 else 325 o->pos = 0; 326 } 327 } 328 dconschat_ready(dc, 1, NULL); 329 return(0); 330 } 331 332 static int 333 dconschat_get_ptr (struct dcons_state *dc) { 334 int dlen, i; 335 u_int32_t ptr[DCONS_NPORT*2+1]; 336 static int retry = RETRY; 337 338 again: 339 dlen = dread(dc, &ptr, sizeof(ptr), 340 dc->paddr + __offsetof(struct dcons_buf, magic)); 341 342 if (dlen < 0) { 343 if (errno == ETIMEDOUT) 344 if (retry -- > 0) 345 goto again; 346 dconschat_ready(dc, 0, "get ptr failed"); 347 return(-1); 348 } 349 if (ptr[0] != htonl(DCONS_MAGIC)) { 350 dconschat_ready(dc, 0, "wrong magic"); 351 return(-1); 352 } 353 retry = RETRY; 354 for (i = 0; i < DCONS_NPORT; i ++) { 355 dc->port[i].optr = ntohl(ptr[i + 1]); 356 dc->port[i].iptr = ntohl(ptr[DCONS_NPORT + i + 1]); 357 } 358 return(0); 359 } 360 361 #define MAX_XFER 2048 362 static int 363 dconschat_read_dcons(struct dcons_state *dc, int port, char *buf, int len) 364 { 365 struct dcons_ch *ch; 366 u_int32_t ptr, pos, gen, next_gen; 367 int rlen, dlen, lost; 368 int retry = RETRY; 369 370 ch = &dc->port[port].o; 371 ptr = dc->port[port].optr; 372 gen = ptr >> DCONS_GEN_SHIFT; 373 pos = ptr & DCONS_POS_MASK; 374 if (gen == ch->gen && pos == ch->pos) 375 return (-1); 376 377 next_gen = DCONS_NEXT_GEN(ch->gen); 378 /* XXX sanity check */ 379 if (gen == ch->gen) { 380 if (pos > ch->pos) 381 goto ok; 382 lost = ch->size * DCONS_GEN_MASK - ch->pos; 383 ch->pos = 0; 384 } else if (gen == next_gen) { 385 if (pos <= ch->pos) 386 goto ok; 387 lost = pos - ch->pos; 388 ch->pos = pos; 389 } else { 390 lost = gen - ch->gen; 391 if (lost < 0) 392 lost += DCONS_GEN_MASK; 393 if (verbose) 394 printf("[genskip %d]", lost); 395 lost = lost * ch->size - ch->pos; 396 ch->pos = 0; 397 ch->gen = gen; 398 } 399 /* generation skipped !! */ 400 /* XXX discard */ 401 if (verbose) 402 printf("[lost %d]", lost); 403 ok: 404 if (gen == ch->gen) 405 rlen = pos - ch->pos; 406 else 407 rlen = ch->size - ch->pos; 408 409 if (rlen > MAX_XFER) 410 rlen = MAX_XFER; 411 if (rlen > len) 412 rlen = len; 413 414 #if 1 415 if (verbose) 416 printf("[%d]", rlen); fflush(stdout); 417 #endif 418 419 again: 420 dlen = dread(dc, buf, rlen, ch->buf + ch->pos); 421 if (dlen < 0) { 422 if (errno == ETIMEDOUT) 423 if (retry -- > 0) 424 goto again; 425 dconschat_ready(dc, 0, "read buffer failed"); 426 return(-1); 427 } 428 if (dlen != rlen) 429 warnx("dlen(%d) != rlen(%d)\n", dlen, rlen); 430 ch->pos += dlen; 431 if (ch->pos >= ch->size) { 432 ch->gen = next_gen; 433 ch->pos = 0; 434 if (verbose) 435 printf("read_dcons: gen=%d", ch->gen); 436 } 437 return (dlen); 438 } 439 440 static int 441 dconschat_write_dcons(struct dcons_state *dc, int port, char *buf, int blen) 442 { 443 struct dcons_ch *ch; 444 u_int32_t ptr; 445 int len, wlen; 446 int retry = RETRY; 447 448 ch = &dc->port[port].i; 449 ptr = dc->port[port].iptr; 450 451 /* the others may advance the pointer sync with it */ 452 ch->gen = ptr >> DCONS_GEN_SHIFT; 453 ch->pos = ptr & DCONS_POS_MASK; 454 455 while(blen > 0) { 456 wlen = MIN(blen, ch->size - ch->pos); 457 wlen = MIN(wlen, MAX_XFER); 458 len = dwrite(dc, buf, wlen, ch->buf + ch->pos); 459 if (len < 0) { 460 if (errno == ETIMEDOUT) 461 if (retry -- > 0) 462 continue; /* try again */ 463 dconschat_ready(dc, 0, "write buffer failed"); 464 return(-1); 465 } 466 ch->pos += len; 467 buf += len; 468 blen -= len; 469 if (ch->pos >= ch->size) { 470 ch->gen = DCONS_NEXT_GEN(ch->gen); 471 ch->pos = 0; 472 if (verbose) 473 printf("write_dcons: gen=%d", ch->gen); 474 475 } 476 } 477 478 ptr = DCONS_MAKE_PTR(ch); 479 dc->port[port].iptr = ptr; 480 481 if (verbose > 2) 482 printf("(iptr: 0x%x)", ptr); 483 again: 484 len = dwrite(dc, &ptr, sizeof(u_int32_t), 485 dc->paddr + __offsetof(struct dcons_buf, iptr[port])); 486 if (len < 0) { 487 if (errno == ETIMEDOUT) 488 if (retry -- > 0) 489 goto again; 490 dconschat_ready(dc, 0, "write ptr failed"); 491 return(-1); 492 } 493 return(0); 494 } 495 496 static int 497 dconschat_write_socket(int fd, char *buf, int len) 498 { 499 write(fd, buf, len); 500 if (verbose > 1) { 501 buf[len] = 0; 502 printf("[%s]", buf); 503 } 504 return (0); 505 } 506 507 static void 508 dconschat_init_socket(struct dcons_state *dc, int port, char *host, int sport) 509 { 510 struct addrinfo hints, *res; 511 int on = 1, error; 512 char service[10]; 513 struct kevent kev; 514 struct dcons_port *p; 515 516 p = &dc->port[port]; 517 p->port = port; 518 p->infd = p->outfd = -1; 519 520 if (sport < 0) 521 return; 522 523 if (sport == 0) { 524 525 /* Use stdin and stdout */ 526 p->infd = STDIN_FILENO; 527 p->outfd = STDOUT_FILENO; 528 p->s = -1; 529 if (tc_set == 0 && 530 tcgetattr(STDIN_FILENO, &dc->tsave) == 0) { 531 struct termios traw; 532 533 traw = dc->tsave; 534 cfmakeraw(&traw); 535 tcsetattr(STDIN_FILENO, TCSADRAIN, &traw); 536 tc_set = 1; 537 } 538 EV_SET(&kev, p->infd, EVFILT_READ, EV_ADD, NOTE_LOWAT, 1, 539 (void *)p); 540 kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero); 541 return; 542 } 543 544 memset(&hints, 0, sizeof(hints)); 545 hints.ai_flags = AI_PASSIVE; 546 #if 1 /* gdb can talk v4 only */ 547 hints.ai_family = PF_INET; 548 #else 549 hints.ai_family = PF_UNSPEC; 550 #endif 551 hints.ai_socktype = SOCK_STREAM; 552 hints.ai_protocol = 0; 553 554 if (verbose) 555 printf("%s:%d for port %d\n", 556 host == NULL ? "*" : host, sport, port); 557 snprintf(service, sizeof(service), "%d", sport); 558 error = getaddrinfo(host, service, &hints, &res); 559 if (error) 560 errx(1, "tcp/%s: %s\n", service, gai_strerror(error)); 561 p->res = res; 562 p->s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 563 if (p->s < 0) 564 err(1, "socket"); 565 setsockopt(p->s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); 566 567 if (bind(p->s, p->res->ai_addr, p->res->ai_addrlen) < 0) { 568 err(1, "bind"); 569 } 570 if (listen(p->s, 1) < 0) 571 err(1, "listen"); 572 EV_SET(&kev, p->s, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, (void *)p); 573 error = kevent(dc->kq, &kev, 1, NULL, 0, &dc->to); 574 if (error < 0) 575 err(1, "kevent"); 576 return; 577 } 578 579 static int 580 dconschat_accept_socket(struct dcons_state *dc, struct dcons_port *p) 581 { 582 int foo, ns, flags; 583 struct kevent kev; 584 585 /* accept connection */ 586 foo = p->res->ai_addrlen; 587 ns = accept(p->s, p->res->ai_addr, &foo); 588 if (ns < 0) 589 err(1, "accept"); 590 if (verbose) 591 printf("port%d accepted\n", p->port); 592 593 flags = fcntl(ns, F_GETFL, 0); 594 flags |= O_NDELAY; 595 fcntl(ns, F_SETFL, flags); 596 #if 1 597 if (IS_CONSOLE(p) && (dc->flags & F_TELNET) != 0) { 598 char sga[] = {IAC, WILL, TELOPT_SGA}; 599 char linemode[] = {IAC, DONT, TELOPT_LINEMODE}; 600 char echo[] = {IAC, WILL, TELOPT_ECHO}; 601 char bin[] = {IAC, DO, TELOPT_BINARY}; 602 603 write(ns, sga, sizeof(sga)); 604 write(ns, linemode, sizeof(linemode)); 605 write(ns, echo, sizeof(echo)); 606 write(ns, bin, sizeof(bin)); 607 p->skip_read = 0; 608 } 609 #endif 610 611 p->infd = p->outfd = ns; 612 EV_SET(&kev, ns, EVFILT_READ, EV_ADD, NOTE_LOWAT, 1, (void *)p); 613 kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero); 614 return(0); 615 } 616 617 static int 618 dconschat_read_filter(struct dcons_state *dc, struct dcons_port *p, 619 u_char *sp, int slen, u_char *dp, int *dlen) 620 { 621 static u_char abreak[3] = {13 /* CR */, 126 /* ~ */, 2 /* ^B */}; 622 623 while (slen > 0) { 624 if (IS_CONSOLE(p)) { 625 if ((dc->flags & F_TELNET) != 0) { 626 /* XXX Telent workarounds */ 627 if (p->skip_read -- > 0) { 628 sp ++; 629 slen --; 630 continue; 631 } 632 if (*sp == IAC) { 633 if (verbose) 634 printf("(IAC)"); 635 p->skip_read = 2; 636 sp ++; 637 slen --; 638 continue; 639 } 640 if (*sp == 0) { 641 if (verbose) 642 printf("(0 stripped)"); 643 sp ++; 644 slen --; 645 continue; 646 } 647 } 648 switch (dc->escape_state) { 649 case STATE1: 650 if (*sp == KEY_TILDE) 651 dc->escape_state = STATE2; 652 else 653 dc->escape_state = STATE0; 654 break; 655 case STATE2: 656 dc->escape_state = STATE0; 657 if (*sp == '.') 658 dconschat_cleanup(0); 659 } 660 if (*sp == KEY_CR) 661 dc->escape_state = STATE1; 662 } else if (IS_GDB(p)) { 663 /* GDB: ^C -> CR+~+^B */ 664 if (*sp == 0x3 && (dc->flags & F_ALT_BREAK) != 0) { 665 bcopy(abreak, dp, 3); 666 dp += 3; 667 sp ++; 668 *dlen += 3; 669 /* discard rest of the packet */ 670 slen = 0; 671 break; 672 } 673 } 674 *dp++ = *sp++; 675 (*dlen) ++; 676 slen --; 677 } 678 return (*dlen); 679 680 } 681 682 static int 683 dconschat_read_socket(struct dcons_state *dc, struct dcons_port *p) 684 { 685 struct kevent kev; 686 int len, wlen; 687 char rbuf[MAX_XFER], wbuf[MAX_XFER+2]; 688 689 if ((len = read(p->infd, rbuf, sizeof(rbuf))) > 0) { 690 wlen = 0; 691 dconschat_read_filter(dc, p, rbuf, len, wbuf, &wlen); 692 /* XXX discard if not ready*/ 693 if (wlen > 0 && (dc->flags & F_READY) != 0) { 694 dconschat_write_dcons(dc, p->port, wbuf, wlen); 695 if (verbose > 1) { 696 wbuf[wlen] = 0; 697 printf("(%s)\n", wbuf); 698 } 699 if (verbose) { 700 printf("(%d)", wlen); 701 fflush(stdout); 702 } 703 } 704 } else { 705 if (verbose) { 706 if (len == 0) 707 warnx("port%d: closed", p->port); 708 else 709 warn("port%d: read", p->port); 710 } 711 EV_SET(&kev, p->infd, EVFILT_READ, 712 EV_DELETE, 0, 0, NULL); 713 kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero); 714 close(p->infd); 715 close(p->outfd); 716 /* XXX exit for pipe case XXX */ 717 EV_SET(&kev, p->s, EVFILT_READ, 718 EV_ADD | EV_ONESHOT, 0, 0, (void *) p); 719 kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero); 720 p->infd = p->outfd = -1; 721 } 722 return(0); 723 } 724 #define NEVENT 5 725 static int 726 dconschat_proc_socket(struct dcons_state *dc) 727 { 728 struct kevent elist[NEVENT], *e; 729 int i, n; 730 struct dcons_port *p; 731 732 n = kevent(dc->kq, NULL, 0, elist, NEVENT, &dc->to); 733 for (i = 0; i < n; i ++) { 734 e = &elist[i]; 735 p = (struct dcons_port *)e->udata; 736 if (e->ident == p->s) { 737 dconschat_accept_socket(dc, p); 738 } else { 739 dconschat_read_socket(dc, p); 740 } 741 } 742 return(0); 743 } 744 745 static int 746 dconschat_proc_dcons(struct dcons_state *dc) 747 { 748 int port, len, err; 749 char buf[MAX_XFER]; 750 struct dcons_port *p; 751 752 err = dconschat_get_ptr(dc); 753 if (err) { 754 /* XXX we should stop write operation too. */ 755 return err; 756 } 757 for (port = 0; port < DCONS_NPORT; port ++) { 758 p = &dc->port[port]; 759 if (p->infd < 0) 760 continue; 761 while ((len = dconschat_read_dcons(dc, port, buf, 762 sizeof(buf))) > 0) { 763 dconschat_write_socket(p->outfd, buf, len); 764 dconschat_get_ptr(dc); 765 } 766 if ((dc->flags & F_ONE_SHOT) != 0 && len <= 0) 767 dconschat_cleanup(0); 768 } 769 return 0; 770 } 771 772 static int 773 dconschat_start_session(struct dcons_state *dc) 774 { 775 int counter = 0; 776 777 while (1) { 778 if ((dc->flags & F_READY) == 0 && 779 (++counter % (poll_hz * DCONS_POLL_OFFLINE)) == 0) 780 dconschat_fetch_header(dc); 781 if ((dc->flags & F_READY) != 0) 782 dconschat_proc_dcons(dc); 783 dconschat_proc_socket(dc); 784 } 785 return (0); 786 } 787 788 static void 789 usage(void) 790 { 791 fprintf(stderr, 792 "usage: dconschat [-brvwRT1] [-h hz] [-C port] [-G port]\n" 793 "\t\t\t[-M core] [-N system]\n" 794 "\t\t\t[-u unit] [-a address] [-t target_eui64]\n" 795 "\t-b translate ctrl-C to CR+~+ctrl-B on gdb port\n" 796 "\t-v verbose\n" 797 "\t-w listen on wildcard address rather than localhost\n" 798 "\t-r replay old buffer on connection\n" 799 "\t-R read-only\n" 800 "\t-T enable Telnet protocol workaround on console port\n" 801 "\t-1 one shot: read buffer and exit\n" 802 "\t-h polling rate\n" 803 "\t-C port number for console port\n" 804 "\t-G port number for gdb port\n" 805 "\t(for KVM)\n" 806 "\t-M core file\n" 807 "\t-N system file\n" 808 "\t(for FireWire)\n" 809 "\t-u specify unit number of the bus\n" 810 "\t-t EUI64 of target host (must be specified)\n" 811 "\t-a physical address of dcons buffer on target host\n" 812 ); 813 exit(0); 814 } 815 int 816 main(int argc, char **argv) 817 { 818 struct dcons_state *dc; 819 struct fw_eui64 eui; 820 struct eui64 target; 821 char devname[256], *core = NULL, *system = NULL; 822 int i, ch, error; 823 int unit=0, wildcard=0; 824 int port[DCONS_NPORT]; 825 826 bzero(&sc, sizeof(sc)); 827 dc = ≻ 828 dc->flags |= USE_CROM ? F_USE_CROM : 0; 829 830 /* defualt ports */ 831 port[0] = 0; /* stdin/out for console */ 832 port[1] = -1; /* disable gdb port */ 833 834 while ((ch = getopt(argc, argv, "a:bh:rt:u:vwC:G:M:N:RT1")) != -1) { 835 switch(ch) { 836 case 'a': 837 dc->paddr = strtoull(optarg, NULL, 0); 838 dc->flags &= ~F_USE_CROM; 839 break; 840 case 'b': 841 dc->flags |= F_ALT_BREAK; 842 break; 843 case 'h': 844 poll_hz = strtoul(optarg, NULL, 0); 845 if (poll_hz == 0) 846 poll_hz = DCONS_POLL_HZ; 847 break; 848 case 'r': 849 dc->flags |= F_REPLAY; 850 break; 851 case 't': 852 if (eui64_hostton(optarg, &target) != 0 && 853 eui64_aton(optarg, &target) != 0) 854 errx(1, "invalid target: %s", optarg); 855 eui.hi = ntohl(*(u_int32_t*)&(target.octet[0])); 856 eui.lo = ntohl(*(u_int32_t*)&(target.octet[4])); 857 dc->type = TYPE_FW; 858 break; 859 case 'u': 860 unit = strtol(optarg, NULL, 0); 861 break; 862 case 'v': 863 verbose ++; 864 break; 865 case 'w': 866 wildcard = 1; 867 break; 868 case 'C': 869 port[0] = strtol(optarg, NULL, 0); 870 break; 871 case 'G': 872 port[1] = strtol(optarg, NULL, 0); 873 break; 874 case 'M': 875 core = optarg; 876 break; 877 case 'N': 878 system = optarg; 879 break; 880 case 'R': 881 dc->flags |= F_RD_ONLY; 882 break; 883 case 'T': 884 dc->flags |= F_TELNET; 885 break; 886 case '1': 887 dc->flags |= F_ONE_SHOT | F_REPLAY; 888 break; 889 default: 890 usage(); 891 } 892 } 893 if (dc->paddr == 0 && (dc->flags & F_USE_CROM) == 0) { 894 warnx("no address specified"); 895 usage(); 896 } 897 898 if (port[0] < 0 && port[1] < 0) { 899 warnx("no port specified"); 900 usage(); 901 } 902 903 /* set signal handler */ 904 signal(SIGHUP, dconschat_cleanup); 905 signal(SIGINT, dconschat_cleanup); 906 signal(SIGPIPE, dconschat_cleanup); 907 signal(SIGTERM, dconschat_cleanup); 908 909 /* init firewire */ 910 switch (dc->type) { 911 case TYPE_FW: 912 #define MAXDEV 10 913 for (i = 0; i < MAXDEV; i ++) { 914 snprintf(devname, sizeof(devname), 915 "/dev/fwmem%d.%d", unit, i); 916 dc->fd = open(devname, O_RDWR); 917 if (dc->fd >= 0) 918 goto found; 919 } 920 err(1, "open"); 921 found: 922 error = ioctl(dc->fd, FW_SDEUI64, &eui); 923 if (error) 924 err(1, "ioctl"); 925 break; 926 case TYPE_KVM: 927 { 928 struct nlist nl[] = {{"dcons_buf"}, {""}}; 929 void *dcons_buf; 930 931 dc->kd = kvm_open(system, core, NULL, 932 (dc->flags & F_RD_ONLY) ? O_RDONLY : O_RDWR, "dconschat"); 933 if (dc->kd == NULL) 934 errx(1, "kvm_open"); 935 936 if (kvm_nlist(dc->kd, nl) < 0) 937 errx(1, "kvm_nlist: %s", kvm_geterr(dc->kd)); 938 939 if (kvm_read(dc->kd, nl[0].n_value, &dcons_buf, 940 sizeof(void *)) < 0) 941 errx(1, "kvm_read: %s", kvm_geterr(dc->kd)); 942 dc->paddr = (uintptr_t)dcons_buf; 943 if (verbose) 944 printf("dcons_buf: 0x%x\n", (uint)dc->paddr); 945 break; 946 } 947 } 948 dconschat_fetch_header(dc); 949 950 /* iniit sockets */ 951 dc->kq = kqueue(); 952 if (poll_hz == 1) { 953 dc->to.tv_sec = 1; 954 dc->to.tv_nsec = 0; 955 } else { 956 dc->to.tv_sec = 0; 957 dc->to.tv_nsec = 1000 * 1000 * 1000 / poll_hz; 958 } 959 dc->zero.tv_sec = 0; 960 dc->zero.tv_nsec = 0; 961 for (i = 0; i < DCONS_NPORT; i++) 962 dconschat_init_socket(dc, i, 963 wildcard ? NULL : "localhost", port[i]); 964 965 dconschat_start_session(dc); 966 967 for (i = 0; i < DCONS_NPORT; i++) { 968 freeaddrinfo(dc->port[i].res); 969 } 970 return (0); 971 } 972