]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/kern/kern_module.c
sound: Remove hw.snd.version and SND_DRV_VERSION
[FreeBSD/FreeBSD.git] / sys / kern / kern_module.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 1997 Doug Rabson
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
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/reboot.h>
40 #include <sys/sx.h>
41 #include <sys/module.h>
42 #include <sys/linker.h>
43
44 static MALLOC_DEFINE(M_MODULE, "module", "module data structures");
45
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 TAILQ_HEAD(modulelist, module) 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
69         switch(what) {
70         case MOD_LOAD:
71                 return (0);
72         case MOD_UNLOAD:
73                 return (EBUSY);
74         default:
75                 return (EOPNOTSUPP);
76         }
77 }
78
79 static void
80 module_init(void *arg)
81 {
82
83         sx_init(&modules_sx, "module subsystem sx lock");
84         TAILQ_INIT(&modules);
85         EVENTHANDLER_REGISTER(shutdown_final, module_shutdown, NULL,
86             SHUTDOWN_PRI_DEFAULT);
87 }
88
89 SYSINIT(module, SI_SUB_KLD, SI_ORDER_FIRST, module_init, NULL);
90
91 static void
92 module_shutdown(void *arg1, int arg2)
93 {
94         module_t mod;
95
96         if (arg2 & RB_NOSYNC)
97                 return;
98         mtx_lock(&Giant);
99         MOD_SLOCK;
100         TAILQ_FOREACH_REVERSE(mod, &modules, modulelist, link)
101                 MOD_EVENT(mod, MOD_SHUTDOWN);
102         MOD_SUNLOCK;
103         mtx_unlock(&Giant);
104 }
105
106 void
107 module_register_init(const void *arg)
108 {
109         const moduledata_t *data = (const moduledata_t *)arg;
110         int error;
111         module_t mod;
112
113         mtx_lock(&Giant);
114         MOD_SLOCK;
115         mod = module_lookupbyname(data->name);
116         if (mod == NULL)
117                 panic("module_register_init: module named %s not found\n",
118                     data->name);
119         MOD_SUNLOCK;
120         error = MOD_EVENT(mod, MOD_LOAD);
121         if (error) {
122                 MOD_EVENT(mod, MOD_UNLOAD);
123                 MOD_XLOCK;
124                 module_release(mod);
125                 MOD_XUNLOCK;
126                 printf("module_register_init: MOD_LOAD (%s, %p, %p) error"
127                     " %d\n", data->name, (void *)data->evhand, data->priv,
128                     error); 
129         } else {
130                 MOD_XLOCK;
131                 if (mod->file) {
132                         /*
133                          * Once a module is successfully loaded, move
134                          * it to the head of the module list for this
135                          * linker file.  This resorts the list so that
136                          * when the kernel linker iterates over the
137                          * modules to unload them, it will unload them
138                          * in the reverse order they were loaded.
139                          */
140                         TAILQ_REMOVE(&mod->file->modules, mod, flink);
141                         TAILQ_INSERT_HEAD(&mod->file->modules, mod, flink);
142                 }
143                 MOD_XUNLOCK;
144         }
145         mtx_unlock(&Giant);
146 }
147
148 int
149 module_register(const moduledata_t *data, linker_file_t container)
150 {
151         size_t namelen;
152         module_t newmod;
153
154         MOD_XLOCK;
155         newmod = module_lookupbyname(data->name);
156         if (newmod != NULL) {
157                 MOD_XUNLOCK;
158                 printf("%s: cannot register %s from %s; already loaded from %s\n",
159                     __func__, data->name, container->filename, newmod->file->filename);
160                 return (EEXIST);
161         }
162         namelen = strlen(data->name) + 1;
163         newmod = malloc(sizeof(struct module) + namelen, M_MODULE, M_WAITOK);
164         newmod->refs = 1;
165         newmod->id = nextid++;
166         newmod->name = (char *)(newmod + 1);
167         strcpy(newmod->name, data->name);
168         newmod->handler = data->evhand ? data->evhand : modevent_nop;
169         newmod->arg = data->priv;
170         bzero(&newmod->data, sizeof(newmod->data));
171         TAILQ_INSERT_TAIL(&modules, newmod, link);
172
173         if (container)
174                 TAILQ_INSERT_TAIL(&container->modules, newmod, flink);
175         newmod->file = container;
176         MOD_XUNLOCK;
177         return (0);
178 }
179
180 void
181 module_reference(module_t mod)
182 {
183
184         MOD_XLOCK_ASSERT;
185
186         MOD_DPF(REFS, ("module_reference: before, refs=%d\n", mod->refs));
187         mod->refs++;
188 }
189
190 void
191 module_release(module_t mod)
192 {
193
194         MOD_XLOCK_ASSERT;
195
196         if (mod->refs <= 0)
197                 panic("module_release: bad reference count");
198
199         MOD_DPF(REFS, ("module_release: before, refs=%d\n", mod->refs));
200
201         mod->refs--;
202         if (mod->refs == 0) {
203                 TAILQ_REMOVE(&modules, mod, link);
204                 if (mod->file)
205                         TAILQ_REMOVE(&mod->file->modules, mod, flink);
206                 free(mod, M_MODULE);
207         }
208 }
209
210 module_t
211 module_lookupbyname(const char *name)
212 {
213         module_t mod;
214         int err;
215
216         MOD_LOCK_ASSERT;
217
218         TAILQ_FOREACH(mod, &modules, link) {
219                 err = strcmp(mod->name, name);
220                 if (err == 0)
221                         return (mod);
222         }
223         return (NULL);
224 }
225
226 module_t
227 module_lookupbyid(int modid)
228 {
229         module_t mod;
230
231         MOD_LOCK_ASSERT;
232
233         TAILQ_FOREACH(mod, &modules, link)
234                 if (mod->id == modid)
235                         return(mod);
236         return (NULL);
237 }
238
239 int
240 module_quiesce(module_t mod)
241 {
242         int error;
243
244         mtx_lock(&Giant);
245         error = MOD_EVENT(mod, MOD_QUIESCE);
246         mtx_unlock(&Giant);
247         if (error == EOPNOTSUPP || error == EINVAL)
248                 error = 0;
249         return (error);
250 }
251
252 int
253 module_unload(module_t mod)
254 {
255         int error;
256
257         mtx_lock(&Giant);
258         error = MOD_EVENT(mod, MOD_UNLOAD);
259         mtx_unlock(&Giant);
260         return (error);
261 }
262
263 int
264 module_getid(module_t mod)
265 {
266
267         MOD_LOCK_ASSERT;
268         return (mod->id);
269 }
270
271 module_t
272 module_getfnext(module_t mod)
273 {
274
275         MOD_LOCK_ASSERT;
276         return (TAILQ_NEXT(mod, flink));
277 }
278
279 const char *
280 module_getname(module_t mod)
281 {
282
283         MOD_LOCK_ASSERT;
284         return (mod->name);
285 }
286
287 void
288 module_setspecific(module_t mod, modspecific_t *datap)
289 {
290
291         MOD_XLOCK_ASSERT;
292         mod->data = *datap;
293 }
294
295 linker_file_t
296 module_file(module_t mod)
297 {
298
299         return (mod->file);
300 }
301
302 /*
303  * Syscalls.
304  */
305 int
306 sys_modnext(struct thread *td, struct modnext_args *uap)
307 {
308         module_t mod;
309         int error = 0;
310
311         td->td_retval[0] = -1;
312
313         MOD_SLOCK;
314         if (uap->modid == 0) {
315                 mod = TAILQ_FIRST(&modules);
316                 if (mod)
317                         td->td_retval[0] = mod->id;
318                 else
319                         error = ENOENT;
320                 goto done2;
321         }
322         mod = module_lookupbyid(uap->modid);
323         if (mod == NULL) {
324                 error = ENOENT;
325                 goto done2;
326         }
327         if (TAILQ_NEXT(mod, link))
328                 td->td_retval[0] = TAILQ_NEXT(mod, link)->id;
329         else
330                 td->td_retval[0] = 0;
331 done2:
332         MOD_SUNLOCK;
333         return (error);
334 }
335
336 int
337 sys_modfnext(struct thread *td, struct modfnext_args *uap)
338 {
339         module_t mod;
340         int error;
341
342         td->td_retval[0] = -1;
343
344         MOD_SLOCK;
345         mod = module_lookupbyid(uap->modid);
346         if (mod == NULL) {
347                 error = ENOENT;
348         } else {
349                 error = 0;
350                 if (TAILQ_NEXT(mod, flink))
351                         td->td_retval[0] = TAILQ_NEXT(mod, flink)->id;
352                 else
353                         td->td_retval[0] = 0;
354         }
355         MOD_SUNLOCK;
356         return (error);
357 }
358
359 struct module_stat_v1 {
360         int     version;                /* set to sizeof(struct module_stat) */
361         char    name[MAXMODNAMEV1V2];
362         int     refs;
363         int     id;
364 };
365
366 struct module_stat_v2 {
367         int     version;                /* set to sizeof(struct module_stat) */
368         char    name[MAXMODNAMEV1V2];
369         int     refs;
370         int     id;
371         modspecific_t   data;
372 };
373
374 int
375 sys_modstat(struct thread *td, struct modstat_args *uap)
376 {
377         module_t mod;
378         modspecific_t data;
379         int error = 0;
380         int id, namelen, refs, version;
381         struct module_stat *stat;
382         struct module_stat_v2 *stat_v2;
383         char *name;
384         bool is_v1v2;
385
386         MOD_SLOCK;
387         mod = module_lookupbyid(uap->modid);
388         if (mod == NULL) {
389                 MOD_SUNLOCK;
390                 return (ENOENT);
391         }
392         id = mod->id;
393         refs = mod->refs;
394         name = mod->name;
395         data = mod->data;
396         MOD_SUNLOCK;
397         stat = uap->stat;
398
399         /*
400          * Check the version of the user's structure.
401          */
402         if ((error = copyin(&stat->version, &version, sizeof(version))) != 0)
403                 return (error);
404         is_v1v2 = (version == sizeof(struct module_stat_v1) ||
405             version == sizeof(struct module_stat_v2));
406         if (!is_v1v2 && version != sizeof(struct module_stat))
407                 return (EINVAL);
408         namelen = strlen(mod->name) + 1;
409         if (is_v1v2 && namelen > MAXMODNAMEV1V2)
410                 namelen = MAXMODNAMEV1V2;
411         else if (namelen > MAXMODNAMEV3)
412                 namelen = MAXMODNAMEV3;
413         if ((error = copyout(name, &stat->name[0], namelen)) != 0)
414                 return (error);
415
416         /* Extending MAXMODNAME gives an offset change for v3. */
417         if (is_v1v2) {
418                 stat_v2 = (struct module_stat_v2 *)stat;
419                 if ((error = copyout(&refs, &stat_v2->refs, sizeof(int))) != 0)
420                         return (error);
421                 if ((error = copyout(&id, &stat_v2->id, sizeof(int))) != 0)
422                         return (error);
423         } else {
424                 if ((error = copyout(&refs, &stat->refs, sizeof(int))) != 0)
425                         return (error);
426                 if ((error = copyout(&id, &stat->id, sizeof(int))) != 0)
427                         return (error);
428         }
429
430         /*
431          * >v1 stat includes module data.
432          */
433         if (version == sizeof(struct module_stat_v2)) {
434                 if ((error = copyout(&data, &stat_v2->data,
435                     sizeof(data))) != 0)
436                         return (error);
437         } else if (version == sizeof(struct module_stat)) {
438                 if ((error = copyout(&data, &stat->data, 
439                     sizeof(data))) != 0)
440                         return (error);
441         }
442         td->td_retval[0] = 0;
443         return (error);
444 }
445
446 int
447 sys_modfind(struct thread *td, struct modfind_args *uap)
448 {
449         int error = 0;
450         char name[MAXMODNAMEV3];
451         module_t mod;
452
453         if ((error = copyinstr(uap->name, name, sizeof name, 0)) != 0)
454                 return (error);
455
456         MOD_SLOCK;
457         mod = module_lookupbyname(name);
458         if (mod == NULL)
459                 error = ENOENT;
460         else
461                 td->td_retval[0] = module_getid(mod);
462         MOD_SUNLOCK;
463         return (error);
464 }
465
466 MODULE_VERSION(kernel, __FreeBSD_version);
467
468 #ifdef COMPAT_FREEBSD32
469 #include <sys/mount.h>
470 #include <sys/socket.h>
471 #include <compat/freebsd32/freebsd32_util.h>
472 #include <compat/freebsd32/freebsd32.h>
473 #include <compat/freebsd32/freebsd32_proto.h>
474
475 typedef union modspecific32 {
476         int             intval;
477         uint32_t        uintval;
478         int             longval;
479         uint32_t        ulongval;
480 } modspecific32_t;
481
482 struct module_stat32_v2 {
483         int             version;
484         char            name[MAXMODNAMEV1V2];
485         int             refs;
486         int             id;
487         modspecific32_t data;
488 };
489
490 struct module_stat32 {
491         int             version;
492         char            name[MAXMODNAME];
493         int             refs;
494         int             id;
495         modspecific32_t data;
496 };
497
498 int
499 freebsd32_modstat(struct thread *td, struct freebsd32_modstat_args *uap)
500 {
501         module_t mod;
502         modspecific32_t data32;
503         int error = 0;
504         int id, namelen, refs, version;
505         struct module_stat32 *stat32;
506         struct module_stat32_v2 *stat32_v2;
507         char *name;
508         bool is_v1v2;
509
510         MOD_SLOCK;
511         mod = module_lookupbyid(uap->modid);
512         if (mod == NULL) {
513                 MOD_SUNLOCK;
514                 return (ENOENT);
515         }
516
517         id = mod->id;
518         refs = mod->refs;
519         name = mod->name;
520         _Static_assert(sizeof(data32) == sizeof(data32.uintval),
521             "bad modspecific32_t size");
522         CP(mod->data, data32, uintval);
523         MOD_SUNLOCK;
524         stat32 = uap->stat;
525
526         if ((error = copyin(&stat32->version, &version, sizeof(version))) != 0)
527                 return (error);
528         is_v1v2 = (version == sizeof(struct module_stat_v1) ||
529             version == sizeof(struct module_stat32_v2));
530         if (!is_v1v2 && version != sizeof(struct module_stat32))
531                 return (EINVAL);
532         namelen = strlen(mod->name) + 1;
533         if (is_v1v2 && namelen > MAXMODNAMEV1V2)
534                 namelen = MAXMODNAMEV1V2;
535         else if (namelen > MAXMODNAMEV3)
536                 namelen = MAXMODNAMEV3;
537         if ((error = copyout(name, &stat32->name[0], namelen)) != 0)
538                 return (error);
539
540         /* Extending MAXMODNAME gives an offset change for v3. */
541         if (is_v1v2) {
542                 stat32_v2 = (struct module_stat32_v2 *)stat32;
543                 if ((error = copyout(&refs, &stat32_v2->refs, sizeof(int))) != 0)
544                         return (error);
545                 if ((error = copyout(&id, &stat32_v2->id, sizeof(int))) != 0)
546                         return (error);
547         } else {
548                 if ((error = copyout(&refs, &stat32->refs, sizeof(int))) != 0)
549                         return (error);
550                 if ((error = copyout(&id, &stat32->id, sizeof(int))) != 0)
551                         return (error);
552         }
553
554         /*
555          * >v1 stat includes module data.
556          */
557         if (version == sizeof(struct module_stat32_v2)) {
558                 if ((error = copyout(&data32, &stat32_v2->data,
559                     sizeof(data32))) != 0)
560                         return (error);
561         } else if (version == sizeof(struct module_stat32)) {
562                 if ((error = copyout(&data32, &stat32->data,
563                     sizeof(data32))) != 0)
564                         return (error);
565         }
566         td->td_retval[0] = 0;
567         return (error);
568 }
569 #endif