xref: /plan9-contrib/sys/src/cmd/rdbfs.c (revision a6a9e07217f318acf170f99684a55fba5200524f)
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