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
dlfree(Dyndev * l)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*
dlload(char * path,Dynsym * tab,int ntab)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
devload(char * name,char * path,char * tag)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**
finddyndev(char * name)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
matched(Dyndev * l,char * path,char * tag)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
devunload(char * name,char * path,char * tag)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
readdynld(void * a,ulong n,ulong offset)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
readsyms(char * a,ulong n,ulong offset)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*
dlattach(char * spec)252 dlattach(char *spec)
253 {
254 return devattach(DEVCHAR, spec);
255 }
256
257 static Walkqid*
dlwalk(Chan * c,Chan * nc,char ** name,int nname)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
dlstat(Chan * c,uchar * db,int n)264 dlstat(Chan *c, uchar *db, int n)
265 {
266 return devstat(c, db, n, dltab, nelem(dltab), devgen);
267 }
268
269 static Chan*
dlopen(Chan * c,int omode)270 dlopen(Chan *c, int omode)
271 {
272 return devopen(c, omode, dltab, nelem(dltab), devgen);
273 }
274
275 static void
dlclose(Chan * c)276 dlclose(Chan *c)
277 {
278 USED(c);
279 }
280
281 static long
dlread(Chan * c,void * a,long n,vlong voffset)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
dlwrite(Chan * c,void * a,long n,vlong voffset)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