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 #include <thread.h> 11 #include <9p.h> 12 13 int dbg = 0; 14 #define DBG if(dbg)fprint 15 16 enum { 17 NHASH = 4096, 18 Readlen = 4, 19 Pagequantum = 1024, 20 }; 21 22 /* caching memory pages: a lot of space to avoid serial communications */ 23 Lock pglock; 24 typedef struct Page Page; 25 struct Page { /* cached memory contents */ 26 Page *link; 27 ulong len; 28 ulong addr; 29 int count; 30 uchar val[Readlen]; 31 }; 32 33 Page *pgtab[NHASH]; 34 35 Page *freelist; 36 37 /* called with pglock locked */ 38 Page* 39 newpg(void) 40 { 41 int i; 42 Page *p, *q; 43 44 if(freelist == nil){ 45 p = malloc(sizeof(Page)*Pagequantum); 46 if(p == nil) 47 sysfatal("out of memory"); 48 49 for(i=0, q=p; i<Pagequantum-1; i++, q++) 50 q->link = q+1; 51 q->link = nil; 52 53 freelist = p; 54 } 55 p = freelist; 56 freelist = freelist->link; 57 return p; 58 } 59 60 #define PHIINV 0.61803398874989484820 61 uint 62 ahash(ulong addr) 63 { 64 return (uint)floor(NHASH*fmod(addr*PHIINV, 1.0)); 65 } 66 67 int 68 lookup(ulong addr, uchar *val, ulong count) 69 { 70 Page *p; 71 72 lock(&pglock); 73 for(p=pgtab[ahash(addr)]; p; p=p->link){ 74 if(p->addr == addr && p->count == count){ 75 memmove(val, p->val, count); 76 unlock(&pglock); 77 return 1; 78 } 79 } 80 unlock(&pglock); 81 return 0; 82 } 83 84 void 85 insert(ulong addr, uchar *val, int count) 86 { 87 Page *p; 88 uint h; 89 90 lock(&pglock); 91 p = newpg(); 92 p->addr = addr; 93 p->count = count; 94 memmove(p->val, val, count); 95 h = ahash(addr); 96 p->link = pgtab[h]; 97 p->len = pgtab[h] ? pgtab[h]->len+1 : 1; 98 pgtab[h] = p; 99 unlock(&pglock); 100 } 101 102 void 103 flushcache(void) 104 { 105 int i; 106 Page *p; 107 108 lock(&pglock); 109 for(i=0; i<NHASH; i++){ 110 if(p=pgtab[i]){ 111 for(;p->link; p=p->link) 112 ; 113 p->link = freelist; 114 freelist = p; 115 } 116 pgtab[i] = nil; 117 } 118 unlock(&pglock); 119 } 120 121 enum 122 { 123 Xctl = 1, 124 Xfpregs, 125 Xkregs, 126 Xmem, 127 Xproc, 128 Xregs, 129 Xtext, 130 Xstatus, 131 132 Rendez = 0x89ABCDEF, 133 }; 134 135 int textfd; 136 int srvfd; 137 int rfd; 138 Biobuf rfb; 139 char* portname = "/dev/eia0"; 140 char* textfile = "/386/9pc"; 141 char* procname = "1"; 142 Channel* rchan; 143 char* Eexist = "file does not exist"; 144 145 char* progname = "rdbfs"; 146 147 void 148 usage(void) 149 { 150 fprint(2, "usage: rdbfs [-p procnum] [-t textfile] [serialport]\n"); 151 exits("usage"); 152 } 153 154 int 155 forkproc(void (*fn)(void)) 156 { 157 int pid; 158 switch(pid=rfork(RFNAMEG|RFMEM|RFPROC)){ 159 case -1: 160 sysfatal("fork: %r"); 161 case 0: 162 fn(); 163 _exits(0); 164 default: 165 return pid; 166 } 167 return -1; /* not reached */ 168 } 169 170 void 171 noalarm(void*, char *msg) 172 { 173 if(strstr(msg, "alarm")) 174 noted(NCONT); 175 noted(NDFLT); 176 } 177 178 /* 179 * send and receive responses on the serial line 180 */ 181 void 182 eiaread(void*) 183 { 184 Req *r; 185 char *p; 186 uchar *data; 187 char err[ERRMAX]; 188 char buf[1000]; 189 int i, tries; 190 191 notify(noalarm); 192 while(r = recvp(rchan)){ 193 DBG(2, "got %F: here goes...", &r->ifcall); 194 if(r->ifcall.type == Tflush){ 195 respond(r, nil); 196 continue; 197 } 198 if(r->ifcall.count > Readlen) 199 r->ifcall.count = Readlen; 200 r->ofcall.count = r->ifcall.count; 201 if(r->type == Tread && lookup(r->ifcall.offset, (uchar*)r->ofcall.data, r->ofcall.count)){ 202 respond(r, nil); 203 continue; 204 } 205 for(tries=0; tries<5; tries++){ 206 if(r->type == Twrite){ 207 DBG(2, "w%.8lux %.8lux...", (ulong)r->ifcall.offset, *(ulong*)r->ifcall.data); 208 fprint(rfd, "w%.8lux %.8lux\n", (ulong)r->ifcall.offset, *(ulong*)r->ifcall.data); 209 }else if(r->type == Tread){ 210 DBG(2, "r%.8lux...", (ulong)r->ifcall.offset); 211 fprint(rfd, "r%.8lux\n", (ulong)r->ifcall.offset); 212 }else{ 213 respond(r, "oops"); 214 break; 215 } 216 for(;;){ 217 werrstr(""); 218 alarm(500); 219 p=Brdline(&rfb, '\n'); 220 alarm(0); 221 if(p == nil){ 222 rerrstr(err, sizeof err); 223 DBG(2, "error %s\n", err); 224 if(strstr(err, "alarm") || strstr(err, "interrupted")) 225 break; 226 if(Blinelen(&rfb) == 0) // true eof 227 sysfatal("eof on serial line?"); 228 Bread(&rfb, buf, Blinelen(&rfb)<sizeof buf ? Blinelen(&rfb) : sizeof buf); 229 continue; 230 } 231 p[Blinelen(&rfb)-1] = 0; 232 if(p[0] == '\r') 233 p++; 234 DBG(2, "serial %s\n", p); 235 if(p[0] == 'R'){ 236 if(strtoul(p+1, 0, 16) == (ulong)r->ifcall.offset){ 237 /* we know that data can handle Readlen bytes */ 238 data = (uchar*)r->ofcall.data; 239 for(i=0; i<r->ifcall.count; i++) 240 data[i] = strtol(p+1+8+1+3*i, 0, 16); 241 insert(r->ifcall.offset, data, r->ifcall.count); 242 respond(r, nil); 243 goto Break2; 244 }else 245 DBG(2, "%.8lux ≠ %.8lux\n", strtoul(p+1, 0, 16), (ulong)r->ifcall.offset); 246 }else if(p[0] == 'W'){ 247 respond(r, nil); 248 goto Break2; 249 }else{ 250 DBG(2, "unknown message\n"); 251 } 252 } 253 } 254 Break2:; 255 } 256 } 257 258 void 259 attachremote(char* name) 260 { 261 int fd; 262 char buf[128]; 263 264 print("attach %s\n", name); 265 rfd = open(name, ORDWR); 266 if(rfd < 0) 267 sysfatal("can't open remote %s", name); 268 269 sprint(buf, "%sctl", name); 270 fd = open(buf, OWRITE); 271 if(fd < 0) 272 sysfatal("can't set baud rate on %s", buf); 273 write(fd, "B9600", 6); 274 close(fd); 275 Binit(&rfb, rfd, OREAD); 276 } 277 278 void 279 fsopen(Req *r) 280 { 281 char buf[ERRMAX]; 282 283 switch((int)r->fid->file->aux){ 284 case Xtext: 285 close(textfd); 286 textfd = open(textfile, OREAD); 287 if(textfd < 0) { 288 snprint(buf, sizeof buf, "text: %r"); 289 respond(r, buf); 290 return; 291 } 292 break; 293 } 294 respond(r, nil); 295 } 296 297 void 298 fsread(Req *r) 299 { 300 int i, n; 301 char buf[512]; 302 303 switch((int)r->fid->file->aux) { 304 case Xfpregs: 305 case Xproc: 306 case Xregs: 307 respond(r, "Egreg"); 308 break; 309 case Xkregs: 310 case Xmem: 311 if(sendp(rchan, r) != 1){ 312 snprint(buf, sizeof buf, "rdbfs sendp: %r"); 313 respond(r, buf); 314 return; 315 } 316 break; 317 case Xtext: 318 n = pread(textfd, r->ofcall.data, r->ifcall.count, r->ifcall.offset); 319 if(n < 0) { 320 rerrstr(buf, sizeof buf); 321 respond(r, buf); 322 break; 323 } 324 r->ofcall.count = n; 325 respond(r, nil); 326 break; 327 case Xstatus: 328 n = sprint(buf, "%-28s%-28s%-28s", "remote", "system", "New"); 329 for(i = 0; i < 9; i++) 330 n += sprint(buf+n, "%-12d", 0); 331 readstr(r, buf); 332 respond(r, nil); 333 break; 334 default: 335 respond(r, "unknown read"); 336 } 337 } 338 339 void 340 fswrite(Req *r) 341 { 342 char buf[ERRMAX]; 343 344 switch((int)r->fid->file->aux) { 345 case Xctl: 346 if(strncmp(r->ifcall.data, "kill", 4) == 0 || 347 strncmp(r->ifcall.data, "exit", 4) == 0) { 348 respond(r, nil); 349 bind("#p", "/proc", MREPL); 350 postnote(PNGROUP, getpid(), "umount"); 351 exits(nil); 352 }else if(strncmp(r->ifcall.data, "refresh", 7) == 0){ 353 flushcache(); 354 respond(r, nil); 355 }else if(strncmp(r->ifcall.data, "hashstats", 9) == 0){ 356 int i; 357 lock(&pglock); 358 for(i=0; i<NHASH; i++) 359 if(pgtab[i]) 360 print("%lud ", pgtab[i]->len); 361 print("\n"); 362 unlock(&pglock); 363 respond(r, nil); 364 }else 365 respond(r, "permission denied"); 366 break; 367 case Xkregs: 368 case Xmem: 369 if(sendp(rchan, r) != 1) { 370 snprint(buf, sizeof buf, "rdbfs sendp: %r"); 371 respond(r, buf); 372 return; 373 } 374 break; 375 default: 376 respond(r, "Egreg"); 377 break; 378 } 379 } 380 381 void 382 fsflush(Req *r) 383 { 384 if(sendp(rchan, r) != 1) 385 respond(r, "rdbfs sendp failed"); 386 } 387 388 struct { 389 char *s; 390 int id; 391 int mode; 392 } tab[] = { 393 "ctl", Xctl, 0222, 394 "fpregs", Xfpregs, 0666, 395 "kregs", Xkregs, 0666, 396 "mem", Xmem, 0666, 397 "proc", Xproc, 0444, 398 "regs", Xregs, 0666, 399 "text", Xtext, 0444, 400 "status", Xstatus, 0444, 401 }; 402 403 void 404 killall(Srv*) 405 { 406 postnote(PNGROUP, getpid(), "kill"); 407 } 408 409 Srv fs = { 410 .open= fsopen, 411 .read= fsread, 412 .write= fswrite, 413 .flush= fsflush, 414 .end= killall, 415 }; 416 417 void 418 threadmain(int argc, char **argv) 419 { 420 int i, p[2]; 421 File *dir; 422 423 rfork(RFNOTEG); 424 ARGBEGIN{ 425 case 'D': 426 chatty9p++; 427 break; 428 case 'd': 429 dbg = 1; 430 break; 431 case 'p': 432 procname = EARGF(usage()); 433 break; 434 case 't': 435 textfile = EARGF(usage()); 436 break; 437 default: 438 usage(); 439 }ARGEND; 440 441 switch(argc){ 442 case 0: 443 break; 444 case 1: 445 portname = argv[0]; 446 break; 447 default: 448 usage(); 449 } 450 451 rchan = chancreate(sizeof(Req*), 10); 452 attachremote(portname); 453 if(pipe(p) < 0) 454 sysfatal("pipe: %r"); 455 456 fmtinstall('F', fcallfmt); 457 srvfd = p[1]; 458 proccreate(eiaread, nil, 8192); 459 460 fs.tree = alloctree("rdbfs", "rdbfs", DMDIR|0555, nil); 461 dir = createfile(fs.tree->root, procname, "rdbfs", DMDIR|0555, 0); 462 for(i=0; i<nelem(tab); i++) 463 closefile(createfile(dir, tab[i].s, "rdbfs", tab[i].mode, (void*)tab[i].id)); 464 closefile(dir); 465 threadpostmountsrv(&fs, nil, "/proc", MBEFORE); 466 exits(0); 467 } 468 469