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