xref: /inferno-os/os/port/devdynld.c (revision 7ef44d652ae9e5e1f5b3465d73684e4a54de73c0)
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