kern_module.c revision 159957
125537Sdfr/*-
225537Sdfr * Copyright (c) 1997 Doug Rabson
325537Sdfr * All rights reserved.
425537Sdfr *
525537Sdfr * Redistribution and use in source and binary forms, with or without
625537Sdfr * modification, are permitted provided that the following conditions
725537Sdfr * are met:
825537Sdfr * 1. Redistributions of source code must retain the above copyright
925537Sdfr *    notice, this list of conditions and the following disclaimer.
1025537Sdfr * 2. Redistributions in binary form must reproduce the above copyright
1125537Sdfr *    notice, this list of conditions and the following disclaimer in the
1225537Sdfr *    documentation and/or other materials provided with the distribution.
1325537Sdfr *
1425537Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1525537Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1625537Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1725537Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1825537Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1925537Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2025537Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2125537Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2225537Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2325537Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2425537Sdfr * SUCH DAMAGE.
2525537Sdfr */
2625537Sdfr
27140482Sps#include "opt_compat.h"
28140482Sps
29116182Sobrien#include <sys/cdefs.h>
30116182Sobrien__FBSDID("$FreeBSD: head/sys/kern/kern_module.c 159957 2006-06-26 18:34:45Z jhb $");
31116182Sobrien
3225537Sdfr#include <sys/param.h>
3325537Sdfr#include <sys/kernel.h>
3425537Sdfr#include <sys/systm.h>
3550107Smsmith#include <sys/eventhandler.h>
3625537Sdfr#include <sys/malloc.h>
3725537Sdfr#include <sys/sysproto.h>
3825537Sdfr#include <sys/sysent.h>
3930994Sphk#include <sys/proc.h>
4082749Sdillon#include <sys/lock.h>
4182749Sdillon#include <sys/mutex.h>
42134092Struckman#include <sys/reboot.h>
4392547Sarr#include <sys/sx.h>
4492547Sarr#include <sys/module.h>
4592547Sarr#include <sys/linker.h>
4625537Sdfr
4769774Sphkstatic MALLOC_DEFINE(M_MODULE, "module", "module data structures");
4825537Sdfr
4960938Sjaketypedef TAILQ_HEAD(, module) modulelist_t;
5025537Sdfrstruct module {
5190963Sarr	TAILQ_ENTRY(module)	link;	/* chain together all modules */
5290963Sarr	TAILQ_ENTRY(module)	flink;	/* all modules in a file */
5390963Sarr	struct linker_file	*file;	/* file which contains this module */
5490963Sarr	int			refs;	/* reference count */
5590963Sarr	int 			id;	/* unique id number */
5690963Sarr	char 			*name;	/* module name */
5790963Sarr	modeventhand_t 		handler;	/* event handler */
5890963Sarr	void 			*arg;	/* argument for handler */
5990963Sarr	modspecific_t 		data;	/* module specific data */
6025537Sdfr};
6125537Sdfr
6290963Sarr#define MOD_EVENT(mod, type)	(mod)->handler((mod), (type), (mod)->arg)
6325537Sdfr
6425537Sdfrstatic modulelist_t modules;
6592547Sarrstruct sx modules_sx;
6625537Sdfrstatic int nextid = 1;
6790963Sarrstatic void module_shutdown(void *, int);
6825537Sdfr
6952991Speterstatic int
7090963Sarrmodevent_nop(module_t mod, int what, void *arg)
7152991Speter{
72132167Sphk
73132167Sphk	switch(what) {
74132167Sphk	case MOD_LOAD:
75132167Sphk		return (0);
76132167Sphk	case MOD_UNLOAD:
77132167Sphk		return (EBUSY);
78132167Sphk	default:
79132167Sphk		return (EOPNOTSUPP);
80132167Sphk	}
8152991Speter}
8252991Speter
8325537Sdfrstatic void
8490963Sarrmodule_init(void *arg)
8525537Sdfr{
8690963Sarr
8792547Sarr	sx_init(&modules_sx, "module subsystem sx lock");
8890963Sarr	TAILQ_INIT(&modules);
89108905Speter	EVENTHANDLER_REGISTER(shutdown_final, module_shutdown, NULL,
9090963Sarr	    SHUTDOWN_PRI_DEFAULT);
9125537Sdfr}
9225537Sdfr
9390963SarrSYSINIT(module, SI_SUB_KLD, SI_ORDER_FIRST, module_init, 0)
9425537Sdfr
9525537Sdfrstatic void
9690963Sarrmodule_shutdown(void *arg1, int arg2)
9725537Sdfr{
9890963Sarr	module_t mod;
9925537Sdfr
100134092Struckman	if (arg2 & RB_NOSYNC)
101134092Struckman		return;
102159957Sjhb	mtx_lock(&Giant);
10392547Sarr	MOD_SLOCK;
10490963Sarr	TAILQ_FOREACH(mod, &modules, link)
10590963Sarr		MOD_EVENT(mod, MOD_SHUTDOWN);
10692547Sarr	MOD_SUNLOCK;
107159957Sjhb	mtx_unlock(&Giant);
10825537Sdfr}
10925537Sdfr
11025537Sdfrvoid
11143387Sdillonmodule_register_init(const void *arg)
11225537Sdfr{
11390963Sarr	const moduledata_t *data = (const moduledata_t *)arg;
11490963Sarr	int error;
11590963Sarr	module_t mod;
11625537Sdfr
117159957Sjhb	mtx_lock(&Giant);
11892547Sarr	MOD_SLOCK;
11990963Sarr	mod = module_lookupbyname(data->name);
12090963Sarr	if (mod == NULL)
12191067Sarr		panic("module_register_init: module named %s not found\n",
12290963Sarr		    data->name);
12392547Sarr	MOD_SUNLOCK;
12490963Sarr	error = MOD_EVENT(mod, MOD_LOAD);
12590963Sarr	if (error) {
12690963Sarr		MOD_EVENT(mod, MOD_UNLOAD);
12792547Sarr		MOD_XLOCK;
12890963Sarr		module_release(mod);
12992547Sarr		MOD_XUNLOCK;
13091261Speter		printf("module_register_init: MOD_LOAD (%s, %p, %p) error"
13191261Speter		    " %d\n", data->name, (void *)data->evhand, data->priv,
13291067Sarr		    error);
13390963Sarr	}
134159957Sjhb	mtx_unlock(&Giant);
13525537Sdfr}
13625537Sdfr
13725537Sdfrint
13846693Spetermodule_register(const moduledata_t *data, linker_file_t container)
13925537Sdfr{
14090963Sarr	size_t namelen;
14190963Sarr	module_t newmod;
14225537Sdfr
143159634Smaxim	MOD_XLOCK;
14490963Sarr	newmod = module_lookupbyname(data->name);
14590963Sarr	if (newmod != NULL) {
146159634Smaxim		MOD_XUNLOCK;
14791067Sarr		printf("module_register: module %s already exists!\n",
14890963Sarr		    data->name);
14990980Sarr		return (EEXIST);
15090963Sarr	}
15190963Sarr	namelen = strlen(data->name) + 1;
152111119Simp	newmod = malloc(sizeof(struct module) + namelen, M_MODULE, M_WAITOK);
153159634Smaxim	if (newmod == NULL) {
154159634Smaxim		MOD_XUNLOCK;
15590980Sarr		return (ENOMEM);
156159634Smaxim	}
15790963Sarr	newmod->refs = 1;
15890963Sarr	newmod->id = nextid++;
15990963Sarr	newmod->name = (char *)(newmod + 1);
16090963Sarr	strcpy(newmod->name, data->name);
16190963Sarr	newmod->handler = data->evhand ? data->evhand : modevent_nop;
16290963Sarr	newmod->arg = data->priv;
16390963Sarr	bzero(&newmod->data, sizeof(newmod->data));
16490963Sarr	TAILQ_INSERT_TAIL(&modules, newmod, link);
16525537Sdfr
16690963Sarr	if (container)
16790963Sarr		TAILQ_INSERT_TAIL(&container->modules, newmod, flink);
16890963Sarr	newmod->file = container;
16992547Sarr	MOD_XUNLOCK;
17090980Sarr	return (0);
17125537Sdfr}
17225537Sdfr
17325537Sdfrvoid
17425537Sdfrmodule_reference(module_t mod)
17525537Sdfr{
17625537Sdfr
17792547Sarr	MOD_XLOCK_ASSERT;
17892547Sarr
17990963Sarr	MOD_DPF(REFS, ("module_reference: before, refs=%d\n", mod->refs));
18090963Sarr	mod->refs++;
18125537Sdfr}
18225537Sdfr
18325537Sdfrvoid
18425537Sdfrmodule_release(module_t mod)
18525537Sdfr{
18625537Sdfr
18792547Sarr	MOD_XLOCK_ASSERT;
18892547Sarr
18990963Sarr	if (mod->refs <= 0)
19090963Sarr		panic("module_release: bad reference count");
19125537Sdfr
19290963Sarr	MOD_DPF(REFS, ("module_release: before, refs=%d\n", mod->refs));
19392547Sarr
19490963Sarr	mod->refs--;
19590963Sarr	if (mod->refs == 0) {
19690963Sarr		TAILQ_REMOVE(&modules, mod, link);
19790963Sarr		if (mod->file)
19890963Sarr			TAILQ_REMOVE(&mod->file->modules, mod, flink);
19992547Sarr		MOD_XUNLOCK;
20090963Sarr		free(mod, M_MODULE);
20192547Sarr		MOD_XLOCK;
20225537Sdfr	}
20325537Sdfr}
20425537Sdfr
20525537Sdfrmodule_t
20690963Sarrmodule_lookupbyname(const char *name)
20725537Sdfr{
20890963Sarr	module_t mod;
20990963Sarr	int err;
21025537Sdfr
21192547Sarr	MOD_LOCK_ASSERT;
21292547Sarr
21390963Sarr	TAILQ_FOREACH(mod, &modules, link) {
21490963Sarr		err = strcmp(mod->name, name);
21590963Sarr		if (err == 0)
21690980Sarr			return (mod);
21790963Sarr	}
21890980Sarr	return (NULL);
21925537Sdfr}
22025537Sdfr
22125537Sdfrmodule_t
22225537Sdfrmodule_lookupbyid(int modid)
22325537Sdfr{
22492547Sarr        module_t mod;
22525537Sdfr
22692547Sarr        MOD_LOCK_ASSERT;
22792547Sarr
22892547Sarr        TAILQ_FOREACH(mod, &modules, link)
22992547Sarr                if (mod->id == modid)
23092547Sarr                        return(mod);
23192547Sarr        return (NULL);
23225537Sdfr}
23325537Sdfr
23425537Sdfrint
235132117Sphkmodule_unload(module_t mod, int flags)
23625537Sdfr{
237132117Sphk	int error;
23890963Sarr
239159957Sjhb	mtx_lock(&Giant);
240132117Sphk	error = MOD_EVENT(mod, MOD_QUIESCE);
241132199Sphk	if (error == EOPNOTSUPP || error == EINVAL)
242132117Sphk		error = 0;
243159957Sjhb	if (error == 0 || flags == LINKER_UNLOAD_FORCE)
244159957Sjhb		error = MOD_EVENT(mod, MOD_UNLOAD);
245159957Sjhb	mtx_unlock(&Giant);
246159957Sjhb	return (error);
24725537Sdfr}
24825537Sdfr
24925537Sdfrint
25025537Sdfrmodule_getid(module_t mod)
25125537Sdfr{
25290963Sarr
25392547Sarr	MOD_LOCK_ASSERT;
25490980Sarr	return (mod->id);
25525537Sdfr}
25625537Sdfr
25725537Sdfrmodule_t
25825537Sdfrmodule_getfnext(module_t mod)
25925537Sdfr{
26090963Sarr
26192547Sarr	MOD_LOCK_ASSERT;
26290980Sarr	return (TAILQ_NEXT(mod, flink));
26325537Sdfr}
26425537Sdfr
26542435Sdfrvoid
26642435Sdfrmodule_setspecific(module_t mod, modspecific_t *datap)
26742435Sdfr{
26890963Sarr
26992547Sarr	MOD_XLOCK_ASSERT;
27090963Sarr	mod->data = *datap;
27142435Sdfr}
27242435Sdfr
273157818Sjhblinker_file_t
274157818Sjhbmodule_file(module_t mod)
275157818Sjhb{
276157818Sjhb
277157818Sjhb	return (mod->file);
278157818Sjhb}
279157818Sjhb
28025537Sdfr/*
28125537Sdfr * Syscalls.
28225537Sdfr */
28382749Sdillon/*
28482749Sdillon * MPSAFE
28582749Sdillon */
28625537Sdfrint
28783366Sjulianmodnext(struct thread *td, struct modnext_args *uap)
28825537Sdfr{
28990963Sarr	module_t mod;
29090963Sarr	int error = 0;
29125537Sdfr
29298835Sarr	td->td_retval[0] = -1;
29382749Sdillon
29492547Sarr	MOD_SLOCK;
295107849Salfred	if (uap->modid == 0) {
29690963Sarr		mod = TAILQ_FIRST(&modules);
29790963Sarr		if (mod)
29890963Sarr			td->td_retval[0] = mod->id;
29990963Sarr		else
30090963Sarr			error = ENOENT;
30190963Sarr		goto done2;
30290963Sarr	}
303107849Salfred	mod = module_lookupbyid(uap->modid);
30490963Sarr	if (mod == NULL) {
30590963Sarr		error = ENOENT;
30690963Sarr		goto done2;
30790963Sarr	}
30890963Sarr	if (TAILQ_NEXT(mod, link))
30990963Sarr		td->td_retval[0] = TAILQ_NEXT(mod, link)->id;
31082749Sdillon	else
31190963Sarr		td->td_retval[0] = 0;
31282749Sdillondone2:
31392547Sarr	MOD_SUNLOCK;
31490963Sarr	return (error);
31525537Sdfr}
31625537Sdfr
31782749Sdillon/*
31882749Sdillon * MPSAFE
31982749Sdillon */
32025537Sdfrint
32183366Sjulianmodfnext(struct thread *td, struct modfnext_args *uap)
32225537Sdfr{
32390963Sarr	module_t mod;
32490963Sarr	int error;
32525537Sdfr
32690963Sarr	td->td_retval[0] = -1;
32725537Sdfr
32892547Sarr	MOD_SLOCK;
329107849Salfred	mod = module_lookupbyid(uap->modid);
33090963Sarr	if (mod == NULL) {
33190963Sarr		error = ENOENT;
33290963Sarr	} else {
33390963Sarr		error = 0;
33490963Sarr		if (TAILQ_NEXT(mod, flink))
33590963Sarr			td->td_retval[0] = TAILQ_NEXT(mod, flink)->id;
33690963Sarr		else
33790963Sarr			td->td_retval[0] = 0;
33890963Sarr	}
33992547Sarr	MOD_SUNLOCK;
34090963Sarr	return (error);
34125537Sdfr}
34225537Sdfr
34342435Sdfrstruct module_stat_v1 {
34491067Sarr	int	version;		/* set to sizeof(struct module_stat) */
34591067Sarr	char	name[MAXMODNAME];
34691067Sarr	int	refs;
34791067Sarr	int	id;
34842435Sdfr};
34942435Sdfr
35082749Sdillon/*
35182749Sdillon * MPSAFE
35282749Sdillon */
35325537Sdfrint
35483366Sjulianmodstat(struct thread *td, struct modstat_args *uap)
35525537Sdfr{
35690963Sarr	module_t mod;
35792547Sarr	modspecific_t data;
35890963Sarr	int error = 0;
35992547Sarr	int id, namelen, refs, version;
36090963Sarr	struct module_stat *stat;
36192547Sarr	char *name;
36225537Sdfr
36392547Sarr	MOD_SLOCK;
364107849Salfred	mod = module_lookupbyid(uap->modid);
36590963Sarr	if (mod == NULL) {
36692547Sarr		MOD_SUNLOCK;
36798835Sarr		return (ENOENT);
36890963Sarr	}
36992547Sarr	id = mod->id;
37092547Sarr	refs = mod->refs;
37192547Sarr	name = mod->name;
37292547Sarr	data = mod->data;
37392547Sarr	MOD_SUNLOCK;
374107849Salfred	stat = uap->stat;
37525537Sdfr
37690963Sarr	/*
37790963Sarr	 * Check the version of the user's structure.
37890963Sarr	 */
37990963Sarr	if ((error = copyin(&stat->version, &version, sizeof(version))) != 0)
38098835Sarr		return (error);
38190963Sarr	if (version != sizeof(struct module_stat_v1)
38298835Sarr	    && version != sizeof(struct module_stat))
38398835Sarr		return (EINVAL);
38490963Sarr	namelen = strlen(mod->name) + 1;
38590963Sarr	if (namelen > MAXMODNAME)
38690963Sarr		namelen = MAXMODNAME;
38792547Sarr	if ((error = copyout(name, &stat->name[0], namelen)) != 0)
38898835Sarr		return (error);
38925537Sdfr
39092547Sarr	if ((error = copyout(&refs, &stat->refs, sizeof(int))) != 0)
39198835Sarr		return (error);
39292547Sarr	if ((error = copyout(&id, &stat->id, sizeof(int))) != 0)
39398835Sarr		return (error);
39425537Sdfr
39590963Sarr	/*
39690963Sarr	 * >v1 stat includes module data.
39790963Sarr	 */
39898835Sarr	if (version == sizeof(struct module_stat))
39992547Sarr		if ((error = copyout(&data, &stat->data,
40092547Sarr		    sizeof(data))) != 0)
40198835Sarr			return (error);
40290963Sarr	td->td_retval[0] = 0;
40390980Sarr	return (error);
40425537Sdfr}
40525537Sdfr
40682749Sdillon/*
40782749Sdillon * MPSAFE
40882749Sdillon */
40925537Sdfrint
41083366Sjulianmodfind(struct thread *td, struct modfind_args *uap)
41125537Sdfr{
41290963Sarr	int error = 0;
41390963Sarr	char name[MAXMODNAME];
41490963Sarr	module_t mod;
41525537Sdfr
416107849Salfred	if ((error = copyinstr(uap->name, name, sizeof name, 0)) != 0)
41798835Sarr		return (error);
41825537Sdfr
41992547Sarr	MOD_SLOCK;
42090963Sarr	mod = module_lookupbyname(name);
42190963Sarr	if (mod == NULL)
42290963Sarr		error = ENOENT;
42390963Sarr	else
42492547Sarr		td->td_retval[0] = module_getid(mod);
42592547Sarr	MOD_SUNLOCK;
42690980Sarr	return (error);
42725537Sdfr}
428140482Sps
429140482Sps#ifdef COMPAT_IA32
430140482Sps#include <sys/mount.h>
431140482Sps#include <compat/freebsd32/freebsd32_util.h>
432140482Sps#include <compat/freebsd32/freebsd32.h>
433140482Sps#include <compat/freebsd32/freebsd32_proto.h>
434140482Sps
435140482Spstypedef union modspecific32 {
436140482Sps	int		intval;
437140482Sps	u_int32_t	uintval;
438140482Sps	int		longval;
439140482Sps	u_int32_t	ulongval;
440140482Sps} modspecific32_t;
441140482Sps
442140482Spsstruct module_stat32 {
443140482Sps	int		version;
444140482Sps	char		name[MAXMODNAME];
445140482Sps	int		refs;
446140482Sps	int		id;
447140482Sps	modspecific32_t	data;
448140482Sps};
449140482Sps
450140482Sps/*
451140482Sps * MPSAFE
452140482Sps */
453140482Spsint
454140482Spsfreebsd32_modstat(struct thread *td, struct freebsd32_modstat_args *uap)
455140482Sps{
456140482Sps	module_t mod;
457140482Sps	modspecific32_t data32;
458140482Sps	int error = 0;
459140482Sps	int id, namelen, refs, version;
460140482Sps	struct module_stat32 *stat32;
461140482Sps	char *name;
462140482Sps
463140482Sps	MOD_SLOCK;
464140482Sps	mod = module_lookupbyid(uap->modid);
465140482Sps	if (mod == NULL) {
466140482Sps		MOD_SUNLOCK;
467140482Sps		return (ENOENT);
468140482Sps	}
469140482Sps
470140482Sps	id = mod->id;
471140482Sps	refs = mod->refs;
472140482Sps	name = mod->name;
473142067Sps	CP(mod->data, data32, intval);
474142067Sps	CP(mod->data, data32, uintval);
475142067Sps	CP(mod->data, data32, longval);
476142067Sps	CP(mod->data, data32, ulongval);
477140482Sps	MOD_SUNLOCK;
478140482Sps	stat32 = uap->stat;
479140482Sps
480140482Sps	if ((error = copyin(&stat32->version, &version, sizeof(version))) != 0)
481140482Sps		return (error);
482140482Sps	if (version != sizeof(struct module_stat_v1)
483140482Sps	    && version != sizeof(struct module_stat32))
484140482Sps		return (EINVAL);
485140482Sps	namelen = strlen(mod->name) + 1;
486140482Sps	if (namelen > MAXMODNAME)
487140482Sps		namelen = MAXMODNAME;
488140482Sps	if ((error = copyout(name, &stat32->name[0], namelen)) != 0)
489140482Sps		return (error);
490140482Sps
491140482Sps	if ((error = copyout(&refs, &stat32->refs, sizeof(int))) != 0)
492140482Sps		return (error);
493140482Sps	if ((error = copyout(&id, &stat32->id, sizeof(int))) != 0)
494140482Sps		return (error);
495140482Sps
496140482Sps	/*
497140482Sps	 * >v1 stat includes module data.
498140482Sps	 */
499140482Sps	if (version == sizeof(struct module_stat32))
500140482Sps		if ((error = copyout(&data32, &stat32->data,
501140482Sps		    sizeof(data32))) != 0)
502140482Sps			return (error);
503140482Sps	td->td_retval[0] = 0;
504140482Sps	return (error);
505140482Sps}
506140482Sps#endif
507