kern_module.c revision 25537
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 *	$Id$
27 */
28
29#include <sys/param.h>
30#include <sys/kernel.h>
31#include <sys/systm.h>
32#include <sys/sysctl.h>
33#include <sys/queue.h>
34#include <sys/libkern.h>
35#include <sys/malloc.h>
36#include <sys/sysproto.h>
37#include <sys/sysent.h>
38#include <sys/module.h>
39#include <sys/linker.h>
40
41#define M_MODULE	M_TEMP		/* XXX */
42
43typedef TAILQ_HEAD(, module) modulelist_t;
44struct module {
45    TAILQ_ENTRY(module)	link;		/* chain together all modules */
46    TAILQ_ENTRY(module)	flink;		/* all modules in a file */
47    struct linker_file*	file;		/* file which contains this module */
48    int			refs;		/* reference count */
49    int			id;		/* unique id number */
50    char		*name;		/* module name */
51    modeventhand_t	handler;	/* event handler */
52    void		*arg;		/* argument for handler */
53};
54
55#define MOD_EVENT(mod, type) (mod)->handler((mod), (type), (mod)->arg)
56
57static modulelist_t modules;
58static int nextid = 1;
59
60static void module_shutdown(int, void*);
61
62static void
63module_init(void* arg)
64{
65    TAILQ_INIT(&modules);
66    at_shutdown(module_shutdown, 0, SHUTDOWN_POST_SYNC);
67}
68
69SYSINIT(module, SI_SUB_KMEM, SI_ORDER_ANY, module_init, 0);
70
71static void
72module_shutdown(int arg1, void* arg2)
73{
74    module_t mod;
75    int error;
76
77    for (mod = TAILQ_FIRST(&modules); mod; mod = TAILQ_NEXT(mod, link))
78	MOD_EVENT(mod, MOD_SHUTDOWN);
79}
80
81void
82module_register_static(void *arg)
83{
84    moduledata_t* data = (moduledata_t*) arg;
85    int error;
86
87    if (error = module_register(data->name, data->evhand, data->priv))
88	printf("module_register_static: module_register(%s, %x, %x) returned %d",
89	       data->name, data->evhand, data->priv, error);
90}
91
92int
93module_register(const char* name, modeventhand_t handler, void* arg)
94{
95    size_t namelen;
96    module_t newmod;
97    int error;
98
99    namelen = strlen(name) + 1;
100    newmod = (module_t) malloc(sizeof(struct module) + namelen,
101			       M_MODULE, M_WAITOK);
102    if (newmod == 0)
103	return ENOMEM;
104
105    newmod->refs = 1;
106    newmod->id = nextid++;
107    newmod->name = (char *) (newmod + 1);
108    strcpy(newmod->name, name);
109    newmod->handler = handler;
110    newmod->arg = arg;
111    TAILQ_INSERT_TAIL(&modules, newmod, link);
112
113    if (linker_current_file) {
114	TAILQ_INSERT_TAIL(&linker_current_file->modules, newmod, flink);
115	newmod->file = linker_current_file;
116    } else
117	newmod->file = 0;
118
119    if (error = MOD_EVENT(newmod, MOD_LOAD)) {
120	module_release(newmod);
121	return error;
122    }
123
124    return 0;
125}
126
127void
128module_reference(module_t mod)
129{
130    MOD_DPF(REFS, ("module_reference: before, refs=%d\n", mod->refs));
131
132    mod->refs++;
133}
134
135void
136module_release(module_t mod)
137{
138    if (mod->refs <= 0)
139	panic("module_release: bad reference count");
140
141    MOD_DPF(REFS, ("module_release: before, refs=%d\n", mod->refs));
142
143    mod->refs--;
144    if (mod->refs == 0) {
145	TAILQ_REMOVE(&modules, mod, link);
146	if (mod->file) {
147	    TAILQ_REMOVE(&mod->file->modules, mod, flink);
148	}
149	free(mod, M_MODULE);
150    }
151}
152
153module_t
154module_lookupbyname(const char* name)
155{
156    module_t mod;
157
158    for (mod = TAILQ_FIRST(&modules); mod; mod = TAILQ_NEXT(mod, link)) {
159	if (!strcmp(mod->name, name))
160	    return mod;
161    }
162
163    return 0;
164}
165
166module_t
167module_lookupbyid(int modid)
168{
169    module_t mod;
170
171    for (mod = TAILQ_FIRST(&modules); mod; mod = TAILQ_NEXT(mod, link)) {
172	if (mod->id == modid)
173	    return mod;
174    }
175
176    return 0;
177}
178
179int
180module_unload(module_t mod)
181{
182    return MOD_EVENT(mod, MOD_UNLOAD);
183}
184
185int
186module_getid(module_t mod)
187{
188    return mod->id;
189}
190
191module_t
192module_getfnext(module_t mod)
193{
194    return TAILQ_NEXT(mod, flink);
195}
196
197/*
198 * Syscalls.
199 */
200int
201modnext(struct proc* p, struct modnext_args* uap, int* retval)
202{
203    module_t mod;
204
205    *retval = -1;
206    if (SCARG(uap, modid) == 0) {
207	mod = TAILQ_FIRST(&modules);
208	if (mod) {
209	    *retval = mod->id;
210	    return 0;
211	} else
212	    return ENOENT;
213    }
214
215    mod = module_lookupbyid(SCARG(uap, modid));
216    if (!mod)
217	return ENOENT;
218
219    if (TAILQ_NEXT(mod, link))
220	*retval = TAILQ_NEXT(mod, link)->id;
221    else
222	*retval = 0;
223    return 0;
224}
225
226int
227modfnext(struct proc* p, struct modfnext_args* uap, int* retval)
228{
229    module_t mod;
230
231    *retval = -1;
232
233    mod = module_lookupbyid(SCARG(uap, modid));
234    if (!mod)
235	return ENOENT;
236
237    if (TAILQ_NEXT(mod, flink))
238	*retval = TAILQ_NEXT(mod, flink)->id;
239    else
240	*retval = 0;
241    return 0;
242}
243
244int
245modstat(struct proc* p, struct modstat_args* uap, int* retval)
246{
247    module_t mod;
248    int error = 0;
249    int namelen;
250    int version;
251    struct module_stat* stat;
252
253    mod = module_lookupbyid(SCARG(uap, modid));
254    if (!mod)
255	return ENOENT;
256
257    stat = SCARG(uap, stat);
258
259    /*
260     * Check the version of the user's structure.
261     */
262    if (error = copyin(&stat->version, &version, sizeof(version)))
263	goto out;
264    if (version != sizeof(struct module_stat)) {
265	error = EINVAL;
266	goto out;
267    }
268
269    namelen = strlen(mod->name) + 1;
270    if (namelen > MAXMODNAME)
271	namelen = MAXMODNAME;
272    if (error = copyout(mod->name, &stat->name[0], namelen))
273	goto out;
274
275    if (error = copyout(&mod->refs, &stat->refs, sizeof(int)))
276	goto out;
277    if (error = copyout(&mod->id, &stat->id, sizeof(int)))
278	goto out;
279
280    *retval = 0;
281
282out:
283    return error;
284}
285
286int
287modfind(struct proc* p, struct modfind_args* uap, int* retval)
288{
289    int error = 0;
290    char name[MAXMODNAME];
291    module_t mod;
292
293    if (error = copyinstr(SCARG(uap, name), name, sizeof name, 0))
294	goto out;
295
296    mod = module_lookupbyname(name);
297    if (!mod)
298	error = ENOENT;
299    else
300	*retval = mod->id;
301
302out:
303    return error;
304}
305