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