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