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