kern_module.c revision 174377
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
27#include "opt_compat.h"
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD: head/sys/kern/kern_module.c 174377 2007-12-06 23:11:27Z jhb $");
31
32#include <sys/param.h>
33#include <sys/kernel.h>
34#include <sys/systm.h>
35#include <sys/eventhandler.h>
36#include <sys/malloc.h>
37#include <sys/sysproto.h>
38#include <sys/sysent.h>
39#include <sys/proc.h>
40#include <sys/lock.h>
41#include <sys/mutex.h>
42#include <sys/reboot.h>
43#include <sys/sx.h>
44#include <sys/module.h>
45#include <sys/linker.h>
46
47static MALLOC_DEFINE(M_MODULE, "module", "module data structures");
48
49typedef TAILQ_HEAD(, module) modulelist_t;
50struct module {
51	TAILQ_ENTRY(module)	link;	/* chain together all modules */
52	TAILQ_ENTRY(module)	flink;	/* all modules in a file */
53	struct linker_file	*file;	/* file which contains this module */
54	int			refs;	/* reference count */
55	int 			id;	/* unique id number */
56	char 			*name;	/* module name */
57	modeventhand_t 		handler;	/* event handler */
58	void 			*arg;	/* argument for handler */
59	modspecific_t 		data;	/* module specific data */
60};
61
62#define MOD_EVENT(mod, type)	(mod)->handler((mod), (type), (mod)->arg)
63
64static modulelist_t modules;
65struct sx modules_sx;
66static int nextid = 1;
67static void module_shutdown(void *, int);
68
69static int
70modevent_nop(module_t mod, int what, void *arg)
71{
72
73	switch(what) {
74	case MOD_LOAD:
75		return (0);
76	case MOD_UNLOAD:
77		return (EBUSY);
78	default:
79		return (EOPNOTSUPP);
80	}
81}
82
83static void
84module_init(void *arg)
85{
86
87	sx_init(&modules_sx, "module subsystem sx lock");
88	TAILQ_INIT(&modules);
89	EVENTHANDLER_REGISTER(shutdown_final, module_shutdown, NULL,
90	    SHUTDOWN_PRI_DEFAULT);
91}
92
93SYSINIT(module, SI_SUB_KLD, SI_ORDER_FIRST, module_init, 0)
94
95static void
96module_shutdown(void *arg1, int arg2)
97{
98	module_t mod;
99
100	if (arg2 & RB_NOSYNC)
101		return;
102	mtx_lock(&Giant);
103	MOD_SLOCK;
104	TAILQ_FOREACH(mod, &modules, link)
105		MOD_EVENT(mod, MOD_SHUTDOWN);
106	MOD_SUNLOCK;
107	mtx_unlock(&Giant);
108}
109
110void
111module_register_init(const void *arg)
112{
113	const moduledata_t *data = (const moduledata_t *)arg;
114	int error;
115	module_t mod;
116
117	mtx_lock(&Giant);
118	MOD_SLOCK;
119	mod = module_lookupbyname(data->name);
120	if (mod == NULL)
121		panic("module_register_init: module named %s not found\n",
122		    data->name);
123	MOD_SUNLOCK;
124	error = MOD_EVENT(mod, MOD_LOAD);
125	if (error) {
126		MOD_EVENT(mod, MOD_UNLOAD);
127		MOD_XLOCK;
128		module_release(mod);
129		MOD_XUNLOCK;
130		printf("module_register_init: MOD_LOAD (%s, %p, %p) error"
131		    " %d\n", data->name, (void *)data->evhand, data->priv,
132		    error);
133	}
134	mtx_unlock(&Giant);
135}
136
137int
138module_register(const moduledata_t *data, linker_file_t container)
139{
140	size_t namelen;
141	module_t newmod;
142
143	MOD_XLOCK;
144	newmod = module_lookupbyname(data->name);
145	if (newmod != NULL) {
146		MOD_XUNLOCK;
147		printf("module_register: module %s already exists!\n",
148		    data->name);
149		return (EEXIST);
150	}
151	namelen = strlen(data->name) + 1;
152	newmod = malloc(sizeof(struct module) + namelen, M_MODULE, M_WAITOK);
153	if (newmod == NULL) {
154		MOD_XUNLOCK;
155		return (ENOMEM);
156	}
157	newmod->refs = 1;
158	newmod->id = nextid++;
159	newmod->name = (char *)(newmod + 1);
160	strcpy(newmod->name, data->name);
161	newmod->handler = data->evhand ? data->evhand : modevent_nop;
162	newmod->arg = data->priv;
163	bzero(&newmod->data, sizeof(newmod->data));
164	TAILQ_INSERT_TAIL(&modules, newmod, link);
165
166	if (container)
167		TAILQ_INSERT_TAIL(&container->modules, newmod, flink);
168	newmod->file = container;
169	MOD_XUNLOCK;
170	return (0);
171}
172
173void
174module_reference(module_t mod)
175{
176
177	MOD_XLOCK_ASSERT;
178
179	MOD_DPF(REFS, ("module_reference: before, refs=%d\n", mod->refs));
180	mod->refs++;
181}
182
183void
184module_release(module_t mod)
185{
186
187	MOD_XLOCK_ASSERT;
188
189	if (mod->refs <= 0)
190		panic("module_release: bad reference count");
191
192	MOD_DPF(REFS, ("module_release: before, refs=%d\n", mod->refs));
193
194	mod->refs--;
195	if (mod->refs == 0) {
196		TAILQ_REMOVE(&modules, mod, link);
197		if (mod->file)
198			TAILQ_REMOVE(&mod->file->modules, mod, flink);
199		MOD_XUNLOCK;
200		free(mod, M_MODULE);
201		MOD_XLOCK;
202	}
203}
204
205module_t
206module_lookupbyname(const char *name)
207{
208	module_t mod;
209	int err;
210
211	MOD_LOCK_ASSERT;
212
213	TAILQ_FOREACH(mod, &modules, link) {
214		err = strcmp(mod->name, name);
215		if (err == 0)
216			return (mod);
217	}
218	return (NULL);
219}
220
221module_t
222module_lookupbyid(int modid)
223{
224        module_t mod;
225
226        MOD_LOCK_ASSERT;
227
228        TAILQ_FOREACH(mod, &modules, link)
229                if (mod->id == modid)
230                        return(mod);
231        return (NULL);
232}
233
234int
235module_unload(module_t mod, int flags)
236{
237	int error;
238
239	mtx_lock(&Giant);
240	error = MOD_EVENT(mod, MOD_QUIESCE);
241	if (error == EOPNOTSUPP || error == EINVAL)
242		error = 0;
243	if (error == 0 || flags == LINKER_UNLOAD_FORCE)
244		error = MOD_EVENT(mod, MOD_UNLOAD);
245	mtx_unlock(&Giant);
246	return (error);
247}
248
249int
250module_getid(module_t mod)
251{
252
253	MOD_LOCK_ASSERT;
254	return (mod->id);
255}
256
257module_t
258module_getfnext(module_t mod)
259{
260
261	MOD_LOCK_ASSERT;
262	return (TAILQ_NEXT(mod, flink));
263}
264
265void
266module_setspecific(module_t mod, modspecific_t *datap)
267{
268
269	MOD_XLOCK_ASSERT;
270	mod->data = *datap;
271}
272
273linker_file_t
274module_file(module_t mod)
275{
276
277	return (mod->file);
278}
279
280/*
281 * Syscalls.
282 */
283int
284modnext(struct thread *td, struct modnext_args *uap)
285{
286	module_t mod;
287	int error = 0;
288
289	td->td_retval[0] = -1;
290
291	MOD_SLOCK;
292	if (uap->modid == 0) {
293		mod = TAILQ_FIRST(&modules);
294		if (mod)
295			td->td_retval[0] = mod->id;
296		else
297			error = ENOENT;
298		goto done2;
299	}
300	mod = module_lookupbyid(uap->modid);
301	if (mod == NULL) {
302		error = ENOENT;
303		goto done2;
304	}
305	if (TAILQ_NEXT(mod, link))
306		td->td_retval[0] = TAILQ_NEXT(mod, link)->id;
307	else
308		td->td_retval[0] = 0;
309done2:
310	MOD_SUNLOCK;
311	return (error);
312}
313
314int
315modfnext(struct thread *td, struct modfnext_args *uap)
316{
317	module_t mod;
318	int error;
319
320	td->td_retval[0] = -1;
321
322	MOD_SLOCK;
323	mod = module_lookupbyid(uap->modid);
324	if (mod == NULL) {
325		error = ENOENT;
326	} else {
327		error = 0;
328		if (TAILQ_NEXT(mod, flink))
329			td->td_retval[0] = TAILQ_NEXT(mod, flink)->id;
330		else
331			td->td_retval[0] = 0;
332	}
333	MOD_SUNLOCK;
334	return (error);
335}
336
337struct module_stat_v1 {
338	int	version;		/* set to sizeof(struct module_stat) */
339	char	name[MAXMODNAME];
340	int	refs;
341	int	id;
342};
343
344int
345modstat(struct thread *td, struct modstat_args *uap)
346{
347	module_t mod;
348	modspecific_t data;
349	int error = 0;
350	int id, namelen, refs, version;
351	struct module_stat *stat;
352	char *name;
353
354	MOD_SLOCK;
355	mod = module_lookupbyid(uap->modid);
356	if (mod == NULL) {
357		MOD_SUNLOCK;
358		return (ENOENT);
359	}
360	id = mod->id;
361	refs = mod->refs;
362	name = mod->name;
363	data = mod->data;
364	MOD_SUNLOCK;
365	stat = uap->stat;
366
367	/*
368	 * Check the version of the user's structure.
369	 */
370	if ((error = copyin(&stat->version, &version, sizeof(version))) != 0)
371		return (error);
372	if (version != sizeof(struct module_stat_v1)
373	    && version != sizeof(struct module_stat))
374		return (EINVAL);
375	namelen = strlen(mod->name) + 1;
376	if (namelen > MAXMODNAME)
377		namelen = MAXMODNAME;
378	if ((error = copyout(name, &stat->name[0], namelen)) != 0)
379		return (error);
380
381	if ((error = copyout(&refs, &stat->refs, sizeof(int))) != 0)
382		return (error);
383	if ((error = copyout(&id, &stat->id, sizeof(int))) != 0)
384		return (error);
385
386	/*
387	 * >v1 stat includes module data.
388	 */
389	if (version == sizeof(struct module_stat))
390		if ((error = copyout(&data, &stat->data,
391		    sizeof(data))) != 0)
392			return (error);
393	td->td_retval[0] = 0;
394	return (error);
395}
396
397int
398modfind(struct thread *td, struct modfind_args *uap)
399{
400	int error = 0;
401	char name[MAXMODNAME];
402	module_t mod;
403
404	if ((error = copyinstr(uap->name, name, sizeof name, 0)) != 0)
405		return (error);
406
407	MOD_SLOCK;
408	mod = module_lookupbyname(name);
409	if (mod == NULL)
410		error = ENOENT;
411	else
412		td->td_retval[0] = module_getid(mod);
413	MOD_SUNLOCK;
414	return (error);
415}
416
417#ifdef COMPAT_IA32
418#include <sys/mount.h>
419#include <sys/socket.h>
420#include <compat/freebsd32/freebsd32_util.h>
421#include <compat/freebsd32/freebsd32.h>
422#include <compat/freebsd32/freebsd32_proto.h>
423
424typedef union modspecific32 {
425	int		intval;
426	u_int32_t	uintval;
427	int		longval;
428	u_int32_t	ulongval;
429} modspecific32_t;
430
431struct module_stat32 {
432	int		version;
433	char		name[MAXMODNAME];
434	int		refs;
435	int		id;
436	modspecific32_t	data;
437};
438
439int
440freebsd32_modstat(struct thread *td, struct freebsd32_modstat_args *uap)
441{
442	module_t mod;
443	modspecific32_t data32;
444	int error = 0;
445	int id, namelen, refs, version;
446	struct module_stat32 *stat32;
447	char *name;
448
449	MOD_SLOCK;
450	mod = module_lookupbyid(uap->modid);
451	if (mod == NULL) {
452		MOD_SUNLOCK;
453		return (ENOENT);
454	}
455
456	id = mod->id;
457	refs = mod->refs;
458	name = mod->name;
459	CP(mod->data, data32, intval);
460	CP(mod->data, data32, uintval);
461	CP(mod->data, data32, longval);
462	CP(mod->data, data32, ulongval);
463	MOD_SUNLOCK;
464	stat32 = uap->stat;
465
466	if ((error = copyin(&stat32->version, &version, sizeof(version))) != 0)
467		return (error);
468	if (version != sizeof(struct module_stat_v1)
469	    && version != sizeof(struct module_stat32))
470		return (EINVAL);
471	namelen = strlen(mod->name) + 1;
472	if (namelen > MAXMODNAME)
473		namelen = MAXMODNAME;
474	if ((error = copyout(name, &stat32->name[0], namelen)) != 0)
475		return (error);
476
477	if ((error = copyout(&refs, &stat32->refs, sizeof(int))) != 0)
478		return (error);
479	if ((error = copyout(&id, &stat32->id, sizeof(int))) != 0)
480		return (error);
481
482	/*
483	 * >v1 stat includes module data.
484	 */
485	if (version == sizeof(struct module_stat32))
486		if ((error = copyout(&data32, &stat32->data,
487		    sizeof(data32))) != 0)
488			return (error);
489	td->td_retval[0] = 0;
490	return (error);
491}
492#endif
493