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