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