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