/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * Remote debugging file system */ #include <u.h>#include <libc.h>#include <auth.h>#include <fcall.h>#include <bio.h>#include <thread.h>#include <9p.h> int dbg = 0;#define DBG if(dbg)fprint enum { NHASH = 4096, Readlen = 4, Pagequantum = 1024,}; /* caching memory pages: a lot of space to avoid serial communications */Lock pglock;typedef struct Page Page;struct Page { /* cached memory contents */ Page *link; ulong len; ulong addr; int count; uchar val[Readlen];}; Page *pgtab[NHASH]; Page *freelist; /* called with pglock locked */Page*newpg(void){ int i; Page *p, *q; if(freelist == nil){ p = malloc(sizeof(Page)*Pagequantum); if(p == nil) sysfatal("out of memory"); for(i=0, q=p; i<Pagequantum-1; i++, q++) q->link = q+1; q->link = nil; freelist = p; } p = freelist; freelist = freelist->link; return p;} #define PHIINV 0.61803398874989484820uintahash(ulong addr){ return (uint)floor(NHASH*fmod(addr*PHIINV, 1.0));} intlookup(ulong addr, uchar *val, ulong count){ Page *p; lock(&pglock); for(p=pgtab[ahash(addr)]; p; p=p->link){ if(p->addr == addr && p->count == count){ memmove(val, p->val, count); unlock(&pglock); return 1; } } unlock(&pglock); return 0;} voidinsert(ulong addr, uchar *val, int count){ Page *p; uint h; lock(&pglock); p = newpg(); p->addr = addr; p->count = count; memmove(p->val, val, count); h = ahash(addr); p->link = pgtab[h]; p->len = pgtab[h] ? pgtab[h]->len+1 : 1; pgtab[h] = p; unlock(&pglock);} voidflushcache(void){ int i; Page *p; lock(&pglock); for(i=0; i<NHASH; i++){ if(p=pgtab[i]){ for(;p->link; p=p->link) ; p->link = freelist; freelist = p; } pgtab[i] = nil; } unlock(&pglock);} enum{ Xctl = 1, Xfpregs, Xkregs, Xmem, Xproc, Xregs, Xtext, Xstatus, }; int textfd;int rfd;Biobuf rfb;char* portname = "/dev/eia0";char* textfile = "/386/9pc";char* procname = "1";char* srvname;Channel* rchan; voidusage(void){ fprint(2, "usage: rdbfs [-p procnum] [-s srvname] [-t textfile] [serialport]\n"); exits("usage");} voidnoalarm(void*, char *msg){ if(strstr(msg, "alarm")) noted(NCONT); noted(NDFLT);} /* * send and receive responses on the serial line */voideiaread(void*){ Req *r; char *p; uchar *data; char err[ERRMAX]; char buf[1000]; int i, tries; notify(noalarm); while(r = recvp(rchan)){ DBG(2, "got %F: here goes...", &r->ifcall); if(r->ifcall.count > Readlen) r->ifcall.count = Readlen; r->ofcall.count = r->ifcall.count; if(r->type == Tread && lookup(r->ifcall.offset, (uchar*)r->ofcall.data, r->ofcall.count)){ respond(r, nil); continue; } for(tries=0; tries<5; tries++){ if(r->type == Twrite){ DBG(2, "w%.8lux %.8lux...", (ulong)r->ifcall.offset, *(ulong*)r->ifcall.data); fprint(rfd, "w%.8lux %.8lux\n", (ulong)r->ifcall.offset, *(ulong*)r->ifcall.data); }else if(r->type == Tread){ DBG(2, "r%.8lux...", (ulong)r->ifcall.offset); fprint(rfd, "r%.8lux\n", (ulong)r->ifcall.offset); }else{ respond(r, "oops"); break; } for(;;){ werrstr(""); alarm(500); p=Brdline(&rfb, '\n'); alarm(0); if(p == nil){ rerrstr(err, sizeof err); DBG(2, "error %s\n", err); if(strstr(err, "alarm") || strstr(err, "interrupted")) break; if(Blinelen(&rfb) == 0) // true eof sysfatal("eof on serial line?"); Bread(&rfb, buf, Blinelen(&rfb)<sizeof buf ? Blinelen(&rfb) : sizeof buf); continue; } p[Blinelen(&rfb)-1] = 0; if(p[0] == '\r') p++; DBG(2, "serial %s\n", p); if(p[0] == 'R'){ if(strtoul(p+1, 0, 16) == (ulong)r->ifcall.offset){ /* we know that data can handle Readlen bytes */ data = (uchar*)r->ofcall.data; for(i=0; i<r->ifcall.count; i++) data[i] = strtol(p+1+8+1+3*i, 0, 16); insert(r->ifcall.offset, data, r->ifcall.count); respond(r, nil); goto Break2; }else DBG(2, "%.8lux ≠ %.8lux\n", strtoul(p+1, 0, 16), (ulong)r->ifcall.offset); }else if(p[0] == 'W'){ respond(r, nil); goto Break2; }else{ DBG(2, "unknown message\n"); } } } Break2:; }} voidattachremote(char* name){ int fd; char buf[128]; print("attach %s\n", name); rfd = open(name, ORDWR); if(rfd < 0) sysfatal("can't open remote %s", name); sprint(buf, "%sctl", name); fd = open(buf, OWRITE); if(fd < 0) sysfatal("can't set baud rate on %s", buf); write(fd, "B9600", 6); close(fd); Binit(&rfb, rfd, OREAD);} voidfsopen(Req *r){ char buf[ERRMAX]; switch((uintptr)r->fid->file->aux){ case Xtext: close(textfd); textfd = open(textfile, OREAD); if(textfd < 0) { snprint(buf, sizeof buf, "text: %r"); respond(r, buf); return; } break; } respond(r, nil);} voidfsread(Req *r){ int i, n; char buf[512]; switch((uintptr)r->fid->file->aux) { case Xfpregs: case Xproc: case Xregs: respond(r, "Egreg"); break; case Xkregs: case Xmem: if(sendp(rchan, r) != 1){ snprint(buf, sizeof buf, "rdbfs sendp: %r"); respond(r, buf); return; } break; case Xtext: n = pread(textfd, r->ofcall.data, r->ifcall.count, r->ifcall.offset); if(n < 0) { rerrstr(buf, sizeof buf); respond(r, buf); break; } r->ofcall.count = n; respond(r, nil); break; case Xstatus: n = sprint(buf, "%-28s%-28s%-28s", "remote", "system", "New"); for(i = 0; i < 9; i++) n += sprint(buf+n, "%-12d", 0); readstr(r, buf); respond(r, nil); break; default: respond(r, "unknown read"); }} voidfswrite(Req *r){ char buf[ERRMAX]; switch((uintptr)r->fid->file->aux) { case Xctl: if(strncmp(r->ifcall.data, "kill", 4) == 0 || strncmp(r->ifcall.data, "exit", 4) == 0) { respond(r, nil); postnote(PNGROUP, getpid(), "umount"); exits(nil); }else if(strncmp(r->ifcall.data, "refresh", 7) == 0){ flushcache(); respond(r, nil); }else if(strncmp(r->ifcall.data, "hashstats", 9) == 0){ int i; lock(&pglock); for(i=0; i<NHASH; i++) if(pgtab[i]) print("%lud ", pgtab[i]->len); print("\n"); unlock(&pglock); respond(r, nil); }else respond(r, "permission denied"); break; case Xkregs: case Xmem: if(sendp(rchan, r) != 1) { snprint(buf, sizeof buf, "rdbfs sendp: %r"); respond(r, buf); return; } break; default: respond(r, "Egreg"); break; }} struct { char *s; int id; int mode;} tab[] = { "ctl", Xctl, 0222, "fpregs", Xfpregs, 0666, "kregs", Xkregs, 0666, "mem", Xmem, 0666, "proc", Xproc, 0444, "regs", Xregs, 0666, "text", Xtext, 0444, "status", Xstatus, 0444,}; voidkillall(Srv*){ postnote(PNGROUP, getpid(), "kill");} Srv fs = {.open= fsopen,.read= fsread,.write= fswrite,.end= killall,}; voidthreadmain(int argc, char **argv){ int i, p[2]; File *dir; rfork(RFNOTEG); ARGBEGIN{ case 'D': chatty9p++; break; case 'd': dbg = 1; break; case 'p': procname = EARGF(usage()); break; case 's': srvname = EARGF(usage()); break; case 't': textfile = EARGF(usage()); break; default: usage(); }ARGEND; switch(argc){ case 0: break; case 1: portname = argv[0]; break; default: usage(); } rchan = chancreate(sizeof(Req*), 10); attachremote(portname); if(pipe(p) < 0) sysfatal("pipe: %r"); fmtinstall('F', fcallfmt); proccreate(eiaread, nil, 8192); fs.tree = alloctree("rdbfs", "rdbfs", DMDIR|0555, nil); dir = createfile(fs.tree->root, procname, "rdbfs", DMDIR|0555, 0); for(i=0; i<nelem(tab); i++) closefile(createfile(dir, tab[i].s, "rdbfs", tab[i].mode, (void*)tab[i].id)); closefile(dir); threadpostmountsrv(&fs, srvname, "/proc", MBEFORE); exits(0);}
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
/*
* Remote debugging file system
*/
#include <u.h>
#include <libc.h>
#include <auth.h>
#include <fcall.h>
#include <bio.h>
#include <thread.h>
#include <9p.h>
int dbg = 0;
#define DBG if(dbg)fprint
enum {
NHASH = 4096,
Readlen = 4,
Pagequantum = 1024,
};
/* caching memory pages: a lot of space to avoid serial communications */
Lock pglock;
typedef struct Page Page;
struct Page { /* cached memory contents */
Page *link;
ulong len;
ulong addr;
int count;
uchar val[Readlen];
};
Page *pgtab[NHASH];
Page *freelist;
/* called with pglock locked */
Page*
newpg(void)
{
int i;
Page *p, *q;
if(freelist == nil){
p = malloc(sizeof(Page)*Pagequantum);
if(p == nil)
sysfatal("out of memory");
for(i=0, q=p; i<Pagequantum-1; i++, q++)
q->link = q+1;
q->link = nil;
freelist = p;
}
p = freelist;
freelist = freelist->link;
return p;
}
#define PHIINV 0.61803398874989484820
uint
ahash(ulong addr)
{
return (uint)floor(NHASH*fmod(addr*PHIINV, 1.0));
}
int
lookup(ulong addr, uchar *val, ulong count)
{
Page *p;
lock(&pglock);
for(p=pgtab[ahash(addr)]; p; p=p->link){
if(p->addr == addr && p->count == count){
memmove(val, p->val, count);
unlock(&pglock);
return 1;
}
}
unlock(&pglock);
return 0;
}
void
insert(ulong addr, uchar *val, int count)
{
Page *p;
uint h;
lock(&pglock);
p = newpg();
p->addr = addr;
p->count = count;
memmove(p->val, val, count);
h = ahash(addr);
p->link = pgtab[h];
p->len = pgtab[h] ? pgtab[h]->len+1 : 1;
pgtab[h] = p;
unlock(&pglock);
}
void
flushcache(void)
{
int i;
Page *p;
lock(&pglock);
for(i=0; i<NHASH; i++){
if(p=pgtab[i]){
for(;p->link; p=p->link)
;
p->link = freelist;
freelist = p;
}
pgtab[i] = nil;
}
unlock(&pglock);
}
enum
{
Xctl = 1,
Xfpregs,
Xkregs,
Xmem,
Xproc,
Xregs,
Xtext,
Xstatus,
};
int textfd;
int rfd;
Biobuf rfb;
char* portname = "/dev/eia0";
char* textfile = "/386/9pc";
char* procname = "1";
char* srvname;
Channel* rchan;
void
usage(void)
{
fprint(2, "usage: rdbfs [-p procnum] [-s srvname] [-t textfile] [serialport]\n");
exits("usage");
}
void
noalarm(void*, char *msg)
{
if(strstr(msg, "alarm"))
noted(NCONT);
noted(NDFLT);
}
/*
* send and receive responses on the serial line
*/
void
eiaread(void*)
{
Req *r;
char *p;
uchar *data;
char err[ERRMAX];
char buf[1000];
int i, tries;
notify(noalarm);
while(r = recvp(rchan)){
DBG(2, "got %F: here goes...", &r->ifcall);
if(r->ifcall.count > Readlen)
r->ifcall.count = Readlen;
r->ofcall.count = r->ifcall.count;
if(r->type == Tread && lookup(r->ifcall.offset, (uchar*)r->ofcall.data, r->ofcall.count)){
respond(r, nil);
continue;
}
for(tries=0; tries<5; tries++){
if(r->type == Twrite){
DBG(2, "w%.8lux %.8lux...", (ulong)r->ifcall.offset, *(ulong*)r->ifcall.data);
fprint(rfd, "w%.8lux %.8lux\n", (ulong)r->ifcall.offset, *(ulong*)r->ifcall.data);
}else if(r->type == Tread){
DBG(2, "r%.8lux...", (ulong)r->ifcall.offset);
fprint(rfd, "r%.8lux\n", (ulong)r->ifcall.offset);
}else{
respond(r, "oops");
break;
}
for(;;){
werrstr("");
alarm(500);
p=Brdline(&rfb, '\n');
alarm(0);
if(p == nil){
rerrstr(err, sizeof err);
DBG(2, "error %s\n", err);
if(strstr(err, "alarm") || strstr(err, "interrupted"))
break;
if(Blinelen(&rfb) == 0) // true eof
sysfatal("eof on serial line?");
Bread(&rfb, buf, Blinelen(&rfb)<sizeof buf ? Blinelen(&rfb) : sizeof buf);
continue;
}
p[Blinelen(&rfb)-1] = 0;
if(p[0] == '\r')
p++;
DBG(2, "serial %s\n", p);
if(p[0] == 'R'){
if(strtoul(p+1, 0, 16) == (ulong)r->ifcall.offset){
/* we know that data can handle Readlen bytes */
data = (uchar*)r->ofcall.data;
for(i=0; i<r->ifcall.count; i++)
data[i] = strtol(p+1+8+1+3*i, 0, 16);
insert(r->ifcall.offset, data, r->ifcall.count);
respond(r, nil);
goto Break2;
}else
DBG(2, "%.8lux ≠ %.8lux\n", strtoul(p+1, 0, 16), (ulong)r->ifcall.offset);
}else if(p[0] == 'W'){
respond(r, nil);
goto Break2;
}else{
DBG(2, "unknown message\n");
}
}
}
Break2:;
}
}
void
attachremote(char* name)
{
int fd;
char buf[128];
print("attach %s\n", name);
rfd = open(name, ORDWR);
if(rfd < 0)
sysfatal("can't open remote %s", name);
sprint(buf, "%sctl", name);
fd = open(buf, OWRITE);
if(fd < 0)
sysfatal("can't set baud rate on %s", buf);
write(fd, "B9600", 6);
close(fd);
Binit(&rfb, rfd, OREAD);
}
void
fsopen(Req *r)
{
char buf[ERRMAX];
switch((uintptr)r->fid->file->aux){
case Xtext:
close(textfd);
textfd = open(textfile, OREAD);
if(textfd < 0) {
snprint(buf, sizeof buf, "text: %r");
respond(r, buf);
return;
}
break;
}
respond(r, nil);
}
void
fsread(Req *r)
{
int i, n;
char buf[512];
switch((uintptr)r->fid->file->aux) {
case Xfpregs:
case Xproc:
case Xregs:
respond(r, "Egreg");
break;
case Xkregs:
case Xmem:
if(sendp(rchan, r) != 1){
snprint(buf, sizeof buf, "rdbfs sendp: %r");
respond(r, buf);
return;
}
break;
case Xtext:
n = pread(textfd, r->ofcall.data, r->ifcall.count, r->ifcall.offset);
if(n < 0) {
rerrstr(buf, sizeof buf);
respond(r, buf);
break;
}
r->ofcall.count = n;
respond(r, nil);
break;
case Xstatus:
n = sprint(buf, "%-28s%-28s%-28s", "remote", "system", "New");
for(i = 0; i < 9; i++)
n += sprint(buf+n, "%-12d", 0);
readstr(r, buf);
respond(r, nil);
break;
default:
respond(r, "unknown read");
}
}
void
fswrite(Req *r)
{
char buf[ERRMAX];
switch((uintptr)r->fid->file->aux) {
case Xctl:
if(strncmp(r->ifcall.data, "kill", 4) == 0 ||
strncmp(r->ifcall.data, "exit", 4) == 0) {
respond(r, nil);
postnote(PNGROUP, getpid(), "umount");
exits(nil);
}else if(strncmp(r->ifcall.data, "refresh", 7) == 0){
flushcache();
respond(r, nil);
}else if(strncmp(r->ifcall.data, "hashstats", 9) == 0){
int i;
lock(&pglock);
for(i=0; i<NHASH; i++)
if(pgtab[i])
print("%lud ", pgtab[i]->len);
print("\n");
unlock(&pglock);
respond(r, nil);
}else
respond(r, "permission denied");
break;
case Xkregs:
case Xmem:
if(sendp(rchan, r) != 1) {
snprint(buf, sizeof buf, "rdbfs sendp: %r");
respond(r, buf);
return;
}
break;
default:
respond(r, "Egreg");
break;
}
}
struct {
char *s;
int id;
int mode;
} tab[] = {
"ctl", Xctl, 0222,
"fpregs", Xfpregs, 0666,
"kregs", Xkregs, 0666,
"mem", Xmem, 0666,
"proc", Xproc, 0444,
"regs", Xregs, 0666,
"text", Xtext, 0444,
"status", Xstatus, 0444,
};
void
killall(Srv*)
{
postnote(PNGROUP, getpid(), "kill");
}
Srv fs = {
.open= fsopen,
.read= fsread,
.write= fswrite,
.end= killall,
};
void
threadmain(int argc, char **argv)
{
int i, p[2];
File *dir;
rfork(RFNOTEG);
ARGBEGIN{
case 'D':
chatty9p++;
break;
case 'd':
dbg = 1;
break;
case 'p':
procname = EARGF(usage());
break;
case 's':
srvname = EARGF(usage());
break;
case 't':
textfile = EARGF(usage());
break;
default:
usage();
}ARGEND;
switch(argc){
case 0:
break;
case 1:
portname = argv[0];
break;
default:
usage();
}
rchan = chancreate(sizeof(Req*), 10);
attachremote(portname);
if(pipe(p) < 0)
sysfatal("pipe: %r");
fmtinstall('F', fcallfmt);
proccreate(eiaread, nil, 8192);
fs.tree = alloctree("rdbfs", "rdbfs", DMDIR|0555, nil);
dir = createfile(fs.tree->root, procname, "rdbfs", DMDIR|0555, 0);
for(i=0; i<nelem(tab); i++)
closefile(createfile(dir, tab[i].s, "rdbfs", tab[i].mode, (void*)tab[i].id));
closefile(dir);
threadpostmountsrv(&fs, srvname, "/proc", MBEFORE);
exits(0);
}