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