diff options
Diffstat (limited to 'emu/port/devdynldx.c')
| -rw-r--r-- | emu/port/devdynldx.c | 357 |
1 files changed, 357 insertions, 0 deletions
diff --git a/emu/port/devdynldx.c b/emu/port/devdynldx.c new file mode 100644 index 00000000..738b9ed5 --- /dev/null +++ b/emu/port/devdynldx.c @@ -0,0 +1,357 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include <a.out.h> +#include <dynld.h> + +/* + * TO DO + * - dynamic allocation of Dev.dc + * - inter-module dependencies + * - reference count on Dev to allow error("inuse") [or how else to do it] + * - is Dev.shutdown the right function to call? Dev.config perhaps? + */ + +#define DBG if(1) print + +extern ulong ndevs; + +enum +{ + Qdir, + Qdynld, + Qdynsyms, + + DEVCHAR = 'L', +}; + +static Dirtab dltab[] = +{ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "dynld", {Qdynld}, 0, 0644, + "dynsyms", {Qdynsyms}, 0, 0444, +}; + +typedef struct Dyndev Dyndev; + +struct Dyndev +{ + char* name; /* device name (eg, "dynld") */ + char* tag; /* version tag (eg, MD5 or SHA1 hash of content) */ + char* path; /* file from whence it came */ + Dynobj* o; + Dev* dev; + Dyndev* next; +}; + +static Dyndev *loaded; +static QLock dllock; + +static Dyndev** finddyndev(char*); +static int matched(Dyndev*, char*, char*); + +extern Dynobj* kdynloadfd(int, Dynsym*, int, ulong); + +static void +dlfree(Dyndev *l) +{ + if(l != nil){ + free(l->tag); + free(l->name); + free(l->path); + dynobjfree(l->o); + free(l); + } +} + +static Dyndev* +dlload(char *path, Dynsym *tab, int ntab) +{ + Dyndev *l; + int fd; + + /* in Plan 9, would probably use Chan* interface here */ + fd = kopen(path, OREAD); + if(fd < 0) + error("cannot open"); + if(waserror()){ + kclose(fd); + nexterror(); + } + l = mallocz(sizeof(Dyndev), 1); + if(l == nil) + error(Enomem); + if(waserror()){ + dlfree(l); + nexterror(); + } + l->path = strdup(path); + if(l->path == nil) + error(Enomem); + l->o = kdynloadfd(fd, tab, ntab, 0); + if(l->o == nil) + error(up->env->errstr); + poperror(); + poperror(); + kclose(fd); + return l; +} + +static void +devload(char *name, char *path, char *tag) +{ + int i; + Dyndev *l, **lp; + Dev *dev; + char tabname[32]; + + lp = finddyndev(name); + if(*lp != nil) + error("already loaded"); /* i'm assuming the name (eg, "cons") is to be unique */ + l = dlload(path, _exporttab, dyntabsize(_exporttab)); + if(waserror()){ + dlfree(l); + nexterror(); + } + snprint(tabname, sizeof(tabname), "%sdevtab", name); + dev = dynimport(l->o, tabname, signof(*dev)); + if(dev == nil) + errorf("can't find %sdevtab in module", name); + kstrdup(&l->name, name); + kstrdup(&l->tag, tag != nil? tag: ""); + if(dev->name == nil) + dev->name = l->name; + else if(strcmp(dev->name, l->name) != 0) + errorf("module file has device %s", dev->name); + /* TO DO: allocate dev->dc dynamically (cf. brucee's driver) */ + if(devno(dev->dc, 1) >= 0) + errorf("devchar %C already used", dev->dc); + for(i = 0; devtab[i] != nil; i++) + ; + if(i >= ndevs || devtab[i+1] != nil) + error("device table full"); +#ifdef NATIVE + i = splhi(); + dev->reset(); + splx(i); +#endif + dev->init(); + l->dev = devtab[i] = dev; + l->next = loaded; + loaded = l; /* recently loaded ones first: good unload order? */ + poperror(); +} + +static Dyndev** +finddyndev(char *name) +{ + Dyndev *l, **lp; + + for(lp = &loaded; (l = *lp) != nil; lp = &l->next) + if(strcmp(l->name, name) == 0) + break; + return lp; +} + +static int +matched(Dyndev *l, char *path, char *tag) +{ + if(path != nil && strcmp(l->path, path) != 0) + return 0; + if(tag != nil && strcmp(l->tag, tag) != 0) + return 0; + return 1; +} + +static void +devunload(char *name, char *path, char *tag) +{ + int i; + Dyndev *l, **lp; + + lp = finddyndev(name); + l = *lp; + if(l == nil) + error("not loaded"); + if(!matched(l, path, tag)) + error("path/tag mismatch"); + for(i = 0; i < ndevs; i++) + if(l->dev == devtab[i]){ + devtab[i] = nil; /* TO DO: ensure driver is not currently in use */ + break; + } +#ifdef NATIVE + l->dev->shutdown(); +#endif + *lp = l->next; + dlfree(l); +} + +static long +readdynld(void *a, ulong n, ulong offset) +{ + char *p; + Dyndev *l; + int m, len; + + m = 0; + for(l = loaded; l != nil; l = l->next) + m += 48 + strlen(l->name) + strlen(l->path) + strlen(l->tag); + p = malloc(m); + if(p == nil) + error(Enomem); + if(waserror()){ + free(p); + nexterror(); + } + *p = 0; + len = 0; + for(l = loaded; l != nil; l = l->next) + if(l->dev) + len += snprint(p+len, m-len, "#%C\t%.8p\t%.8lud\t%q\t%q\t%q\n", + l->dev->dc, l->o->base, l->o->size, l->name, l->path, l->tag); + n = readstr(offset, a, n, p); + poperror(); + free(p); + return n; +} + +static long +readsyms(char *a, ulong n, ulong offset) +{ + char *p; + Dynsym *t; + long l, nr; + + p = malloc(READSTR); + if(p == nil) + error(Enomem); + if(waserror()){ + free(p); + nexterror(); + } + nr = 0; + for(t = _exporttab; n > 0 && t->name != nil; t++){ + l = snprint(p, READSTR, "%.8lux %.8lux %s\n", t->addr, t->sig, t->name); + if(offset >= l){ + offset -= l; + continue; + } + l = readstr(offset, a, n, p); + offset = 0; + n -= l; + a += l; + nr += l; + } + poperror(); + free(p); + return nr; +} + +static Chan* +dlattach(char *spec) +{ + return devattach(DEVCHAR, spec); +} + +static Walkqid* +dlwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, dltab, nelem(dltab), devgen); +} + +static int +dlstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, dltab, nelem(dltab), devgen); +} + +static Chan* +dlopen(Chan *c, int omode) +{ + return devopen(c, omode, dltab, nelem(dltab), devgen); +} + +static void +dlclose(Chan *c) +{ + USED(c); +} + +static long +dlread(Chan *c, void *a, long n, vlong voffset) +{ + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, a, n, dltab, nelem(dltab), devgen); + case Qdynld: + return readdynld(a, n, voffset); + case Qdynsyms: + return readsyms(a, n, voffset); + default: + error(Egreg); + } + return n; +} + +static long +dlwrite(Chan *c, void *a, long n, vlong voffset) +{ + Cmdbuf *cb; + char *name, *tag, *path; + + USED(voffset); + switch((ulong)c->qid.path){ + case Qdynld: + cb = parsecmd(a, n); + qlock(&dllock); + if(waserror()){ + qunlock(&dllock); + free(cb); + nexterror(); + } + if(cb->nf < 3 || strcmp(cb->f[1], "dev") != 0) /* only do devices */ + cmderror(cb, Ebadctl); + name = cb->f[2]; + path = nil; + if(cb->nf > 3) + path = cb->f[3]; + tag = nil; + if(cb->nf > 4) + tag = cb->f[4]; + if(strcmp(cb->f[0], "load") == 0){ + if(path == nil) + cmderror(cb, "missing load path"); + devload(name, path, tag); /* TO DO: remaining parameters might be dependencies? */ + }else if(strcmp(cb->f[0], "unload") == 0) + devunload(name, path, tag); + else + cmderror(cb, Ebadctl); + poperror(); + qunlock(&dllock); + free(cb); + break; + default: + error(Egreg); + } + return n; +} + +Dev dynlddevtab = { + DEVCHAR, + "dynld", + + devinit, + dlattach, + dlwalk, + dlstat, + dlopen, + devcreate, + dlclose, + dlread, + devbread, + dlwrite, + devbwrite, + devremove, + devwstat, +}; |
