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: stable/11/sys/kern/kern_module.c 359652 2020-04-06 07:16:31Z hselasky $");
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
92359652ShselaskySYSINIT(module, SI_SUB_KLD, SI_ORDER_FIRST, module_init, NULL);
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;
161289111Strasz		printf("%s: cannot register %s from %s; already loaded from %s\n",
162289111Strasz		    __func__, data->name, container->filename, newmod->file->filename);
16390980Sarr		return (EEXIST);
16490963Sarr	}
16590963Sarr	namelen = strlen(data->name) + 1;
166111119Simp	newmod = malloc(sizeof(struct module) + namelen, M_MODULE, M_WAITOK);
16790963Sarr	newmod->refs = 1;
16890963Sarr	newmod->id = nextid++;
16990963Sarr	newmod->name = (char *)(newmod + 1);
17090963Sarr	strcpy(newmod->name, data->name);
17190963Sarr	newmod->handler = data->evhand ? data->evhand : modevent_nop;
17290963Sarr	newmod->arg = data->priv;
17390963Sarr	bzero(&newmod->data, sizeof(newmod->data));
17490963Sarr	TAILQ_INSERT_TAIL(&modules, newmod, link);
17525537Sdfr
17690963Sarr	if (container)
17790963Sarr		TAILQ_INSERT_TAIL(&container->modules, newmod, flink);
17890963Sarr	newmod->file = container;
17992547Sarr	MOD_XUNLOCK;
18090980Sarr	return (0);
18125537Sdfr}
18225537Sdfr
18325537Sdfrvoid
18425537Sdfrmodule_reference(module_t mod)
18525537Sdfr{
18625537Sdfr
18792547Sarr	MOD_XLOCK_ASSERT;
18892547Sarr
18990963Sarr	MOD_DPF(REFS, ("module_reference: before, refs=%d\n", mod->refs));
19090963Sarr	mod->refs++;
19125537Sdfr}
19225537Sdfr
19325537Sdfrvoid
19425537Sdfrmodule_release(module_t mod)
19525537Sdfr{
19625537Sdfr
19792547Sarr	MOD_XLOCK_ASSERT;
19892547Sarr
19990963Sarr	if (mod->refs <= 0)
20090963Sarr		panic("module_release: bad reference count");
20125537Sdfr
20290963Sarr	MOD_DPF(REFS, ("module_release: before, refs=%d\n", mod->refs));
20392547Sarr
20490963Sarr	mod->refs--;
20590963Sarr	if (mod->refs == 0) {
20690963Sarr		TAILQ_REMOVE(&modules, mod, link);
20790963Sarr		if (mod->file)
20890963Sarr			TAILQ_REMOVE(&mod->file->modules, mod, flink);
20990963Sarr		free(mod, M_MODULE);
21025537Sdfr	}
21125537Sdfr}
21225537Sdfr
21325537Sdfrmodule_t
21490963Sarrmodule_lookupbyname(const char *name)
21525537Sdfr{
21690963Sarr	module_t mod;
21790963Sarr	int err;
21825537Sdfr
21992547Sarr	MOD_LOCK_ASSERT;
22092547Sarr
22190963Sarr	TAILQ_FOREACH(mod, &modules, link) {
22290963Sarr		err = strcmp(mod->name, name);
22390963Sarr		if (err == 0)
22490980Sarr			return (mod);
22590963Sarr	}
22690980Sarr	return (NULL);
22725537Sdfr}
22825537Sdfr
22925537Sdfrmodule_t
23025537Sdfrmodule_lookupbyid(int modid)
23125537Sdfr{
23292547Sarr        module_t mod;
23325537Sdfr
23492547Sarr        MOD_LOCK_ASSERT;
23592547Sarr
23692547Sarr        TAILQ_FOREACH(mod, &modules, link)
23792547Sarr                if (mod->id == modid)
23892547Sarr                        return(mod);
23992547Sarr        return (NULL);
24025537Sdfr}
24125537Sdfr
24225537Sdfrint
243185635Sjhbmodule_quiesce(module_t mod)
24425537Sdfr{
245132117Sphk	int error;
24690963Sarr
247159957Sjhb	mtx_lock(&Giant);
248132117Sphk	error = MOD_EVENT(mod, MOD_QUIESCE);
249185635Sjhb	mtx_unlock(&Giant);
250132199Sphk	if (error == EOPNOTSUPP || error == EINVAL)
251132117Sphk		error = 0;
252185635Sjhb	return (error);
253185635Sjhb}
254185635Sjhb
255185635Sjhbint
256185635Sjhbmodule_unload(module_t mod)
257185635Sjhb{
258185635Sjhb	int error;
259185635Sjhb
260185635Sjhb	mtx_lock(&Giant);
261185635Sjhb	error = MOD_EVENT(mod, MOD_UNLOAD);
262159957Sjhb	mtx_unlock(&Giant);
263159957Sjhb	return (error);
26425537Sdfr}
26525537Sdfr
26625537Sdfrint
26725537Sdfrmodule_getid(module_t mod)
26825537Sdfr{
26990963Sarr
27092547Sarr	MOD_LOCK_ASSERT;
27190980Sarr	return (mod->id);
27225537Sdfr}
27325537Sdfr
27425537Sdfrmodule_t
27525537Sdfrmodule_getfnext(module_t mod)
27625537Sdfr{
27790963Sarr
27892547Sarr	MOD_LOCK_ASSERT;
27990980Sarr	return (TAILQ_NEXT(mod, flink));
28025537Sdfr}
28125537Sdfr
282185635Sjhbconst char *
283185635Sjhbmodule_getname(module_t mod)
284185635Sjhb{
285185635Sjhb
286185635Sjhb	MOD_LOCK_ASSERT;
287185635Sjhb	return (mod->name);
288185635Sjhb}
289185635Sjhb
29042435Sdfrvoid
29142435Sdfrmodule_setspecific(module_t mod, modspecific_t *datap)
29242435Sdfr{
29390963Sarr
29492547Sarr	MOD_XLOCK_ASSERT;
29590963Sarr	mod->data = *datap;
29642435Sdfr}
29742435Sdfr
298157818Sjhblinker_file_t
299157818Sjhbmodule_file(module_t mod)
300157818Sjhb{
301157818Sjhb
302157818Sjhb	return (mod->file);
303157818Sjhb}
304157818Sjhb
30525537Sdfr/*
30625537Sdfr * Syscalls.
30725537Sdfr */
30825537Sdfrint
309225617Skmacysys_modnext(struct thread *td, struct modnext_args *uap)
31025537Sdfr{
31190963Sarr	module_t mod;
31290963Sarr	int error = 0;
31325537Sdfr
31498835Sarr	td->td_retval[0] = -1;
31582749Sdillon
31692547Sarr	MOD_SLOCK;
317107849Salfred	if (uap->modid == 0) {
31890963Sarr		mod = TAILQ_FIRST(&modules);
31990963Sarr		if (mod)
32090963Sarr			td->td_retval[0] = mod->id;
32190963Sarr		else
32290963Sarr			error = ENOENT;
32390963Sarr		goto done2;
32490963Sarr	}
325107849Salfred	mod = module_lookupbyid(uap->modid);
32690963Sarr	if (mod == NULL) {
32790963Sarr		error = ENOENT;
32890963Sarr		goto done2;
32990963Sarr	}
33090963Sarr	if (TAILQ_NEXT(mod, link))
33190963Sarr		td->td_retval[0] = TAILQ_NEXT(mod, link)->id;
33282749Sdillon	else
33390963Sarr		td->td_retval[0] = 0;
33482749Sdillondone2:
33592547Sarr	MOD_SUNLOCK;
33690963Sarr	return (error);
33725537Sdfr}
33825537Sdfr
33925537Sdfrint
340225617Skmacysys_modfnext(struct thread *td, struct modfnext_args *uap)
34125537Sdfr{
34290963Sarr	module_t mod;
34390963Sarr	int error;
34425537Sdfr
34590963Sarr	td->td_retval[0] = -1;
34625537Sdfr
34792547Sarr	MOD_SLOCK;
348107849Salfred	mod = module_lookupbyid(uap->modid);
34990963Sarr	if (mod == NULL) {
35090963Sarr		error = ENOENT;
35190963Sarr	} else {
35290963Sarr		error = 0;
35390963Sarr		if (TAILQ_NEXT(mod, flink))
35490963Sarr			td->td_retval[0] = TAILQ_NEXT(mod, flink)->id;
35590963Sarr		else
35690963Sarr			td->td_retval[0] = 0;
35790963Sarr	}
35892547Sarr	MOD_SUNLOCK;
35990963Sarr	return (error);
36025537Sdfr}
36125537Sdfr
36242435Sdfrstruct module_stat_v1 {
36391067Sarr	int	version;		/* set to sizeof(struct module_stat) */
36491067Sarr	char	name[MAXMODNAME];
36591067Sarr	int	refs;
36691067Sarr	int	id;
36742435Sdfr};
36842435Sdfr
36925537Sdfrint
370225617Skmacysys_modstat(struct thread *td, struct modstat_args *uap)
37125537Sdfr{
37290963Sarr	module_t mod;
37392547Sarr	modspecific_t data;
37490963Sarr	int error = 0;
37592547Sarr	int id, namelen, refs, version;
37690963Sarr	struct module_stat *stat;
37792547Sarr	char *name;
37825537Sdfr
37992547Sarr	MOD_SLOCK;
380107849Salfred	mod = module_lookupbyid(uap->modid);
38190963Sarr	if (mod == NULL) {
38292547Sarr		MOD_SUNLOCK;
38398835Sarr		return (ENOENT);
38490963Sarr	}
38592547Sarr	id = mod->id;
38692547Sarr	refs = mod->refs;
38792547Sarr	name = mod->name;
38892547Sarr	data = mod->data;
38992547Sarr	MOD_SUNLOCK;
390107849Salfred	stat = uap->stat;
39125537Sdfr
39290963Sarr	/*
39390963Sarr	 * Check the version of the user's structure.
39490963Sarr	 */
39590963Sarr	if ((error = copyin(&stat->version, &version, sizeof(version))) != 0)
39698835Sarr		return (error);
39790963Sarr	if (version != sizeof(struct module_stat_v1)
39898835Sarr	    && version != sizeof(struct module_stat))
39998835Sarr		return (EINVAL);
40090963Sarr	namelen = strlen(mod->name) + 1;
40190963Sarr	if (namelen > MAXMODNAME)
40290963Sarr		namelen = MAXMODNAME;
40392547Sarr	if ((error = copyout(name, &stat->name[0], namelen)) != 0)
40498835Sarr		return (error);
40525537Sdfr
40692547Sarr	if ((error = copyout(&refs, &stat->refs, sizeof(int))) != 0)
40798835Sarr		return (error);
40892547Sarr	if ((error = copyout(&id, &stat->id, sizeof(int))) != 0)
40998835Sarr		return (error);
41025537Sdfr
41190963Sarr	/*
41290963Sarr	 * >v1 stat includes module data.
41390963Sarr	 */
41498835Sarr	if (version == sizeof(struct module_stat))
41592547Sarr		if ((error = copyout(&data, &stat->data,
41692547Sarr		    sizeof(data))) != 0)
41798835Sarr			return (error);
41890963Sarr	td->td_retval[0] = 0;
41990980Sarr	return (error);
42025537Sdfr}
42125537Sdfr
42225537Sdfrint
423225617Skmacysys_modfind(struct thread *td, struct modfind_args *uap)
42425537Sdfr{
42590963Sarr	int error = 0;
42690963Sarr	char name[MAXMODNAME];
42790963Sarr	module_t mod;
42825537Sdfr
429107849Salfred	if ((error = copyinstr(uap->name, name, sizeof name, 0)) != 0)
43098835Sarr		return (error);
43125537Sdfr
43292547Sarr	MOD_SLOCK;
43390963Sarr	mod = module_lookupbyname(name);
43490963Sarr	if (mod == NULL)
43590963Sarr		error = ENOENT;
43690963Sarr	else
43792547Sarr		td->td_retval[0] = module_getid(mod);
43892547Sarr	MOD_SUNLOCK;
43990980Sarr	return (error);
44025537Sdfr}
441140482Sps
442176252SjhbMODULE_VERSION(kernel, __FreeBSD_version);
443176252Sjhb
444205014Snwhitehorn#ifdef COMPAT_FREEBSD32
445140482Sps#include <sys/mount.h>
446174377Sjhb#include <sys/socket.h>
447140482Sps#include <compat/freebsd32/freebsd32_util.h>
448140482Sps#include <compat/freebsd32/freebsd32.h>
449140482Sps#include <compat/freebsd32/freebsd32_proto.h>
450140482Sps
451140482Spstypedef union modspecific32 {
452140482Sps	int		intval;
453209390Sed	uint32_t	uintval;
454140482Sps	int		longval;
455209390Sed	uint32_t	ulongval;
456140482Sps} modspecific32_t;
457140482Sps
458140482Spsstruct module_stat32 {
459140482Sps	int		version;
460140482Sps	char		name[MAXMODNAME];
461140482Sps	int		refs;
462140482Sps	int		id;
463140482Sps	modspecific32_t	data;
464140482Sps};
465140482Sps
466140482Spsint
467140482Spsfreebsd32_modstat(struct thread *td, struct freebsd32_modstat_args *uap)
468140482Sps{
469140482Sps	module_t mod;
470140482Sps	modspecific32_t data32;
471140482Sps	int error = 0;
472140482Sps	int id, namelen, refs, version;
473140482Sps	struct module_stat32 *stat32;
474140482Sps	char *name;
475140482Sps
476140482Sps	MOD_SLOCK;
477140482Sps	mod = module_lookupbyid(uap->modid);
478140482Sps	if (mod == NULL) {
479140482Sps		MOD_SUNLOCK;
480140482Sps		return (ENOENT);
481140482Sps	}
482140482Sps
483140482Sps	id = mod->id;
484140482Sps	refs = mod->refs;
485140482Sps	name = mod->name;
486142067Sps	CP(mod->data, data32, intval);
487142067Sps	CP(mod->data, data32, uintval);
488142067Sps	CP(mod->data, data32, longval);
489142067Sps	CP(mod->data, data32, ulongval);
490140482Sps	MOD_SUNLOCK;
491140482Sps	stat32 = uap->stat;
492140482Sps
493140482Sps	if ((error = copyin(&stat32->version, &version, sizeof(version))) != 0)
494140482Sps		return (error);
495140482Sps	if (version != sizeof(struct module_stat_v1)
496140482Sps	    && version != sizeof(struct module_stat32))
497140482Sps		return (EINVAL);
498140482Sps	namelen = strlen(mod->name) + 1;
499140482Sps	if (namelen > MAXMODNAME)
500140482Sps		namelen = MAXMODNAME;
501140482Sps	if ((error = copyout(name, &stat32->name[0], namelen)) != 0)
502140482Sps		return (error);
503140482Sps
504140482Sps	if ((error = copyout(&refs, &stat32->refs, sizeof(int))) != 0)
505140482Sps		return (error);
506140482Sps	if ((error = copyout(&id, &stat32->id, sizeof(int))) != 0)
507140482Sps		return (error);
508140482Sps
509140482Sps	/*
510140482Sps	 * >v1 stat includes module data.
511140482Sps	 */
512140482Sps	if (version == sizeof(struct module_stat32))
513140482Sps		if ((error = copyout(&data32, &stat32->data,
514140482Sps		    sizeof(data32))) != 0)
515140482Sps			return (error);
516140482Sps	td->td_retval[0] = 0;
517140482Sps	return (error);
518140482Sps}
519140482Sps#endif
520