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