Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

/*
 * 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);
}