xref: /inferno-os/emu/port/devdynldx.c (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
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