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 }; 133 134 int textfd; 135 int srvfd; 136 int rfd; 137 Biobuf rfb; 138 char* portname = "/dev/eia0"; 139 char* textfile = "/386/9pc"; 140 char* procname = "1"; 141 Channel* rchan; 142 char* Eexist = "file does not exist"; 143 144 char* progname = "rdbfs"; 145 146 void 147 usage(void) 148 { 149 fprint(2, "usage: rdbfs [-p procnum] [-t textfile] [serialport]\n"); 150 exits("usage"); 151 } 152 153 int 154 forkproc(void (*fn)(void)) 155 { 156 int pid; 157 switch(pid=rfork(RFNAMEG|RFMEM|RFPROC)){ 158 case -1: 159 sysfatal("fork: %r"); 160 case 0: 161 fn(); 162 _exits(0); 163 default: 164 return pid; 165 } 166 return -1; /* not reached */ 167 } 168 169 void 170 noalarm(void*, char *msg) 171 { 172 if(strstr(msg, "alarm")) 173 noted(NCONT); 174 noted(NDFLT); 175 } 176 177 /* 178 * send and receive responses on the serial line 179 */ 180 void 181 eiaread(void*) 182 { 183 Req *r; 184 char *p; 185 uchar *data; 186 char err[ERRMAX]; 187 char buf[1000]; 188 int i, tries; 189 190 notify(noalarm); 191 while(r = recvp(rchan)){ 192 DBG(2, "got %F: here goes...", &r->ifcall); 193 if(r->ifcall.count > Readlen) 194 r->ifcall.count = Readlen; 195 r->ofcall.count = r->ifcall.count; 196 if(r->type == Tread && lookup(r->ifcall.offset, (uchar*)r->ofcall.data, r->ofcall.count)){ 197 respond(r, nil); 198 continue; 199 } 200 for(tries=0; tries<5; tries++){ 201 if(r->type == Twrite){ 202 DBG(2, "w%.8lux %.8lux...", (ulong)r->ifcall.offset, *(ulong*)r->ifcall.data); 203 fprint(rfd, "w%.8lux %.8lux\n", (ulong)r->ifcall.offset, *(ulong*)r->ifcall.data); 204 }else if(r->type == Tread){ 205 DBG(2, "r%.8lux...", (ulong)r->ifcall.offset); 206 fprint(rfd, "r%.8lux\n", (ulong)r->ifcall.offset); 207 }else{ 208 respond(r, "oops"); 209 break; 210 } 211 for(;;){ 212 werrstr(""); 213 alarm(500); 214 p=Brdline(&rfb, '\n'); 215 alarm(0); 216 if(p == nil){ 217 rerrstr(err, sizeof err); 218 DBG(2, "error %s\n", err); 219 if(strstr(err, "alarm") || strstr(err, "interrupted")) 220 break; 221 if(Blinelen(&rfb) == 0) // true eof 222 sysfatal("eof on serial line?"); 223 Bread(&rfb, buf, Blinelen(&rfb)<sizeof buf ? Blinelen(&rfb) : sizeof buf); 224 continue; 225 } 226 p[Blinelen(&rfb)-1] = 0; 227 if(p[0] == '\r') 228 p++; 229 DBG(2, "serial %s\n", p); 230 if(p[0] == 'R'){ 231 if(strtoul(p+1, 0, 16) == (ulong)r->ifcall.offset){ 232 /* we know that data can handle Readlen bytes */ 233 data = (uchar*)r->ofcall.data; 234 for(i=0; i<r->ifcall.count; i++) 235 data[i] = strtol(p+1+8+1+3*i, 0, 16); 236 insert(r->ifcall.offset, data, r->ifcall.count); 237 respond(r, nil); 238 goto Break2; 239 }else 240 DBG(2, "%.8lux ≠ %.8lux\n", strtoul(p+1, 0, 16), (ulong)r->ifcall.offset); 241 }else if(p[0] == 'W'){ 242 respond(r, nil); 243 goto Break2; 244 }else{ 245 DBG(2, "unknown message\n"); 246 } 247 } 248 } 249 Break2:; 250 } 251 } 252 253 void 254 attachremote(char* name) 255 { 256 int fd; 257 char buf[128]; 258 259 print("attach %s\n", name); 260 rfd = open(name, ORDWR); 261 if(rfd < 0) 262 sysfatal("can't open remote %s", name); 263 264 sprint(buf, "%sctl", name); 265 fd = open(buf, OWRITE); 266 if(fd < 0) 267 sysfatal("can't set baud rate on %s", buf); 268 write(fd, "B9600", 6); 269 close(fd); 270 Binit(&rfb, rfd, OREAD); 271 } 272 273 void 274 fsopen(Req *r) 275 { 276 char buf[ERRMAX]; 277 278 switch((int)r->fid->file->aux){ 279 case Xtext: 280 close(textfd); 281 textfd = open(textfile, OREAD); 282 if(textfd < 0) { 283 snprint(buf, sizeof buf, "text: %r"); 284 respond(r, buf); 285 return; 286 } 287 break; 288 } 289 respond(r, nil); 290 } 291 292 void 293 fsread(Req *r) 294 { 295 int i, n; 296 char buf[512]; 297 298 switch((int)r->fid->file->aux) { 299 case Xfpregs: 300 case Xproc: 301 case Xregs: 302 respond(r, "Egreg"); 303 break; 304 case Xkregs: 305 case Xmem: 306 if(sendp(rchan, r) != 1){ 307 snprint(buf, sizeof buf, "rdbfs sendp: %r"); 308 respond(r, buf); 309 return; 310 } 311 break; 312 case Xtext: 313 n = pread(textfd, r->ofcall.data, r->ifcall.count, r->ifcall.offset); 314 if(n < 0) { 315 rerrstr(buf, sizeof buf); 316 respond(r, buf); 317 break; 318 } 319 r->ofcall.count = n; 320 respond(r, nil); 321 break; 322 case Xstatus: 323 n = sprint(buf, "%-28s%-28s%-28s", "remote", "system", "New"); 324 for(i = 0; i < 9; i++) 325 n += sprint(buf+n, "%-12d", 0); 326 readstr(r, buf); 327 respond(r, nil); 328 break; 329 default: 330 respond(r, "unknown read"); 331 } 332 } 333 334 void 335 fswrite(Req *r) 336 { 337 char buf[ERRMAX]; 338 339 switch((int)r->fid->file->aux) { 340 case Xctl: 341 if(strncmp(r->ifcall.data, "kill", 4) == 0 || 342 strncmp(r->ifcall.data, "exit", 4) == 0) { 343 respond(r, nil); 344 bind("#p", "/proc", MREPL); 345 postnote(PNGROUP, getpid(), "umount"); 346 exits(nil); 347 }else if(strncmp(r->ifcall.data, "refresh", 7) == 0){ 348 flushcache(); 349 respond(r, nil); 350 }else if(strncmp(r->ifcall.data, "hashstats", 9) == 0){ 351 int i; 352 lock(&pglock); 353 for(i=0; i<NHASH; i++) 354 if(pgtab[i]) 355 print("%lud ", pgtab[i]->len); 356 print("\n"); 357 unlock(&pglock); 358 respond(r, nil); 359 }else 360 respond(r, "permission denied"); 361 break; 362 case Xkregs: 363 case Xmem: 364 if(sendp(rchan, r) != 1) { 365 snprint(buf, sizeof buf, "rdbfs sendp: %r"); 366 respond(r, buf); 367 return; 368 } 369 break; 370 default: 371 respond(r, "Egreg"); 372 break; 373 } 374 } 375 376 struct { 377 char *s; 378 int id; 379 int mode; 380 } tab[] = { 381 "ctl", Xctl, 0222, 382 "fpregs", Xfpregs, 0666, 383 "kregs", Xkregs, 0666, 384 "mem", Xmem, 0666, 385 "proc", Xproc, 0444, 386 "regs", Xregs, 0666, 387 "text", Xtext, 0444, 388 "status", Xstatus, 0444, 389 }; 390 391 void 392 killall(Srv*) 393 { 394 postnote(PNGROUP, getpid(), "kill"); 395 } 396 397 Srv fs = { 398 .open= fsopen, 399 .read= fsread, 400 .write= fswrite, 401 .end= killall, 402 }; 403 404 void 405 threadmain(int argc, char **argv) 406 { 407 int i, p[2]; 408 File *dir; 409 410 rfork(RFNOTEG); 411 ARGBEGIN{ 412 case 'D': 413 chatty9p++; 414 break; 415 case 'd': 416 dbg = 1; 417 break; 418 case 'p': 419 procname = EARGF(usage()); 420 break; 421 case 't': 422 textfile = EARGF(usage()); 423 break; 424 default: 425 usage(); 426 }ARGEND; 427 428 switch(argc){ 429 case 0: 430 break; 431 case 1: 432 portname = argv[0]; 433 break; 434 default: 435 usage(); 436 } 437 438 rchan = chancreate(sizeof(Req*), 10); 439 attachremote(portname); 440 if(pipe(p) < 0) 441 sysfatal("pipe: %r"); 442 443 fmtinstall('F', fcallfmt); 444 srvfd = p[1]; 445 proccreate(eiaread, nil, 8192); 446 447 fs.tree = alloctree("rdbfs", "rdbfs", DMDIR|0555, nil); 448 dir = createfile(fs.tree->root, procname, "rdbfs", DMDIR|0555, 0); 449 for(i=0; i<nelem(tab); i++) 450 closefile(createfile(dir, tab[i].s, "rdbfs", tab[i].mode, (void*)tab[i].id)); 451 closefile(dir); 452 threadpostmountsrv(&fs, nil, "/proc", MBEFORE); 453 exits(0); 454 } 455 456