xref: /freebsd/sys/kern/kern_module.c (revision 287698b4f1801ca8a1ae50e576eaa323e990f680)
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;
61fcb893a8SMike Smith static void module_shutdown(void *, int);
62cea6c86cSDoug Rabson 
63845ccef4SPeter Wemm static int
64845ccef4SPeter Wemm modevent_nop(module_t mod, int what, void *arg)
65845ccef4SPeter Wemm {
66845ccef4SPeter Wemm 	return 0;
67845ccef4SPeter Wemm }
68845ccef4SPeter Wemm 
69845ccef4SPeter Wemm 
70cea6c86cSDoug Rabson static void
71cea6c86cSDoug Rabson module_init(void *arg)
72cea6c86cSDoug Rabson {
73287698b4SAndrew R. Reiter 
74cea6c86cSDoug Rabson 	TAILQ_INIT(&modules);
75fcb893a8SMike Smith 	EVENTHANDLER_REGISTER(shutdown_post_sync, module_shutdown, NULL,
76fcb893a8SMike Smith 	    SHUTDOWN_PRI_DEFAULT);
77cea6c86cSDoug Rabson }
78cea6c86cSDoug Rabson 
79287698b4SAndrew R. Reiter 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)
99287698b4SAndrew R. Reiter 		panic("module_register_init: module named %s not found\n",
100287698b4SAndrew R. Reiter 		    data->name);
101b5b15c3fSPeter Wemm 	error = MOD_EVENT(mod, MOD_LOAD);
102b5b15c3fSPeter Wemm 	if (error) {
103b5b15c3fSPeter Wemm 		MOD_EVENT(mod, MOD_UNLOAD);
104b5b15c3fSPeter Wemm 		module_release(mod);
105287698b4SAndrew R. Reiter 		printf(
106287698b4SAndrew R. Reiter 	"module_register_init: MOD_LOAD (%s, %lx, %p) error %d\n",
107287698b4SAndrew R. Reiter 		data->name, (u_long)(uintfptr_t)data->evhand, data->priv,
108287698b4SAndrew R. Reiter 		error);
109cea6c86cSDoug Rabson 	}
110b5b15c3fSPeter Wemm }
111cea6c86cSDoug Rabson 
112cea6c86cSDoug Rabson int
113b5b15c3fSPeter Wemm module_register(const moduledata_t *data, linker_file_t container)
114cea6c86cSDoug Rabson {
115cea6c86cSDoug Rabson 	size_t namelen;
116cea6c86cSDoug Rabson 	module_t newmod;
117cea6c86cSDoug Rabson 
118b5b15c3fSPeter Wemm 	newmod = module_lookupbyname(data->name);
119b5b15c3fSPeter Wemm 	if (newmod != NULL) {
120287698b4SAndrew R. Reiter 		printf("module_register: module %s already exists!\n",
121287698b4SAndrew R. Reiter 		    data->name);
122b5b15c3fSPeter Wemm 		return EEXIST;
123b5b15c3fSPeter Wemm 	}
124b5b15c3fSPeter Wemm 	namelen = strlen(data->name) + 1;
125287698b4SAndrew R. Reiter 	newmod = malloc(sizeof(struct module) + namelen, M_MODULE, M_WAITOK);
126287698b4SAndrew R. Reiter 	if (newmod == NULL)
127cea6c86cSDoug Rabson 		return ENOMEM;
128cea6c86cSDoug Rabson 	newmod->refs = 1;
129cea6c86cSDoug Rabson 	newmod->id = nextid++;
130cea6c86cSDoug Rabson 	newmod->name = (char *)(newmod + 1);
131b5b15c3fSPeter Wemm 	strcpy(newmod->name, data->name);
132845ccef4SPeter Wemm 	newmod->handler = data->evhand ? data->evhand : modevent_nop;
133b5b15c3fSPeter Wemm 	newmod->arg = data->priv;
134a35261efSDoug Rabson 	bzero(&newmod->data, sizeof(newmod->data));
135cea6c86cSDoug Rabson 	TAILQ_INSERT_TAIL(&modules, newmod, link);
136cea6c86cSDoug Rabson 
137b5b15c3fSPeter Wemm 	if (container)
138f3b0d442SPeter Wemm 		TAILQ_INSERT_TAIL(&container->modules, newmod, flink);
1391d23cba9SPeter Wemm 	newmod->file = container;
140cea6c86cSDoug Rabson 	return 0;
141cea6c86cSDoug Rabson }
142cea6c86cSDoug Rabson 
143cea6c86cSDoug Rabson void
144cea6c86cSDoug Rabson module_reference(module_t mod)
145cea6c86cSDoug Rabson {
146cea6c86cSDoug Rabson 
147287698b4SAndrew R. Reiter 	MOD_DPF(REFS, ("module_reference: before, refs=%d\n", mod->refs));
148cea6c86cSDoug Rabson 	mod->refs++;
149cea6c86cSDoug Rabson }
150cea6c86cSDoug Rabson 
151cea6c86cSDoug Rabson void
152cea6c86cSDoug Rabson module_release(module_t mod)
153cea6c86cSDoug Rabson {
154287698b4SAndrew R. Reiter 
155cea6c86cSDoug Rabson 	if (mod->refs <= 0)
156cea6c86cSDoug Rabson 		panic("module_release: bad reference count");
157cea6c86cSDoug Rabson 
158cea6c86cSDoug Rabson 	MOD_DPF(REFS, ("module_release: before, refs=%d\n", mod->refs));
159cea6c86cSDoug Rabson 
160cea6c86cSDoug Rabson 	mod->refs--;
161cea6c86cSDoug Rabson 	if (mod->refs == 0) {
162cea6c86cSDoug Rabson 		TAILQ_REMOVE(&modules, mod, link);
163287698b4SAndrew R. Reiter 		if (mod->file)
164cea6c86cSDoug Rabson 			TAILQ_REMOVE(&mod->file->modules, mod, flink);
165cea6c86cSDoug Rabson 		free(mod, M_MODULE);
166cea6c86cSDoug Rabson 	}
167cea6c86cSDoug Rabson }
168cea6c86cSDoug Rabson 
169cea6c86cSDoug Rabson module_t
170cea6c86cSDoug Rabson module_lookupbyname(const char *name)
171cea6c86cSDoug Rabson {
172cea6c86cSDoug Rabson 	module_t mod;
173287698b4SAndrew R. Reiter 	int err;
174cea6c86cSDoug Rabson 
175fc2ffbe6SPoul-Henning Kamp 	TAILQ_FOREACH(mod, &modules, link) {
176287698b4SAndrew R. Reiter 		err = strcmp(mod->name, name);
177287698b4SAndrew R. Reiter 		if (err == 0)
178cea6c86cSDoug Rabson 			return mod;
179cea6c86cSDoug Rabson 	}
180cea6c86cSDoug Rabson 	return 0;
181cea6c86cSDoug Rabson }
182cea6c86cSDoug Rabson 
183cea6c86cSDoug Rabson module_t
184cea6c86cSDoug Rabson module_lookupbyid(int modid)
185cea6c86cSDoug Rabson {
186cea6c86cSDoug Rabson 	module_t        mod;
187cea6c86cSDoug Rabson 
188fc2ffbe6SPoul-Henning Kamp 	TAILQ_FOREACH(mod, &modules, link) {
189cea6c86cSDoug Rabson 		if (mod->id == modid)
190cea6c86cSDoug Rabson 			return mod;
191cea6c86cSDoug Rabson 	}
192cea6c86cSDoug Rabson 	return 0;
193cea6c86cSDoug Rabson }
194cea6c86cSDoug Rabson 
195cea6c86cSDoug Rabson int
196cea6c86cSDoug Rabson module_unload(module_t mod)
197cea6c86cSDoug Rabson {
198287698b4SAndrew R. Reiter 
199cea6c86cSDoug Rabson 	return MOD_EVENT(mod, MOD_UNLOAD);
200cea6c86cSDoug Rabson }
201cea6c86cSDoug Rabson 
202cea6c86cSDoug Rabson int
203cea6c86cSDoug Rabson module_getid(module_t mod)
204cea6c86cSDoug Rabson {
205287698b4SAndrew R. Reiter 
206cea6c86cSDoug Rabson 	return mod->id;
207cea6c86cSDoug Rabson }
208cea6c86cSDoug Rabson 
209cea6c86cSDoug Rabson module_t
210cea6c86cSDoug Rabson module_getfnext(module_t mod)
211cea6c86cSDoug Rabson {
212287698b4SAndrew R. Reiter 
213cea6c86cSDoug Rabson 	return TAILQ_NEXT(mod, flink);
214cea6c86cSDoug Rabson }
215cea6c86cSDoug Rabson 
216a35261efSDoug Rabson void
217a35261efSDoug Rabson module_setspecific(module_t mod, modspecific_t *datap)
218a35261efSDoug Rabson {
219287698b4SAndrew R. Reiter 
220a35261efSDoug Rabson 	mod->data = *datap;
221a35261efSDoug Rabson }
222a35261efSDoug Rabson 
223cea6c86cSDoug Rabson /*
224cea6c86cSDoug Rabson  * Syscalls.
225cea6c86cSDoug Rabson  */
226835a82eeSMatthew Dillon /*
227835a82eeSMatthew Dillon  * MPSAFE
228835a82eeSMatthew Dillon  */
229cea6c86cSDoug Rabson int
230b40ce416SJulian Elischer modnext(struct thread *td, struct modnext_args *uap)
231cea6c86cSDoug Rabson {
232cea6c86cSDoug Rabson 	module_t mod;
233835a82eeSMatthew Dillon 	int error = 0;
234835a82eeSMatthew Dillon 
235835a82eeSMatthew Dillon 	mtx_lock(&Giant);
236cea6c86cSDoug Rabson 
237b40ce416SJulian Elischer 	td->td_retval[0] = -1;
238cea6c86cSDoug Rabson 	if (SCARG(uap, modid) == 0) {
239cea6c86cSDoug Rabson 		mod = TAILQ_FIRST(&modules);
240835a82eeSMatthew Dillon 		if (mod)
241b40ce416SJulian Elischer 			td->td_retval[0] = mod->id;
242835a82eeSMatthew Dillon 		else
243835a82eeSMatthew Dillon 			error = ENOENT;
244835a82eeSMatthew Dillon 		goto done2;
245cea6c86cSDoug Rabson 	}
246cea6c86cSDoug Rabson 	mod = module_lookupbyid(SCARG(uap, modid));
247835a82eeSMatthew Dillon 	if (mod == NULL) {
248835a82eeSMatthew Dillon 		error = ENOENT;
249835a82eeSMatthew Dillon 		goto done2;
250835a82eeSMatthew Dillon 	}
251cea6c86cSDoug Rabson 	if (TAILQ_NEXT(mod, link))
252b40ce416SJulian Elischer 		td->td_retval[0] = TAILQ_NEXT(mod, link)->id;
253cea6c86cSDoug Rabson 	else
254b40ce416SJulian Elischer 		td->td_retval[0] = 0;
255835a82eeSMatthew Dillon done2:
256835a82eeSMatthew Dillon 	mtx_unlock(&Giant);
257835a82eeSMatthew Dillon 	return (error);
258cea6c86cSDoug Rabson }
259cea6c86cSDoug Rabson 
260835a82eeSMatthew Dillon /*
261835a82eeSMatthew Dillon  * MPSAFE
262835a82eeSMatthew Dillon  */
263cea6c86cSDoug Rabson int
264b40ce416SJulian Elischer modfnext(struct thread *td, struct modfnext_args *uap)
265cea6c86cSDoug Rabson {
266cea6c86cSDoug Rabson 	module_t mod;
267835a82eeSMatthew Dillon 	int error;
268cea6c86cSDoug Rabson 
269b40ce416SJulian Elischer 	td->td_retval[0] = -1;
270cea6c86cSDoug Rabson 
271835a82eeSMatthew Dillon 	mtx_lock(&Giant);
272cea6c86cSDoug Rabson 
273835a82eeSMatthew Dillon 	mod = module_lookupbyid(SCARG(uap, modid));
274835a82eeSMatthew Dillon 	if (mod == NULL) {
275835a82eeSMatthew Dillon 		error = ENOENT;
276835a82eeSMatthew Dillon 	} else {
277835a82eeSMatthew Dillon 		error = 0;
278cea6c86cSDoug Rabson 		if (TAILQ_NEXT(mod, flink))
279b40ce416SJulian Elischer 			td->td_retval[0] = TAILQ_NEXT(mod, flink)->id;
280cea6c86cSDoug Rabson 		else
281b40ce416SJulian Elischer 			td->td_retval[0] = 0;
282835a82eeSMatthew Dillon 	}
283835a82eeSMatthew Dillon 	mtx_unlock(&Giant);
284835a82eeSMatthew Dillon 	return (error);
285cea6c86cSDoug Rabson }
286cea6c86cSDoug Rabson 
287a35261efSDoug Rabson struct module_stat_v1 {
288a35261efSDoug Rabson 	int version;	/* set to sizeof(struct module_stat) */
289a35261efSDoug Rabson 	char name[MAXMODNAME];
290a35261efSDoug Rabson 	int refs;
291a35261efSDoug Rabson 	int id;
292a35261efSDoug Rabson };
293a35261efSDoug Rabson 
294835a82eeSMatthew Dillon /*
295835a82eeSMatthew Dillon  * MPSAFE
296835a82eeSMatthew Dillon  */
297cea6c86cSDoug Rabson int
298b40ce416SJulian Elischer modstat(struct thread *td, struct modstat_args *uap)
299cea6c86cSDoug Rabson {
300cea6c86cSDoug Rabson 	module_t mod;
301cea6c86cSDoug Rabson 	int error = 0;
302cea6c86cSDoug Rabson 	int namelen;
303cea6c86cSDoug Rabson 	int version;
304cea6c86cSDoug Rabson 	struct module_stat *stat;
305cea6c86cSDoug Rabson 
306835a82eeSMatthew Dillon 	mtx_lock(&Giant);
307835a82eeSMatthew Dillon 
308cea6c86cSDoug Rabson 	mod = module_lookupbyid(SCARG(uap, modid));
309835a82eeSMatthew Dillon 	if (mod == NULL) {
310835a82eeSMatthew Dillon 		error = ENOENT;
311835a82eeSMatthew Dillon 		goto out;
312835a82eeSMatthew Dillon 	}
313cea6c86cSDoug Rabson 	stat = SCARG(uap, stat);
314cea6c86cSDoug Rabson 
315cea6c86cSDoug Rabson 	/*
316cea6c86cSDoug Rabson 	 * Check the version of the user's structure.
317cea6c86cSDoug Rabson 	 */
318d254af07SMatthew Dillon 	if ((error = copyin(&stat->version, &version, sizeof(version))) != 0)
319cea6c86cSDoug Rabson 		goto out;
320a35261efSDoug Rabson 	if (version != sizeof(struct module_stat_v1)
321a35261efSDoug Rabson 	    && version != sizeof(struct module_stat)) {
322cea6c86cSDoug Rabson 		error = EINVAL;
323cea6c86cSDoug Rabson 		goto out;
324cea6c86cSDoug Rabson 	}
325cea6c86cSDoug Rabson 	namelen = strlen(mod->name) + 1;
326cea6c86cSDoug Rabson 	if (namelen > MAXMODNAME)
327cea6c86cSDoug Rabson 		namelen = MAXMODNAME;
328d254af07SMatthew Dillon 	if ((error = copyout(mod->name, &stat->name[0], namelen)) != 0)
329cea6c86cSDoug Rabson 		goto out;
330cea6c86cSDoug Rabson 
331d254af07SMatthew Dillon 	if ((error = copyout(&mod->refs, &stat->refs, sizeof(int))) != 0)
332cea6c86cSDoug Rabson 		goto out;
333d254af07SMatthew Dillon 	if ((error = copyout(&mod->id, &stat->id, sizeof(int))) != 0)
334cea6c86cSDoug Rabson 		goto out;
335cea6c86cSDoug Rabson 
336a35261efSDoug Rabson 	/*
337a35261efSDoug Rabson 	 * >v1 stat includes module data.
338a35261efSDoug Rabson 	 */
339a35261efSDoug Rabson 	if (version == sizeof(struct module_stat)) {
340287698b4SAndrew R. Reiter 		if ((error = copyout(&mod->data, &stat->data,
341287698b4SAndrew R. Reiter 		    sizeof(mod->data))) != 0)
342a35261efSDoug Rabson 			goto out;
343f8047d25SDoug Rabson 	}
344b40ce416SJulian Elischer 	td->td_retval[0] = 0;
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