xref: /freebsd/sys/kern/kern_module.c (revision ce834215a70ff69e7e222827437116eee2f9ac6f)
1 /*-
2  * Copyright (c) 1997 Doug Rabson
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  *	$Id$
27  */
28 
29 #include <sys/param.h>
30 #include <sys/kernel.h>
31 #include <sys/systm.h>
32 #include <sys/sysctl.h>
33 #include <sys/queue.h>
34 #include <sys/libkern.h>
35 #include <sys/malloc.h>
36 #include <sys/sysproto.h>
37 #include <sys/sysent.h>
38 #include <sys/module.h>
39 #include <sys/linker.h>
40 
41 #define M_MODULE	M_TEMP		/* XXX */
42 
43 typedef TAILQ_HEAD(, module) modulelist_t;
44 struct module {
45     TAILQ_ENTRY(module)	link;		/* chain together all modules */
46     TAILQ_ENTRY(module)	flink;		/* all modules in a file */
47     struct linker_file*	file;		/* file which contains this module */
48     int			refs;		/* reference count */
49     int			id;		/* unique id number */
50     char		*name;		/* module name */
51     modeventhand_t	handler;	/* event handler */
52     void		*arg;		/* argument for handler */
53 };
54 
55 #define MOD_EVENT(mod, type) (mod)->handler((mod), (type), (mod)->arg)
56 
57 static modulelist_t modules;
58 static int nextid = 1;
59 
60 static void module_shutdown(int, void*);
61 
62 static void
63 module_init(void* arg)
64 {
65     TAILQ_INIT(&modules);
66     at_shutdown(module_shutdown, 0, SHUTDOWN_POST_SYNC);
67 }
68 
69 SYSINIT(module, SI_SUB_KMEM, SI_ORDER_ANY, module_init, 0);
70 
71 static void
72 module_shutdown(int arg1, void* arg2)
73 {
74     module_t mod;
75     int error;
76 
77     for (mod = TAILQ_FIRST(&modules); mod; mod = TAILQ_NEXT(mod, link))
78 	MOD_EVENT(mod, MOD_SHUTDOWN);
79 }
80 
81 void
82 module_register_static(void *arg)
83 {
84     moduledata_t* data = (moduledata_t*) arg;
85     int error;
86 
87     if (error = module_register(data->name, data->evhand, data->priv))
88 	printf("module_register_static: module_register(%s, %x, %x) returned %d",
89 	       data->name, data->evhand, data->priv, error);
90 }
91 
92 int
93 module_register(const char* name, modeventhand_t handler, void* arg)
94 {
95     size_t namelen;
96     module_t newmod;
97     int error;
98 
99     namelen = strlen(name) + 1;
100     newmod = (module_t) malloc(sizeof(struct module) + namelen,
101 			       M_MODULE, M_WAITOK);
102     if (newmod == 0)
103 	return ENOMEM;
104 
105     newmod->refs = 1;
106     newmod->id = nextid++;
107     newmod->name = (char *) (newmod + 1);
108     strcpy(newmod->name, name);
109     newmod->handler = handler;
110     newmod->arg = arg;
111     TAILQ_INSERT_TAIL(&modules, newmod, link);
112 
113     if (linker_current_file) {
114 	TAILQ_INSERT_TAIL(&linker_current_file->modules, newmod, flink);
115 	newmod->file = linker_current_file;
116     } else
117 	newmod->file = 0;
118 
119     if (error = MOD_EVENT(newmod, MOD_LOAD)) {
120 	module_release(newmod);
121 	return error;
122     }
123 
124     return 0;
125 }
126 
127 void
128 module_reference(module_t mod)
129 {
130     MOD_DPF(REFS, ("module_reference: before, refs=%d\n", mod->refs));
131 
132     mod->refs++;
133 }
134 
135 void
136 module_release(module_t mod)
137 {
138     if (mod->refs <= 0)
139 	panic("module_release: bad reference count");
140 
141     MOD_DPF(REFS, ("module_release: before, refs=%d\n", mod->refs));
142 
143     mod->refs--;
144     if (mod->refs == 0) {
145 	TAILQ_REMOVE(&modules, mod, link);
146 	if (mod->file) {
147 	    TAILQ_REMOVE(&mod->file->modules, mod, flink);
148 	}
149 	free(mod, M_MODULE);
150     }
151 }
152 
153 module_t
154 module_lookupbyname(const char* name)
155 {
156     module_t mod;
157 
158     for (mod = TAILQ_FIRST(&modules); mod; mod = TAILQ_NEXT(mod, link)) {
159 	if (!strcmp(mod->name, name))
160 	    return mod;
161     }
162 
163     return 0;
164 }
165 
166 module_t
167 module_lookupbyid(int modid)
168 {
169     module_t mod;
170 
171     for (mod = TAILQ_FIRST(&modules); mod; mod = TAILQ_NEXT(mod, link)) {
172 	if (mod->id == modid)
173 	    return mod;
174     }
175 
176     return 0;
177 }
178 
179 int
180 module_unload(module_t mod)
181 {
182     return MOD_EVENT(mod, MOD_UNLOAD);
183 }
184 
185 int
186 module_getid(module_t mod)
187 {
188     return mod->id;
189 }
190 
191 module_t
192 module_getfnext(module_t mod)
193 {
194     return TAILQ_NEXT(mod, flink);
195 }
196 
197 /*
198  * Syscalls.
199  */
200 int
201 modnext(struct proc* p, struct modnext_args* uap, int* retval)
202 {
203     module_t mod;
204 
205     *retval = -1;
206     if (SCARG(uap, modid) == 0) {
207 	mod = TAILQ_FIRST(&modules);
208 	if (mod) {
209 	    *retval = mod->id;
210 	    return 0;
211 	} else
212 	    return ENOENT;
213     }
214 
215     mod = module_lookupbyid(SCARG(uap, modid));
216     if (!mod)
217 	return ENOENT;
218 
219     if (TAILQ_NEXT(mod, link))
220 	*retval = TAILQ_NEXT(mod, link)->id;
221     else
222 	*retval = 0;
223     return 0;
224 }
225 
226 int
227 modfnext(struct proc* p, struct modfnext_args* uap, int* retval)
228 {
229     module_t mod;
230 
231     *retval = -1;
232 
233     mod = module_lookupbyid(SCARG(uap, modid));
234     if (!mod)
235 	return ENOENT;
236 
237     if (TAILQ_NEXT(mod, flink))
238 	*retval = TAILQ_NEXT(mod, flink)->id;
239     else
240 	*retval = 0;
241     return 0;
242 }
243 
244 int
245 modstat(struct proc* p, struct modstat_args* uap, int* retval)
246 {
247     module_t mod;
248     int error = 0;
249     int namelen;
250     int version;
251     struct module_stat* stat;
252 
253     mod = module_lookupbyid(SCARG(uap, modid));
254     if (!mod)
255 	return ENOENT;
256 
257     stat = SCARG(uap, stat);
258 
259     /*
260      * Check the version of the user's structure.
261      */
262     if (error = copyin(&stat->version, &version, sizeof(version)))
263 	goto out;
264     if (version != sizeof(struct module_stat)) {
265 	error = EINVAL;
266 	goto out;
267     }
268 
269     namelen = strlen(mod->name) + 1;
270     if (namelen > MAXMODNAME)
271 	namelen = MAXMODNAME;
272     if (error = copyout(mod->name, &stat->name[0], namelen))
273 	goto out;
274 
275     if (error = copyout(&mod->refs, &stat->refs, sizeof(int)))
276 	goto out;
277     if (error = copyout(&mod->id, &stat->id, sizeof(int)))
278 	goto out;
279 
280     *retval = 0;
281 
282 out:
283     return error;
284 }
285 
286 int
287 modfind(struct proc* p, struct modfind_args* uap, int* retval)
288 {
289     int error = 0;
290     char name[MAXMODNAME];
291     module_t mod;
292 
293     if (error = copyinstr(SCARG(uap, name), name, sizeof name, 0))
294 	goto out;
295 
296     mod = module_lookupbyname(name);
297     if (!mod)
298 	error = ENOENT;
299     else
300 	*retval = mod->id;
301 
302 out:
303     return error;
304 }
305