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