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