/*
* 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,
Xnote,
Xproc,
Xregs,
Xtext,
Xstatus,
};
int textfd;
int rfd;
Biobuf rfb;
char* portname = "/dev/eia0";
char* textfile = "/386/9pc";
char* procname = "1";
Channel* rchan;
void
usage(void)
{
fprint(2, "usage: rdbfs [-p procnum] [-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");
break;
}
}
}
respond(r, "timed out");
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", getuser(), "Broken");
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:
case Xnote:
if(strncmp(r->ifcall.data, "kill", 4) == 0 ||
strncmp(r->ifcall.data, "exit", 4) == 0) {
respond(r, nil);
threadexitsall(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,
"note", Xnote, 0222,
"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 '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, nil, "/proc", MBEFORE);
exits(0);
}
|