xref: /freebsd/sys/kern/kern_module.c (revision 6b3455a7665208c366849f0b2b3bc916fb97516e)
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 <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 #include <sys/param.h>
31 #include <sys/kernel.h>
32 #include <sys/systm.h>
33 #include <sys/eventhandler.h>
34 #include <sys/malloc.h>
35 #include <sys/sysproto.h>
36 #include <sys/sysent.h>
37 #include <sys/proc.h>
38 #include <sys/lock.h>
39 #include <sys/mutex.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 typedef TAILQ_HEAD(, module) modulelist_t;
47 struct module {
48 	TAILQ_ENTRY(module)	link;	/* chain together all modules */
49 	TAILQ_ENTRY(module)	flink;	/* all modules in a file */
50 	struct linker_file	*file;	/* file which contains this module */
51 	int			refs;	/* reference count */
52 	int 			id;	/* unique id number */
53 	char 			*name;	/* module name */
54 	modeventhand_t 		handler;	/* event handler */
55 	void 			*arg;	/* argument for handler */
56 	modspecific_t 		data;	/* module specific data */
57 };
58 
59 #define MOD_EVENT(mod, type)	(mod)->handler((mod), (type), (mod)->arg)
60 
61 static modulelist_t modules;
62 struct sx modules_sx;
63 static int nextid = 1;
64 static void module_shutdown(void *, int);
65 
66 static int
67 modevent_nop(module_t mod, int what, void *arg)
68 {
69 
70 	switch(what) {
71 	case MOD_LOAD:
72 		return (0);
73 	case MOD_UNLOAD:
74 		return (EBUSY);
75 	default:
76 		return (EOPNOTSUPP);
77 	}
78 }
79 
80 static void
81 module_init(void *arg)
82 {
83 
84 	sx_init(&modules_sx, "module subsystem sx lock");
85 	TAILQ_INIT(&modules);
86 	EVENTHANDLER_REGISTER(shutdown_final, module_shutdown, NULL,
87 	    SHUTDOWN_PRI_DEFAULT);
88 }
89 
90 SYSINIT(module, SI_SUB_KLD, SI_ORDER_FIRST, module_init, 0)
91 
92 static void
93 module_shutdown(void *arg1, int arg2)
94 {
95 	module_t mod;
96 
97 	MOD_SLOCK;
98 	TAILQ_FOREACH(mod, &modules, link)
99 		MOD_EVENT(mod, MOD_SHUTDOWN);
100 	MOD_SUNLOCK;
101 }
102 
103 void
104 module_register_init(const void *arg)
105 {
106 	const moduledata_t *data = (const moduledata_t *)arg;
107 	int error;
108 	module_t mod;
109 
110 	MOD_SLOCK;
111 	mod = module_lookupbyname(data->name);
112 	if (mod == NULL)
113 		panic("module_register_init: module named %s not found\n",
114 		    data->name);
115 	MOD_SUNLOCK;
116 	error = MOD_EVENT(mod, MOD_LOAD);
117 	if (error) {
118 		MOD_EVENT(mod, MOD_UNLOAD);
119 		MOD_XLOCK;
120 		module_release(mod);
121 		MOD_XUNLOCK;
122 		printf("module_register_init: MOD_LOAD (%s, %p, %p) error"
123 		    " %d\n", data->name, (void *)data->evhand, data->priv,
124 		    error);
125 	}
126 }
127 
128 int
129 module_register(const moduledata_t *data, linker_file_t container)
130 {
131 	size_t namelen;
132 	module_t newmod;
133 
134 	MOD_SLOCK;
135 	newmod = module_lookupbyname(data->name);
136 	if (newmod != NULL) {
137 		MOD_SUNLOCK;
138 		printf("module_register: module %s already exists!\n",
139 		    data->name);
140 		return (EEXIST);
141 	}
142 	MOD_SUNLOCK;
143 	namelen = strlen(data->name) + 1;
144 	newmod = malloc(sizeof(struct module) + namelen, M_MODULE, M_WAITOK);
145 	if (newmod == NULL)
146 		return (ENOMEM);
147 	MOD_XLOCK;
148 	newmod->refs = 1;
149 	newmod->id = nextid++;
150 	newmod->name = (char *)(newmod + 1);
151 	strcpy(newmod->name, data->name);
152 	newmod->handler = data->evhand ? data->evhand : modevent_nop;
153 	newmod->arg = data->priv;
154 	bzero(&newmod->data, sizeof(newmod->data));
155 	TAILQ_INSERT_TAIL(&modules, newmod, link);
156 
157 	if (container)
158 		TAILQ_INSERT_TAIL(&container->modules, newmod, flink);
159 	newmod->file = container;
160 	MOD_XUNLOCK;
161 	return (0);
162 }
163 
164 void
165 module_reference(module_t mod)
166 {
167 
168 	MOD_XLOCK_ASSERT;
169 
170 	MOD_DPF(REFS, ("module_reference: before, refs=%d\n", mod->refs));
171 	mod->refs++;
172 }
173 
174 void
175 module_release(module_t mod)
176 {
177 
178 	MOD_XLOCK_ASSERT;
179 
180 	if (mod->refs <= 0)
181 		panic("module_release: bad reference count");
182 
183 	MOD_DPF(REFS, ("module_release: before, refs=%d\n", mod->refs));
184 
185 	mod->refs--;
186 	if (mod->refs == 0) {
187 		TAILQ_REMOVE(&modules, mod, link);
188 		if (mod->file)
189 			TAILQ_REMOVE(&mod->file->modules, mod, flink);
190 		MOD_XUNLOCK;
191 		free(mod, M_MODULE);
192 		MOD_XLOCK;
193 	}
194 }
195 
196 module_t
197 module_lookupbyname(const char *name)
198 {
199 	module_t mod;
200 	int err;
201 
202 	MOD_LOCK_ASSERT;
203 
204 	TAILQ_FOREACH(mod, &modules, link) {
205 		err = strcmp(mod->name, name);
206 		if (err == 0)
207 			return (mod);
208 	}
209 	return (NULL);
210 }
211 
212 module_t
213 module_lookupbyid(int modid)
214 {
215         module_t mod;
216 
217         MOD_LOCK_ASSERT;
218 
219         TAILQ_FOREACH(mod, &modules, link)
220                 if (mod->id == modid)
221                         return(mod);
222         return (NULL);
223 }
224 
225 int
226 module_unload(module_t mod, int flags)
227 {
228 	int error;
229 
230 	error = MOD_EVENT(mod, MOD_QUIESCE);
231 	if (error == EOPNOTSUPP || error == EINVAL)
232 		error = 0;
233 	if (flags == LINKER_UNLOAD_NORMAL && error != 0)
234 		return (error);
235         return (MOD_EVENT(mod, MOD_UNLOAD));
236 }
237 
238 int
239 module_getid(module_t mod)
240 {
241 
242 	MOD_LOCK_ASSERT;
243 	return (mod->id);
244 }
245 
246 module_t
247 module_getfnext(module_t mod)
248 {
249 
250 	MOD_LOCK_ASSERT;
251 	return (TAILQ_NEXT(mod, flink));
252 }
253 
254 void
255 module_setspecific(module_t mod, modspecific_t *datap)
256 {
257 
258 	MOD_XLOCK_ASSERT;
259 	mod->data = *datap;
260 }
261 
262 /*
263  * Syscalls.
264  */
265 /*
266  * MPSAFE
267  */
268 int
269 modnext(struct thread *td, struct modnext_args *uap)
270 {
271 	module_t mod;
272 	int error = 0;
273 
274 	td->td_retval[0] = -1;
275 
276 	MOD_SLOCK;
277 	if (uap->modid == 0) {
278 		mod = TAILQ_FIRST(&modules);
279 		if (mod)
280 			td->td_retval[0] = mod->id;
281 		else
282 			error = ENOENT;
283 		goto done2;
284 	}
285 	mod = module_lookupbyid(uap->modid);
286 	if (mod == NULL) {
287 		error = ENOENT;
288 		goto done2;
289 	}
290 	if (TAILQ_NEXT(mod, link))
291 		td->td_retval[0] = TAILQ_NEXT(mod, link)->id;
292 	else
293 		td->td_retval[0] = 0;
294 done2:
295 	MOD_SUNLOCK;
296 	return (error);
297 }
298 
299 /*
300  * MPSAFE
301  */
302 int
303 modfnext(struct thread *td, struct modfnext_args *uap)
304 {
305 	module_t mod;
306 	int error;
307 
308 	td->td_retval[0] = -1;
309 
310 	MOD_SLOCK;
311 	mod = module_lookupbyid(uap->modid);
312 	if (mod == NULL) {
313 		error = ENOENT;
314 	} else {
315 		error = 0;
316 		if (TAILQ_NEXT(mod, flink))
317 			td->td_retval[0] = TAILQ_NEXT(mod, flink)->id;
318 		else
319 			td->td_retval[0] = 0;
320 	}
321 	MOD_SUNLOCK;
322 	return (error);
323 }
324 
325 struct module_stat_v1 {
326 	int	version;		/* set to sizeof(struct module_stat) */
327 	char	name[MAXMODNAME];
328 	int	refs;
329 	int	id;
330 };
331 
332 /*
333  * MPSAFE
334  */
335 int
336 modstat(struct thread *td, struct modstat_args *uap)
337 {
338 	module_t mod;
339 	modspecific_t data;
340 	int error = 0;
341 	int id, namelen, refs, version;
342 	struct module_stat *stat;
343 	char *name;
344 
345 	MOD_SLOCK;
346 	mod = module_lookupbyid(uap->modid);
347 	if (mod == NULL) {
348 		MOD_SUNLOCK;
349 		return (ENOENT);
350 	}
351 	id = mod->id;
352 	refs = mod->refs;
353 	name = mod->name;
354 	data = mod->data;
355 	MOD_SUNLOCK;
356 	stat = uap->stat;
357 
358 	/*
359 	 * Check the version of the user's structure.
360 	 */
361 	if ((error = copyin(&stat->version, &version, sizeof(version))) != 0)
362 		return (error);
363 	if (version != sizeof(struct module_stat_v1)
364 	    && version != sizeof(struct module_stat))
365 		return (EINVAL);
366 	namelen = strlen(mod->name) + 1;
367 	if (namelen > MAXMODNAME)
368 		namelen = MAXMODNAME;
369 	if ((error = copyout(name, &stat->name[0], namelen)) != 0)
370 		return (error);
371 
372 	if ((error = copyout(&refs, &stat->refs, sizeof(int))) != 0)
373 		return (error);
374 	if ((error = copyout(&id, &stat->id, sizeof(int))) != 0)
375 		return (error);
376 
377 	/*
378 	 * >v1 stat includes module data.
379 	 */
380 	if (version == sizeof(struct module_stat))
381 		if ((error = copyout(&data, &stat->data,
382 		    sizeof(data))) != 0)
383 			return (error);
384 	td->td_retval[0] = 0;
385 	return (error);
386 }
387 
388 /*
389  * MPSAFE
390  */
391 int
392 modfind(struct thread *td, struct modfind_args *uap)
393 {
394 	int error = 0;
395 	char name[MAXMODNAME];
396 	module_t mod;
397 
398 	if ((error = copyinstr(uap->name, name, sizeof name, 0)) != 0)
399 		return (error);
400 
401 	MOD_SLOCK;
402 	mod = module_lookupbyname(name);
403 	if (mod == NULL)
404 		error = ENOENT;
405 	else
406 		td->td_retval[0] = module_getid(mod);
407 	MOD_SUNLOCK;
408 	return (error);
409 }
410