1 #include "dat.h" 2 #include "fns.h" 3 #include "error.h" 4 #include <a.out.h> 5 #include <dynld.h> 6 7 /* 8 * TO DO 9 * - dynamic allocation of Dev.dc 10 * - inter-module dependencies 11 * - reference count on Dev to allow error("inuse") [or how else to do it] 12 * - is Dev.shutdown the right function to call? Dev.config perhaps? 13 */ 14 15 #define DBG if(1) print 16 17 extern ulong ndevs; 18 19 enum 20 { 21 Qdir, 22 Qdynld, 23 Qdynsyms, 24 25 DEVCHAR = 'L', 26 }; 27 28 static Dirtab dltab[] = 29 { 30 ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, 31 "dynld", {Qdynld}, 0, 0644, 32 "dynsyms", {Qdynsyms}, 0, 0444, 33 }; 34 35 typedef struct Dyndev Dyndev; 36 37 struct Dyndev 38 { 39 char* name; /* device name (eg, "dynld") */ 40 char* tag; /* version tag (eg, MD5 or SHA1 hash of content) */ 41 char* path; /* file from whence it came */ 42 Dynobj* o; 43 Dev* dev; 44 Dyndev* next; 45 }; 46 47 static Dyndev *loaded; 48 static QLock dllock; 49 50 static Dyndev** finddyndev(char*); 51 static int matched(Dyndev*, char*, char*); 52 53 extern Dynobj* kdynloadfd(int, Dynsym*, int, ulong); 54 55 static void 56 dlfree(Dyndev *l) 57 { 58 if(l != nil){ 59 free(l->tag); 60 free(l->name); 61 free(l->path); 62 dynobjfree(l->o); 63 free(l); 64 } 65 } 66 67 static Dyndev* 68 dlload(char *path, Dynsym *tab, int ntab) 69 { 70 Dyndev *l; 71 int fd; 72 73 /* in Plan 9, would probably use Chan* interface here */ 74 fd = kopen(path, OREAD); 75 if(fd < 0) 76 error("cannot open"); 77 if(waserror()){ 78 kclose(fd); 79 nexterror(); 80 } 81 l = mallocz(sizeof(Dyndev), 1); 82 if(l == nil) 83 error(Enomem); 84 if(waserror()){ 85 dlfree(l); 86 nexterror(); 87 } 88 l->path = strdup(path); 89 if(l->path == nil) 90 error(Enomem); 91 l->o = kdynloadfd(fd, tab, ntab, 0); 92 if(l->o == nil) 93 error(up->env->errstr); 94 poperror(); 95 poperror(); 96 kclose(fd); 97 return l; 98 } 99 100 static void 101 devload(char *name, char *path, char *tag) 102 { 103 int i; 104 Dyndev *l, **lp; 105 Dev *dev; 106 char tabname[32]; 107 108 lp = finddyndev(name); 109 if(*lp != nil) 110 error("already loaded"); /* i'm assuming the name (eg, "cons") is to be unique */ 111 l = dlload(path, _exporttab, dyntabsize(_exporttab)); 112 if(waserror()){ 113 dlfree(l); 114 nexterror(); 115 } 116 snprint(tabname, sizeof(tabname), "%sdevtab", name); 117 dev = dynimport(l->o, tabname, signof(*dev)); 118 if(dev == nil) 119 errorf("can't find %sdevtab in module", name); 120 kstrdup(&l->name, name); 121 kstrdup(&l->tag, tag != nil? tag: ""); 122 if(dev->name == nil) 123 dev->name = l->name; 124 else if(strcmp(dev->name, l->name) != 0) 125 errorf("module file has device %s", dev->name); 126 /* TO DO: allocate dev->dc dynamically (cf. brucee's driver) */ 127 if(devno(dev->dc, 1) >= 0) 128 errorf("devchar %C already used", dev->dc); 129 for(i = 0; devtab[i] != nil; i++) 130 ; 131 if(i >= ndevs || devtab[i+1] != nil) 132 error("device table full"); 133 #ifdef NATIVE 134 i = splhi(); 135 dev->reset(); 136 splx(i); 137 #endif 138 dev->init(); 139 l->dev = devtab[i] = dev; 140 l->next = loaded; 141 loaded = l; /* recently loaded ones first: good unload order? */ 142 poperror(); 143 } 144 145 static Dyndev** 146 finddyndev(char *name) 147 { 148 Dyndev *l, **lp; 149 150 for(lp = &loaded; (l = *lp) != nil; lp = &l->next) 151 if(strcmp(l->name, name) == 0) 152 break; 153 return lp; 154 } 155 156 static int 157 matched(Dyndev *l, char *path, char *tag) 158 { 159 if(path != nil && strcmp(l->path, path) != 0) 160 return 0; 161 if(tag != nil && strcmp(l->tag, tag) != 0) 162 return 0; 163 return 1; 164 } 165 166 static void 167 devunload(char *name, char *path, char *tag) 168 { 169 int i; 170 Dyndev *l, **lp; 171 172 lp = finddyndev(name); 173 l = *lp; 174 if(l == nil) 175 error("not loaded"); 176 if(!matched(l, path, tag)) 177 error("path/tag mismatch"); 178 for(i = 0; i < ndevs; i++) 179 if(l->dev == devtab[i]){ 180 devtab[i] = nil; /* TO DO: ensure driver is not currently in use */ 181 break; 182 } 183 #ifdef NATIVE 184 l->dev->shutdown(); 185 #endif 186 *lp = l->next; 187 dlfree(l); 188 } 189 190 static long 191 readdynld(void *a, ulong n, ulong offset) 192 { 193 char *p; 194 Dyndev *l; 195 int m, len; 196 197 m = 0; 198 for(l = loaded; l != nil; l = l->next) 199 m += 48 + strlen(l->name) + strlen(l->path) + strlen(l->tag); 200 p = malloc(m); 201 if(p == nil) 202 error(Enomem); 203 if(waserror()){ 204 free(p); 205 nexterror(); 206 } 207 *p = 0; 208 len = 0; 209 for(l = loaded; l != nil; l = l->next) 210 if(l->dev) 211 len += snprint(p+len, m-len, "#%C\t%.8p\t%.8lud\t%q\t%q\t%q\n", 212 l->dev->dc, l->o->base, l->o->size, l->name, l->path, l->tag); 213 n = readstr(offset, a, n, p); 214 poperror(); 215 free(p); 216 return n; 217 } 218 219 static long 220 readsyms(char *a, ulong n, ulong offset) 221 { 222 char *p; 223 Dynsym *t; 224 long l, nr; 225 226 p = malloc(READSTR); 227 if(p == nil) 228 error(Enomem); 229 if(waserror()){ 230 free(p); 231 nexterror(); 232 } 233 nr = 0; 234 for(t = _exporttab; n > 0 && t->name != nil; t++){ 235 l = snprint(p, READSTR, "%.8lux %.8lux %s\n", t->addr, t->sig, t->name); 236 if(offset >= l){ 237 offset -= l; 238 continue; 239 } 240 l = readstr(offset, a, n, p); 241 offset = 0; 242 n -= l; 243 a += l; 244 nr += l; 245 } 246 poperror(); 247 free(p); 248 return nr; 249 } 250 251 static Chan* 252 dlattach(char *spec) 253 { 254 return devattach(DEVCHAR, spec); 255 } 256 257 static Walkqid* 258 dlwalk(Chan *c, Chan *nc, char **name, int nname) 259 { 260 return devwalk(c, nc, name, nname, dltab, nelem(dltab), devgen); 261 } 262 263 static int 264 dlstat(Chan *c, uchar *db, int n) 265 { 266 return devstat(c, db, n, dltab, nelem(dltab), devgen); 267 } 268 269 static Chan* 270 dlopen(Chan *c, int omode) 271 { 272 return devopen(c, omode, dltab, nelem(dltab), devgen); 273 } 274 275 static void 276 dlclose(Chan *c) 277 { 278 USED(c); 279 } 280 281 static long 282 dlread(Chan *c, void *a, long n, vlong voffset) 283 { 284 switch((ulong)c->qid.path){ 285 case Qdir: 286 return devdirread(c, a, n, dltab, nelem(dltab), devgen); 287 case Qdynld: 288 return readdynld(a, n, voffset); 289 case Qdynsyms: 290 return readsyms(a, n, voffset); 291 default: 292 error(Egreg); 293 } 294 return n; 295 } 296 297 static long 298 dlwrite(Chan *c, void *a, long n, vlong voffset) 299 { 300 Cmdbuf *cb; 301 char *name, *tag, *path; 302 303 USED(voffset); 304 switch((ulong)c->qid.path){ 305 case Qdynld: 306 cb = parsecmd(a, n); 307 qlock(&dllock); 308 if(waserror()){ 309 qunlock(&dllock); 310 free(cb); 311 nexterror(); 312 } 313 if(cb->nf < 3 || strcmp(cb->f[1], "dev") != 0) /* only do devices */ 314 cmderror(cb, Ebadctl); 315 name = cb->f[2]; 316 path = nil; 317 if(cb->nf > 3) 318 path = cb->f[3]; 319 tag = nil; 320 if(cb->nf > 4) 321 tag = cb->f[4]; 322 if(strcmp(cb->f[0], "load") == 0){ 323 if(path == nil) 324 cmderror(cb, "missing load path"); 325 devload(name, path, tag); /* TO DO: remaining parameters might be dependencies? */ 326 }else if(strcmp(cb->f[0], "unload") == 0) 327 devunload(name, path, tag); 328 else 329 cmderror(cb, Ebadctl); 330 poperror(); 331 qunlock(&dllock); 332 free(cb); 333 break; 334 default: 335 error(Egreg); 336 } 337 return n; 338 } 339 340 Dev dynlddevtab = { 341 DEVCHAR, 342 "dynld", 343 344 devinit, 345 dlattach, 346 dlwalk, 347 dlstat, 348 dlopen, 349 devcreate, 350 dlclose, 351 dlread, 352 devbread, 353 dlwrite, 354 devbwrite, 355 devremove, 356 devwstat, 357 }; 358