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