xref: /freebsd/sys/kern/kern_module.c (revision f9218d3d4fd34f082473b3a021c6d4d109fb47cf)
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_final, 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 	td->td_retval[0] = -1;
260 
261 	MOD_SLOCK;
262 	if (uap->modid == 0) {
263 		mod = TAILQ_FIRST(&modules);
264 		if (mod)
265 			td->td_retval[0] = mod->id;
266 		else
267 			error = ENOENT;
268 		goto done2;
269 	}
270 	mod = module_lookupbyid(uap->modid);
271 	if (mod == NULL) {
272 		error = ENOENT;
273 		goto done2;
274 	}
275 	if (TAILQ_NEXT(mod, link))
276 		td->td_retval[0] = TAILQ_NEXT(mod, link)->id;
277 	else
278 		td->td_retval[0] = 0;
279 done2:
280 	MOD_SUNLOCK;
281 	return (error);
282 }
283 
284 /*
285  * MPSAFE
286  */
287 int
288 modfnext(struct thread *td, struct modfnext_args *uap)
289 {
290 	module_t mod;
291 	int error;
292 
293 	td->td_retval[0] = -1;
294 
295 	MOD_SLOCK;
296 	mod = module_lookupbyid(uap->modid);
297 	if (mod == NULL) {
298 		error = ENOENT;
299 	} else {
300 		error = 0;
301 		if (TAILQ_NEXT(mod, flink))
302 			td->td_retval[0] = TAILQ_NEXT(mod, flink)->id;
303 		else
304 			td->td_retval[0] = 0;
305 	}
306 	MOD_SUNLOCK;
307 	return (error);
308 }
309 
310 struct module_stat_v1 {
311 	int	version;		/* set to sizeof(struct module_stat) */
312 	char	name[MAXMODNAME];
313 	int	refs;
314 	int	id;
315 };
316 
317 /*
318  * MPSAFE
319  */
320 int
321 modstat(struct thread *td, struct modstat_args *uap)
322 {
323 	module_t mod;
324 	modspecific_t data;
325 	int error = 0;
326 	int id, namelen, refs, version;
327 	struct module_stat *stat;
328 	char *name;
329 
330 	MOD_SLOCK;
331 	mod = module_lookupbyid(uap->modid);
332 	if (mod == NULL) {
333 		MOD_SUNLOCK;
334 		return (ENOENT);
335 	}
336 	id = mod->id;
337 	refs = mod->refs;
338 	name = mod->name;
339 	data = mod->data;
340 	MOD_SUNLOCK;
341 	stat = uap->stat;
342 
343 	/*
344 	 * Check the version of the user's structure.
345 	 */
346 	if ((error = copyin(&stat->version, &version, sizeof(version))) != 0)
347 		return (error);
348 	if (version != sizeof(struct module_stat_v1)
349 	    && version != sizeof(struct module_stat))
350 		return (EINVAL);
351 	namelen = strlen(mod->name) + 1;
352 	if (namelen > MAXMODNAME)
353 		namelen = MAXMODNAME;
354 	if ((error = copyout(name, &stat->name[0], namelen)) != 0)
355 		return (error);
356 
357 	if ((error = copyout(&refs, &stat->refs, sizeof(int))) != 0)
358 		return (error);
359 	if ((error = copyout(&id, &stat->id, sizeof(int))) != 0)
360 		return (error);
361 
362 	/*
363 	 * >v1 stat includes module data.
364 	 */
365 	if (version == sizeof(struct module_stat))
366 		if ((error = copyout(&data, &stat->data,
367 		    sizeof(data))) != 0)
368 			return (error);
369 	td->td_retval[0] = 0;
370 	return (error);
371 }
372 
373 /*
374  * MPSAFE
375  */
376 int
377 modfind(struct thread *td, struct modfind_args *uap)
378 {
379 	int error = 0;
380 	char name[MAXMODNAME];
381 	module_t mod;
382 
383 	if ((error = copyinstr(uap->name, name, sizeof name, 0)) != 0)
384 		return (error);
385 
386 	MOD_SLOCK;
387 	mod = module_lookupbyname(name);
388 	if (mod == NULL)
389 		error = ENOENT;
390 	else
391 		td->td_retval[0] = module_getid(mod);
392 	MOD_SUNLOCK;
393 	return (error);
394 }
395