kern_module.c revision 176252
134192Sjdp/*-
234192Sjdp * Copyright (c) 1997 Doug Rabson
334192Sjdp * All rights reserved.
434192Sjdp *
534192Sjdp * Redistribution and use in source and binary forms, with or without
634192Sjdp * modification, are permitted provided that the following conditions
734192Sjdp * are met:
834192Sjdp * 1. Redistributions of source code must retain the above copyright
934192Sjdp *    notice, this list of conditions and the following disclaimer.
1034192Sjdp * 2. Redistributions in binary form must reproduce the above copyright
1134192Sjdp *    notice, this list of conditions and the following disclaimer in the
1234192Sjdp *    documentation and/or other materials provided with the distribution.
1334192Sjdp *
1434192Sjdp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1534192Sjdp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1634192Sjdp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1734192Sjdp * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1834192Sjdp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1934192Sjdp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2034192Sjdp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2134192Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2234192Sjdp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2334192Sjdp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2434192Sjdp * SUCH DAMAGE.
2534192Sjdp */
2634192Sjdp
2734192Sjdp#include "opt_compat.h"
2834192Sjdp
2934192Sjdp#include <sys/cdefs.h>
3034192Sjdp__FBSDID("$FreeBSD: head/sys/kern/kern_module.c 176252 2008-02-13 21:34:06Z jhb $");
3134192Sjdp
3234192Sjdp#include <sys/param.h>
3334192Sjdp#include <sys/kernel.h>
3434192Sjdp#include <sys/systm.h>
3534192Sjdp#include <sys/eventhandler.h>
3650476Speter#include <sys/malloc.h>
3734192Sjdp#include <sys/sysproto.h>
3834192Sjdp#include <sys/sysent.h>
3934192Sjdp#include <sys/proc.h>
4034192Sjdp#include <sys/lock.h>
4134192Sjdp#include <sys/mutex.h>
4234192Sjdp#include <sys/reboot.h>
4334192Sjdp#include <sys/sx.h>
4434192Sjdp#include <sys/module.h>
4534192Sjdp#include <sys/linker.h>
4634192Sjdp
4734192Sjdpstatic MALLOC_DEFINE(M_MODULE, "module", "module data structures");
4834192Sjdp
4934192Sjdptypedef TAILQ_HEAD(, module) modulelist_t;
5034192Sjdpstruct module {
5134192Sjdp	TAILQ_ENTRY(module)	link;	/* chain together all modules */
5269793Sobrien	TAILQ_ENTRY(module)	flink;	/* all modules in a file */
53110803Skan	struct linker_file	*file;	/* file which contains this module */
54119255Simp	int			refs;	/* reference count */
55110803Skan	int 			id;	/* unique id number */
5634192Sjdp	char 			*name;	/* module name */
5734192Sjdp	modeventhand_t 		handler;	/* event handler */
5834192Sjdp	void 			*arg;	/* argument for handler */
5934192Sjdp	modspecific_t 		data;	/* module specific data */
6034192Sjdp};
6134192Sjdp
6234192Sjdp#define MOD_EVENT(mod, type)	(mod)->handler((mod), (type), (mod)->arg)
6334192Sjdp
6434192Sjdpstatic modulelist_t modules;
6534192Sjdpstruct sx modules_sx;
6634192Sjdpstatic int nextid = 1;
6734192Sjdpstatic void module_shutdown(void *, int);
6834192Sjdp
6934192Sjdpstatic int
7034192Sjdpmodevent_nop(module_t mod, int what, void *arg)
7134192Sjdp{
7234192Sjdp
7334192Sjdp	switch(what) {
7434192Sjdp	case MOD_LOAD:
7534192Sjdp		return (0);
7634192Sjdp	case MOD_UNLOAD:
7734192Sjdp		return (EBUSY);
7834192Sjdp	default:
7934192Sjdp		return (EOPNOTSUPP);
8034192Sjdp	}
8134192Sjdp}
8234192Sjdp
8334192Sjdpstatic void
8434192Sjdpmodule_init(void *arg)
8534192Sjdp{
8634192Sjdp
8734192Sjdp	sx_init(&modules_sx, "module subsystem sx lock");
8834192Sjdp	TAILQ_INIT(&modules);
8934192Sjdp	EVENTHANDLER_REGISTER(shutdown_final, module_shutdown, NULL,
9034192Sjdp	    SHUTDOWN_PRI_DEFAULT);
9134192Sjdp}
9234192Sjdp
9334192SjdpSYSINIT(module, SI_SUB_KLD, SI_ORDER_FIRST, module_init, 0)
9434192Sjdp
9534192Sjdpstatic void
9634192Sjdpmodule_shutdown(void *arg1, int arg2)
9734192Sjdp{
9834192Sjdp	module_t mod;
9934192Sjdp
10034192Sjdp	if (arg2 & RB_NOSYNC)
10134192Sjdp		return;
10234192Sjdp	mtx_lock(&Giant);
10334192Sjdp	MOD_SLOCK;
10434192Sjdp	TAILQ_FOREACH(mod, &modules, link)
10534192Sjdp		MOD_EVENT(mod, MOD_SHUTDOWN);
10634192Sjdp	MOD_SUNLOCK;
10734192Sjdp	mtx_unlock(&Giant);
10834192Sjdp}
10934192Sjdp
11034192Sjdpvoid
11134192Sjdpmodule_register_init(const void *arg)
11234192Sjdp{
11334192Sjdp	const moduledata_t *data = (const moduledata_t *)arg;
11434192Sjdp	int error;
11534192Sjdp	module_t mod;
11634192Sjdp
11734192Sjdp	mtx_lock(&Giant);
11834192Sjdp	MOD_SLOCK;
11934192Sjdp	mod = module_lookupbyname(data->name);
12034192Sjdp	if (mod == NULL)
12134192Sjdp		panic("module_register_init: module named %s not found\n",
12234192Sjdp		    data->name);
12334192Sjdp	MOD_SUNLOCK;
12434192Sjdp	error = MOD_EVENT(mod, MOD_LOAD);
12534192Sjdp	if (error) {
12634192Sjdp		MOD_EVENT(mod, MOD_UNLOAD);
12734192Sjdp		MOD_XLOCK;
12834192Sjdp		module_release(mod);
12934192Sjdp		MOD_XUNLOCK;
13034192Sjdp		printf("module_register_init: MOD_LOAD (%s, %p, %p) error"
13134192Sjdp		    " %d\n", data->name, (void *)data->evhand, data->priv,
13234192Sjdp		    error);
13334192Sjdp	}
13434192Sjdp	mtx_unlock(&Giant);
13534192Sjdp}
13634192Sjdp
13734192Sjdpint
13834192Sjdpmodule_register(const moduledata_t *data, linker_file_t container)
13934192Sjdp{
14034192Sjdp	size_t namelen;
14134192Sjdp	module_t newmod;
14234192Sjdp
14334192Sjdp	MOD_XLOCK;
14434192Sjdp	newmod = module_lookupbyname(data->name);
14534192Sjdp	if (newmod != NULL) {
14634192Sjdp		MOD_XUNLOCK;
14734192Sjdp		printf("module_register: module %s already exists!\n",
14834192Sjdp		    data->name);
14934192Sjdp		return (EEXIST);
15034192Sjdp	}
15134192Sjdp	namelen = strlen(data->name) + 1;
152110801Skan	newmod = malloc(sizeof(struct module) + namelen, M_MODULE, M_WAITOK);
15334192Sjdp	if (newmod == NULL) {
15434192Sjdp		MOD_XUNLOCK;
15534192Sjdp		return (ENOMEM);
15634192Sjdp	}
15734192Sjdp	newmod->refs = 1;
15834192Sjdp	newmod->id = nextid++;
15934192Sjdp	newmod->name = (char *)(newmod + 1);
16038816Sdfr	strcpy(newmod->name, data->name);
16138816Sdfr	newmod->handler = data->evhand ? data->evhand : modevent_nop;
16234192Sjdp	newmod->arg = data->priv;
16334192Sjdp	bzero(&newmod->data, sizeof(newmod->data));
16434192Sjdp	TAILQ_INSERT_TAIL(&modules, newmod, link);
16534192Sjdp
16634192Sjdp	if (container)
16734192Sjdp		TAILQ_INSERT_TAIL(&container->modules, newmod, flink);
16834192Sjdp	newmod->file = container;
16934192Sjdp	MOD_XUNLOCK;
17034192Sjdp	return (0);
17134192Sjdp}
17234192Sjdp
17338816Sdfrvoid
17434192Sjdpmodule_reference(module_t mod)
17534192Sjdp{
17634192Sjdp
17734192Sjdp	MOD_XLOCK_ASSERT;
17834192Sjdp
17934192Sjdp	MOD_DPF(REFS, ("module_reference: before, refs=%d\n", mod->refs));
18034192Sjdp	mod->refs++;
181114625Sobrien}
18234192Sjdp
18334192Sjdpvoid
18434192Sjdpmodule_release(module_t mod)
18534192Sjdp{
18634192Sjdp
18734192Sjdp	MOD_XLOCK_ASSERT;
18834192Sjdp
18934192Sjdp	if (mod->refs <= 0)
19034192Sjdp		panic("module_release: bad reference count");
19134192Sjdp
192114625Sobrien	MOD_DPF(REFS, ("module_release: before, refs=%d\n", mod->refs));
19334192Sjdp
19434192Sjdp	mod->refs--;
19534192Sjdp	if (mod->refs == 0) {
19634192Sjdp		TAILQ_REMOVE(&modules, mod, link);
19734192Sjdp		if (mod->file)
19834192Sjdp			TAILQ_REMOVE(&mod->file->modules, mod, flink);
19934192Sjdp		MOD_XUNLOCK;
20034192Sjdp		free(mod, M_MODULE);
20134192Sjdp		MOD_XLOCK;
20234192Sjdp	}
20334192Sjdp}
20434192Sjdp
20534192Sjdpmodule_t
20634192Sjdpmodule_lookupbyname(const char *name)
20734192Sjdp{
20834192Sjdp	module_t mod;
20934192Sjdp	int err;
21034192Sjdp
21134192Sjdp	MOD_LOCK_ASSERT;
21234192Sjdp
21334192Sjdp	TAILQ_FOREACH(mod, &modules, link) {
21434192Sjdp		err = strcmp(mod->name, name);
21534192Sjdp		if (err == 0)
21634192Sjdp			return (mod);
21734192Sjdp	}
21834192Sjdp	return (NULL);
21934192Sjdp}
22034192Sjdp
22134192Sjdpmodule_t
22234192Sjdpmodule_lookupbyid(int modid)
22334192Sjdp{
22434192Sjdp        module_t mod;
22534192Sjdp
22634192Sjdp        MOD_LOCK_ASSERT;
22734192Sjdp
22834192Sjdp        TAILQ_FOREACH(mod, &modules, link)
22934192Sjdp                if (mod->id == modid)
23034192Sjdp                        return(mod);
23134192Sjdp        return (NULL);
23234192Sjdp}
23334192Sjdp
23434192Sjdpint
23534192Sjdpmodule_unload(module_t mod, int flags)
23634192Sjdp{
23734192Sjdp	int error;
23834192Sjdp
23934192Sjdp	mtx_lock(&Giant);
24034192Sjdp	error = MOD_EVENT(mod, MOD_QUIESCE);
24134192Sjdp	if (error == EOPNOTSUPP || error == EINVAL)
24234192Sjdp		error = 0;
24334192Sjdp	if (error == 0 || flags == LINKER_UNLOAD_FORCE)
24434192Sjdp		error = MOD_EVENT(mod, MOD_UNLOAD);
24534192Sjdp	mtx_unlock(&Giant);
24634192Sjdp	return (error);
24734192Sjdp}
24834192Sjdp
24934192Sjdpint
25034192Sjdpmodule_getid(module_t mod)
25134192Sjdp{
25234192Sjdp
25334192Sjdp	MOD_LOCK_ASSERT;
25434192Sjdp	return (mod->id);
25534192Sjdp}
25634192Sjdp
25734192Sjdpmodule_t
25834192Sjdpmodule_getfnext(module_t mod)
25934192Sjdp{
26034192Sjdp
26134192Sjdp	MOD_LOCK_ASSERT;
26234192Sjdp	return (TAILQ_NEXT(mod, flink));
26334192Sjdp}
26434192Sjdp
26534192Sjdpvoid
26634192Sjdpmodule_setspecific(module_t mod, modspecific_t *datap)
26734192Sjdp{
26834192Sjdp
26934192Sjdp	MOD_XLOCK_ASSERT;
27034192Sjdp	mod->data = *datap;
27134192Sjdp}
27234192Sjdp
27334192Sjdplinker_file_t
27434192Sjdpmodule_file(module_t mod)
27534192Sjdp{
27634192Sjdp
27734192Sjdp	return (mod->file);
27834192Sjdp}
27934192Sjdp
28034192Sjdp/*
28134192Sjdp * Syscalls.
28234192Sjdp */
28334192Sjdpint
28434192Sjdpmodnext(struct thread *td, struct modnext_args *uap)
28534192Sjdp{
28634192Sjdp	module_t mod;
28734192Sjdp	int error = 0;
28834192Sjdp
28934192Sjdp	td->td_retval[0] = -1;
29034192Sjdp
29134192Sjdp	MOD_SLOCK;
29234192Sjdp	if (uap->modid == 0) {
29334192Sjdp		mod = TAILQ_FIRST(&modules);
29434192Sjdp		if (mod)
29534192Sjdp			td->td_retval[0] = mod->id;
29634192Sjdp		else
29734192Sjdp			error = ENOENT;
29834192Sjdp		goto done2;
29934192Sjdp	}
30034192Sjdp	mod = module_lookupbyid(uap->modid);
30134192Sjdp	if (mod == NULL) {
30234192Sjdp		error = ENOENT;
30334192Sjdp		goto done2;
30434192Sjdp	}
30534192Sjdp	if (TAILQ_NEXT(mod, link))
30634192Sjdp		td->td_retval[0] = TAILQ_NEXT(mod, link)->id;
30734192Sjdp	else
30834192Sjdp		td->td_retval[0] = 0;
30934192Sjdpdone2:
31034192Sjdp	MOD_SUNLOCK;
31134192Sjdp	return (error);
31234192Sjdp}
31334192Sjdp
31434192Sjdpint
31534192Sjdpmodfnext(struct thread *td, struct modfnext_args *uap)
31634192Sjdp{
31734192Sjdp	module_t mod;
31834192Sjdp	int error;
31934192Sjdp
32034192Sjdp	td->td_retval[0] = -1;
32134192Sjdp
32234192Sjdp	MOD_SLOCK;
32334192Sjdp	mod = module_lookupbyid(uap->modid);
32434192Sjdp	if (mod == NULL) {
32534192Sjdp		error = ENOENT;
32634192Sjdp	} else {
32734192Sjdp		error = 0;
32834192Sjdp		if (TAILQ_NEXT(mod, flink))
32934192Sjdp			td->td_retval[0] = TAILQ_NEXT(mod, flink)->id;
33034192Sjdp		else
33134192Sjdp			td->td_retval[0] = 0;
33234192Sjdp	}
33334192Sjdp	MOD_SUNLOCK;
33434192Sjdp	return (error);
33534192Sjdp}
33634192Sjdp
33734192Sjdpstruct module_stat_v1 {
33834192Sjdp	int	version;		/* set to sizeof(struct module_stat) */
33934192Sjdp	char	name[MAXMODNAME];
34034192Sjdp	int	refs;
34134192Sjdp	int	id;
34234192Sjdp};
34334192Sjdp
34434192Sjdpint
34534192Sjdpmodstat(struct thread *td, struct modstat_args *uap)
34634192Sjdp{
34734192Sjdp	module_t mod;
34834192Sjdp	modspecific_t data;
34934192Sjdp	int error = 0;
35034192Sjdp	int id, namelen, refs, version;
35134192Sjdp	struct module_stat *stat;
35234192Sjdp	char *name;
35334192Sjdp
35434192Sjdp	MOD_SLOCK;
35534192Sjdp	mod = module_lookupbyid(uap->modid);
35634192Sjdp	if (mod == NULL) {
35734192Sjdp		MOD_SUNLOCK;
35834192Sjdp		return (ENOENT);
35934192Sjdp	}
36034192Sjdp	id = mod->id;
36134192Sjdp	refs = mod->refs;
36234192Sjdp	name = mod->name;
36334192Sjdp	data = mod->data;
36434192Sjdp	MOD_SUNLOCK;
365114625Sobrien	stat = uap->stat;
36634192Sjdp
36734192Sjdp	/*
36834192Sjdp	 * Check the version of the user's structure.
36934192Sjdp	 */
37034192Sjdp	if ((error = copyin(&stat->version, &version, sizeof(version))) != 0)
37134192Sjdp		return (error);
37234192Sjdp	if (version != sizeof(struct module_stat_v1)
37334192Sjdp	    && version != sizeof(struct module_stat))
37434192Sjdp		return (EINVAL);
37534192Sjdp	namelen = strlen(mod->name) + 1;
37634192Sjdp	if (namelen > MAXMODNAME)
37734192Sjdp		namelen = MAXMODNAME;
378114625Sobrien	if ((error = copyout(name, &stat->name[0], namelen)) != 0)
37934192Sjdp		return (error);
38034192Sjdp
38134192Sjdp	if ((error = copyout(&refs, &stat->refs, sizeof(int))) != 0)
38234192Sjdp		return (error);
38334192Sjdp	if ((error = copyout(&id, &stat->id, sizeof(int))) != 0)
38434192Sjdp		return (error);
38534192Sjdp
38634192Sjdp	/*
38734192Sjdp	 * >v1 stat includes module data.
38834192Sjdp	 */
38934192Sjdp	if (version == sizeof(struct module_stat))
39034192Sjdp		if ((error = copyout(&data, &stat->data,
39134192Sjdp		    sizeof(data))) != 0)
39234192Sjdp			return (error);
39334192Sjdp	td->td_retval[0] = 0;
39434192Sjdp	return (error);
39534192Sjdp}
39634192Sjdp
39734192Sjdpint
39834192Sjdpmodfind(struct thread *td, struct modfind_args *uap)
39934192Sjdp{
40034192Sjdp	int error = 0;
40134192Sjdp	char name[MAXMODNAME];
40234192Sjdp	module_t mod;
40334192Sjdp
40434192Sjdp	if ((error = copyinstr(uap->name, name, sizeof name, 0)) != 0)
40534192Sjdp		return (error);
40634192Sjdp
40734192Sjdp	MOD_SLOCK;
40834192Sjdp	mod = module_lookupbyname(name);
40934192Sjdp	if (mod == NULL)
41034192Sjdp		error = ENOENT;
41134192Sjdp	else
41234192Sjdp		td->td_retval[0] = module_getid(mod);
41334192Sjdp	MOD_SUNLOCK;
41434192Sjdp	return (error);
41534192Sjdp}
41634192Sjdp
41734192SjdpMODULE_VERSION(kernel, __FreeBSD_version);
41834192Sjdp
41934192Sjdp#ifdef COMPAT_IA32
42034192Sjdp#include <sys/mount.h>
42134192Sjdp#include <sys/socket.h>
42234192Sjdp#include <compat/freebsd32/freebsd32_util.h>
42334192Sjdp#include <compat/freebsd32/freebsd32.h>
42434192Sjdp#include <compat/freebsd32/freebsd32_proto.h>
42534192Sjdp
42634192Sjdptypedef union modspecific32 {
42734192Sjdp	int		intval;
42834192Sjdp	u_int32_t	uintval;
42934192Sjdp	int		longval;
43034192Sjdp	u_int32_t	ulongval;
43134192Sjdp} modspecific32_t;
43234192Sjdp
43334192Sjdpstruct module_stat32 {
43434192Sjdp	int		version;
43534192Sjdp	char		name[MAXMODNAME];
43634192Sjdp	int		refs;
43734192Sjdp	int		id;
43834192Sjdp	modspecific32_t	data;
43934192Sjdp};
44034192Sjdp
44134192Sjdpint
44234192Sjdpfreebsd32_modstat(struct thread *td, struct freebsd32_modstat_args *uap)
44334192Sjdp{
44434192Sjdp	module_t mod;
44534192Sjdp	modspecific32_t data32;
44634192Sjdp	int error = 0;
44734192Sjdp	int id, namelen, refs, version;
44834192Sjdp	struct module_stat32 *stat32;
44934192Sjdp	char *name;
45034192Sjdp
45134192Sjdp	MOD_SLOCK;
45234192Sjdp	mod = module_lookupbyid(uap->modid);
45334192Sjdp	if (mod == NULL) {
45434192Sjdp		MOD_SUNLOCK;
45534192Sjdp		return (ENOENT);
45634192Sjdp	}
45734192Sjdp
45834192Sjdp	id = mod->id;
45934192Sjdp	refs = mod->refs;
46069793Sobrien	name = mod->name;
46134192Sjdp	CP(mod->data, data32, intval);
46269793Sobrien	CP(mod->data, data32, uintval);
46334192Sjdp	CP(mod->data, data32, longval);
46434192Sjdp	CP(mod->data, data32, ulongval);
46534192Sjdp	MOD_SUNLOCK;
46634192Sjdp	stat32 = uap->stat;
46738816Sdfr
46834192Sjdp	if ((error = copyin(&stat32->version, &version, sizeof(version))) != 0)
46934192Sjdp		return (error);
47034192Sjdp	if (version != sizeof(struct module_stat_v1)
47134192Sjdp	    && version != sizeof(struct module_stat32))
47238816Sdfr		return (EINVAL);
47334192Sjdp	namelen = strlen(mod->name) + 1;
47434192Sjdp	if (namelen > MAXMODNAME)
47534192Sjdp		namelen = MAXMODNAME;
47634192Sjdp	if ((error = copyout(name, &stat32->name[0], namelen)) != 0)
47734192Sjdp		return (error);
47834192Sjdp
47934192Sjdp	if ((error = copyout(&refs, &stat32->refs, sizeof(int))) != 0)
48034192Sjdp		return (error);
48134192Sjdp	if ((error = copyout(&id, &stat32->id, sizeof(int))) != 0)
48234192Sjdp		return (error);
48334192Sjdp
48434192Sjdp	/*
48534192Sjdp	 * >v1 stat includes module data.
48634192Sjdp	 */
48734192Sjdp	if (version == sizeof(struct module_stat32))
488110801Skan		if ((error = copyout(&data32, &stat32->data,
489110801Skan		    sizeof(data32))) != 0)
490110801Skan			return (error);
491110801Skan	td->td_retval[0] = 0;
492110801Skan	return (error);
493110801Skan}
494110801Skan#endif
495110801Skan