1/*-
2 * Copyright (c) 1997 Doug Rabson
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include "opt_compat.h"
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD: stable/11/sys/kern/kern_module.c 359652 2020-04-06 07:16:31Z hselasky $");
31
32#include <sys/param.h>
33#include <sys/kernel.h>
34#include <sys/systm.h>
35#include <sys/eventhandler.h>
36#include <sys/malloc.h>
37#include <sys/sysproto.h>
38#include <sys/sysent.h>
39#include <sys/proc.h>
40#include <sys/lock.h>
41#include <sys/mutex.h>
42#include <sys/reboot.h>
43#include <sys/sx.h>
44#include <sys/module.h>
45#include <sys/linker.h>
46
47static MALLOC_DEFINE(M_MODULE, "module", "module data structures");
48
49struct module {
50	TAILQ_ENTRY(module)	link;	/* chain together all modules */
51	TAILQ_ENTRY(module)	flink;	/* all modules in a file */
52	struct linker_file	*file;	/* file which contains this module */
53	int			refs;	/* reference count */
54	int 			id;	/* unique id number */
55	char 			*name;	/* module name */
56	modeventhand_t 		handler;	/* event handler */
57	void 			*arg;	/* argument for handler */
58	modspecific_t 		data;	/* module specific data */
59};
60
61#define MOD_EVENT(mod, type)	(mod)->handler((mod), (type), (mod)->arg)
62
63static TAILQ_HEAD(modulelist, module) modules;
64struct sx modules_sx;
65static int nextid = 1;
66static void module_shutdown(void *, int);
67
68static int
69modevent_nop(module_t mod, int what, void *arg)
70{
71
72	switch(what) {
73	case MOD_LOAD:
74		return (0);
75	case MOD_UNLOAD:
76		return (EBUSY);
77	default:
78		return (EOPNOTSUPP);
79	}
80}
81
82static void
83module_init(void *arg)
84{
85
86	sx_init(&modules_sx, "module subsystem sx lock");
87	TAILQ_INIT(&modules);
88	EVENTHANDLER_REGISTER(shutdown_final, module_shutdown, NULL,
89	    SHUTDOWN_PRI_DEFAULT);
90}
91
92SYSINIT(module, SI_SUB_KLD, SI_ORDER_FIRST, module_init, NULL);
93
94static void
95module_shutdown(void *arg1, int arg2)
96{
97	module_t mod;
98
99	if (arg2 & RB_NOSYNC)
100		return;
101	mtx_lock(&Giant);
102	MOD_SLOCK;
103	TAILQ_FOREACH_REVERSE(mod, &modules, modulelist, link)
104		MOD_EVENT(mod, MOD_SHUTDOWN);
105	MOD_SUNLOCK;
106	mtx_unlock(&Giant);
107}
108
109void
110module_register_init(const void *arg)
111{
112	const moduledata_t *data = (const moduledata_t *)arg;
113	int error;
114	module_t mod;
115
116	mtx_lock(&Giant);
117	MOD_SLOCK;
118	mod = module_lookupbyname(data->name);
119	if (mod == NULL)
120		panic("module_register_init: module named %s not found\n",
121		    data->name);
122	MOD_SUNLOCK;
123	error = MOD_EVENT(mod, MOD_LOAD);
124	if (error) {
125		MOD_EVENT(mod, MOD_UNLOAD);
126		MOD_XLOCK;
127		module_release(mod);
128		MOD_XUNLOCK;
129		printf("module_register_init: MOD_LOAD (%s, %p, %p) error"
130		    " %d\n", data->name, (void *)data->evhand, data->priv,
131		    error);
132	} else {
133		MOD_XLOCK;
134		if (mod->file) {
135			/*
136			 * Once a module is successfully loaded, move
137			 * it to the head of the module list for this
138			 * linker file.  This resorts the list so that
139			 * when the kernel linker iterates over the
140			 * modules to unload them, it will unload them
141			 * in the reverse order they were loaded.
142			 */
143			TAILQ_REMOVE(&mod->file->modules, mod, flink);
144			TAILQ_INSERT_HEAD(&mod->file->modules, mod, flink);
145		}
146		MOD_XUNLOCK;
147	}
148	mtx_unlock(&Giant);
149}
150
151int
152module_register(const moduledata_t *data, linker_file_t container)
153{
154	size_t namelen;
155	module_t newmod;
156
157	MOD_XLOCK;
158	newmod = module_lookupbyname(data->name);
159	if (newmod != NULL) {
160		MOD_XUNLOCK;
161		printf("%s: cannot register %s from %s; already loaded from %s\n",
162		    __func__, data->name, container->filename, newmod->file->filename);
163		return (EEXIST);
164	}
165	namelen = strlen(data->name) + 1;
166	newmod = malloc(sizeof(struct module) + namelen, M_MODULE, M_WAITOK);
167	newmod->refs = 1;
168	newmod->id = nextid++;
169	newmod->name = (char *)(newmod + 1);
170	strcpy(newmod->name, data->name);
171	newmod->handler = data->evhand ? data->evhand : modevent_nop;
172	newmod->arg = data->priv;
173	bzero(&newmod->data, sizeof(newmod->data));
174	TAILQ_INSERT_TAIL(&modules, newmod, link);
175
176	if (container)
177		TAILQ_INSERT_TAIL(&container->modules, newmod, flink);
178	newmod->file = container;
179	MOD_XUNLOCK;
180	return (0);
181}
182
183void
184module_reference(module_t mod)
185{
186
187	MOD_XLOCK_ASSERT;
188
189	MOD_DPF(REFS, ("module_reference: before, refs=%d\n", mod->refs));
190	mod->refs++;
191}
192
193void
194module_release(module_t mod)
195{
196
197	MOD_XLOCK_ASSERT;
198
199	if (mod->refs <= 0)
200		panic("module_release: bad reference count");
201
202	MOD_DPF(REFS, ("module_release: before, refs=%d\n", mod->refs));
203
204	mod->refs--;
205	if (mod->refs == 0) {
206		TAILQ_REMOVE(&modules, mod, link);
207		if (mod->file)
208			TAILQ_REMOVE(&mod->file->modules, mod, flink);
209		free(mod, M_MODULE);
210	}
211}
212
213module_t
214module_lookupbyname(const char *name)
215{
216	module_t mod;
217	int err;
218
219	MOD_LOCK_ASSERT;
220
221	TAILQ_FOREACH(mod, &modules, link) {
222		err = strcmp(mod->name, name);
223		if (err == 0)
224			return (mod);
225	}
226	return (NULL);
227}
228
229module_t
230module_lookupbyid(int modid)
231{
232        module_t mod;
233
234        MOD_LOCK_ASSERT;
235
236        TAILQ_FOREACH(mod, &modules, link)
237                if (mod->id == modid)
238                        return(mod);
239        return (NULL);
240}
241
242int
243module_quiesce(module_t mod)
244{
245	int error;
246
247	mtx_lock(&Giant);
248	error = MOD_EVENT(mod, MOD_QUIESCE);
249	mtx_unlock(&Giant);
250	if (error == EOPNOTSUPP || error == EINVAL)
251		error = 0;
252	return (error);
253}
254
255int
256module_unload(module_t mod)
257{
258	int error;
259
260	mtx_lock(&Giant);
261	error = MOD_EVENT(mod, MOD_UNLOAD);
262	mtx_unlock(&Giant);
263	return (error);
264}
265
266int
267module_getid(module_t mod)
268{
269
270	MOD_LOCK_ASSERT;
271	return (mod->id);
272}
273
274module_t
275module_getfnext(module_t mod)
276{
277
278	MOD_LOCK_ASSERT;
279	return (TAILQ_NEXT(mod, flink));
280}
281
282const char *
283module_getname(module_t mod)
284{
285
286	MOD_LOCK_ASSERT;
287	return (mod->name);
288}
289
290void
291module_setspecific(module_t mod, modspecific_t *datap)
292{
293
294	MOD_XLOCK_ASSERT;
295	mod->data = *datap;
296}
297
298linker_file_t
299module_file(module_t mod)
300{
301
302	return (mod->file);
303}
304
305/*
306 * Syscalls.
307 */
308int
309sys_modnext(struct thread *td, struct modnext_args *uap)
310{
311	module_t mod;
312	int error = 0;
313
314	td->td_retval[0] = -1;
315
316	MOD_SLOCK;
317	if (uap->modid == 0) {
318		mod = TAILQ_FIRST(&modules);
319		if (mod)
320			td->td_retval[0] = mod->id;
321		else
322			error = ENOENT;
323		goto done2;
324	}
325	mod = module_lookupbyid(uap->modid);
326	if (mod == NULL) {
327		error = ENOENT;
328		goto done2;
329	}
330	if (TAILQ_NEXT(mod, link))
331		td->td_retval[0] = TAILQ_NEXT(mod, link)->id;
332	else
333		td->td_retval[0] = 0;
334done2:
335	MOD_SUNLOCK;
336	return (error);
337}
338
339int
340sys_modfnext(struct thread *td, struct modfnext_args *uap)
341{
342	module_t mod;
343	int error;
344
345	td->td_retval[0] = -1;
346
347	MOD_SLOCK;
348	mod = module_lookupbyid(uap->modid);
349	if (mod == NULL) {
350		error = ENOENT;
351	} else {
352		error = 0;
353		if (TAILQ_NEXT(mod, flink))
354			td->td_retval[0] = TAILQ_NEXT(mod, flink)->id;
355		else
356			td->td_retval[0] = 0;
357	}
358	MOD_SUNLOCK;
359	return (error);
360}
361
362struct module_stat_v1 {
363	int	version;		/* set to sizeof(struct module_stat) */
364	char	name[MAXMODNAME];
365	int	refs;
366	int	id;
367};
368
369int
370sys_modstat(struct thread *td, struct modstat_args *uap)
371{
372	module_t mod;
373	modspecific_t data;
374	int error = 0;
375	int id, namelen, refs, version;
376	struct module_stat *stat;
377	char *name;
378
379	MOD_SLOCK;
380	mod = module_lookupbyid(uap->modid);
381	if (mod == NULL) {
382		MOD_SUNLOCK;
383		return (ENOENT);
384	}
385	id = mod->id;
386	refs = mod->refs;
387	name = mod->name;
388	data = mod->data;
389	MOD_SUNLOCK;
390	stat = uap->stat;
391
392	/*
393	 * Check the version of the user's structure.
394	 */
395	if ((error = copyin(&stat->version, &version, sizeof(version))) != 0)
396		return (error);
397	if (version != sizeof(struct module_stat_v1)
398	    && version != sizeof(struct module_stat))
399		return (EINVAL);
400	namelen = strlen(mod->name) + 1;
401	if (namelen > MAXMODNAME)
402		namelen = MAXMODNAME;
403	if ((error = copyout(name, &stat->name[0], namelen)) != 0)
404		return (error);
405
406	if ((error = copyout(&refs, &stat->refs, sizeof(int))) != 0)
407		return (error);
408	if ((error = copyout(&id, &stat->id, sizeof(int))) != 0)
409		return (error);
410
411	/*
412	 * >v1 stat includes module data.
413	 */
414	if (version == sizeof(struct module_stat))
415		if ((error = copyout(&data, &stat->data,
416		    sizeof(data))) != 0)
417			return (error);
418	td->td_retval[0] = 0;
419	return (error);
420}
421
422int
423sys_modfind(struct thread *td, struct modfind_args *uap)
424{
425	int error = 0;
426	char name[MAXMODNAME];
427	module_t mod;
428
429	if ((error = copyinstr(uap->name, name, sizeof name, 0)) != 0)
430		return (error);
431
432	MOD_SLOCK;
433	mod = module_lookupbyname(name);
434	if (mod == NULL)
435		error = ENOENT;
436	else
437		td->td_retval[0] = module_getid(mod);
438	MOD_SUNLOCK;
439	return (error);
440}
441
442MODULE_VERSION(kernel, __FreeBSD_version);
443
444#ifdef COMPAT_FREEBSD32
445#include <sys/mount.h>
446#include <sys/socket.h>
447#include <compat/freebsd32/freebsd32_util.h>
448#include <compat/freebsd32/freebsd32.h>
449#include <compat/freebsd32/freebsd32_proto.h>
450
451typedef union modspecific32 {
452	int		intval;
453	uint32_t	uintval;
454	int		longval;
455	uint32_t	ulongval;
456} modspecific32_t;
457
458struct module_stat32 {
459	int		version;
460	char		name[MAXMODNAME];
461	int		refs;
462	int		id;
463	modspecific32_t	data;
464};
465
466int
467freebsd32_modstat(struct thread *td, struct freebsd32_modstat_args *uap)
468{
469	module_t mod;
470	modspecific32_t data32;
471	int error = 0;
472	int id, namelen, refs, version;
473	struct module_stat32 *stat32;
474	char *name;
475
476	MOD_SLOCK;
477	mod = module_lookupbyid(uap->modid);
478	if (mod == NULL) {
479		MOD_SUNLOCK;
480		return (ENOENT);
481	}
482
483	id = mod->id;
484	refs = mod->refs;
485	name = mod->name;
486	CP(mod->data, data32, intval);
487	CP(mod->data, data32, uintval);
488	CP(mod->data, data32, longval);
489	CP(mod->data, data32, ulongval);
490	MOD_SUNLOCK;
491	stat32 = uap->stat;
492
493	if ((error = copyin(&stat32->version, &version, sizeof(version))) != 0)
494		return (error);
495	if (version != sizeof(struct module_stat_v1)
496	    && version != sizeof(struct module_stat32))
497		return (EINVAL);
498	namelen = strlen(mod->name) + 1;
499	if (namelen > MAXMODNAME)
500		namelen = MAXMODNAME;
501	if ((error = copyout(name, &stat32->name[0], namelen)) != 0)
502		return (error);
503
504	if ((error = copyout(&refs, &stat32->refs, sizeof(int))) != 0)
505		return (error);
506	if ((error = copyout(&id, &stat32->id, sizeof(int))) != 0)
507		return (error);
508
509	/*
510	 * >v1 stat includes module data.
511	 */
512	if (version == sizeof(struct module_stat32))
513		if ((error = copyout(&data32, &stat32->data,
514		    sizeof(data32))) != 0)
515			return (error);
516	td->td_retval[0] = 0;
517	return (error);
518}
519#endif
520