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$");
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
4925537Sdfrstruct module {
5090963Sarr	TAILQ_ENTRY(module)	link;	/* chain together all modules */
5190963Sarr	TAILQ_ENTRY(module)	flink;	/* all modules in a file */
5290963Sarr	struct linker_file	*file;	/* file which contains this module */
5390963Sarr	int			refs;	/* reference count */
5490963Sarr	int 			id;	/* unique id number */
5590963Sarr	char 			*name;	/* module name */
5690963Sarr	modeventhand_t 		handler;	/* event handler */
5790963Sarr	void 			*arg;	/* argument for handler */
5890963Sarr	modspecific_t 		data;	/* module specific data */
5925537Sdfr};
6025537Sdfr
6190963Sarr#define MOD_EVENT(mod, type)	(mod)->handler((mod), (type), (mod)->arg)
6225537Sdfr
63215683Sattiliostatic TAILQ_HEAD(modulelist, module) modules;
6492547Sarrstruct sx modules_sx;
6525537Sdfrstatic int nextid = 1;
6690963Sarrstatic void module_shutdown(void *, int);
6725537Sdfr
6852991Speterstatic int
6990963Sarrmodevent_nop(module_t mod, int what, void *arg)
7052991Speter{
71132167Sphk
72132167Sphk	switch(what) {
73132167Sphk	case MOD_LOAD:
74132167Sphk		return (0);
75132167Sphk	case MOD_UNLOAD:
76132167Sphk		return (EBUSY);
77132167Sphk	default:
78132167Sphk		return (EOPNOTSUPP);
79132167Sphk	}
8052991Speter}
8152991Speter
8225537Sdfrstatic void
8390963Sarrmodule_init(void *arg)
8425537Sdfr{
8590963Sarr
8692547Sarr	sx_init(&modules_sx, "module subsystem sx lock");
8790963Sarr	TAILQ_INIT(&modules);
88108905Speter	EVENTHANDLER_REGISTER(shutdown_final, module_shutdown, NULL,
8990963Sarr	    SHUTDOWN_PRI_DEFAULT);
9025537Sdfr}
9125537Sdfr
92177253SrwatsonSYSINIT(module, SI_SUB_KLD, SI_ORDER_FIRST, module_init, 0);
9325537Sdfr
9425537Sdfrstatic void
9590963Sarrmodule_shutdown(void *arg1, int arg2)
9625537Sdfr{
9790963Sarr	module_t mod;
9825537Sdfr
99134092Struckman	if (arg2 & RB_NOSYNC)
100134092Struckman		return;
101159957Sjhb	mtx_lock(&Giant);
10292547Sarr	MOD_SLOCK;
103215683Sattilio	TAILQ_FOREACH_REVERSE(mod, &modules, modulelist, link)
10490963Sarr		MOD_EVENT(mod, MOD_SHUTDOWN);
10592547Sarr	MOD_SUNLOCK;
106159957Sjhb	mtx_unlock(&Giant);
10725537Sdfr}
10825537Sdfr
10925537Sdfrvoid
11043387Sdillonmodule_register_init(const void *arg)
11125537Sdfr{
11290963Sarr	const moduledata_t *data = (const moduledata_t *)arg;
11390963Sarr	int error;
11490963Sarr	module_t mod;
11525537Sdfr
116159957Sjhb	mtx_lock(&Giant);
11792547Sarr	MOD_SLOCK;
11890963Sarr	mod = module_lookupbyname(data->name);
11990963Sarr	if (mod == NULL)
12091067Sarr		panic("module_register_init: module named %s not found\n",
12190963Sarr		    data->name);
12292547Sarr	MOD_SUNLOCK;
12390963Sarr	error = MOD_EVENT(mod, MOD_LOAD);
12490963Sarr	if (error) {
12590963Sarr		MOD_EVENT(mod, MOD_UNLOAD);
12692547Sarr		MOD_XLOCK;
12790963Sarr		module_release(mod);
12892547Sarr		MOD_XUNLOCK;
12991261Speter		printf("module_register_init: MOD_LOAD (%s, %p, %p) error"
13091261Speter		    " %d\n", data->name, (void *)data->evhand, data->priv,
13191067Sarr		    error);
132185642Sjhb	} else {
133185642Sjhb		MOD_XLOCK;
134185642Sjhb		if (mod->file) {
135185642Sjhb			/*
136239586Sjhb			 * Once a module is successfully loaded, move
137185642Sjhb			 * it to the head of the module list for this
138185642Sjhb			 * linker file.  This resorts the list so that
139185642Sjhb			 * when the kernel linker iterates over the
140185642Sjhb			 * modules to unload them, it will unload them
141185642Sjhb			 * in the reverse order they were loaded.
142185642Sjhb			 */
143185642Sjhb			TAILQ_REMOVE(&mod->file->modules, mod, flink);
144185642Sjhb			TAILQ_INSERT_HEAD(&mod->file->modules, mod, flink);
145185642Sjhb		}
146185642Sjhb		MOD_XUNLOCK;
14790963Sarr	}
148159957Sjhb	mtx_unlock(&Giant);
14925537Sdfr}
15025537Sdfr
15125537Sdfrint
15246693Spetermodule_register(const moduledata_t *data, linker_file_t container)
15325537Sdfr{
15490963Sarr	size_t namelen;
15590963Sarr	module_t newmod;
15625537Sdfr
157159634Smaxim	MOD_XLOCK;
15890963Sarr	newmod = module_lookupbyname(data->name);
15990963Sarr	if (newmod != NULL) {
160159634Smaxim		MOD_XUNLOCK;
16191067Sarr		printf("module_register: module %s already exists!\n",
16290963Sarr		    data->name);
16390980Sarr		return (EEXIST);
16490963Sarr	}
16590963Sarr	namelen = strlen(data->name) + 1;
166111119Simp	newmod = malloc(sizeof(struct module) + namelen, M_MODULE, M_WAITOK);
167159634Smaxim	if (newmod == NULL) {
168159634Smaxim		MOD_XUNLOCK;
16990980Sarr		return (ENOMEM);
170159634Smaxim	}
17190963Sarr	newmod->refs = 1;
17290963Sarr	newmod->id = nextid++;
17390963Sarr	newmod->name = (char *)(newmod + 1);
17490963Sarr	strcpy(newmod->name, data->name);
17590963Sarr	newmod->handler = data->evhand ? data->evhand : modevent_nop;
17690963Sarr	newmod->arg = data->priv;
17790963Sarr	bzero(&newmod->data, sizeof(newmod->data));
17890963Sarr	TAILQ_INSERT_TAIL(&modules, newmod, link);
17925537Sdfr
18090963Sarr	if (container)
18190963Sarr		TAILQ_INSERT_TAIL(&container->modules, newmod, flink);
18290963Sarr	newmod->file = container;
18392547Sarr	MOD_XUNLOCK;
18490980Sarr	return (0);
18525537Sdfr}
18625537Sdfr
18725537Sdfrvoid
18825537Sdfrmodule_reference(module_t mod)
18925537Sdfr{
19025537Sdfr
19192547Sarr	MOD_XLOCK_ASSERT;
19292547Sarr
19390963Sarr	MOD_DPF(REFS, ("module_reference: before, refs=%d\n", mod->refs));
19490963Sarr	mod->refs++;
19525537Sdfr}
19625537Sdfr
19725537Sdfrvoid
19825537Sdfrmodule_release(module_t mod)
19925537Sdfr{
20025537Sdfr
20192547Sarr	MOD_XLOCK_ASSERT;
20292547Sarr
20390963Sarr	if (mod->refs <= 0)
20490963Sarr		panic("module_release: bad reference count");
20525537Sdfr
20690963Sarr	MOD_DPF(REFS, ("module_release: before, refs=%d\n", mod->refs));
20792547Sarr
20890963Sarr	mod->refs--;
20990963Sarr	if (mod->refs == 0) {
21090963Sarr		TAILQ_REMOVE(&modules, mod, link);
21190963Sarr		if (mod->file)
21290963Sarr			TAILQ_REMOVE(&mod->file->modules, mod, flink);
21390963Sarr		free(mod, M_MODULE);
21425537Sdfr	}
21525537Sdfr}
21625537Sdfr
21725537Sdfrmodule_t
21890963Sarrmodule_lookupbyname(const char *name)
21925537Sdfr{
22090963Sarr	module_t mod;
22190963Sarr	int err;
22225537Sdfr
22392547Sarr	MOD_LOCK_ASSERT;
22492547Sarr
22590963Sarr	TAILQ_FOREACH(mod, &modules, link) {
22690963Sarr		err = strcmp(mod->name, name);
22790963Sarr		if (err == 0)
22890980Sarr			return (mod);
22990963Sarr	}
23090980Sarr	return (NULL);
23125537Sdfr}
23225537Sdfr
23325537Sdfrmodule_t
23425537Sdfrmodule_lookupbyid(int modid)
23525537Sdfr{
23692547Sarr        module_t mod;
23725537Sdfr
23892547Sarr        MOD_LOCK_ASSERT;
23992547Sarr
24092547Sarr        TAILQ_FOREACH(mod, &modules, link)
24192547Sarr                if (mod->id == modid)
24292547Sarr                        return(mod);
24392547Sarr        return (NULL);
24425537Sdfr}
24525537Sdfr
24625537Sdfrint
247185635Sjhbmodule_quiesce(module_t mod)
24825537Sdfr{
249132117Sphk	int error;
25090963Sarr
251159957Sjhb	mtx_lock(&Giant);
252132117Sphk	error = MOD_EVENT(mod, MOD_QUIESCE);
253185635Sjhb	mtx_unlock(&Giant);
254132199Sphk	if (error == EOPNOTSUPP || error == EINVAL)
255132117Sphk		error = 0;
256185635Sjhb	return (error);
257185635Sjhb}
258185635Sjhb
259185635Sjhbint
260185635Sjhbmodule_unload(module_t mod)
261185635Sjhb{
262185635Sjhb	int error;
263185635Sjhb
264185635Sjhb	mtx_lock(&Giant);
265185635Sjhb	error = MOD_EVENT(mod, MOD_UNLOAD);
266159957Sjhb	mtx_unlock(&Giant);
267159957Sjhb	return (error);
26825537Sdfr}
26925537Sdfr
27025537Sdfrint
27125537Sdfrmodule_getid(module_t mod)
27225537Sdfr{
27390963Sarr
27492547Sarr	MOD_LOCK_ASSERT;
27590980Sarr	return (mod->id);
27625537Sdfr}
27725537Sdfr
27825537Sdfrmodule_t
27925537Sdfrmodule_getfnext(module_t mod)
28025537Sdfr{
28190963Sarr
28292547Sarr	MOD_LOCK_ASSERT;
28390980Sarr	return (TAILQ_NEXT(mod, flink));
28425537Sdfr}
28525537Sdfr
286185635Sjhbconst char *
287185635Sjhbmodule_getname(module_t mod)
288185635Sjhb{
289185635Sjhb
290185635Sjhb	MOD_LOCK_ASSERT;
291185635Sjhb	return (mod->name);
292185635Sjhb}
293185635Sjhb
29442435Sdfrvoid
29542435Sdfrmodule_setspecific(module_t mod, modspecific_t *datap)
29642435Sdfr{
29790963Sarr
29892547Sarr	MOD_XLOCK_ASSERT;
29990963Sarr	mod->data = *datap;
30042435Sdfr}
30142435Sdfr
302157818Sjhblinker_file_t
303157818Sjhbmodule_file(module_t mod)
304157818Sjhb{
305157818Sjhb
306157818Sjhb	return (mod->file);
307157818Sjhb}
308157818Sjhb
30925537Sdfr/*
31025537Sdfr * Syscalls.
31125537Sdfr */
31225537Sdfrint
313225617Skmacysys_modnext(struct thread *td, struct modnext_args *uap)
31425537Sdfr{
31590963Sarr	module_t mod;
31690963Sarr	int error = 0;
31725537Sdfr
31898835Sarr	td->td_retval[0] = -1;
31982749Sdillon
32092547Sarr	MOD_SLOCK;
321107849Salfred	if (uap->modid == 0) {
32290963Sarr		mod = TAILQ_FIRST(&modules);
32390963Sarr		if (mod)
32490963Sarr			td->td_retval[0] = mod->id;
32590963Sarr		else
32690963Sarr			error = ENOENT;
32790963Sarr		goto done2;
32890963Sarr	}
329107849Salfred	mod = module_lookupbyid(uap->modid);
33090963Sarr	if (mod == NULL) {
33190963Sarr		error = ENOENT;
33290963Sarr		goto done2;
33390963Sarr	}
33490963Sarr	if (TAILQ_NEXT(mod, link))
33590963Sarr		td->td_retval[0] = TAILQ_NEXT(mod, link)->id;
33682749Sdillon	else
33790963Sarr		td->td_retval[0] = 0;
33882749Sdillondone2:
33992547Sarr	MOD_SUNLOCK;
34090963Sarr	return (error);
34125537Sdfr}
34225537Sdfr
34325537Sdfrint
344225617Skmacysys_modfnext(struct thread *td, struct modfnext_args *uap)
34525537Sdfr{
34690963Sarr	module_t mod;
34790963Sarr	int error;
34825537Sdfr
34990963Sarr	td->td_retval[0] = -1;
35025537Sdfr
35192547Sarr	MOD_SLOCK;
352107849Salfred	mod = module_lookupbyid(uap->modid);
35390963Sarr	if (mod == NULL) {
35490963Sarr		error = ENOENT;
35590963Sarr	} else {
35690963Sarr		error = 0;
35790963Sarr		if (TAILQ_NEXT(mod, flink))
35890963Sarr			td->td_retval[0] = TAILQ_NEXT(mod, flink)->id;
35990963Sarr		else
36090963Sarr			td->td_retval[0] = 0;
36190963Sarr	}
36292547Sarr	MOD_SUNLOCK;
36390963Sarr	return (error);
36425537Sdfr}
36525537Sdfr
36642435Sdfrstruct module_stat_v1 {
36791067Sarr	int	version;		/* set to sizeof(struct module_stat) */
36891067Sarr	char	name[MAXMODNAME];
36991067Sarr	int	refs;
37091067Sarr	int	id;
37142435Sdfr};
37242435Sdfr
37325537Sdfrint
374225617Skmacysys_modstat(struct thread *td, struct modstat_args *uap)
37525537Sdfr{
37690963Sarr	module_t mod;
37792547Sarr	modspecific_t data;
37890963Sarr	int error = 0;
37992547Sarr	int id, namelen, refs, version;
38090963Sarr	struct module_stat *stat;
38192547Sarr	char *name;
38225537Sdfr
38392547Sarr	MOD_SLOCK;
384107849Salfred	mod = module_lookupbyid(uap->modid);
38590963Sarr	if (mod == NULL) {
38692547Sarr		MOD_SUNLOCK;
38798835Sarr		return (ENOENT);
38890963Sarr	}
38992547Sarr	id = mod->id;
39092547Sarr	refs = mod->refs;
39192547Sarr	name = mod->name;
39292547Sarr	data = mod->data;
39392547Sarr	MOD_SUNLOCK;
394107849Salfred	stat = uap->stat;
39525537Sdfr
39690963Sarr	/*
39790963Sarr	 * Check the version of the user's structure.
39890963Sarr	 */
39990963Sarr	if ((error = copyin(&stat->version, &version, sizeof(version))) != 0)
40098835Sarr		return (error);
40190963Sarr	if (version != sizeof(struct module_stat_v1)
40298835Sarr	    && version != sizeof(struct module_stat))
40398835Sarr		return (EINVAL);
40490963Sarr	namelen = strlen(mod->name) + 1;
40590963Sarr	if (namelen > MAXMODNAME)
40690963Sarr		namelen = MAXMODNAME;
40792547Sarr	if ((error = copyout(name, &stat->name[0], namelen)) != 0)
40898835Sarr		return (error);
40925537Sdfr
41092547Sarr	if ((error = copyout(&refs, &stat->refs, sizeof(int))) != 0)
41198835Sarr		return (error);
41292547Sarr	if ((error = copyout(&id, &stat->id, sizeof(int))) != 0)
41398835Sarr		return (error);
41425537Sdfr
41590963Sarr	/*
41690963Sarr	 * >v1 stat includes module data.
41790963Sarr	 */
41898835Sarr	if (version == sizeof(struct module_stat))
41992547Sarr		if ((error = copyout(&data, &stat->data,
42092547Sarr		    sizeof(data))) != 0)
42198835Sarr			return (error);
42290963Sarr	td->td_retval[0] = 0;
42390980Sarr	return (error);
42425537Sdfr}
42525537Sdfr
42625537Sdfrint
427225617Skmacysys_modfind(struct thread *td, struct modfind_args *uap)
42825537Sdfr{
42990963Sarr	int error = 0;
43090963Sarr	char name[MAXMODNAME];
43190963Sarr	module_t mod;
43225537Sdfr
433107849Salfred	if ((error = copyinstr(uap->name, name, sizeof name, 0)) != 0)
43498835Sarr		return (error);
43525537Sdfr
43692547Sarr	MOD_SLOCK;
43790963Sarr	mod = module_lookupbyname(name);
43890963Sarr	if (mod == NULL)
43990963Sarr		error = ENOENT;
44090963Sarr	else
44192547Sarr		td->td_retval[0] = module_getid(mod);
44292547Sarr	MOD_SUNLOCK;
44390980Sarr	return (error);
44425537Sdfr}
445140482Sps
446176252SjhbMODULE_VERSION(kernel, __FreeBSD_version);
447176252Sjhb
448205014Snwhitehorn#ifdef COMPAT_FREEBSD32
449140482Sps#include <sys/mount.h>
450174377Sjhb#include <sys/socket.h>
451140482Sps#include <compat/freebsd32/freebsd32_util.h>
452140482Sps#include <compat/freebsd32/freebsd32.h>
453140482Sps#include <compat/freebsd32/freebsd32_proto.h>
454140482Sps
455140482Spstypedef union modspecific32 {
456140482Sps	int		intval;
457209390Sed	uint32_t	uintval;
458140482Sps	int		longval;
459209390Sed	uint32_t	ulongval;
460140482Sps} modspecific32_t;
461140482Sps
462140482Spsstruct module_stat32 {
463140482Sps	int		version;
464140482Sps	char		name[MAXMODNAME];
465140482Sps	int		refs;
466140482Sps	int		id;
467140482Sps	modspecific32_t	data;
468140482Sps};
469140482Sps
470140482Spsint
471140482Spsfreebsd32_modstat(struct thread *td, struct freebsd32_modstat_args *uap)
472140482Sps{
473140482Sps	module_t mod;
474140482Sps	modspecific32_t data32;
475140482Sps	int error = 0;
476140482Sps	int id, namelen, refs, version;
477140482Sps	struct module_stat32 *stat32;
478140482Sps	char *name;
479140482Sps
480140482Sps	MOD_SLOCK;
481140482Sps	mod = module_lookupbyid(uap->modid);
482140482Sps	if (mod == NULL) {
483140482Sps		MOD_SUNLOCK;
484140482Sps		return (ENOENT);
485140482Sps	}
486140482Sps
487140482Sps	id = mod->id;
488140482Sps	refs = mod->refs;
489140482Sps	name = mod->name;
490142067Sps	CP(mod->data, data32, intval);
491142067Sps	CP(mod->data, data32, uintval);
492142067Sps	CP(mod->data, data32, longval);
493142067Sps	CP(mod->data, data32, ulongval);
494140482Sps	MOD_SUNLOCK;
495140482Sps	stat32 = uap->stat;
496140482Sps
497140482Sps	if ((error = copyin(&stat32->version, &version, sizeof(version))) != 0)
498140482Sps		return (error);
499140482Sps	if (version != sizeof(struct module_stat_v1)
500140482Sps	    && version != sizeof(struct module_stat32))
501140482Sps		return (EINVAL);
502140482Sps	namelen = strlen(mod->name) + 1;
503140482Sps	if (namelen > MAXMODNAME)
504140482Sps		namelen = MAXMODNAME;
505140482Sps	if ((error = copyout(name, &stat32->name[0], namelen)) != 0)
506140482Sps		return (error);
507140482Sps
508140482Sps	if ((error = copyout(&refs, &stat32->refs, sizeof(int))) != 0)
509140482Sps		return (error);
510140482Sps	if ((error = copyout(&id, &stat32->id, sizeof(int))) != 0)
511140482Sps		return (error);
512140482Sps
513140482Sps	/*
514140482Sps	 * >v1 stat includes module data.
515140482Sps	 */
516140482Sps	if (version == sizeof(struct module_stat32))
517140482Sps		if ((error = copyout(&data32, &stat32->data,
518140482Sps		    sizeof(data32))) != 0)
519140482Sps			return (error);
520140482Sps	td->td_retval[0] = 0;
521140482Sps	return (error);
522140482Sps}
523140482Sps#endif
524