1 /* 2 * Remote debugging file system 3 */ 4 5 #include <u.h> 6 #include <libc.h> 7 #include <auth.h> 8 #include <fcall.h> 9 #include <bio.h> 10 11 /* 12 * Like the library routine, but does postnote to the group. 13 */ 14 void 15 sysfatal(char *fmt, ...) 16 { 17 char buf[1024]; 18 va_list arg; 19 20 va_start(arg, fmt); 21 doprint(buf, buf+sizeof(buf), fmt, arg); 22 va_end(arg); 23 fprint(2, "%s: %s\n", argv0, buf); 24 postnote(PNGROUP, getpid(), "die yankee pig dog"); 25 exits(buf); 26 } 27 28 int dbg = 0; 29 #define DBG if(dbg)fprint 30 31 enum { 32 NHASH = 4096, 33 Readlen = 4, 34 Pagequantum = 1024, 35 }; 36 37 /* caching memory pages: a lot of space to avoid serial communications */ 38 Lock pglock; 39 typedef struct Page Page; 40 struct Page { /* cached memory contents */ 41 Page *link; 42 ulong len; 43 ulong addr; 44 uchar val[Readlen]; 45 }; 46 47 Page *pgtab[NHASH]; 48 49 Page *freelist; 50 51 /* called with pglock locked */ 52 Page* 53 newpg(void) 54 { 55 int i; 56 Page *p, *q; 57 58 if(freelist == nil){ 59 p = malloc(sizeof(Page)*Pagequantum); 60 if(p == nil) 61 sysfatal("out of memory"); 62 63 for(i=0, q=p; i<Pagequantum-1; i++, q++) 64 q->link = q+1; 65 q->link = nil; 66 67 freelist = p; 68 } 69 p = freelist; 70 freelist = freelist->link; 71 return p; 72 } 73 74 #define PHIINV 0.61803398874989484820 75 uint 76 ahash(ulong addr) 77 { 78 return (uint)floor(NHASH*fmod(addr*PHIINV, 1.0)); 79 } 80 81 int 82 lookup(ulong addr, uchar *val) 83 { 84 Page *p; 85 86 lock(&pglock); 87 for(p=pgtab[ahash(addr)]; p; p=p->link){ 88 if(p->addr == addr){ 89 memmove(val, p->val, Readlen); 90 unlock(&pglock); 91 return 1; 92 } 93 } 94 unlock(&pglock); 95 return 0; 96 } 97 98 void 99 insert(ulong addr, uchar *val) 100 { 101 Page *p; 102 uint h; 103 104 lock(&pglock); 105 p = newpg(); 106 p->addr = addr; 107 memmove(p->val, val, Readlen); 108 h = ahash(addr); 109 p->link = pgtab[h]; 110 p->len = pgtab[h] ? pgtab[h]->len+1 : 1; 111 pgtab[h] = p; 112 unlock(&pglock); 113 } 114 115 void 116 flushcache(void) 117 { 118 int i; 119 Page *p; 120 121 lock(&pglock); 122 for(i=0; i<NHASH; i++){ 123 if(p=pgtab[i]){ 124 for(;p->link; p=p->link) 125 ; 126 p->link = freelist; 127 freelist = p; 128 } 129 pgtab[i] = nil; 130 } 131 unlock(&pglock); 132 } 133 134 enum 135 { 136 NFHASH = 32, 137 MSGSIZE = 10, 138 NPROCS = 2, /* number of slave processes */ 139 140 /* 141 * Qid 0 is root 142 * Subdirectory named "procname" ("1" is default) 143 * Files: mem, proc, and text 144 */ 145 Qroot = CHDIR, 146 Q1 = CHDIR|0x01, 147 Qctl = 1, 148 Qfpregs, 149 Qkregs, 150 Qmem, 151 Qproc, 152 Qregs, 153 Qtext, 154 Qstatus, 155 NFILES, 156 157 STACKSIZE = 4096, 158 }; 159 160 typedef struct Fid Fid; 161 struct Fid 162 { 163 int fid; 164 int open; 165 Fid *hash; 166 Qid qid; 167 }; 168 169 /* 170 * we only serve files needed by the debuggers 171 */ 172 char* fnames[] = 173 { 174 [Qctl] "ctl", 175 [Qfpregs] "fpregs", 176 [Qkregs] "kregs", 177 [Qmem] "mem", 178 [Qproc] "proc", 179 [Qregs] "regs", 180 [Qtext] "text", 181 [Qstatus] "status", 182 }; 183 184 int textfd; 185 int srvfd; 186 int rfd; 187 Biobuf rfb; 188 int pids[NPROCS]; 189 int nopen; 190 Fid* hash[NFHASH]; 191 char dirbuf[DIRLEN*NFILES]; 192 char iob[MSGSIZE]; 193 char* portname = "/dev/eia0"; 194 char* textfile = "/386/9pc"; 195 char* procname = "1"; 196 char dbuf[MAXFDATA]; 197 198 char* Eexist = "file does not exist"; 199 200 void mflush(Fcall*); 201 void msession(Fcall*); 202 void mnop(Fcall*); 203 void mattach(Fcall*); 204 void mclone(Fcall*); 205 void mwalk(Fcall*); 206 void mopen(Fcall*); 207 void mcreate(Fcall*); 208 void mread(Fcall*); 209 void mwrite(Fcall*); 210 void mclunk(Fcall*); 211 void mremove(Fcall*); 212 void mstat(Fcall*); 213 void mwstat(Fcall*); 214 215 void (*fcall[])(Fcall *f) = 216 { 217 [Tflush] mflush, 218 [Tsession] msession, 219 [Tnop] mnop, 220 [Tattach] mattach, 221 [Tclone] mclone, 222 [Twalk] mwalk, 223 [Topen] mopen, 224 [Tcreate] mcreate, 225 [Tread] mread, 226 [Twrite] mwrite, 227 [Tclunk] mclunk, 228 [Tremove] mremove, 229 [Tstat] mstat, 230 [Twstat] mwstat 231 }; 232 233 Fid* newfid(int); 234 Fid* lookfid(int); 235 int delfid(Fcall*); 236 void attachremote(char*); 237 void reply(Fcall*, char*); 238 void eiaread(void); 239 void fsysread(void); 240 char* progname = "rdbfs"; 241 242 enum { 243 Rendez = 0x89ABCDEF 244 }; 245 246 void 247 usage(void) 248 { 249 fprint(2, "Usage: rdbfs [-p procnum] [-t textfile] [serialport]\n"); 250 exits("usage"); 251 } 252 253 int 254 forkproc(void (*fn)(void)) 255 { 256 int pid; 257 switch(pid=rfork(RFNAMEG|RFMEM|RFPROC)){ 258 case -1: 259 sysfatal("fork: %r"); 260 case 0: 261 fn(); 262 _exits(0); 263 default: 264 return pid; 265 } 266 return -1; /* not reached */ 267 } 268 269 void 270 main(int argc, char **argv) 271 { 272 int p[2]; 273 274 rfork(RFNOTEG|RFREND); 275 276 ARGBEGIN{ 277 case 'd': 278 dbg = 1; 279 break; 280 case 'p': 281 if((procname=ARGF()) == nil) 282 usage(); 283 break; 284 case 't': 285 if((textfile=ARGF()) == nil) 286 usage(); 287 break; 288 default: 289 usage(); 290 }ARGEND; 291 292 switch(argc){ 293 case 0: 294 break; 295 case 1: 296 portname = argv[0]; 297 break; 298 default: 299 usage(); 300 } 301 302 attachremote(portname); 303 if(pipe(p) < 0) 304 sysfatal("pipe: %r"); 305 306 fmtinstall('F', fcallconv); 307 srvfd = p[1]; 308 forkproc(eiaread); 309 forkproc(fsysread); 310 311 if(mount(p[0], "/proc", MBEFORE, "") < 0) 312 sysfatal("mount failed: %r"); 313 exits(0); 314 } 315 316 /* 317 * read protocol messages 318 */ 319 void 320 fsysread(void) 321 { 322 int n; 323 Fcall f; 324 char buf[MAXMSG+MAXFDATA]; 325 326 pids[0] = getpid(); 327 for(;;) { 328 n = read(srvfd, buf, sizeof(buf)); 329 if(n < 0) 330 sysfatal("read mntpt: %r"); 331 n = convM2S(buf, &f, n); 332 if(n < 0) 333 continue; 334 fcall[f.type](&f); 335 } 336 } 337 338 /* 339 * write a protocol reply to the debugger 340 */ 341 void 342 reply(Fcall *r, char *error) 343 { 344 int n; 345 char rbuf[MAXMSG+MAXFDATA]; 346 347 if(error == nil) 348 r->type++; 349 else { 350 r->type = Rerror; 351 strncpy(r->ename, error, sizeof(r->ename)); 352 } 353 n = convS2M(r, rbuf); 354 if(write(srvfd, rbuf, n) < 0) 355 sysfatal("reply: %r"); 356 357 DBG(2, "%F\n", r); 358 } 359 360 void 361 noalarm(void*, char *msg) 362 { 363 if(strstr(msg, "alarm")) 364 noted(NCONT); 365 noted(NDFLT); 366 } 367 368 /* 369 * send and receive responses on the serial line 370 */ 371 void 372 eiaread(void) 373 { 374 Fcall* r; 375 char *p; 376 uchar *data; 377 char err[ERRLEN]; 378 char buf[1000]; 379 int i, tries; 380 381 notify(noalarm); 382 for(; r = (Fcall*)rendezvous(Rendez, 0); free(r)){ 383 DBG(2, "got %F: here goes...", r); 384 if(r->count > Readlen) 385 r->count = Readlen; 386 if(lookup(r->offset, (uchar*)r->data)){ 387 reply(r, nil); 388 continue; 389 } 390 for(tries=0; tries<5; tries++){ 391 if(r->type == Twrite){ 392 DBG(2, "w%.8lux %.8lux...", (ulong)r->offset, *(ulong*)r->data); 393 fprint(rfd, "w%.8lux %.8lux\n", (ulong)r->offset, *(ulong*)r->data); 394 }else if(r->type == Tread){ 395 DBG(2, "r%.8lux...", (ulong)r->offset); 396 fprint(rfd, "r%.8lux\n", (ulong)r->offset); 397 }else{ 398 reply(r, "oops"); 399 break; 400 } 401 for(;;){ 402 werrstr(""); 403 alarm(500); 404 p=Brdline(&rfb, '\n'); 405 alarm(0); 406 if(p == nil){ 407 err[0] = 0; 408 errstr(err); 409 DBG(2, "error %s\n", err); 410 if(strstr(err, "alarm") || strstr(err, "interrupted")) 411 break; 412 if(Blinelen(&rfb) == 0) // true eof 413 sysfatal("eof on serial line?"); 414 Bread(&rfb, buf, Blinelen(&rfb)<sizeof buf ? Blinelen(&rfb) : sizeof buf); 415 continue; 416 } 417 p[Blinelen(&rfb)-1] = 0; 418 if(p[0] == '\r') 419 p++; 420 DBG(2, "serial %s\n", p); 421 if(p[0] == 'R'){ 422 if(strtoul(p+1, 0, 16) == (ulong)r->offset){ 423 data = (uchar*)r->data; 424 for(i=0; i<Readlen; i++) 425 data[i] = strtol(p+1+8+1+3*i, 0, 16); 426 insert(r->offset, (uchar*)r->data); 427 reply(r, nil); 428 goto Break2; 429 }else 430 DBG(2, "%.8lux ≠ %.8lux\n", strtoul(p+1, 0, 16), (ulong)r->offset); 431 }else if(p[0] == 'W'){ 432 r->count = 4; 433 reply(r, nil); 434 goto Break2; 435 }else{ 436 DBG(2, "unknown message\n"); 437 } 438 } 439 } 440 Break2:; 441 } 442 } 443 444 void 445 attachremote(char* name) 446 { 447 int fd; 448 char buf[128]; 449 450 print("attach %s\n", name); 451 rfd = open(name, ORDWR); 452 if(rfd < 0) 453 sysfatal("can't open remote %s", name); 454 455 sprint(buf, "%sctl", name); 456 fd = open(buf, OWRITE); 457 if(fd < 0) 458 sysfatal("can't set baud rate on %s", buf); 459 write(fd, "B9600", 6); 460 close(fd); 461 Binit(&rfb, rfd, OREAD); 462 } 463 464 int 465 sendremote(Fcall *r) 466 { 467 Fcall *nr; 468 469 if(r->type == Tread || r->type == Twrite) 470 nr = malloc(sizeof(Fcall)+Readlen); 471 else 472 nr = malloc(sizeof(Fcall)); 473 474 if(nr == nil) 475 return -1; 476 477 *nr = *r; 478 if(r->type == Tread) 479 nr->data = (char*)(nr+1); 480 else if(r->type == Twrite){ 481 nr->data = (char*)(nr+1); 482 *(ulong*)nr->data = *(ulong*)r->data; 483 } 484 rendezvous(Rendez, (ulong)nr); 485 return 0; 486 } 487 488 void 489 mflush(Fcall *f) 490 { 491 reply(f, nil); 492 } 493 494 void 495 msession(Fcall *f) 496 { 497 memset(f->authid, 0, sizeof(f->authid)); 498 memset(f->authdom, 0, sizeof(f->authdom)); 499 memset(f->chal, 0, sizeof(f->chal)); 500 reply(f, nil); 501 } 502 503 void 504 mnop(Fcall *f) 505 { 506 reply(f, nil); 507 } 508 509 void 510 mattach(Fcall *f) 511 { 512 Fid *nf; 513 514 nf = newfid(f->fid); 515 if(nf == nil) { 516 reply(f, "fid busy"); 517 return; 518 } 519 nf->qid = (Qid){Qroot, 0}; 520 f->qid = nf->qid; 521 memset(f->rauth, 0, sizeof(f->rauth)); 522 nopen++; 523 reply(f, nil); 524 } 525 526 void 527 mclone(Fcall *f) 528 { 529 Fid *of, *nf; 530 531 of = lookfid(f->fid); 532 if(of == nil) { 533 reply(f, "bad fid"); 534 return; 535 } 536 nf = newfid(f->newfid); 537 if(nf == nil) { 538 reply(f, "fid busy"); 539 return; 540 } 541 nf->qid = of->qid; 542 f->qid = nf->qid; 543 nopen++; 544 reply(f, nil); 545 } 546 547 void 548 mwalk(Fcall *f) 549 { 550 Fid *nf; 551 int i; 552 553 nf = lookfid(f->fid); 554 if(nf == nil) { 555 reply(f, "bad fid"); 556 return; 557 } 558 if(strcmp(f->name, ".") == 0) { 559 f->qid = nf->qid; 560 reply(f, nil); 561 return; 562 } 563 564 switch(nf->qid.path) { 565 case Qroot: 566 if (strcmp(f->name, procname) == 0) { 567 nf->qid.path = Q1; 568 f->qid = nf->qid; 569 reply(f, nil); 570 return; 571 } 572 break; 573 case Q1: 574 if(strcmp(f->name, "..") == 0) { 575 f->qid.path = Qroot; 576 reply(f, nil); 577 return; 578 } 579 for(i = 0; i < NFILES; i++) { 580 if(fnames[i] != nil && strcmp(f->name, fnames[i]) == 0) { 581 nf->qid.path = i; 582 f->qid = nf->qid; 583 reply(f, nil); 584 return; 585 } 586 } 587 break; 588 } 589 reply(f, Eexist); 590 } 591 592 void 593 statgen(Qid *q, char *buf) 594 { 595 Dir dir; 596 597 strcpy(dir.uid, "none"); 598 strcpy(dir.gid, "none"); 599 dir.qid = *q; 600 dir.length = 0; 601 dir.atime = time(0); 602 dir.mtime = dir.atime; 603 memset(dir.name, 0, NAMELEN); 604 605 switch(q->path) { 606 case Qroot: 607 strcpy(dir.name, "."); 608 dir.mode = CHDIR|0555; 609 break; 610 case Q1: 611 strcpy(dir.name, procname); 612 dir.mode = CHDIR|0555; 613 break; 614 case Qctl: 615 case Qfpregs: 616 case Qkregs: 617 case Qmem: 618 case Qproc: 619 case Qregs: 620 case Qtext: 621 case Qstatus: 622 strcpy(dir.name, fnames[q->path]); 623 dir.mode = 0644; 624 break; 625 default: 626 sysfatal("stat: bad qid %lux", q->path); 627 } 628 convD2M(&dir, buf); 629 } 630 631 void 632 mopen(Fcall *f) 633 { 634 Fid *of; 635 char buf[ERRLEN]; 636 637 of = lookfid(f->fid); 638 if(of == nil) { 639 reply(f, "bad fid"); 640 return; 641 } 642 643 switch(of->qid.path) { 644 case Qroot: 645 case Q1: 646 break; 647 case Qctl: 648 case Qfpregs: 649 case Qkregs: 650 case Qmem: 651 case Qproc: 652 case Qregs: 653 case Qstatus: 654 f->qid = of->qid; 655 break; 656 case Qtext: 657 close(textfd); 658 textfd = open(textfile, OREAD); 659 if(textfd < 0) { 660 sprint(buf, "text: %r"); 661 reply(f, buf); 662 return; 663 } 664 break; 665 default: 666 reply(f, "bad qid"); 667 return; 668 } 669 of->open = 1; 670 reply(f, nil); 671 } 672 673 void 674 mread(Fcall *f) 675 { 676 Fid *of; 677 Qid qid; 678 int i, n; 679 char *p, buf[512]; 680 681 of = lookfid(f->fid); 682 if(of == nil) { 683 reply(f, "bad fid"); 684 return; 685 } 686 687 switch(of->qid.path) { 688 case Qroot: 689 qid = (Qid){Q1, 0}; 690 statgen(&qid, dirbuf); 691 if(f->offset >= DIRLEN) 692 f->count = 0; 693 else if(f->offset+f->count > DIRLEN) 694 f->count = DIRLEN-f->offset; 695 f->data = dirbuf+f->offset; 696 reply(f, nil); 697 break; 698 case Q1: 699 if(!of->open) { 700 reply(f, "not open"); 701 return; 702 } 703 p = dirbuf; 704 for(i = 0; i < NFILES; i++) { 705 if(fnames[i]) { 706 qid = (Qid){i, 0}; 707 statgen(&qid, p); 708 p += DIRLEN; 709 } 710 } 711 n = p-dirbuf; 712 if(f->offset >= n) 713 f->count = 0; 714 else if(f->offset+f->count > n) 715 f->count = n-f->offset; 716 f->data = dirbuf+f->offset; 717 reply(f, nil); 718 break; 719 case Qctl: 720 case Qfpregs: 721 case Qkregs: 722 case Qmem: 723 case Qproc: 724 case Qregs: 725 if(!of->open) { 726 reply(f, "not open"); 727 return; 728 } 729 if(sendremote(f) < 0) { 730 snprint(buf, sizeof buf, "remote write %r"); 731 reply(f, buf); 732 return; 733 } 734 break; 735 case Qtext: 736 n = seek(textfd, f->offset, 0); 737 if(n < 0) { 738 errstr(buf); 739 reply(f, buf); 740 break; 741 } 742 n = read(textfd, dbuf, f->count); 743 if(n < 0) { 744 errstr(buf); 745 reply(f, buf); 746 break; 747 } 748 f->count = n; 749 f->data = dbuf; 750 reply(f, nil); 751 break; 752 case Qstatus: 753 n = sprint(buf, "%-28s%-28s%-28s", "remote", "system", "New"); 754 for(i = 0; i < 9; i++) 755 n += sprint(buf+n, "%-12d", 0); 756 if(f->offset+f->count > n) 757 f->count = n - f->offset; 758 if(f->count < 0) 759 f->count = 0; 760 f->data = buf+f->offset; 761 reply(f, nil); 762 break; 763 default: 764 sysfatal("unknown read"); 765 } 766 } 767 768 /* 769 * disentangle our file system from the namespace 770 */ 771 void 772 umount(void) 773 { 774 bind("#p", "/proc", MREPL); 775 exits(nil); 776 } 777 778 void 779 mwrite(Fcall *f) 780 { 781 Fid *of; 782 783 of = lookfid(f->fid); 784 if(of == nil) { 785 reply(f, "bad fid"); 786 return; 787 } 788 789 switch(of->qid.path) { 790 case Qroot: 791 case Q1: 792 reply(f, "permission denied"); 793 break; 794 case Qctl: 795 if(strncmp(f->data, "kill", f->count-1) == 0 || 796 strncmp(f->data, "exit", f->count-1) == 0) { 797 reply(f, nil); 798 umount(); 799 }else if(strncmp(f->data, "refresh", 7) == 0){ 800 flushcache(); 801 reply(f, nil); 802 }else if(strncmp(f->data, "hashstats", 9) == 0){ 803 int i; 804 lock(&pglock); 805 for(i=0; i<NHASH; i++) 806 if(pgtab[i]) 807 print("%lud ", pgtab[i]->len); 808 print("\n"); 809 unlock(&pglock); 810 reply(f, nil); 811 }else 812 reply(f, "permission denied"); 813 break; 814 case Qfpregs: 815 case Qkregs: 816 case Qmem: 817 case Qproc: 818 case Qregs: 819 if(!of->open) { 820 reply(f, "not open"); 821 return; 822 } 823 if(sendremote(f) < 0) { 824 reply(f, "remote write %r"); 825 return; 826 } 827 break; 828 case Qtext: 829 reply(f, "text write protected"); 830 default: 831 reply(f, "bad path"); 832 break; 833 } 834 } 835 836 void 837 mclunk(Fcall *f) 838 { 839 Fid *of; 840 841 of = lookfid(f->fid); 842 if(of == nil) { 843 reply(f, "bad fid"); 844 return; 845 } 846 delfid(f); 847 reply(f, nil); 848 nopen--; 849 if(nopen <= 0) { 850 postnote(PNGROUP, getpid(), "kill"); 851 exits(nil); 852 } 853 } 854 855 void 856 mstat(Fcall *f) 857 { 858 Fid *of; 859 860 of = lookfid(f->fid); 861 if(of == nil) { 862 reply(f, "bad fid"); 863 return; 864 } 865 866 statgen(&of->qid, f->stat); 867 reply(f, nil); 868 } 869 870 void 871 mwstat(Fcall *f) 872 { 873 reply(f, "permission denied"); 874 } 875 876 void 877 mcreate(Fcall *f) 878 { 879 reply(f, "permission denied"); 880 } 881 882 void 883 mremove(Fcall *f) 884 { 885 reply(f, "permission denied"); 886 } 887 888 Fid* 889 newfid(int fid) 890 { 891 Fid *nf, **l; 892 893 if(lookfid(fid)) 894 return nil; 895 896 nf = mallocz(sizeof(*nf), 1); 897 assert(nf != nil); 898 899 nf->fid = fid; 900 nf->open = 0; 901 nf->qid = (Qid){0, 0}; 902 903 l = &hash[fid%NFHASH]; 904 nf->hash = *l; 905 *l = nf; 906 907 return nf; 908 } 909 910 Fid* 911 lookfid(int fid) 912 { 913 Fid *l; 914 915 for(l = hash[fid%NFHASH]; l; l = l->hash) 916 if(l->fid == fid) 917 return l; 918 919 return nil; 920 } 921 922 int 923 delfid(Fcall *f) 924 { 925 Fid **l, *nf; 926 927 l = &hash[f->fid%NFHASH]; 928 for(nf = *l; nf; nf = nf->hash) { 929 if(nf->fid == f->fid) { 930 *l = nf->hash; 931 free(nf); 932 return 1; 933 } 934 } 935 return 0; 936 } 937