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