xref: /freebsd/sys/kern/kern_module.c (revision b40ce4165d5eb3a5de1515245055350ae3dbab8e)
1cea6c86cSDoug Rabson /*-
2cea6c86cSDoug Rabson  * Copyright (c) 1997 Doug Rabson
3cea6c86cSDoug Rabson  * All rights reserved.
4cea6c86cSDoug Rabson  *
5cea6c86cSDoug Rabson  * Redistribution and use in source and binary forms, with or without
6cea6c86cSDoug Rabson  * modification, are permitted provided that the following conditions
7cea6c86cSDoug Rabson  * are met:
8cea6c86cSDoug Rabson  * 1. Redistributions of source code must retain the above copyright
9cea6c86cSDoug Rabson  *    notice, this list of conditions and the following disclaimer.
10cea6c86cSDoug Rabson  * 2. Redistributions in binary form must reproduce the above copyright
11cea6c86cSDoug Rabson  *    notice, this list of conditions and the following disclaimer in the
12cea6c86cSDoug Rabson  *    documentation and/or other materials provided with the distribution.
13cea6c86cSDoug Rabson  *
14cea6c86cSDoug Rabson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15cea6c86cSDoug Rabson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16cea6c86cSDoug Rabson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17cea6c86cSDoug Rabson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18cea6c86cSDoug Rabson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19cea6c86cSDoug Rabson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20cea6c86cSDoug Rabson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21cea6c86cSDoug Rabson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22cea6c86cSDoug Rabson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23cea6c86cSDoug Rabson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24cea6c86cSDoug Rabson  * SUCH DAMAGE.
25cea6c86cSDoug Rabson  *
26c3aac50fSPeter Wemm  * $FreeBSD$
27cea6c86cSDoug Rabson  */
28cea6c86cSDoug Rabson 
29cea6c86cSDoug Rabson #include <sys/param.h>
30cea6c86cSDoug Rabson #include <sys/kernel.h>
31cea6c86cSDoug Rabson #include <sys/systm.h>
32fcb893a8SMike Smith #include <sys/eventhandler.h>
33cea6c86cSDoug Rabson #include <sys/malloc.h>
34cea6c86cSDoug Rabson #include <sys/sysproto.h>
35cea6c86cSDoug Rabson #include <sys/sysent.h>
36cea6c86cSDoug Rabson #include <sys/module.h>
37cea6c86cSDoug Rabson #include <sys/linker.h>
38cb226aaaSPoul-Henning Kamp #include <sys/proc.h>
39835a82eeSMatthew Dillon #include <sys/lock.h>
40835a82eeSMatthew Dillon #include <sys/mutex.h>
41cea6c86cSDoug Rabson 
42959b7375SPoul-Henning Kamp static MALLOC_DEFINE(M_MODULE, "module", "module data structures");
43cea6c86cSDoug Rabson 
44e3975643SJake Burkholder typedef TAILQ_HEAD(, module) modulelist_t;
45cea6c86cSDoug Rabson struct module {
46e3975643SJake Burkholder     TAILQ_ENTRY(module)	link;		/* chain together all modules */
47e3975643SJake Burkholder     TAILQ_ENTRY(module)	flink;		/* all modules in a file */
48cea6c86cSDoug Rabson     struct linker_file*	file;		/* file which contains this module */
49cea6c86cSDoug Rabson     int			refs;		/* reference count */
50cea6c86cSDoug Rabson     int			id;		/* unique id number */
51cea6c86cSDoug Rabson     char		*name;		/* module name */
52cea6c86cSDoug Rabson     modeventhand_t	handler;	/* event handler */
53cea6c86cSDoug Rabson     void		*arg;		/* argument for handler */
54a35261efSDoug Rabson     modspecific_t	data;		/* module specific data */
55cea6c86cSDoug Rabson };
56cea6c86cSDoug Rabson 
57cea6c86cSDoug Rabson #define MOD_EVENT(mod, type) (mod)->handler((mod), (type), (mod)->arg)
58cea6c86cSDoug Rabson 
59cea6c86cSDoug Rabson static modulelist_t modules;
60cea6c86cSDoug Rabson static int nextid = 1;
61cea6c86cSDoug Rabson 
62fcb893a8SMike Smith static void module_shutdown(void*, int);
63cea6c86cSDoug Rabson 
64845ccef4SPeter Wemm static int
65845ccef4SPeter Wemm modevent_nop(module_t mod, int what, void* arg)
66845ccef4SPeter Wemm {
67845ccef4SPeter Wemm 	return 0;
68845ccef4SPeter Wemm }
69845ccef4SPeter Wemm 
70845ccef4SPeter Wemm 
71cea6c86cSDoug Rabson static void
72cea6c86cSDoug Rabson module_init(void* arg)
73cea6c86cSDoug Rabson {
74cea6c86cSDoug Rabson     TAILQ_INIT(&modules);
75fcb893a8SMike Smith     EVENTHANDLER_REGISTER(shutdown_post_sync, module_shutdown, NULL,
76fcb893a8SMike Smith 			  SHUTDOWN_PRI_DEFAULT);
77cea6c86cSDoug Rabson }
78cea6c86cSDoug Rabson 
79aa855a59SPeter Wemm SYSINIT(module, SI_SUB_KLD, SI_ORDER_FIRST, module_init, 0);
80cea6c86cSDoug Rabson 
81cea6c86cSDoug Rabson static void
82fcb893a8SMike Smith module_shutdown(void* arg1, int arg2)
83cea6c86cSDoug Rabson {
84cea6c86cSDoug Rabson     module_t mod;
85cea6c86cSDoug Rabson 
86fc2ffbe6SPoul-Henning Kamp     TAILQ_FOREACH(mod, &modules, link)
87cea6c86cSDoug Rabson 	MOD_EVENT(mod, MOD_SHUTDOWN);
88cea6c86cSDoug Rabson }
89cea6c86cSDoug Rabson 
90cea6c86cSDoug Rabson void
913cfc69e6SMatthew Dillon module_register_init(const void *arg)
92cea6c86cSDoug Rabson {
933cfc69e6SMatthew Dillon     const moduledata_t* data = (const moduledata_t*) arg;
94cea6c86cSDoug Rabson     int error;
95b5b15c3fSPeter Wemm     module_t mod;
96cea6c86cSDoug Rabson 
97b5b15c3fSPeter Wemm     mod = module_lookupbyname(data->name);
98b5b15c3fSPeter Wemm     if (mod == NULL)
9954823af2SPeter Wemm 	panic("module_register_init: module named %s not found\n", data->name);
100b5b15c3fSPeter Wemm     error = MOD_EVENT(mod, MOD_LOAD);
101b5b15c3fSPeter Wemm     if (error) {
102b5b15c3fSPeter Wemm 	MOD_EVENT(mod, MOD_UNLOAD);
103b5b15c3fSPeter Wemm 	module_release(mod);
104b5b15c3fSPeter Wemm 	printf("module_register_init: MOD_LOAD (%s, %lx, %p) error %d\n",
10537889b39SBruce Evans 	       data->name, (u_long)(uintfptr_t)data->evhand, data->priv, error);
106cea6c86cSDoug Rabson     }
107b5b15c3fSPeter Wemm }
108cea6c86cSDoug Rabson 
109cea6c86cSDoug Rabson int
110b5b15c3fSPeter Wemm module_register(const moduledata_t *data, linker_file_t container)
111cea6c86cSDoug Rabson {
112cea6c86cSDoug Rabson     size_t namelen;
113cea6c86cSDoug Rabson     module_t newmod;
114cea6c86cSDoug Rabson 
115b5b15c3fSPeter Wemm     newmod = module_lookupbyname(data->name);
116b5b15c3fSPeter Wemm     if (newmod != NULL) {
117b5b15c3fSPeter Wemm 	printf("module_register: module %s already exists!\n", data->name);
118b5b15c3fSPeter Wemm 	return EEXIST;
119b5b15c3fSPeter Wemm     }
120b5b15c3fSPeter Wemm     namelen = strlen(data->name) + 1;
121cea6c86cSDoug Rabson     newmod = (module_t) malloc(sizeof(struct module) + namelen,
122cea6c86cSDoug Rabson 			       M_MODULE, M_WAITOK);
123cea6c86cSDoug Rabson     if (newmod == 0)
124cea6c86cSDoug Rabson 	return ENOMEM;
125cea6c86cSDoug Rabson 
126cea6c86cSDoug Rabson     newmod->refs = 1;
127cea6c86cSDoug Rabson     newmod->id = nextid++;
128cea6c86cSDoug Rabson     newmod->name = (char *) (newmod + 1);
129b5b15c3fSPeter Wemm     strcpy(newmod->name, data->name);
130845ccef4SPeter Wemm     newmod->handler = data->evhand ? data->evhand : modevent_nop;
131b5b15c3fSPeter Wemm     newmod->arg = data->priv;
132a35261efSDoug Rabson     bzero(&newmod->data, sizeof(newmod->data));
133cea6c86cSDoug Rabson     TAILQ_INSERT_TAIL(&modules, newmod, link);
134cea6c86cSDoug Rabson 
135b5b15c3fSPeter Wemm     if (container)
136f3b0d442SPeter Wemm 	TAILQ_INSERT_TAIL(&container->modules, newmod, flink);
1371d23cba9SPeter Wemm     newmod->file = container;
138cea6c86cSDoug Rabson 
139cea6c86cSDoug Rabson     return 0;
140cea6c86cSDoug Rabson }
141cea6c86cSDoug Rabson 
142cea6c86cSDoug Rabson void
143cea6c86cSDoug Rabson module_reference(module_t mod)
144cea6c86cSDoug Rabson {
145cea6c86cSDoug Rabson     MOD_DPF(REFS, ("module_reference: before, refs=%d\n", mod->refs));
146cea6c86cSDoug Rabson 
147cea6c86cSDoug Rabson     mod->refs++;
148cea6c86cSDoug Rabson }
149cea6c86cSDoug Rabson 
150cea6c86cSDoug Rabson void
151cea6c86cSDoug Rabson module_release(module_t mod)
152cea6c86cSDoug Rabson {
153cea6c86cSDoug Rabson     if (mod->refs <= 0)
154cea6c86cSDoug Rabson 	panic("module_release: bad reference count");
155cea6c86cSDoug Rabson 
156cea6c86cSDoug Rabson     MOD_DPF(REFS, ("module_release: before, refs=%d\n", mod->refs));
157cea6c86cSDoug Rabson 
158cea6c86cSDoug Rabson     mod->refs--;
159cea6c86cSDoug Rabson     if (mod->refs == 0) {
160cea6c86cSDoug Rabson 	TAILQ_REMOVE(&modules, mod, link);
161cea6c86cSDoug Rabson 	if (mod->file) {
162cea6c86cSDoug Rabson 	    TAILQ_REMOVE(&mod->file->modules, mod, flink);
163cea6c86cSDoug Rabson 	}
164cea6c86cSDoug Rabson 	free(mod, M_MODULE);
165cea6c86cSDoug Rabson     }
166cea6c86cSDoug Rabson }
167cea6c86cSDoug Rabson 
168cea6c86cSDoug Rabson module_t
169cea6c86cSDoug Rabson module_lookupbyname(const char* name)
170cea6c86cSDoug Rabson {
171cea6c86cSDoug Rabson     module_t mod;
172cea6c86cSDoug Rabson 
173fc2ffbe6SPoul-Henning Kamp     TAILQ_FOREACH(mod, &modules, link) {
174cea6c86cSDoug Rabson 	if (!strcmp(mod->name, name))
175cea6c86cSDoug Rabson 	    return mod;
176cea6c86cSDoug Rabson     }
177cea6c86cSDoug Rabson 
178cea6c86cSDoug Rabson     return 0;
179cea6c86cSDoug Rabson }
180cea6c86cSDoug Rabson 
181cea6c86cSDoug Rabson module_t
182cea6c86cSDoug Rabson module_lookupbyid(int modid)
183cea6c86cSDoug Rabson {
184cea6c86cSDoug Rabson     module_t mod;
185cea6c86cSDoug Rabson 
186fc2ffbe6SPoul-Henning Kamp     TAILQ_FOREACH(mod, &modules, link) {
187cea6c86cSDoug Rabson 	if (mod->id == modid)
188cea6c86cSDoug Rabson 	    return mod;
189cea6c86cSDoug Rabson     }
190cea6c86cSDoug Rabson 
191cea6c86cSDoug Rabson     return 0;
192cea6c86cSDoug Rabson }
193cea6c86cSDoug Rabson 
194cea6c86cSDoug Rabson int
195cea6c86cSDoug Rabson module_unload(module_t mod)
196cea6c86cSDoug Rabson {
197cea6c86cSDoug Rabson     return MOD_EVENT(mod, MOD_UNLOAD);
198cea6c86cSDoug Rabson }
199cea6c86cSDoug Rabson 
200cea6c86cSDoug Rabson int
201cea6c86cSDoug Rabson module_getid(module_t mod)
202cea6c86cSDoug Rabson {
203cea6c86cSDoug Rabson     return mod->id;
204cea6c86cSDoug Rabson }
205cea6c86cSDoug Rabson 
206cea6c86cSDoug Rabson module_t
207cea6c86cSDoug Rabson module_getfnext(module_t mod)
208cea6c86cSDoug Rabson {
209cea6c86cSDoug Rabson     return TAILQ_NEXT(mod, flink);
210cea6c86cSDoug Rabson }
211cea6c86cSDoug Rabson 
212a35261efSDoug Rabson void
213a35261efSDoug Rabson module_setspecific(module_t mod, modspecific_t *datap)
214a35261efSDoug Rabson {
215a35261efSDoug Rabson     mod->data = *datap;
216a35261efSDoug Rabson }
217a35261efSDoug Rabson 
218cea6c86cSDoug Rabson /*
219cea6c86cSDoug Rabson  * Syscalls.
220cea6c86cSDoug Rabson  */
221835a82eeSMatthew Dillon /*
222835a82eeSMatthew Dillon  * MPSAFE
223835a82eeSMatthew Dillon  */
224cea6c86cSDoug Rabson int
225b40ce416SJulian Elischer modnext(struct thread *td, struct modnext_args *uap)
226cea6c86cSDoug Rabson {
227cea6c86cSDoug Rabson     module_t mod;
228835a82eeSMatthew Dillon     int error = 0;
229835a82eeSMatthew Dillon 
230835a82eeSMatthew Dillon     mtx_lock(&Giant);
231cea6c86cSDoug Rabson 
232b40ce416SJulian Elischer     td->td_retval[0] = -1;
233cea6c86cSDoug Rabson     if (SCARG(uap, modid) == 0) {
234cea6c86cSDoug Rabson 	mod = TAILQ_FIRST(&modules);
235835a82eeSMatthew Dillon 	if (mod)
236b40ce416SJulian Elischer 	    td->td_retval[0] = mod->id;
237835a82eeSMatthew Dillon 	else
238835a82eeSMatthew Dillon 	    error = ENOENT;
239835a82eeSMatthew Dillon 	goto done2;
240cea6c86cSDoug Rabson     }
241cea6c86cSDoug Rabson 
242cea6c86cSDoug Rabson     mod = module_lookupbyid(SCARG(uap, modid));
243835a82eeSMatthew Dillon     if (mod == NULL) {
244835a82eeSMatthew Dillon 	error = ENOENT;
245835a82eeSMatthew Dillon 	goto done2;
246835a82eeSMatthew Dillon     }
247cea6c86cSDoug Rabson 
248cea6c86cSDoug Rabson     if (TAILQ_NEXT(mod, link))
249b40ce416SJulian Elischer 	td->td_retval[0] = TAILQ_NEXT(mod, link)->id;
250cea6c86cSDoug Rabson     else
251b40ce416SJulian Elischer 	td->td_retval[0] = 0;
252835a82eeSMatthew Dillon done2:
253835a82eeSMatthew Dillon     mtx_unlock(&Giant);
254835a82eeSMatthew Dillon     return (error);
255cea6c86cSDoug Rabson }
256cea6c86cSDoug Rabson 
257835a82eeSMatthew Dillon /*
258835a82eeSMatthew Dillon  * MPSAFE
259835a82eeSMatthew Dillon  */
260cea6c86cSDoug Rabson int
261b40ce416SJulian Elischer modfnext(struct thread *td, struct modfnext_args *uap)
262cea6c86cSDoug Rabson {
263cea6c86cSDoug Rabson     module_t mod;
264835a82eeSMatthew Dillon     int error;
265cea6c86cSDoug Rabson 
266b40ce416SJulian Elischer     td->td_retval[0] = -1;
267cea6c86cSDoug Rabson 
268835a82eeSMatthew Dillon     mtx_lock(&Giant);
269cea6c86cSDoug Rabson 
270835a82eeSMatthew Dillon     mod = module_lookupbyid(SCARG(uap, modid));
271835a82eeSMatthew Dillon     if (mod == NULL) {
272835a82eeSMatthew Dillon 	error = ENOENT;
273835a82eeSMatthew Dillon     } else {
274835a82eeSMatthew Dillon 	error = 0;
275cea6c86cSDoug Rabson 	if (TAILQ_NEXT(mod, flink))
276b40ce416SJulian Elischer 	    td->td_retval[0] = TAILQ_NEXT(mod, flink)->id;
277cea6c86cSDoug Rabson 	else
278b40ce416SJulian Elischer 	    td->td_retval[0] = 0;
279835a82eeSMatthew Dillon     }
280835a82eeSMatthew Dillon     mtx_unlock(&Giant);
281835a82eeSMatthew Dillon     return (error);
282cea6c86cSDoug Rabson }
283cea6c86cSDoug Rabson 
284a35261efSDoug Rabson struct module_stat_v1 {
285a35261efSDoug Rabson     int		version;	/* set to sizeof(struct module_stat) */
286a35261efSDoug Rabson     char	name[MAXMODNAME];
287a35261efSDoug Rabson     int		refs;
288a35261efSDoug Rabson     int		id;
289a35261efSDoug Rabson };
290a35261efSDoug Rabson 
291835a82eeSMatthew Dillon /*
292835a82eeSMatthew Dillon  * MPSAFE
293835a82eeSMatthew Dillon  */
294cea6c86cSDoug Rabson int
295b40ce416SJulian Elischer modstat(struct thread *td, struct modstat_args *uap)
296cea6c86cSDoug Rabson {
297cea6c86cSDoug Rabson     module_t mod;
298cea6c86cSDoug Rabson     int error = 0;
299cea6c86cSDoug Rabson     int namelen;
300cea6c86cSDoug Rabson     int version;
301cea6c86cSDoug Rabson     struct module_stat* stat;
302cea6c86cSDoug Rabson 
303835a82eeSMatthew Dillon     mtx_lock(&Giant);
304835a82eeSMatthew Dillon 
305cea6c86cSDoug Rabson     mod = module_lookupbyid(SCARG(uap, modid));
306835a82eeSMatthew Dillon     if (mod == NULL) {
307835a82eeSMatthew Dillon 	error = ENOENT;
308835a82eeSMatthew Dillon 	goto out;
309835a82eeSMatthew Dillon     }
310cea6c86cSDoug Rabson 
311cea6c86cSDoug Rabson     stat = SCARG(uap, stat);
312cea6c86cSDoug Rabson 
313cea6c86cSDoug Rabson     /*
314cea6c86cSDoug Rabson      * Check the version of the user's structure.
315cea6c86cSDoug Rabson      */
316d254af07SMatthew Dillon     if ((error = copyin(&stat->version, &version, sizeof(version))) != 0)
317cea6c86cSDoug Rabson 	goto out;
318a35261efSDoug Rabson     if (version != sizeof(struct module_stat_v1)
319a35261efSDoug Rabson 	&& version != sizeof(struct module_stat)) {
320cea6c86cSDoug Rabson 	error = EINVAL;
321cea6c86cSDoug Rabson 	goto out;
322cea6c86cSDoug Rabson     }
323cea6c86cSDoug Rabson 
324cea6c86cSDoug Rabson     namelen = strlen(mod->name) + 1;
325cea6c86cSDoug Rabson     if (namelen > MAXMODNAME)
326cea6c86cSDoug Rabson 	namelen = MAXMODNAME;
327d254af07SMatthew Dillon     if ((error = copyout(mod->name, &stat->name[0], namelen)) != 0)
328cea6c86cSDoug Rabson 	goto out;
329cea6c86cSDoug Rabson 
330d254af07SMatthew Dillon     if ((error = copyout(&mod->refs, &stat->refs, sizeof(int))) != 0)
331cea6c86cSDoug Rabson 	goto out;
332d254af07SMatthew Dillon     if ((error = copyout(&mod->id, &stat->id, sizeof(int))) != 0)
333cea6c86cSDoug Rabson 	goto out;
334cea6c86cSDoug Rabson 
335a35261efSDoug Rabson     /*
336a35261efSDoug Rabson      * >v1 stat includes module data.
337a35261efSDoug Rabson      */
338a35261efSDoug Rabson     if (version == sizeof(struct module_stat)) {
339d254af07SMatthew Dillon 	if ((error = copyout(&mod->data, &stat->data, sizeof(mod->data))) != 0)
340a35261efSDoug Rabson 	    goto out;
341f8047d25SDoug Rabson     }
342a35261efSDoug Rabson 
343b40ce416SJulian Elischer     td->td_retval[0] = 0;
344cea6c86cSDoug Rabson 
345cea6c86cSDoug Rabson out:
346835a82eeSMatthew Dillon     mtx_unlock(&Giant);
347cea6c86cSDoug Rabson     return error;
348cea6c86cSDoug Rabson }
349cea6c86cSDoug Rabson 
350835a82eeSMatthew Dillon /*
351835a82eeSMatthew Dillon  * MPSAFE
352835a82eeSMatthew Dillon  */
353cea6c86cSDoug Rabson int
354b40ce416SJulian Elischer modfind(struct thread *td, struct modfind_args *uap)
355cea6c86cSDoug Rabson {
356cea6c86cSDoug Rabson     int error = 0;
357cea6c86cSDoug Rabson     char name[MAXMODNAME];
358cea6c86cSDoug Rabson     module_t mod;
359cea6c86cSDoug Rabson 
360d254af07SMatthew Dillon     if ((error = copyinstr(SCARG(uap, name), name, sizeof name, 0)) != 0)
361cea6c86cSDoug Rabson 	goto out;
362cea6c86cSDoug Rabson 
363835a82eeSMatthew Dillon     mtx_lock(&Giant);
364cea6c86cSDoug Rabson     mod = module_lookupbyname(name);
365835a82eeSMatthew Dillon     if (mod == NULL)
366cea6c86cSDoug Rabson 	error = ENOENT;
367cea6c86cSDoug Rabson     else
368b40ce416SJulian Elischer 	td->td_retval[0] = mod->id;
369835a82eeSMatthew Dillon     mtx_unlock(&Giant);
370cea6c86cSDoug Rabson out:
371cea6c86cSDoug Rabson     return error;
372cea6c86cSDoug Rabson }
373