xref: /freebsd/sys/kern/kern_module.c (revision 6990ffd8a95caaba6858ad44ff1b3157d1efba8f)
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  * $FreeBSD$
27  */
28 
29 #include <sys/param.h>
30 #include <sys/kernel.h>
31 #include <sys/systm.h>
32 #include <sys/eventhandler.h>
33 #include <sys/malloc.h>
34 #include <sys/sysproto.h>
35 #include <sys/sysent.h>
36 #include <sys/module.h>
37 #include <sys/linker.h>
38 #include <sys/proc.h>
39 #include <sys/lock.h>
40 #include <sys/mutex.h>
41 
42 static MALLOC_DEFINE(M_MODULE, "module", "module data structures");
43 
44 typedef TAILQ_HEAD(, module) modulelist_t;
45 struct module {
46     TAILQ_ENTRY(module)	link;		/* chain together all modules */
47     TAILQ_ENTRY(module)	flink;		/* all modules in a file */
48     struct linker_file*	file;		/* file which contains this module */
49     int			refs;		/* reference count */
50     int			id;		/* unique id number */
51     char		*name;		/* module name */
52     modeventhand_t	handler;	/* event handler */
53     void		*arg;		/* argument for handler */
54     modspecific_t	data;		/* module specific data */
55 };
56 
57 #define MOD_EVENT(mod, type) (mod)->handler((mod), (type), (mod)->arg)
58 
59 static modulelist_t modules;
60 static int nextid = 1;
61 
62 static void module_shutdown(void*, int);
63 
64 static int
65 modevent_nop(module_t mod, int what, void* arg)
66 {
67 	return 0;
68 }
69 
70 
71 static void
72 module_init(void* arg)
73 {
74     TAILQ_INIT(&modules);
75     EVENTHANDLER_REGISTER(shutdown_post_sync, module_shutdown, NULL,
76 			  SHUTDOWN_PRI_DEFAULT);
77 }
78 
79 SYSINIT(module, SI_SUB_KLD, SI_ORDER_FIRST, module_init, 0);
80 
81 static void
82 module_shutdown(void* arg1, int arg2)
83 {
84     module_t mod;
85 
86     TAILQ_FOREACH(mod, &modules, link)
87 	MOD_EVENT(mod, MOD_SHUTDOWN);
88 }
89 
90 void
91 module_register_init(const void *arg)
92 {
93     const moduledata_t* data = (const moduledata_t*) arg;
94     int error;
95     module_t mod;
96 
97     mod = module_lookupbyname(data->name);
98     if (mod == NULL)
99 	panic("module_register_init: module named %s not found\n", data->name);
100     error = MOD_EVENT(mod, MOD_LOAD);
101     if (error) {
102 	MOD_EVENT(mod, MOD_UNLOAD);
103 	module_release(mod);
104 	printf("module_register_init: MOD_LOAD (%s, %lx, %p) error %d\n",
105 	       data->name, (u_long)(uintfptr_t)data->evhand, data->priv, error);
106     }
107 }
108 
109 int
110 module_register(const moduledata_t *data, linker_file_t container)
111 {
112     size_t namelen;
113     module_t newmod;
114 
115     newmod = module_lookupbyname(data->name);
116     if (newmod != NULL) {
117 	printf("module_register: module %s already exists!\n", data->name);
118 	return EEXIST;
119     }
120     namelen = strlen(data->name) + 1;
121     newmod = (module_t) malloc(sizeof(struct module) + namelen,
122 			       M_MODULE, M_WAITOK);
123     if (newmod == 0)
124 	return ENOMEM;
125 
126     newmod->refs = 1;
127     newmod->id = nextid++;
128     newmod->name = (char *) (newmod + 1);
129     strcpy(newmod->name, data->name);
130     newmod->handler = data->evhand ? data->evhand : modevent_nop;
131     newmod->arg = data->priv;
132     bzero(&newmod->data, sizeof(newmod->data));
133     TAILQ_INSERT_TAIL(&modules, newmod, link);
134 
135     if (container)
136 	TAILQ_INSERT_TAIL(&container->modules, newmod, flink);
137     newmod->file = container;
138 
139     return 0;
140 }
141 
142 void
143 module_reference(module_t mod)
144 {
145     MOD_DPF(REFS, ("module_reference: before, refs=%d\n", mod->refs));
146 
147     mod->refs++;
148 }
149 
150 void
151 module_release(module_t mod)
152 {
153     if (mod->refs <= 0)
154 	panic("module_release: bad reference count");
155 
156     MOD_DPF(REFS, ("module_release: before, refs=%d\n", mod->refs));
157 
158     mod->refs--;
159     if (mod->refs == 0) {
160 	TAILQ_REMOVE(&modules, mod, link);
161 	if (mod->file) {
162 	    TAILQ_REMOVE(&mod->file->modules, mod, flink);
163 	}
164 	free(mod, M_MODULE);
165     }
166 }
167 
168 module_t
169 module_lookupbyname(const char* name)
170 {
171     module_t mod;
172 
173     TAILQ_FOREACH(mod, &modules, link) {
174 	if (!strcmp(mod->name, name))
175 	    return mod;
176     }
177 
178     return 0;
179 }
180 
181 module_t
182 module_lookupbyid(int modid)
183 {
184     module_t mod;
185 
186     TAILQ_FOREACH(mod, &modules, link) {
187 	if (mod->id == modid)
188 	    return mod;
189     }
190 
191     return 0;
192 }
193 
194 int
195 module_unload(module_t mod)
196 {
197     return MOD_EVENT(mod, MOD_UNLOAD);
198 }
199 
200 int
201 module_getid(module_t mod)
202 {
203     return mod->id;
204 }
205 
206 module_t
207 module_getfnext(module_t mod)
208 {
209     return TAILQ_NEXT(mod, flink);
210 }
211 
212 void
213 module_setspecific(module_t mod, modspecific_t *datap)
214 {
215     mod->data = *datap;
216 }
217 
218 /*
219  * Syscalls.
220  */
221 /*
222  * MPSAFE
223  */
224 int
225 modnext(struct thread *td, struct modnext_args *uap)
226 {
227     module_t mod;
228     int error = 0;
229 
230     mtx_lock(&Giant);
231 
232     td->td_retval[0] = -1;
233     if (SCARG(uap, modid) == 0) {
234 	mod = TAILQ_FIRST(&modules);
235 	if (mod)
236 	    td->td_retval[0] = mod->id;
237 	else
238 	    error = ENOENT;
239 	goto done2;
240     }
241 
242     mod = module_lookupbyid(SCARG(uap, modid));
243     if (mod == NULL) {
244 	error = ENOENT;
245 	goto done2;
246     }
247 
248     if (TAILQ_NEXT(mod, link))
249 	td->td_retval[0] = TAILQ_NEXT(mod, link)->id;
250     else
251 	td->td_retval[0] = 0;
252 done2:
253     mtx_unlock(&Giant);
254     return (error);
255 }
256 
257 /*
258  * MPSAFE
259  */
260 int
261 modfnext(struct thread *td, struct modfnext_args *uap)
262 {
263     module_t mod;
264     int error;
265 
266     td->td_retval[0] = -1;
267 
268     mtx_lock(&Giant);
269 
270     mod = module_lookupbyid(SCARG(uap, modid));
271     if (mod == NULL) {
272 	error = ENOENT;
273     } else {
274 	error = 0;
275 	if (TAILQ_NEXT(mod, flink))
276 	    td->td_retval[0] = TAILQ_NEXT(mod, flink)->id;
277 	else
278 	    td->td_retval[0] = 0;
279     }
280     mtx_unlock(&Giant);
281     return (error);
282 }
283 
284 struct module_stat_v1 {
285     int		version;	/* set to sizeof(struct module_stat) */
286     char	name[MAXMODNAME];
287     int		refs;
288     int		id;
289 };
290 
291 /*
292  * MPSAFE
293  */
294 int
295 modstat(struct thread *td, struct modstat_args *uap)
296 {
297     module_t mod;
298     int error = 0;
299     int namelen;
300     int version;
301     struct module_stat* stat;
302 
303     mtx_lock(&Giant);
304 
305     mod = module_lookupbyid(SCARG(uap, modid));
306     if (mod == NULL) {
307 	error = ENOENT;
308 	goto out;
309     }
310 
311     stat = SCARG(uap, stat);
312 
313     /*
314      * Check the version of the user's structure.
315      */
316     if ((error = copyin(&stat->version, &version, sizeof(version))) != 0)
317 	goto out;
318     if (version != sizeof(struct module_stat_v1)
319 	&& version != sizeof(struct module_stat)) {
320 	error = EINVAL;
321 	goto out;
322     }
323 
324     namelen = strlen(mod->name) + 1;
325     if (namelen > MAXMODNAME)
326 	namelen = MAXMODNAME;
327     if ((error = copyout(mod->name, &stat->name[0], namelen)) != 0)
328 	goto out;
329 
330     if ((error = copyout(&mod->refs, &stat->refs, sizeof(int))) != 0)
331 	goto out;
332     if ((error = copyout(&mod->id, &stat->id, sizeof(int))) != 0)
333 	goto out;
334 
335     /*
336      * >v1 stat includes module data.
337      */
338     if (version == sizeof(struct module_stat)) {
339 	if ((error = copyout(&mod->data, &stat->data, sizeof(mod->data))) != 0)
340 	    goto out;
341     }
342 
343     td->td_retval[0] = 0;
344 
345 out:
346     mtx_unlock(&Giant);
347     return error;
348 }
349 
350 /*
351  * MPSAFE
352  */
353 int
354 modfind(struct thread *td, struct modfind_args *uap)
355 {
356     int error = 0;
357     char name[MAXMODNAME];
358     module_t mod;
359 
360     if ((error = copyinstr(SCARG(uap, name), name, sizeof name, 0)) != 0)
361 	goto out;
362 
363     mtx_lock(&Giant);
364     mod = module_lookupbyname(name);
365     if (mod == NULL)
366 	error = ENOENT;
367     else
368 	td->td_retval[0] = mod->id;
369     mtx_unlock(&Giant);
370 out:
371     return error;
372 }
373