kern_module.c revision 177253
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 177253 2008-03-16 10:58:09Z rwatson $");
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
93177253SrwatsonSYSINIT(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 */
28325537Sdfrint
28483366Sjulianmodnext(struct thread *td, struct modnext_args *uap)
28525537Sdfr{
28690963Sarr	module_t mod;
28790963Sarr	int error = 0;
28825537Sdfr
28998835Sarr	td->td_retval[0] = -1;
29082749Sdillon
29192547Sarr	MOD_SLOCK;
292107849Salfred	if (uap->modid == 0) {
29390963Sarr		mod = TAILQ_FIRST(&modules);
29490963Sarr		if (mod)
29590963Sarr			td->td_retval[0] = mod->id;
29690963Sarr		else
29790963Sarr			error = ENOENT;
29890963Sarr		goto done2;
29990963Sarr	}
300107849Salfred	mod = module_lookupbyid(uap->modid);
30190963Sarr	if (mod == NULL) {
30290963Sarr		error = ENOENT;
30390963Sarr		goto done2;
30490963Sarr	}
30590963Sarr	if (TAILQ_NEXT(mod, link))
30690963Sarr		td->td_retval[0] = TAILQ_NEXT(mod, link)->id;
30782749Sdillon	else
30890963Sarr		td->td_retval[0] = 0;
30982749Sdillondone2:
31092547Sarr	MOD_SUNLOCK;
31190963Sarr	return (error);
31225537Sdfr}
31325537Sdfr
31425537Sdfrint
31583366Sjulianmodfnext(struct thread *td, struct modfnext_args *uap)
31625537Sdfr{
31790963Sarr	module_t mod;
31890963Sarr	int error;
31925537Sdfr
32090963Sarr	td->td_retval[0] = -1;
32125537Sdfr
32292547Sarr	MOD_SLOCK;
323107849Salfred	mod = module_lookupbyid(uap->modid);
32490963Sarr	if (mod == NULL) {
32590963Sarr		error = ENOENT;
32690963Sarr	} else {
32790963Sarr		error = 0;
32890963Sarr		if (TAILQ_NEXT(mod, flink))
32990963Sarr			td->td_retval[0] = TAILQ_NEXT(mod, flink)->id;
33090963Sarr		else
33190963Sarr			td->td_retval[0] = 0;
33290963Sarr	}
33392547Sarr	MOD_SUNLOCK;
33490963Sarr	return (error);
33525537Sdfr}
33625537Sdfr
33742435Sdfrstruct module_stat_v1 {
33891067Sarr	int	version;		/* set to sizeof(struct module_stat) */
33991067Sarr	char	name[MAXMODNAME];
34091067Sarr	int	refs;
34191067Sarr	int	id;
34242435Sdfr};
34342435Sdfr
34425537Sdfrint
34583366Sjulianmodstat(struct thread *td, struct modstat_args *uap)
34625537Sdfr{
34790963Sarr	module_t mod;
34892547Sarr	modspecific_t data;
34990963Sarr	int error = 0;
35092547Sarr	int id, namelen, refs, version;
35190963Sarr	struct module_stat *stat;
35292547Sarr	char *name;
35325537Sdfr
35492547Sarr	MOD_SLOCK;
355107849Salfred	mod = module_lookupbyid(uap->modid);
35690963Sarr	if (mod == NULL) {
35792547Sarr		MOD_SUNLOCK;
35898835Sarr		return (ENOENT);
35990963Sarr	}
36092547Sarr	id = mod->id;
36192547Sarr	refs = mod->refs;
36292547Sarr	name = mod->name;
36392547Sarr	data = mod->data;
36492547Sarr	MOD_SUNLOCK;
365107849Salfred	stat = uap->stat;
36625537Sdfr
36790963Sarr	/*
36890963Sarr	 * Check the version of the user's structure.
36990963Sarr	 */
37090963Sarr	if ((error = copyin(&stat->version, &version, sizeof(version))) != 0)
37198835Sarr		return (error);
37290963Sarr	if (version != sizeof(struct module_stat_v1)
37398835Sarr	    && version != sizeof(struct module_stat))
37498835Sarr		return (EINVAL);
37590963Sarr	namelen = strlen(mod->name) + 1;
37690963Sarr	if (namelen > MAXMODNAME)
37790963Sarr		namelen = MAXMODNAME;
37892547Sarr	if ((error = copyout(name, &stat->name[0], namelen)) != 0)
37998835Sarr		return (error);
38025537Sdfr
38192547Sarr	if ((error = copyout(&refs, &stat->refs, sizeof(int))) != 0)
38298835Sarr		return (error);
38392547Sarr	if ((error = copyout(&id, &stat->id, sizeof(int))) != 0)
38498835Sarr		return (error);
38525537Sdfr
38690963Sarr	/*
38790963Sarr	 * >v1 stat includes module data.
38890963Sarr	 */
38998835Sarr	if (version == sizeof(struct module_stat))
39092547Sarr		if ((error = copyout(&data, &stat->data,
39192547Sarr		    sizeof(data))) != 0)
39298835Sarr			return (error);
39390963Sarr	td->td_retval[0] = 0;
39490980Sarr	return (error);
39525537Sdfr}
39625537Sdfr
39725537Sdfrint
39883366Sjulianmodfind(struct thread *td, struct modfind_args *uap)
39925537Sdfr{
40090963Sarr	int error = 0;
40190963Sarr	char name[MAXMODNAME];
40290963Sarr	module_t mod;
40325537Sdfr
404107849Salfred	if ((error = copyinstr(uap->name, name, sizeof name, 0)) != 0)
40598835Sarr		return (error);
40625537Sdfr
40792547Sarr	MOD_SLOCK;
40890963Sarr	mod = module_lookupbyname(name);
40990963Sarr	if (mod == NULL)
41090963Sarr		error = ENOENT;
41190963Sarr	else
41292547Sarr		td->td_retval[0] = module_getid(mod);
41392547Sarr	MOD_SUNLOCK;
41490980Sarr	return (error);
41525537Sdfr}
416140482Sps
417176252SjhbMODULE_VERSION(kernel, __FreeBSD_version);
418176252Sjhb
419140482Sps#ifdef COMPAT_IA32
420140482Sps#include <sys/mount.h>
421174377Sjhb#include <sys/socket.h>
422140482Sps#include <compat/freebsd32/freebsd32_util.h>
423140482Sps#include <compat/freebsd32/freebsd32.h>
424140482Sps#include <compat/freebsd32/freebsd32_proto.h>
425140482Sps
426140482Spstypedef union modspecific32 {
427140482Sps	int		intval;
428140482Sps	u_int32_t	uintval;
429140482Sps	int		longval;
430140482Sps	u_int32_t	ulongval;
431140482Sps} modspecific32_t;
432140482Sps
433140482Spsstruct module_stat32 {
434140482Sps	int		version;
435140482Sps	char		name[MAXMODNAME];
436140482Sps	int		refs;
437140482Sps	int		id;
438140482Sps	modspecific32_t	data;
439140482Sps};
440140482Sps
441140482Spsint
442140482Spsfreebsd32_modstat(struct thread *td, struct freebsd32_modstat_args *uap)
443140482Sps{
444140482Sps	module_t mod;
445140482Sps	modspecific32_t data32;
446140482Sps	int error = 0;
447140482Sps	int id, namelen, refs, version;
448140482Sps	struct module_stat32 *stat32;
449140482Sps	char *name;
450140482Sps
451140482Sps	MOD_SLOCK;
452140482Sps	mod = module_lookupbyid(uap->modid);
453140482Sps	if (mod == NULL) {
454140482Sps		MOD_SUNLOCK;
455140482Sps		return (ENOENT);
456140482Sps	}
457140482Sps
458140482Sps	id = mod->id;
459140482Sps	refs = mod->refs;
460140482Sps	name = mod->name;
461142067Sps	CP(mod->data, data32, intval);
462142067Sps	CP(mod->data, data32, uintval);
463142067Sps	CP(mod->data, data32, longval);
464142067Sps	CP(mod->data, data32, ulongval);
465140482Sps	MOD_SUNLOCK;
466140482Sps	stat32 = uap->stat;
467140482Sps
468140482Sps	if ((error = copyin(&stat32->version, &version, sizeof(version))) != 0)
469140482Sps		return (error);
470140482Sps	if (version != sizeof(struct module_stat_v1)
471140482Sps	    && version != sizeof(struct module_stat32))
472140482Sps		return (EINVAL);
473140482Sps	namelen = strlen(mod->name) + 1;
474140482Sps	if (namelen > MAXMODNAME)
475140482Sps		namelen = MAXMODNAME;
476140482Sps	if ((error = copyout(name, &stat32->name[0], namelen)) != 0)
477140482Sps		return (error);
478140482Sps
479140482Sps	if ((error = copyout(&refs, &stat32->refs, sizeof(int))) != 0)
480140482Sps		return (error);
481140482Sps	if ((error = copyout(&id, &stat32->id, sizeof(int))) != 0)
482140482Sps		return (error);
483140482Sps
484140482Sps	/*
485140482Sps	 * >v1 stat includes module data.
486140482Sps	 */
487140482Sps	if (version == sizeof(struct module_stat32))
488140482Sps		if ((error = copyout(&data32, &stat32->data,
489140482Sps		    sizeof(data32))) != 0)
490140482Sps			return (error);
491140482Sps	td->td_retval[0] = 0;
492140482Sps	return (error);
493140482Sps}
494140482Sps#endif
495