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