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*
newpg(void)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
ahash(ulong addr)62 ahash(ulong addr)
63 {
64 return (uint)floor(NHASH*fmod(addr*PHIINV, 1.0));
65 }
66
67 int
lookup(ulong addr,uchar * val,ulong count)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
insert(ulong addr,uchar * val,int count)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
flushcache(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
usage(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
noalarm(void *,char * msg)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
eiaread(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
attachremote(char * name)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
fsopen(Req * r)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
fsread(Req * r)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
fswrite(Req * r)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
killall(Srv *)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
threadmain(int argc,char ** argv)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