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