1*d40aaeaeSmsaitoh /* $NetBSD: kern_module.c,v 1.162 2024/05/13 00:32:09 msaitoh Exp $ */
2bbc79e58Sad
3bbc79e58Sad /*-
4bbc79e58Sad * Copyright (c) 2008 The NetBSD Foundation, Inc.
5bbc79e58Sad * All rights reserved.
6bbc79e58Sad *
70efea177Sad * This code is derived from software developed for The NetBSD Foundation
80efea177Sad * by Andrew Doran.
90efea177Sad *
10bbc79e58Sad * Redistribution and use in source and binary forms, with or without
11bbc79e58Sad * modification, are permitted provided that the following conditions
12bbc79e58Sad * are met:
13bbc79e58Sad * 1. Redistributions of source code must retain the above copyright
14bbc79e58Sad * notice, this list of conditions and the following disclaimer.
15bbc79e58Sad * 2. Redistributions in binary form must reproduce the above copyright
16bbc79e58Sad * notice, this list of conditions and the following disclaimer in the
17bbc79e58Sad * documentation and/or other materials provided with the distribution.
18bbc79e58Sad *
19bbc79e58Sad * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20bbc79e58Sad * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21bbc79e58Sad * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22bbc79e58Sad * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23bbc79e58Sad * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24bbc79e58Sad * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25bbc79e58Sad * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26bbc79e58Sad * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27bbc79e58Sad * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28bbc79e58Sad * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29bbc79e58Sad * POSSIBILITY OF SUCH DAMAGE.
30bbc79e58Sad */
31bbc79e58Sad
32bbc79e58Sad /*
33a2134a7aSad * Kernel module support.
34bbc79e58Sad */
35bbc79e58Sad
36bbc79e58Sad #include <sys/cdefs.h>
37*d40aaeaeSmsaitoh __KERNEL_RCSID(0, "$NetBSD: kern_module.c,v 1.162 2024/05/13 00:32:09 msaitoh Exp $");
38a8ed404dSpooka
39a8ed404dSpooka #define _MODULE_INTERNAL
4031afc5b6Sad
4131afc5b6Sad #ifdef _KERNEL_OPT
4231afc5b6Sad #include "opt_ddb.h"
430cc72e51Sapb #include "opt_modular.h"
4431afc5b6Sad #endif
45bbc79e58Sad
46bbc79e58Sad #include <sys/param.h>
47bbc79e58Sad #include <sys/systm.h>
4879d9beffSad #include <sys/kernel.h>
49bbc79e58Sad #include <sys/proc.h>
5095b31e28Spgoyette #include <sys/lwp.h>
51bbc79e58Sad #include <sys/kauth.h>
52bbc79e58Sad #include <sys/kobj.h>
53bbc79e58Sad #include <sys/kmem.h>
54bbc79e58Sad #include <sys/module.h>
555d4e5c04Spgoyette #include <sys/module_hook.h>
5679d9beffSad #include <sys/kthread.h>
5711701b3bSad #include <sys/sysctl.h>
581bdbe18dSjnemeth #include <sys/lock.h>
5969626800Spgoyette #include <sys/evcnt.h>
60bbc79e58Sad
61bbc79e58Sad #include <uvm/uvm_extern.h>
62bbc79e58Sad
630efea177Sad struct vm_map *module_map;
649d9e6580Smatt const char *module_machine;
65a8ed404dSpooka char module_base[MODULE_BASE_SIZE];
66a2134a7aSad
67bbc79e58Sad struct modlist module_list = TAILQ_HEAD_INITIALIZER(module_list);
68ee7bfacdSpooka struct modlist module_builtins = TAILQ_HEAD_INITIALIZER(module_builtins);
69ee7bfacdSpooka static struct modlist module_bootlist = TAILQ_HEAD_INITIALIZER(module_bootlist);
70ee7bfacdSpooka
71d5abe492Schs struct module_callbacks {
72d5abe492Schs TAILQ_ENTRY(module_callbacks) modcb_list;
73d5abe492Schs void (*modcb_load)(struct module *);
74d5abe492Schs void (*modcb_unload)(struct module *);
75d5abe492Schs };
76d5abe492Schs TAILQ_HEAD(modcblist, module_callbacks);
77d5abe492Schs static struct modcblist modcblist;
78d5abe492Schs
79d5abe492Schs static module_t *module_netbsd;
80d5abe492Schs static const modinfo_t module_netbsd_modinfo = {
81d5abe492Schs .mi_version = __NetBSD_Version__,
82d5abe492Schs .mi_class = MODULE_CLASS_MISC,
83d5abe492Schs .mi_name = "netbsd"
84d5abe492Schs };
85d5abe492Schs
861bb1fee7Sad static module_t *module_active;
878ea840ceSrin #ifdef MODULAR_DEFAULT_VERBOSE
888ea840ceSrin bool module_verbose_on = true;
898ea840ceSrin #else
908ea840ceSrin bool module_verbose_on = false;
918ea840ceSrin #endif
9261876aebSnonaka #ifdef MODULAR_DEFAULT_AUTOLOAD
939969e676Spgoyette bool module_autoload_on = true;
94ee79d5b0Sjnemeth #else
959969e676Spgoyette bool module_autoload_on = false;
96ee79d5b0Sjnemeth #endif
97c4bc9856Sriastradh bool module_autounload_unsafe = 0;
98bbc79e58Sad u_int module_count;
99ee7bfacdSpooka u_int module_builtinlist;
10024b57760Sriastradh u_int module_autotime = 10;
10179d9beffSad u_int module_gen = 1;
10279d9beffSad static kcondvar_t module_thread_cv;
10379d9beffSad static kmutex_t module_thread_lock;
10479d9beffSad static int module_thread_ticks;
105c77c9696Spgoyette int (*module_load_vfs_vec)(const char *, int, bool, module_t *,
106c77c9696Spgoyette prop_dictionary_t *) = (void *)eopnotsupp;
107bbc79e58Sad
108abc7a429Selad static kauth_listener_t module_listener;
109abc7a429Selad
110d5abe492Schs static specificdata_domain_t module_specificdata_domain;
111d5abe492Schs
112bbc79e58Sad /* Ensure that the kernel's link set isn't empty. */
113bbc79e58Sad static modinfo_t module_dummy;
114bbc79e58Sad __link_set_add_rodata(modules, module_dummy);
115bbc79e58Sad
116224f73d8Spgoyette static module_t *module_newmodule(modsrc_t);
117d5abe492Schs static void module_free(module_t *);
118224f73d8Spgoyette static void module_require_force(module_t *);
1193fd8e29aSjmmv static int module_do_load(const char *, bool, int, prop_dictionary_t,
120a35d1a8cSmatt module_t **, modclass_t modclass, bool);
121224f73d8Spgoyette static int module_do_unload(const char *, bool);
12275495633Schristos static int module_do_builtin(const module_t *, const char *, module_t **,
12375495633Schristos prop_dictionary_t);
1248ef40c77Sad static int module_fetch_info(module_t *);
12579d9beffSad static void module_thread(void *);
126a8ed404dSpooka
127effc302aSpooka static module_t *module_lookup(const char *);
128effc302aSpooka static void module_enqueue(module_t *);
129effc302aSpooka
13032b67097Sjnemeth static bool module_merge_dicts(prop_dictionary_t, const prop_dictionary_t);
131bbc79e58Sad
132f0c67b07Spooka static void sysctl_module_setup(void);
1332b1203b7Spgoyette static int sysctl_module_autotime(SYSCTLFN_PROTO);
1342b1203b7Spgoyette
135d5abe492Schs static void module_callback_load(struct module *);
136d5abe492Schs static void module_callback_unload(struct module *);
137d5abe492Schs
138a35d1a8cSmatt #define MODULE_CLASS_MATCH(mi, modclass) \
139a35d1a8cSmatt ((modclass) == MODULE_CLASS_ANY || (modclass) == (mi)->mi_class)
1402a0d04a7Schristos
1412a0d04a7Schristos static void
module_incompat(const modinfo_t * mi,int modclass)142a35d1a8cSmatt module_incompat(const modinfo_t *mi, int modclass)
1432a0d04a7Schristos {
14425e8a2c7Spgoyette module_error("incompatible module class %d for `%s' (wanted %d)",
14525e8a2c7Spgoyette mi->mi_class, mi->mi_name, modclass);
1462a0d04a7Schristos }
147f0c67b07Spooka
148d5abe492Schs struct module *
module_kernel(void)149d5abe492Schs module_kernel(void)
150d5abe492Schs {
151d5abe492Schs
152d5abe492Schs return module_netbsd;
153d5abe492Schs }
154d5abe492Schs
155bbc79e58Sad /*
156bbc79e58Sad * module_error:
157bbc79e58Sad *
158bbc79e58Sad * Utility function: log an error.
159bbc79e58Sad */
160a8ed404dSpooka void
module_error(const char * fmt,...)161bbc79e58Sad module_error(const char *fmt, ...)
162bbc79e58Sad {
163bbc79e58Sad va_list ap;
164bbc79e58Sad
165bbc79e58Sad va_start(ap, fmt);
166bbc79e58Sad printf("WARNING: module error: ");
167bbc79e58Sad vprintf(fmt, ap);
168bbc79e58Sad printf("\n");
169bbc79e58Sad va_end(ap);
170bbc79e58Sad }
171bbc79e58Sad
172bbc79e58Sad /*
17311701b3bSad * module_print:
17411701b3bSad *
17511701b3bSad * Utility function: log verbose output.
17611701b3bSad */
177a8ed404dSpooka void
module_print(const char * fmt,...)17811701b3bSad module_print(const char *fmt, ...)
17911701b3bSad {
18011701b3bSad va_list ap;
18111701b3bSad
18211701b3bSad if (module_verbose_on) {
18311701b3bSad va_start(ap, fmt);
18411701b3bSad printf("DEBUG: module: ");
18511701b3bSad vprintf(fmt, ap);
18611701b3bSad printf("\n");
18711701b3bSad va_end(ap);
18811701b3bSad }
18911701b3bSad }
19011701b3bSad
191d5abe492Schs /*
192d5abe492Schs * module_name:
193d5abe492Schs *
194d5abe492Schs * Utility function: return the module's name.
195d5abe492Schs */
196d5abe492Schs const char *
module_name(struct module * mod)197d5abe492Schs module_name(struct module *mod)
198d5abe492Schs {
199d5abe492Schs
200d5abe492Schs return mod->mod_info->mi_name;
201d5abe492Schs }
202d5abe492Schs
203d5abe492Schs /*
204d5abe492Schs * module_source:
205d5abe492Schs *
206d5abe492Schs * Utility function: return the module's source.
207d5abe492Schs */
208d5abe492Schs modsrc_t
module_source(struct module * mod)209d5abe492Schs module_source(struct module *mod)
210d5abe492Schs {
211d5abe492Schs
212d5abe492Schs return mod->mod_source;
213d5abe492Schs }
214d5abe492Schs
2154046eb05Selad static int
module_listener_cb(kauth_cred_t cred,kauth_action_t action,void * cookie,void * arg0,void * arg1,void * arg2,void * arg3)2164046eb05Selad module_listener_cb(kauth_cred_t cred, kauth_action_t action, void *cookie,
2174046eb05Selad void *arg0, void *arg1, void *arg2, void *arg3)
2184046eb05Selad {
2194046eb05Selad int result;
2204046eb05Selad
2214046eb05Selad result = KAUTH_RESULT_DEFER;
2224046eb05Selad
2234046eb05Selad if (action != KAUTH_SYSTEM_MODULE)
2244046eb05Selad return result;
2254046eb05Selad
2264046eb05Selad if ((uintptr_t)arg2 != 0) /* autoload */
2274046eb05Selad result = KAUTH_RESULT_ALLOW;
2284046eb05Selad
2294046eb05Selad return result;
2304046eb05Selad }
2314046eb05Selad
23211701b3bSad /*
233224f73d8Spgoyette * Allocate a new module_t
234224f73d8Spgoyette */
235224f73d8Spgoyette static module_t *
module_newmodule(modsrc_t source)236224f73d8Spgoyette module_newmodule(modsrc_t source)
237224f73d8Spgoyette {
238224f73d8Spgoyette module_t *mod;
239224f73d8Spgoyette
240224f73d8Spgoyette mod = kmem_zalloc(sizeof(*mod), KM_SLEEP);
241224f73d8Spgoyette mod->mod_source = source;
242d5abe492Schs specificdata_init(module_specificdata_domain, &mod->mod_sdref);
243224f73d8Spgoyette return mod;
244224f73d8Spgoyette }
245224f73d8Spgoyette
246224f73d8Spgoyette /*
247d5abe492Schs * Free a module_t
248d5abe492Schs */
249d5abe492Schs static void
module_free(module_t * mod)250d5abe492Schs module_free(module_t *mod)
251d5abe492Schs {
252d5abe492Schs
253d5abe492Schs specificdata_fini(module_specificdata_domain, &mod->mod_sdref);
254d91f98a8Spgoyette if (mod->mod_required)
255d91f98a8Spgoyette kmem_free(mod->mod_required, mod->mod_arequired *
256d91f98a8Spgoyette sizeof(module_t *));
257d5abe492Schs kmem_free(mod, sizeof(*mod));
258d5abe492Schs }
259d5abe492Schs
260d5abe492Schs /*
261224f73d8Spgoyette * Require the -f (force) flag to load a module
262224f73d8Spgoyette */
263224f73d8Spgoyette static void
module_require_force(struct module * mod)264224f73d8Spgoyette module_require_force(struct module *mod)
265224f73d8Spgoyette {
266d91f98a8Spgoyette SET(mod->mod_flags, MODFLG_MUST_FORCE);
267224f73d8Spgoyette }
268224f73d8Spgoyette
269224f73d8Spgoyette /*
270ee7bfacdSpooka * Add modules to the builtin list. This can done at boottime or
271ee7bfacdSpooka * at runtime if the module is linked into the kernel with an
272ee7bfacdSpooka * external linker. All or none of the input will be handled.
273ee7bfacdSpooka * Optionally, the modules can be initialized. If they are not
274ee7bfacdSpooka * initialized, module_init_class() or module_load() can be used
275ee7bfacdSpooka * later, but these are not guaranteed to give atomic results.
276ee7bfacdSpooka */
277ee7bfacdSpooka int
module_builtin_add(modinfo_t * const * mip,size_t nmodinfo,bool init)278ee7bfacdSpooka module_builtin_add(modinfo_t *const *mip, size_t nmodinfo, bool init)
279ee7bfacdSpooka {
280ee7bfacdSpooka struct module **modp = NULL, *mod_iter;
281ee7bfacdSpooka int rv = 0, i, mipskip;
282ee7bfacdSpooka
283ee7bfacdSpooka if (init) {
284ee7bfacdSpooka rv = kauth_authorize_system(kauth_cred_get(),
285ee7bfacdSpooka KAUTH_SYSTEM_MODULE, 0, (void *)(uintptr_t)MODCTL_LOAD,
286ee7bfacdSpooka (void *)(uintptr_t)1, NULL);
287ee7bfacdSpooka if (rv) {
288ee7bfacdSpooka return rv;
289ee7bfacdSpooka }
290ee7bfacdSpooka }
291ee7bfacdSpooka
292ee7bfacdSpooka for (i = 0, mipskip = 0; i < nmodinfo; i++) {
293ee7bfacdSpooka if (mip[i] == &module_dummy) {
294ee7bfacdSpooka KASSERT(nmodinfo > 0);
295ee7bfacdSpooka nmodinfo--;
296ee7bfacdSpooka }
297ee7bfacdSpooka }
298ee7bfacdSpooka if (nmodinfo == 0)
299ee7bfacdSpooka return 0;
300ee7bfacdSpooka
301ee7bfacdSpooka modp = kmem_zalloc(sizeof(*modp) * nmodinfo, KM_SLEEP);
302ee7bfacdSpooka for (i = 0, mipskip = 0; i < nmodinfo; i++) {
303ee7bfacdSpooka if (mip[i+mipskip] == &module_dummy) {
304ee7bfacdSpooka mipskip++;
305ee7bfacdSpooka continue;
306ee7bfacdSpooka }
307224f73d8Spgoyette modp[i] = module_newmodule(MODULE_SOURCE_KERNEL);
308ee7bfacdSpooka modp[i]->mod_info = mip[i+mipskip];
309ee7bfacdSpooka }
3104a743ad4Spgoyette kernconfig_lock();
311ee7bfacdSpooka
312ee7bfacdSpooka /* do this in three stages for error recovery and atomicity */
313ee7bfacdSpooka
314ee7bfacdSpooka /* first check for presence */
315ee7bfacdSpooka for (i = 0; i < nmodinfo; i++) {
316ee7bfacdSpooka TAILQ_FOREACH(mod_iter, &module_builtins, mod_chain) {
317ee7bfacdSpooka if (strcmp(mod_iter->mod_info->mi_name,
318ee7bfacdSpooka modp[i]->mod_info->mi_name) == 0)
319ee7bfacdSpooka break;
320ee7bfacdSpooka }
321ee7bfacdSpooka if (mod_iter) {
322ee7bfacdSpooka rv = EEXIST;
323ee7bfacdSpooka goto out;
324ee7bfacdSpooka }
325ee7bfacdSpooka
326ee7bfacdSpooka if (module_lookup(modp[i]->mod_info->mi_name) != NULL) {
327ee7bfacdSpooka rv = EEXIST;
328ee7bfacdSpooka goto out;
329ee7bfacdSpooka }
330ee7bfacdSpooka }
331ee7bfacdSpooka
332ee7bfacdSpooka /* then add to list */
333ee7bfacdSpooka for (i = 0; i < nmodinfo; i++) {
334ee7bfacdSpooka TAILQ_INSERT_TAIL(&module_builtins, modp[i], mod_chain);
335ee7bfacdSpooka module_builtinlist++;
336ee7bfacdSpooka }
337ee7bfacdSpooka
338ee7bfacdSpooka /* finally, init (if required) */
339ee7bfacdSpooka if (init) {
340ee7bfacdSpooka for (i = 0; i < nmodinfo; i++) {
34175495633Schristos rv = module_do_builtin(modp[i],
34275495633Schristos modp[i]->mod_info->mi_name, NULL, NULL);
343ee7bfacdSpooka /* throw in the towel, recovery hard & not worth it */
344ee7bfacdSpooka if (rv)
3457ebc13ffSchristos panic("%s: builtin module \"%s\" init failed:"
3467ebc13ffSchristos " %d", __func__,
347ee7bfacdSpooka modp[i]->mod_info->mi_name, rv);
348ee7bfacdSpooka }
349ee7bfacdSpooka }
350ee7bfacdSpooka
351ee7bfacdSpooka out:
3524a743ad4Spgoyette kernconfig_unlock();
353ee7bfacdSpooka if (rv != 0) {
354ee7bfacdSpooka for (i = 0; i < nmodinfo; i++) {
355ee7bfacdSpooka if (modp[i])
356d5abe492Schs module_free(modp[i]);
357ee7bfacdSpooka }
358ee7bfacdSpooka }
359ee7bfacdSpooka kmem_free(modp, sizeof(*modp) * nmodinfo);
360ee7bfacdSpooka return rv;
361ee7bfacdSpooka }
362ee7bfacdSpooka
363ee7bfacdSpooka /*
364ee7bfacdSpooka * Optionally fini and remove builtin module from the kernel.
365ee7bfacdSpooka * Note: the module will now be unreachable except via mi && builtin_add.
366ee7bfacdSpooka */
367ee7bfacdSpooka int
module_builtin_remove(modinfo_t * mi,bool fini)368ee7bfacdSpooka module_builtin_remove(modinfo_t *mi, bool fini)
369ee7bfacdSpooka {
370ee7bfacdSpooka struct module *mod;
371ee7bfacdSpooka int rv = 0;
372ee7bfacdSpooka
373ee7bfacdSpooka if (fini) {
374ee7bfacdSpooka rv = kauth_authorize_system(kauth_cred_get(),
375ee7bfacdSpooka KAUTH_SYSTEM_MODULE, 0, (void *)(uintptr_t)MODCTL_UNLOAD,
376ee7bfacdSpooka NULL, NULL);
377ee7bfacdSpooka if (rv)
378ee7bfacdSpooka return rv;
379ee7bfacdSpooka
3804a743ad4Spgoyette kernconfig_lock();
381224f73d8Spgoyette rv = module_do_unload(mi->mi_name, true);
382ee7bfacdSpooka if (rv) {
383ee7bfacdSpooka goto out;
384ee7bfacdSpooka }
385ee7bfacdSpooka } else {
3864a743ad4Spgoyette kernconfig_lock();
387ee7bfacdSpooka }
388ee7bfacdSpooka TAILQ_FOREACH(mod, &module_builtins, mod_chain) {
389ee7bfacdSpooka if (strcmp(mod->mod_info->mi_name, mi->mi_name) == 0)
390ee7bfacdSpooka break;
391ee7bfacdSpooka }
392ee7bfacdSpooka if (mod) {
393ee7bfacdSpooka TAILQ_REMOVE(&module_builtins, mod, mod_chain);
394ee7bfacdSpooka module_builtinlist--;
395ee7bfacdSpooka } else {
396ee7bfacdSpooka KASSERT(fini == false);
397ee7bfacdSpooka rv = ENOENT;
398ee7bfacdSpooka }
399ee7bfacdSpooka
400ee7bfacdSpooka out:
4014a743ad4Spgoyette kernconfig_unlock();
402ee7bfacdSpooka return rv;
403ee7bfacdSpooka }
404ee7bfacdSpooka
405ee7bfacdSpooka /*
406bbc79e58Sad * module_init:
407bbc79e58Sad *
408bbc79e58Sad * Initialize the module subsystem.
409bbc79e58Sad */
410bbc79e58Sad void
module_init(void)411bbc79e58Sad module_init(void)
412bbc79e58Sad {
413ee7bfacdSpooka __link_set_decl(modules, modinfo_t);
414ee7bfacdSpooka modinfo_t *const *mip;
415ee7bfacdSpooka int rv;
416bbc79e58Sad
4170efea177Sad if (module_map == NULL) {
4180efea177Sad module_map = kernel_map;
4190efea177Sad }
420bf43b300Spgoyette cv_init(&module_thread_cv, "mod_unld");
42179d9beffSad mutex_init(&module_thread_lock, MUTEX_DEFAULT, IPL_NONE);
422d5abe492Schs TAILQ_INIT(&modcblist);
4232c4465aaSpgoyette
4245d413581Sad #ifdef MODULAR /* XXX */
4258ef40c77Sad module_init_md();
4261bb1fee7Sad #endif
4277a3561a8Sad
428d8e96a2dSchristos #ifdef KERNEL_DIR
429d8e96a2dSchristos const char *booted_kernel = get_booted_kernel();
430d8e96a2dSchristos if (booted_kernel) {
431d8e96a2dSchristos char *ptr = strrchr(booted_kernel, '/');
432d8e96a2dSchristos snprintf(module_base, sizeof(module_base), "/%.*s/modules",
433d8e96a2dSchristos (int)(ptr - booted_kernel), booted_kernel);
434d8e96a2dSchristos } else {
435d8e96a2dSchristos strlcpy(module_base, "/netbsd/modules", sizeof(module_base));
436d8e96a2dSchristos printf("Cannot find kernel name, loading modules from \"%s\"\n",
437d8e96a2dSchristos module_base);
438d8e96a2dSchristos }
439d8e96a2dSchristos #else
4402101d540Smrg if (!module_machine)
4412101d540Smrg module_machine = machine;
4427a3561a8Sad #if __NetBSD_Version__ / 1000000 % 100 == 99 /* -current */
4436d73ddd3Srmind snprintf(module_base, sizeof(module_base), "/stand/%s/%s/modules",
4442101d540Smrg module_machine, osrelease);
4457a3561a8Sad #else /* release */
4466d73ddd3Srmind snprintf(module_base, sizeof(module_base), "/stand/%s/%d.%d/modules",
4472101d540Smrg module_machine, __NetBSD_Version__ / 100000000,
4487a3561a8Sad __NetBSD_Version__ / 1000000 % 100);
4497a3561a8Sad #endif
450d8e96a2dSchristos #endif
45153ca19a3Selad
4524046eb05Selad module_listener = kauth_listen_scope(KAUTH_SCOPE_SYSTEM,
4534046eb05Selad module_listener_cb, NULL);
454ee7bfacdSpooka
455ee7bfacdSpooka __link_set_foreach(mip, modules) {
4561571556bSmbalmer if ((rv = module_builtin_add(mip, 1, false)) != 0)
457ee7bfacdSpooka module_error("builtin %s failed: %d\n",
458ee7bfacdSpooka (*mip)->mi_name, rv);
459ee7bfacdSpooka }
460f0c67b07Spooka
461f0c67b07Spooka sysctl_module_setup();
462d5abe492Schs module_specificdata_domain = specificdata_domain_create();
463d5abe492Schs
464d5abe492Schs module_netbsd = module_newmodule(MODULE_SOURCE_KERNEL);
465d5abe492Schs module_netbsd->mod_refcnt = 1;
466d5abe492Schs module_netbsd->mod_info = &module_netbsd_modinfo;
467abc7a429Selad }
468abc7a429Selad
46953ca19a3Selad /*
470224f73d8Spgoyette * module_start_unload_thread:
47153ca19a3Selad *
47253ca19a3Selad * Start the auto unload kthread.
47353ca19a3Selad */
47453ca19a3Selad void
module_start_unload_thread(void)475224f73d8Spgoyette module_start_unload_thread(void)
47653ca19a3Selad {
47753ca19a3Selad int error;
47879d9beffSad
47979d9beffSad error = kthread_create(PRI_VM, KTHREAD_MPSAFE, NULL, module_thread,
48079d9beffSad NULL, NULL, "modunload");
48179d9beffSad if (error != 0)
4827ebc13ffSchristos panic("%s: %d", __func__, error);
483bbc79e58Sad }
484bbc79e58Sad
485224f73d8Spgoyette /*
486224f73d8Spgoyette * module_builtin_require_force
487224f73d8Spgoyette *
488224f73d8Spgoyette * Require MODCTL_MUST_FORCE to load any built-in modules that have
489224f73d8Spgoyette * not yet been initialized
490224f73d8Spgoyette */
491224f73d8Spgoyette void
module_builtin_require_force(void)492224f73d8Spgoyette module_builtin_require_force(void)
493224f73d8Spgoyette {
494224f73d8Spgoyette module_t *mod;
495224f73d8Spgoyette
4964a743ad4Spgoyette kernconfig_lock();
497224f73d8Spgoyette TAILQ_FOREACH(mod, &module_builtins, mod_chain) {
498224f73d8Spgoyette module_require_force(mod);
499224f73d8Spgoyette }
5004a743ad4Spgoyette kernconfig_unlock();
501224f73d8Spgoyette }
502224f73d8Spgoyette
503f0c67b07Spooka static struct sysctllog *module_sysctllog;
504f0c67b07Spooka
5052b1203b7Spgoyette static int
sysctl_module_autotime(SYSCTLFN_ARGS)5062b1203b7Spgoyette sysctl_module_autotime(SYSCTLFN_ARGS)
5072b1203b7Spgoyette {
5082b1203b7Spgoyette struct sysctlnode node;
5092b1203b7Spgoyette int t, error;
5102b1203b7Spgoyette
5112b1203b7Spgoyette t = *(int *)rnode->sysctl_data;
5122b1203b7Spgoyette
5132b1203b7Spgoyette node = *rnode;
5142b1203b7Spgoyette node.sysctl_data = &t;
5152b1203b7Spgoyette error = sysctl_lookup(SYSCTLFN_CALL(&node));
5162b1203b7Spgoyette if (error || newp == NULL)
5172b1203b7Spgoyette return (error);
5182b1203b7Spgoyette
5192b1203b7Spgoyette if (t < 0)
5202b1203b7Spgoyette return (EINVAL);
5212b1203b7Spgoyette
5222b1203b7Spgoyette *(int *)rnode->sysctl_data = t;
5232b1203b7Spgoyette return (0);
5242b1203b7Spgoyette }
5252b1203b7Spgoyette
526f0c67b07Spooka static void
sysctl_module_setup(void)527f0c67b07Spooka sysctl_module_setup(void)
52811701b3bSad {
52911701b3bSad const struct sysctlnode *node = NULL;
53011701b3bSad
531f0c67b07Spooka sysctl_createv(&module_sysctllog, 0, NULL, &node,
53211701b3bSad CTLFLAG_PERMANENT,
53311701b3bSad CTLTYPE_NODE, "module",
53411701b3bSad SYSCTL_DESCR("Module options"),
53511701b3bSad NULL, 0, NULL, 0,
53611701b3bSad CTL_KERN, CTL_CREATE, CTL_EOL);
53711701b3bSad
53811701b3bSad if (node == NULL)
53911701b3bSad return;
54011701b3bSad
541f0c67b07Spooka sysctl_createv(&module_sysctllog, 0, &node, NULL,
54211701b3bSad CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
5431cbdcd8dSjruoho CTLTYPE_BOOL, "autoload",
54411701b3bSad SYSCTL_DESCR("Enable automatic load of modules"),
54511701b3bSad NULL, 0, &module_autoload_on, 0,
54611701b3bSad CTL_CREATE, CTL_EOL);
547f0c67b07Spooka sysctl_createv(&module_sysctllog, 0, &node, NULL,
54811701b3bSad CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
549c4bc9856Sriastradh CTLTYPE_BOOL, "autounload_unsafe",
550c4bc9856Sriastradh SYSCTL_DESCR("Enable automatic unload of unaudited modules"),
551c4bc9856Sriastradh NULL, 0, &module_autounload_unsafe, 0,
552c4bc9856Sriastradh CTL_CREATE, CTL_EOL);
553c4bc9856Sriastradh sysctl_createv(&module_sysctllog, 0, &node, NULL,
554c4bc9856Sriastradh CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
5551cbdcd8dSjruoho CTLTYPE_BOOL, "verbose",
55611701b3bSad SYSCTL_DESCR("Enable verbose output"),
55711701b3bSad NULL, 0, &module_verbose_on, 0,
55811701b3bSad CTL_CREATE, CTL_EOL);
5592b1203b7Spgoyette sysctl_createv(&module_sysctllog, 0, &node, NULL,
56034815295Sjnemeth CTLFLAG_PERMANENT | CTLFLAG_READONLY,
56134815295Sjnemeth CTLTYPE_STRING, "path",
56234815295Sjnemeth SYSCTL_DESCR("Default module load path"),
56334815295Sjnemeth NULL, 0, module_base, 0,
56434815295Sjnemeth CTL_CREATE, CTL_EOL);
56534815295Sjnemeth sysctl_createv(&module_sysctllog, 0, &node, NULL,
5662b1203b7Spgoyette CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
5672b1203b7Spgoyette CTLTYPE_INT, "autotime",
5682b1203b7Spgoyette SYSCTL_DESCR("Auto-unload delay"),
5692b1203b7Spgoyette sysctl_module_autotime, 0, &module_autotime, 0,
5702b1203b7Spgoyette CTL_CREATE, CTL_EOL);
57111701b3bSad }
57211701b3bSad
573bbc79e58Sad /*
574bbc79e58Sad * module_init_class:
575bbc79e58Sad *
576bbc79e58Sad * Initialize all built-in and pre-loaded modules of the
577bbc79e58Sad * specified class.
578bbc79e58Sad */
579bbc79e58Sad void
module_init_class(modclass_t modclass)580a35d1a8cSmatt module_init_class(modclass_t modclass)
581bbc79e58Sad {
582d8c53959Spooka TAILQ_HEAD(, module) bi_fail = TAILQ_HEAD_INITIALIZER(bi_fail);
583ee7bfacdSpooka module_t *mod;
584ee7bfacdSpooka modinfo_t *mi;
585bbc79e58Sad
5864a743ad4Spgoyette kernconfig_lock();
587bbc79e58Sad /*
588ee7bfacdSpooka * Builtins first. These will not depend on pre-loaded modules
589ee7bfacdSpooka * (because the kernel would not link).
590bbc79e58Sad */
591ee7bfacdSpooka do {
592ee7bfacdSpooka TAILQ_FOREACH(mod, &module_builtins, mod_chain) {
593ee7bfacdSpooka mi = mod->mod_info;
594a35d1a8cSmatt if (!MODULE_CLASS_MATCH(mi, modclass))
595bbc79e58Sad continue;
596d8c53959Spooka /*
597d8c53959Spooka * If initializing a builtin module fails, don't try
598d8c53959Spooka * to load it again. But keep it around and queue it
599224f73d8Spgoyette * on the builtins list after we're done with module
600224f73d8Spgoyette * init. Don't set it to MODFLG_MUST_FORCE in case a
601224f73d8Spgoyette * future attempt to initialize can be successful.
602224f73d8Spgoyette * (If the module has previously been set to
603224f73d8Spgoyette * MODFLG_MUST_FORCE, don't try to override that!)
604d8c53959Spooka */
605d91f98a8Spgoyette if (ISSET(mod->mod_flags, MODFLG_MUST_FORCE) ||
60675495633Schristos module_do_builtin(mod, mi->mi_name, NULL,
60775495633Schristos NULL) != 0) {
608d8c53959Spooka TAILQ_REMOVE(&module_builtins, mod, mod_chain);
609d8c53959Spooka TAILQ_INSERT_TAIL(&bi_fail, mod, mod_chain);
610d8c53959Spooka }
611ee7bfacdSpooka break;
612bbc79e58Sad }
613ee7bfacdSpooka } while (mod != NULL);
614ee7bfacdSpooka
615bbc79e58Sad /*
616bbc79e58Sad * Now preloaded modules. These will be pulled off the
617bbc79e58Sad * list as we call module_do_load();
618bbc79e58Sad */
6198ef40c77Sad do {
620ee7bfacdSpooka TAILQ_FOREACH(mod, &module_bootlist, mod_chain) {
6218ef40c77Sad mi = mod->mod_info;
622a35d1a8cSmatt if (!MODULE_CLASS_MATCH(mi, modclass))
6238ef40c77Sad continue;
62467280de1Sad module_do_load(mi->mi_name, false, 0, NULL, NULL,
625a35d1a8cSmatt modclass, false);
6268ef40c77Sad break;
627bbc79e58Sad }
6288ef40c77Sad } while (mod != NULL);
629d8c53959Spooka
630224f73d8Spgoyette /* return failed builtin modules to builtin list */
631d8c53959Spooka while ((mod = TAILQ_FIRST(&bi_fail)) != NULL) {
632d8c53959Spooka TAILQ_REMOVE(&bi_fail, mod, mod_chain);
633d8c53959Spooka TAILQ_INSERT_TAIL(&module_builtins, mod, mod_chain);
634d8c53959Spooka }
635d8c53959Spooka
6364a743ad4Spgoyette kernconfig_unlock();
637bbc79e58Sad }
638bbc79e58Sad
639bbc79e58Sad /*
6404c57df4aSad * module_compatible:
641bbc79e58Sad *
6424c57df4aSad * Return true if the two supplied kernel versions are said to
6434c57df4aSad * have the same binary interface for kernel code. The entire
644*d40aaeaeSmsaitoh * version is significant for the development tree (-current),
6454c57df4aSad * major and minor versions are significant for official
6464c57df4aSad * releases of the system.
647bbc79e58Sad */
648b629cd42Spooka bool
module_compatible(int v1,int v2)6494c57df4aSad module_compatible(int v1, int v2)
650bbc79e58Sad {
651bbc79e58Sad
6524c57df4aSad #if __NetBSD_Version__ / 1000000 % 100 == 99 /* -current */
6534c57df4aSad return v1 == v2;
6544c57df4aSad #else /* release */
6554c57df4aSad return abs(v1 - v2) < 10000;
6564c57df4aSad #endif
657bbc79e58Sad }
658bbc79e58Sad
659bbc79e58Sad /*
660bbc79e58Sad * module_load:
661bbc79e58Sad *
6623fd8e29aSjmmv * Load a single module from the file system.
663bbc79e58Sad */
664bbc79e58Sad int
module_load(const char * filename,int flags,prop_dictionary_t props,modclass_t modclass)66567280de1Sad module_load(const char *filename, int flags, prop_dictionary_t props,
666a35d1a8cSmatt modclass_t modclass)
667bbc79e58Sad {
6681aa80133Smaya module_t *mod;
669bbc79e58Sad int error;
670bbc79e58Sad
6711aa80133Smaya /* Test if we already have the module loaded before
6721aa80133Smaya * authorizing so we have the opportunity to return EEXIST. */
6731aa80133Smaya kernconfig_lock();
6741aa80133Smaya mod = module_lookup(filename);
6751aa80133Smaya if (mod != NULL) {
6761aa80133Smaya module_print("%s module `%s' already loaded",
6771aa80133Smaya "requested", filename);
6781aa80133Smaya error = EEXIST;
6791aa80133Smaya goto out;
6801aa80133Smaya }
6811aa80133Smaya
68267280de1Sad /* Authorize. */
68367280de1Sad error = kauth_authorize_system(kauth_cred_get(), KAUTH_SYSTEM_MODULE,
68467280de1Sad 0, (void *)(uintptr_t)MODCTL_LOAD, NULL, NULL);
6851aa80133Smaya if (error != 0)
6861aa80133Smaya goto out;
68767280de1Sad
688a35d1a8cSmatt error = module_do_load(filename, false, flags, props, NULL, modclass,
6897b228b5aSad false);
690441aa9cfSmaya
6911aa80133Smaya out:
6921aa80133Smaya kernconfig_unlock();
693bbc79e58Sad return error;
694bbc79e58Sad }
695bbc79e58Sad
696bbc79e58Sad /*
6977b228b5aSad * module_autoload:
6987b228b5aSad *
6997b228b5aSad * Load a single module from the file system, system initiated.
7007b228b5aSad */
7017b228b5aSad int
module_autoload(const char * filename,modclass_t modclass)702a35d1a8cSmatt module_autoload(const char *filename, modclass_t modclass)
7037b228b5aSad {
7047b228b5aSad int error;
70595b31e28Spgoyette struct proc *p = curlwp->l_proc;
7067b228b5aSad
7074a743ad4Spgoyette kernconfig_lock();
7087b228b5aSad
70911701b3bSad /* Nothing if the user has disabled it. */
71011701b3bSad if (!module_autoload_on) {
7114a743ad4Spgoyette kernconfig_unlock();
71211701b3bSad return EPERM;
71311701b3bSad }
71411701b3bSad
715a4ce70f1Sdholland /* Disallow path separators and magic symlinks. */
716950585e9Sad if (strchr(filename, '/') != NULL || strchr(filename, '@') != NULL ||
717950585e9Sad strchr(filename, '.') != NULL) {
7184a743ad4Spgoyette kernconfig_unlock();
719950585e9Sad return EPERM;
720950585e9Sad }
721950585e9Sad
7227b228b5aSad /* Authorize. */
7237b228b5aSad error = kauth_authorize_system(kauth_cred_get(), KAUTH_SYSTEM_MODULE,
7247b228b5aSad 0, (void *)(uintptr_t)MODCTL_LOAD, (void *)(uintptr_t)1, NULL);
7257b228b5aSad
7264a743ad4Spgoyette if (error == 0)
727a35d1a8cSmatt error = module_do_load(filename, false, 0, NULL, NULL, modclass,
7284a743ad4Spgoyette true);
7294a743ad4Spgoyette
73086f2feafSpgoyette module_print("Autoload for `%s' requested by pid %d (%s), status %d",
73195b31e28Spgoyette filename, p->p_pid, p->p_comm, error);
7324a743ad4Spgoyette kernconfig_unlock();
7334a743ad4Spgoyette return error;
7347b228b5aSad }
7357b228b5aSad
7367b228b5aSad /*
737bbc79e58Sad * module_unload:
738bbc79e58Sad *
739bbc79e58Sad * Find and unload a module by name.
740bbc79e58Sad */
741bbc79e58Sad int
module_unload(const char * name)742bbc79e58Sad module_unload(const char *name)
743bbc79e58Sad {
744bbc79e58Sad int error;
745bbc79e58Sad
74667280de1Sad /* Authorize. */
74767280de1Sad error = kauth_authorize_system(kauth_cred_get(), KAUTH_SYSTEM_MODULE,
74867280de1Sad 0, (void *)(uintptr_t)MODCTL_UNLOAD, NULL, NULL);
74967280de1Sad if (error != 0) {
75067280de1Sad return error;
75167280de1Sad }
75267280de1Sad
7534a743ad4Spgoyette kernconfig_lock();
754224f73d8Spgoyette error = module_do_unload(name, true);
7554a743ad4Spgoyette kernconfig_unlock();
756bbc79e58Sad
757bbc79e58Sad return error;
758bbc79e58Sad }
759bbc79e58Sad
760bbc79e58Sad /*
761bbc79e58Sad * module_lookup:
762bbc79e58Sad *
763bbc79e58Sad * Look up a module by name.
764bbc79e58Sad */
765bbc79e58Sad module_t *
module_lookup(const char * name)766bbc79e58Sad module_lookup(const char *name)
767bbc79e58Sad {
768bbc79e58Sad module_t *mod;
769bbc79e58Sad
7704a743ad4Spgoyette KASSERT(kernconfig_is_held());
771bbc79e58Sad
772bbc79e58Sad TAILQ_FOREACH(mod, &module_list, mod_chain) {
773d91f98a8Spgoyette if (strcmp(mod->mod_info->mi_name, name) == 0)
774bbc79e58Sad break;
775bbc79e58Sad }
776bbc79e58Sad
777bbc79e58Sad return mod;
778bbc79e58Sad }
779bbc79e58Sad
780bbc79e58Sad /*
781bbc79e58Sad * module_hold:
782bbc79e58Sad *
783bbc79e58Sad * Add a single reference to a module. It's the caller's
784bbc79e58Sad * responsibility to ensure that the reference is dropped
785bbc79e58Sad * later.
786bbc79e58Sad */
787d5abe492Schs void
module_hold(module_t * mod)788d5abe492Schs module_hold(module_t *mod)
789bbc79e58Sad {
790bbc79e58Sad
7914a743ad4Spgoyette kernconfig_lock();
792bbc79e58Sad mod->mod_refcnt++;
7934a743ad4Spgoyette kernconfig_unlock();
794bbc79e58Sad }
795bbc79e58Sad
796bbc79e58Sad /*
797bbc79e58Sad * module_rele:
798bbc79e58Sad *
799bbc79e58Sad * Release a reference acquired with module_hold().
800bbc79e58Sad */
801bbc79e58Sad void
module_rele(module_t * mod)802d5abe492Schs module_rele(module_t *mod)
803bbc79e58Sad {
804bbc79e58Sad
8054a743ad4Spgoyette kernconfig_lock();
806d5abe492Schs KASSERT(mod->mod_refcnt > 0);
807bbc79e58Sad mod->mod_refcnt--;
8084a743ad4Spgoyette kernconfig_unlock();
809bbc79e58Sad }
810bbc79e58Sad
811bbc79e58Sad /*
812f94a8f91Sad * module_enqueue:
813f94a8f91Sad *
814f94a8f91Sad * Put a module onto the global list and update counters.
815f94a8f91Sad */
81635a75982Spooka void
module_enqueue(module_t * mod)817f94a8f91Sad module_enqueue(module_t *mod)
818f94a8f91Sad {
819f94a8f91Sad int i;
820f94a8f91Sad
8214a743ad4Spgoyette KASSERT(kernconfig_is_held());
82235a75982Spooka
823f94a8f91Sad /*
8241cd7e75aSpgoyette * Put new entry at the head of the queue so autounload can unload
8251cd7e75aSpgoyette * requisite modules with only one pass through the queue.
826f94a8f91Sad */
827f94a8f91Sad TAILQ_INSERT_HEAD(&module_list, mod, mod_chain);
8281cd7e75aSpgoyette if (mod->mod_nrequired) {
829f94a8f91Sad
830f94a8f91Sad /* Add references to the requisite modules. */
831f94a8f91Sad for (i = 0; i < mod->mod_nrequired; i++) {
832d91f98a8Spgoyette KASSERT((*mod->mod_required)[i] != NULL);
833d91f98a8Spgoyette (*mod->mod_required)[i]->mod_refcnt++;
834f94a8f91Sad }
835f94a8f91Sad }
836f94a8f91Sad module_count++;
837f94a8f91Sad module_gen++;
838f94a8f91Sad }
839f94a8f91Sad
840f94a8f91Sad /*
841d91f98a8Spgoyette * Our array of required module pointers starts with zero entries. If we
842d91f98a8Spgoyette * need to add a new entry, and the list is already full, we reallocate a
843d91f98a8Spgoyette * larger array, adding MAXMODDEPS entries.
844d91f98a8Spgoyette */
845d91f98a8Spgoyette static void
alloc_required(module_t * mod)846d91f98a8Spgoyette alloc_required(module_t *mod)
847d91f98a8Spgoyette {
848d91f98a8Spgoyette module_t *(*new)[], *(*old)[];
849d91f98a8Spgoyette int areq;
850d91f98a8Spgoyette int i;
851d91f98a8Spgoyette
852d91f98a8Spgoyette if (mod->mod_nrequired >= mod->mod_arequired) {
853d91f98a8Spgoyette areq = mod->mod_arequired + MAXMODDEPS;
854d91f98a8Spgoyette old = mod->mod_required;
855d91f98a8Spgoyette new = kmem_zalloc(areq * sizeof(module_t *), KM_SLEEP);
856d91f98a8Spgoyette for (i = 0; i < mod->mod_arequired; i++)
857d91f98a8Spgoyette (*new)[i] = (*old)[i];
858d91f98a8Spgoyette mod->mod_required = new;
859d91f98a8Spgoyette if (old)
860d91f98a8Spgoyette kmem_free(old, mod->mod_arequired * sizeof(module_t *));
861d91f98a8Spgoyette mod->mod_arequired = areq;
862d91f98a8Spgoyette }
863d91f98a8Spgoyette }
864d91f98a8Spgoyette
865d91f98a8Spgoyette /*
866bbc79e58Sad * module_do_builtin:
867bbc79e58Sad *
868ee7bfacdSpooka * Initialize a module from the list of modules that are
869ee7bfacdSpooka * already linked into the kernel.
870bbc79e58Sad */
871bbc79e58Sad static int
module_do_builtin(const module_t * pmod,const char * name,module_t ** modp,prop_dictionary_t props)87275495633Schristos module_do_builtin(const module_t *pmod, const char *name, module_t **modp,
87375495633Schristos prop_dictionary_t props)
874bbc79e58Sad {
875bbc79e58Sad const char *p, *s;
876bbc79e58Sad char buf[MAXMODNAME];
877ee7bfacdSpooka modinfo_t *mi = NULL;
8784a743ad4Spgoyette module_t *mod, *mod2, *mod_loaded, *prev_active;
879bbc79e58Sad size_t len;
880f94a8f91Sad int error;
881bbc79e58Sad
8824a743ad4Spgoyette KASSERT(kernconfig_is_held());
883bbc79e58Sad
884bbc79e58Sad /*
885bbc79e58Sad * Search the list to see if we have a module by this name.
886bbc79e58Sad */
887ee7bfacdSpooka TAILQ_FOREACH(mod, &module_builtins, mod_chain) {
888ee7bfacdSpooka if (strcmp(mod->mod_info->mi_name, name) == 0) {
889ee7bfacdSpooka mi = mod->mod_info;
890bbc79e58Sad break;
891bbc79e58Sad }
892bbc79e58Sad }
893ee7bfacdSpooka
894ee7bfacdSpooka /*
895ee7bfacdSpooka * Check to see if already loaded. This might happen if we
896ee7bfacdSpooka * were already loaded as a dependency.
897ee7bfacdSpooka */
898ee7bfacdSpooka if ((mod_loaded = module_lookup(name)) != NULL) {
899ee7bfacdSpooka KASSERT(mod == NULL);
900ee7bfacdSpooka if (modp)
901ee7bfacdSpooka *modp = mod_loaded;
902ee7bfacdSpooka return 0;
903bbc79e58Sad }
904bbc79e58Sad
905ee7bfacdSpooka /* Note! This is from TAILQ, not immediate above */
906193bcc77Spooka if (mi == NULL) {
907193bcc77Spooka /*
908193bcc77Spooka * XXX: We'd like to panic here, but currently in some
909193bcc77Spooka * cases (such as nfsserver + nfs), the dependee can be
91072e44f84Sandvar * successfully linked without the dependencies.
911193bcc77Spooka */
912d91f98a8Spgoyette module_error("built-in module %s can't find builtin "
913d91f98a8Spgoyette "dependency `%s'", pmod->mod_info->mi_name, name);
914193bcc77Spooka return ENOENT;
915193bcc77Spooka }
916ee7bfacdSpooka
917bbc79e58Sad /*
918bbc79e58Sad * Initialize pre-requisites.
919bbc79e58Sad */
920d91f98a8Spgoyette KASSERT(mod->mod_required == NULL);
921d91f98a8Spgoyette KASSERT(mod->mod_arequired == 0);
922d91f98a8Spgoyette KASSERT(mod->mod_nrequired == 0);
923bbc79e58Sad if (mi->mi_required != NULL) {
924bbc79e58Sad for (s = mi->mi_required; *s != '\0'; s = p) {
925bbc79e58Sad if (*s == ',')
926bbc79e58Sad s++;
927bbc79e58Sad p = s;
928bbc79e58Sad while (*p != '\0' && *p != ',')
929bbc79e58Sad p++;
930d1579b2dSriastradh len = uimin(p - s + 1, sizeof(buf));
931bbc79e58Sad strlcpy(buf, s, len);
932bbc79e58Sad if (buf[0] == '\0')
933bbc79e58Sad break;
934d91f98a8Spgoyette alloc_required(mod);
93575495633Schristos error = module_do_builtin(mod, buf, &mod2, NULL);
936bbc79e58Sad if (error != 0) {
937d91f98a8Spgoyette module_error("built-in module %s prerequisite "
938d91f98a8Spgoyette "%s failed, error %d", name, buf, error);
939d91f98a8Spgoyette goto fail;
940bbc79e58Sad }
941d91f98a8Spgoyette (*mod->mod_required)[mod->mod_nrequired++] = mod2;
942bbc79e58Sad }
943bbc79e58Sad }
944bbc79e58Sad
945bbc79e58Sad /*
946bbc79e58Sad * Try to initialize the module.
947bbc79e58Sad */
9484a743ad4Spgoyette prev_active = module_active;
9491bb1fee7Sad module_active = mod;
950d9b1a59bSpooka error = (*mi->mi_modcmd)(MODULE_CMD_INIT, props);
9514a743ad4Spgoyette module_active = prev_active;
952bbc79e58Sad if (error != 0) {
953d91f98a8Spgoyette module_error("built-in module %s failed its MODULE_CMD_INIT, "
954d91f98a8Spgoyette "error %d", mi->mi_name, error);
955d91f98a8Spgoyette goto fail;
956bbc79e58Sad }
957ee7bfacdSpooka
958ee7bfacdSpooka /* load always succeeds after this point */
959ee7bfacdSpooka
960ee7bfacdSpooka TAILQ_REMOVE(&module_builtins, mod, mod_chain);
961ee7bfacdSpooka module_builtinlist--;
962ee7bfacdSpooka if (modp != NULL) {
963ee7bfacdSpooka *modp = mod;
964ee7bfacdSpooka }
965f94a8f91Sad module_enqueue(mod);
966bbc79e58Sad return 0;
967d91f98a8Spgoyette
968d91f98a8Spgoyette fail:
969d91f98a8Spgoyette if (mod->mod_required)
970d91f98a8Spgoyette kmem_free(mod->mod_required, mod->mod_arequired *
971d91f98a8Spgoyette sizeof(module_t *));
972d91f98a8Spgoyette mod->mod_arequired = 0;
973d91f98a8Spgoyette mod->mod_nrequired = 0;
974d91f98a8Spgoyette mod->mod_required = NULL;
975d91f98a8Spgoyette return error;
976bbc79e58Sad }
977bbc79e58Sad
978bbc79e58Sad /*
97997b627ecSpgoyette * module_load_sysctl
98097b627ecSpgoyette *
9810c750f3eSpgoyette * Check to see if a non-builtin module has any SYSCTL_SETUP() routine(s)
98297b627ecSpgoyette * registered. If so, call it (them).
98397b627ecSpgoyette */
98497b627ecSpgoyette
98597b627ecSpgoyette static void
module_load_sysctl(module_t * mod)98697b627ecSpgoyette module_load_sysctl(module_t *mod)
98797b627ecSpgoyette {
98897b627ecSpgoyette void (**ls_funcp)(struct sysctllog **);
98997b627ecSpgoyette void *ls_start;
99097b627ecSpgoyette size_t ls_size, count;
99197b627ecSpgoyette int error;
99297b627ecSpgoyette
9930c750f3eSpgoyette /*
9940c750f3eSpgoyette * Built-in modules don't have a mod_kobj so we cannot search
9950c750f3eSpgoyette * for their link_set_sysctl_funcs
9960c750f3eSpgoyette */
9970c750f3eSpgoyette if (mod->mod_source == MODULE_SOURCE_KERNEL)
9980c750f3eSpgoyette return;
9990c750f3eSpgoyette
100097b627ecSpgoyette error = kobj_find_section(mod->mod_kobj, "link_set_sysctl_funcs",
100197b627ecSpgoyette &ls_start, &ls_size);
100297b627ecSpgoyette if (error == 0) {
100397b627ecSpgoyette count = ls_size / sizeof(ls_start);
100497b627ecSpgoyette ls_funcp = ls_start;
100597b627ecSpgoyette while (count--) {
100697b627ecSpgoyette (**ls_funcp)(&mod->mod_sysctllog);
100797b627ecSpgoyette ls_funcp++;
100897b627ecSpgoyette }
100997b627ecSpgoyette }
101069626800Spgoyette }
101169626800Spgoyette
101269626800Spgoyette /*
101369626800Spgoyette * module_load_evcnt
101469626800Spgoyette *
101569626800Spgoyette * Check to see if a non-builtin module has any static evcnt's defined;
101669626800Spgoyette * if so, attach them.
101769626800Spgoyette */
101869626800Spgoyette
101969626800Spgoyette static void
module_load_evcnt(module_t * mod)102069626800Spgoyette module_load_evcnt(module_t *mod)
102169626800Spgoyette {
102269626800Spgoyette struct evcnt * const *ls_evp;
102369626800Spgoyette void *ls_start;
102469626800Spgoyette size_t ls_size, count;
102569626800Spgoyette int error;
102669626800Spgoyette
102769626800Spgoyette /*
102869626800Spgoyette * Built-in modules' static evcnt stuff will be handled
102969626800Spgoyette * automatically as part of general kernel initialization
103069626800Spgoyette */
103169626800Spgoyette if (mod->mod_source == MODULE_SOURCE_KERNEL)
103269626800Spgoyette return;
103369626800Spgoyette
103469626800Spgoyette error = kobj_find_section(mod->mod_kobj, "link_set_evcnts",
103569626800Spgoyette &ls_start, &ls_size);
103669626800Spgoyette if (error == 0) {
103769626800Spgoyette count = ls_size / sizeof(*ls_evp);
103869626800Spgoyette ls_evp = ls_start;
103969626800Spgoyette while (count--) {
104069626800Spgoyette evcnt_attach_static(*ls_evp++);
104169626800Spgoyette }
104269626800Spgoyette }
104369626800Spgoyette }
104469626800Spgoyette
104569626800Spgoyette /*
104669626800Spgoyette * module_unload_evcnt
104769626800Spgoyette *
104869626800Spgoyette * Check to see if a non-builtin module has any static evcnt's defined;
104969626800Spgoyette * if so, detach them.
105069626800Spgoyette */
105169626800Spgoyette
105269626800Spgoyette static void
module_unload_evcnt(module_t * mod)105369626800Spgoyette module_unload_evcnt(module_t *mod)
105469626800Spgoyette {
105569626800Spgoyette struct evcnt * const *ls_evp;
105669626800Spgoyette void *ls_start;
105769626800Spgoyette size_t ls_size, count;
105869626800Spgoyette int error;
105969626800Spgoyette
106069626800Spgoyette /*
106169626800Spgoyette * Built-in modules' static evcnt stuff will be handled
106269626800Spgoyette * automatically as part of general kernel initialization
106369626800Spgoyette */
106469626800Spgoyette if (mod->mod_source == MODULE_SOURCE_KERNEL)
106569626800Spgoyette return;
106669626800Spgoyette
106769626800Spgoyette error = kobj_find_section(mod->mod_kobj, "link_set_evcnts",
106869626800Spgoyette &ls_start, &ls_size);
106969626800Spgoyette if (error == 0) {
107069626800Spgoyette count = ls_size / sizeof(*ls_evp);
107169626800Spgoyette ls_evp = (void *)((char *)ls_start + ls_size);
107269626800Spgoyette while (count--) {
107369626800Spgoyette evcnt_detach(*--ls_evp);
107469626800Spgoyette }
107569626800Spgoyette }
107697b627ecSpgoyette }
107797b627ecSpgoyette
107897b627ecSpgoyette /*
1079bbc79e58Sad * module_do_load:
1080bbc79e58Sad *
1081bbc79e58Sad * Helper routine: load a module from the file system, or one
1082bbc79e58Sad * pushed by the boot loader.
1083bbc79e58Sad */
1084bbc79e58Sad static int
module_do_load(const char * name,bool isdep,int flags,prop_dictionary_t props,module_t ** modp,modclass_t modclass,bool autoload)1085f94a8f91Sad module_do_load(const char *name, bool isdep, int flags,
1086a35d1a8cSmatt prop_dictionary_t props, module_t **modp, modclass_t modclass,
108761270d54Sad bool autoload)
1088bbc79e58Sad {
1089d91f98a8Spgoyette /* The pending list for this level of recursion */
10904a743ad4Spgoyette TAILQ_HEAD(pending_t, module);
10914a743ad4Spgoyette struct pending_t *pending;
10924a743ad4Spgoyette struct pending_t new_pending = TAILQ_HEAD_INITIALIZER(new_pending);
1093d91f98a8Spgoyette
1094d91f98a8Spgoyette /* The stack of pending lists */
1095d91f98a8Spgoyette static SLIST_HEAD(pend_head, pend_entry) pend_stack =
1096d91f98a8Spgoyette SLIST_HEAD_INITIALIZER(pend_stack);
1097d91f98a8Spgoyette struct pend_entry {
1098d91f98a8Spgoyette SLIST_ENTRY(pend_entry) pe_entry;
1099d91f98a8Spgoyette struct pending_t *pe_pending;
1100d91f98a8Spgoyette } my_pend_entry;
1101d91f98a8Spgoyette
1102bbc79e58Sad modinfo_t *mi;
11034a743ad4Spgoyette module_t *mod, *mod2, *prev_active;
11041bdbe18dSjnemeth prop_dictionary_t filedict;
1105a8ed404dSpooka char buf[MAXMODNAME];
1106bbc79e58Sad const char *s, *p;
1107bbc79e58Sad int error;
1108a8ed404dSpooka size_t len;
1109bbc79e58Sad
11104a743ad4Spgoyette KASSERT(kernconfig_is_held());
1111bbc79e58Sad
11121bdbe18dSjnemeth filedict = NULL;
11131bdbe18dSjnemeth error = 0;
1114bbc79e58Sad
1115bbc79e58Sad /*
1116d91f98a8Spgoyette * Set up the pending list for this entry. If this is an
1117d91f98a8Spgoyette * internal entry (for a dependency), then use the same list
1118d91f98a8Spgoyette * as for the outer call; otherwise, it's an external entry
1119d91f98a8Spgoyette * (possibly recursive, ie a module's xxx_modcmd(init, ...)
1120d91f98a8Spgoyette * routine called us), so use the locally allocated list. In
1121d91f98a8Spgoyette * either case, add it to our stack.
11224a743ad4Spgoyette */
11234a743ad4Spgoyette if (isdep) {
1124d91f98a8Spgoyette KASSERT(SLIST_FIRST(&pend_stack) != NULL);
1125d91f98a8Spgoyette pending = SLIST_FIRST(&pend_stack)->pe_pending;
11264a743ad4Spgoyette } else
11274a743ad4Spgoyette pending = &new_pending;
1128d91f98a8Spgoyette my_pend_entry.pe_pending = pending;
1129d91f98a8Spgoyette SLIST_INSERT_HEAD(&pend_stack, &my_pend_entry, pe_entry);
11304a743ad4Spgoyette
11314a743ad4Spgoyette /*
1132ee7bfacdSpooka * Search the list of disabled builtins first.
1133ee7bfacdSpooka */
1134ee7bfacdSpooka TAILQ_FOREACH(mod, &module_builtins, mod_chain) {
1135ee7bfacdSpooka if (strcmp(mod->mod_info->mi_name, name) == 0) {
1136ee7bfacdSpooka break;
1137ee7bfacdSpooka }
1138ee7bfacdSpooka }
1139ee7bfacdSpooka if (mod) {
1140d91f98a8Spgoyette if (ISSET(mod->mod_flags, MODFLG_MUST_FORCE) &&
1141d91f98a8Spgoyette !ISSET(flags, MODCTL_LOAD_FORCE)) {
11421c558542Spooka if (!autoload) {
1143ee7bfacdSpooka module_error("use -f to reinstate "
114438cec6f0Schristos "builtin module `%s'", name);
11451c558542Spooka }
1146d91f98a8Spgoyette SLIST_REMOVE_HEAD(&pend_stack, pe_entry);
1147ee7bfacdSpooka return EPERM;
1148ee7bfacdSpooka } else {
1149d91f98a8Spgoyette SLIST_REMOVE_HEAD(&pend_stack, pe_entry);
115075495633Schristos error = module_do_builtin(mod, name, modp, props);
1151ee7bfacdSpooka return error;
1152ee7bfacdSpooka }
1153ee7bfacdSpooka }
1154ee7bfacdSpooka
1155ee7bfacdSpooka /*
1156bbc79e58Sad * Load the module and link. Before going to the file system,
115727d89016Spooka * scan the list of modules loaded by the boot loader.
1158bbc79e58Sad */
1159bbc79e58Sad TAILQ_FOREACH(mod, &module_bootlist, mod_chain) {
1160f94a8f91Sad if (strcmp(mod->mod_info->mi_name, name) == 0) {
1161bbc79e58Sad TAILQ_REMOVE(&module_bootlist, mod, mod_chain);
1162bbc79e58Sad break;
1163bbc79e58Sad }
1164bbc79e58Sad }
11657846a186Srumble if (mod != NULL) {
11664a743ad4Spgoyette TAILQ_INSERT_TAIL(pending, mod, mod_chain);
11677846a186Srumble } else {
1168f94a8f91Sad /*
1169c1df4ef1Spgoyette * Check to see if module is already present.
1170f94a8f91Sad */
11714a743ad4Spgoyette mod = module_lookup(name);
1172f94a8f91Sad if (mod != NULL) {
1173f94a8f91Sad if (modp != NULL) {
1174f94a8f91Sad *modp = mod;
1175f94a8f91Sad }
1176c1df4ef1Spgoyette module_print("%s module `%s' already loaded",
1177c1df4ef1Spgoyette isdep ? "dependent" : "requested", name);
1178d91f98a8Spgoyette SLIST_REMOVE_HEAD(&pend_stack, pe_entry);
1179c1df4ef1Spgoyette return EEXIST;
1180f94a8f91Sad }
1181c1df4ef1Spgoyette
1182224f73d8Spgoyette mod = module_newmodule(MODULE_SOURCE_FILESYS);
1183bbc79e58Sad if (mod == NULL) {
118455d90faeSchristos module_error("out of memory for `%s'", name);
1185d91f98a8Spgoyette SLIST_REMOVE_HEAD(&pend_stack, pe_entry);
1186bbc79e58Sad return ENOMEM;
1187bbc79e58Sad }
1188a8ed404dSpooka
1189c269eb06Spgoyette error = module_load_vfs_vec(name, flags, autoload, mod,
11902c4465aaSpgoyette &filedict);
1191bbc79e58Sad if (error != 0) {
1192fd5d831fSchristos #ifdef DEBUG
11931ebd0720Schristos /*
11941ebd0720Schristos * The exec class of modules contains a list of
11951ebd0720Schristos * modules that is the union of all the modules
11961ebd0720Schristos * available for each architecture, so we don't
11971ebd0720Schristos * print an error if they are missing.
11981ebd0720Schristos */
11999eac063fSchristos if ((modclass != MODULE_CLASS_EXEC || error != ENOENT)
1200eb18a20aSchristos && root_device != NULL)
12011ebd0720Schristos module_error("vfs load failed for `%s', "
12021ebd0720Schristos "error %d", name, error);
1203fd5d831fSchristos #endif
1204d91f98a8Spgoyette SLIST_REMOVE_HEAD(&pend_stack, pe_entry);
1205d5abe492Schs module_free(mod);
1206b5723bb6Srumble return error;
12078a7c44d0Srumble }
12084a743ad4Spgoyette TAILQ_INSERT_TAIL(pending, mod, mod_chain);
1209a8ed404dSpooka
12108ef40c77Sad error = module_fetch_info(mod);
12118ef40c77Sad if (error != 0) {
121238cec6f0Schristos module_error("cannot fetch info for `%s', error %d",
121338cec6f0Schristos name, error);
12148ef40c77Sad goto fail;
12158ef40c77Sad }
1216bbc79e58Sad }
1217bbc79e58Sad
1218bbc79e58Sad /*
12198ef40c77Sad * Check compatibility.
1220bbc79e58Sad */
1221bbc79e58Sad mi = mod->mod_info;
122247d97d24Spgoyette if (strnlen(mi->mi_name, MAXMODNAME) >= MAXMODNAME) {
122375030bbbSrumble error = EINVAL;
122438cec6f0Schristos module_error("module name `%s' longer than %d", mi->mi_name,
122538cec6f0Schristos MAXMODNAME);
122675030bbbSrumble goto fail;
122775030bbbSrumble }
122847d97d24Spgoyette if (mi->mi_class <= MODULE_CLASS_ANY ||
122947d97d24Spgoyette mi->mi_class >= MODULE_CLASS_MAX) {
123047d97d24Spgoyette error = EINVAL;
123147d97d24Spgoyette module_error("module `%s' has invalid class %d",
123247d97d24Spgoyette mi->mi_name, mi->mi_class);
123347d97d24Spgoyette goto fail;
123447d97d24Spgoyette }
12354c57df4aSad if (!module_compatible(mi->mi_version, __NetBSD_Version__)) {
12360c9d7240Schristos module_error("module `%s' built for `%d', system `%d'",
12370c9d7240Schristos mi->mi_name, mi->mi_version, __NetBSD_Version__);
1238d91f98a8Spgoyette if (ISSET(flags, MODCTL_LOAD_FORCE)) {
123952e9644bSad module_error("forced load, system may be unstable");
124052e9644bSad } else {
124152e9644bSad error = EPROGMISMATCH;
12424c57df4aSad goto fail;
12434c57df4aSad }
124452e9644bSad }
124575030bbbSrumble
1246bbc79e58Sad /*
124767280de1Sad * If a specific kind of module was requested, ensure that we have
124867280de1Sad * a match.
124967280de1Sad */
1250a35d1a8cSmatt if (!MODULE_CLASS_MATCH(mi, modclass)) {
1251a35d1a8cSmatt module_incompat(mi, modclass);
125267280de1Sad error = ENOENT;
125367280de1Sad goto fail;
125467280de1Sad }
125567280de1Sad
125667280de1Sad /*
1257f94a8f91Sad * If loading a dependency, `name' is a plain module name.
1258bbc79e58Sad * The name must match.
1259bbc79e58Sad */
1260f94a8f91Sad if (isdep && strcmp(mi->mi_name, name) != 0) {
126178d77f51Schristos module_error("dependency name mismatch (`%s' != `%s')",
126278d77f51Schristos name, mi->mi_name);
1263bbc79e58Sad error = ENOENT;
1264bbc79e58Sad goto fail;
1265bbc79e58Sad }
1266bbc79e58Sad
1267bbc79e58Sad /*
126857f560bcSpgoyette * If we loaded a module from the filesystem, check the actual
126957f560bcSpgoyette * module name (from the modinfo_t) to ensure another module
127057f560bcSpgoyette * with the same name doesn't already exist. (There's no
127157f560bcSpgoyette * guarantee the filename will match the module name, and the
127257f560bcSpgoyette * dup-symbols check may not be sufficient.)
127357f560bcSpgoyette */
127457f560bcSpgoyette if (mod->mod_source == MODULE_SOURCE_FILESYS) {
127557f560bcSpgoyette mod2 = module_lookup(mod->mod_info->mi_name);
1276a036e37dSpgoyette if ( mod2 && mod2 != mod) {
127757f560bcSpgoyette module_error("module with name `%s' already loaded",
127857f560bcSpgoyette mod2->mod_info->mi_name);
127957f560bcSpgoyette error = EEXIST;
1280d91f98a8Spgoyette if (modp != NULL)
1281d91f98a8Spgoyette *modp = mod2;
128257f560bcSpgoyette goto fail;
128357f560bcSpgoyette }
128457f560bcSpgoyette }
128557f560bcSpgoyette
128657f560bcSpgoyette /*
128775030bbbSrumble * Block circular dependencies.
128875030bbbSrumble */
12894a743ad4Spgoyette TAILQ_FOREACH(mod2, pending, mod_chain) {
1290bbc79e58Sad if (mod == mod2) {
1291bbc79e58Sad continue;
1292bbc79e58Sad }
1293bbc79e58Sad if (strcmp(mod2->mod_info->mi_name, mi->mi_name) == 0) {
1294bbc79e58Sad error = EDEADLK;
129578d77f51Schristos module_error("circular dependency detected for `%s'",
129678d77f51Schristos mi->mi_name);
1297bbc79e58Sad goto fail;
1298bbc79e58Sad }
1299bbc79e58Sad }
1300bbc79e58Sad
1301bbc79e58Sad /*
1302bbc79e58Sad * Now try to load any requisite modules.
1303bbc79e58Sad */
1304bbc79e58Sad if (mi->mi_required != NULL) {
1305d91f98a8Spgoyette mod->mod_arequired = 0;
1306bbc79e58Sad for (s = mi->mi_required; *s != '\0'; s = p) {
1307bbc79e58Sad if (*s == ',')
1308bbc79e58Sad s++;
1309bbc79e58Sad p = s;
1310bbc79e58Sad while (*p != '\0' && *p != ',')
1311bbc79e58Sad p++;
131275030bbbSrumble len = p - s + 1;
131375030bbbSrumble if (len >= MAXMODNAME) {
131475030bbbSrumble error = EINVAL;
131578d77f51Schristos module_error("required module name `%s' "
131638cec6f0Schristos "longer than %d", mi->mi_required,
131738cec6f0Schristos MAXMODNAME);
131875030bbbSrumble goto fail;
131975030bbbSrumble }
1320bbc79e58Sad strlcpy(buf, s, len);
1321bbc79e58Sad if (buf[0] == '\0')
1322bbc79e58Sad break;
1323d91f98a8Spgoyette alloc_required(mod);
1324cab53485Srumble if (strcmp(buf, mi->mi_name) == 0) {
1325cab53485Srumble error = EDEADLK;
132678d77f51Schristos module_error("self-dependency detected for "
132778d77f51Schristos "`%s'", mi->mi_name);
1328cab53485Srumble goto fail;
1329cab53485Srumble }
13303fd8e29aSjmmv error = module_do_load(buf, true, flags, NULL,
133170ae357eSchristos &mod2, MODULE_CLASS_ANY, true);
1332c1df4ef1Spgoyette if (error != 0 && error != EEXIST) {
133318143631Smaxv module_error("recursive load failed for `%s' "
133418143631Smaxv "(`%s' required), error %d", mi->mi_name,
133518143631Smaxv buf, error);
1336bbc79e58Sad goto fail;
13372a0d04a7Schristos }
1338d91f98a8Spgoyette (*mod->mod_required)[mod->mod_nrequired++] = mod2;
1339bbc79e58Sad }
1340bbc79e58Sad }
1341bbc79e58Sad
1342bbc79e58Sad /*
1343d0bd9aa4Sad * We loaded all needed modules successfully: perform global
1344d0bd9aa4Sad * relocations and initialize.
1345bbc79e58Sad */
1346e30b8a7cSpgoyette {
1347e30b8a7cSpgoyette char xname[MAXMODNAME];
1348e30b8a7cSpgoyette
1349e30b8a7cSpgoyette /*
1350e30b8a7cSpgoyette * In case of error the entire module is gone, so we
1351e30b8a7cSpgoyette * need to save its name for possible error report.
1352e30b8a7cSpgoyette */
1353e30b8a7cSpgoyette
1354e30b8a7cSpgoyette strlcpy(xname, mi->mi_name, MAXMODNAME);
1355d0bd9aa4Sad error = kobj_affix(mod->mod_kobj, mi->mi_name);
1356d0bd9aa4Sad if (error != 0) {
1357e30b8a7cSpgoyette module_error("unable to affix module `%s', error %d",
1358e30b8a7cSpgoyette xname, error);
1359d0bd9aa4Sad goto fail2;
1360d0bd9aa4Sad }
1361e30b8a7cSpgoyette }
1362d0bd9aa4Sad
1363a8ed404dSpooka if (filedict) {
1364a8ed404dSpooka if (!module_merge_dicts(filedict, props)) {
136538cec6f0Schristos module_error("module properties failed for %s", name);
13661bdbe18dSjnemeth error = EINVAL;
13671bdbe18dSjnemeth goto fail;
13681bdbe18dSjnemeth }
13691bdbe18dSjnemeth }
1370d91f98a8Spgoyette
13714a743ad4Spgoyette prev_active = module_active;
13721bb1fee7Sad module_active = mod;
1373e24002baSpgoyette
1374e24002baSpgoyette /*
1375e24002baSpgoyette * Note that we handle sysctl and evcnt setup _before_ we
1376e24002baSpgoyette * initialize the module itself. This maintains a consistent
1377e24002baSpgoyette * order between built-in and run-time-loaded modules. If
1378e24002baSpgoyette * initialization then fails, we'll need to undo these, too.
1379e24002baSpgoyette */
1380e24002baSpgoyette module_load_sysctl(mod); /* Set-up module's sysctl if any */
1381e24002baSpgoyette module_load_evcnt(mod); /* Attach any static evcnt needed */
1382e24002baSpgoyette
1383e24002baSpgoyette
1384a8ed404dSpooka error = (*mi->mi_modcmd)(MODULE_CMD_INIT, filedict ? filedict : props);
13854a743ad4Spgoyette module_active = prev_active;
1386a8ed404dSpooka if (filedict) {
13871bdbe18dSjnemeth prop_object_release(filedict);
1388a8ed404dSpooka filedict = NULL;
13891bdbe18dSjnemeth }
1390bbc79e58Sad if (error != 0) {
13912448a817Spgoyette module_error("modcmd(CMD_INIT) failed for `%s', error %d",
139238cec6f0Schristos mi->mi_name, error);
1393e24002baSpgoyette goto fail3;
1394bbc79e58Sad }
1395a8ed404dSpooka
1396bbc79e58Sad /*
1397a036e37dSpgoyette * If a recursive load already added a module with the same
1398a036e37dSpgoyette * name, abort.
1399a036e37dSpgoyette */
1400a036e37dSpgoyette mod2 = module_lookup(mi->mi_name);
1401a036e37dSpgoyette if (mod2 && mod2 != mod) {
1402a036e37dSpgoyette module_error("recursive load causes duplicate module `%s'",
1403a036e37dSpgoyette mi->mi_name);
1404a036e37dSpgoyette error = EEXIST;
1405a036e37dSpgoyette goto fail1;
1406a036e37dSpgoyette }
1407a036e37dSpgoyette
1408a036e37dSpgoyette /*
1409bbc79e58Sad * Good, the module loaded successfully. Put it onto the
1410bbc79e58Sad * list and add references to its requisite modules.
1411bbc79e58Sad */
14124a743ad4Spgoyette TAILQ_REMOVE(pending, mod, mod_chain);
1413f94a8f91Sad module_enqueue(mod);
1414bbc79e58Sad if (modp != NULL) {
1415bbc79e58Sad *modp = mod;
1416bbc79e58Sad }
14172b1203b7Spgoyette if (autoload && module_autotime > 0) {
141879d9beffSad /*
141979d9beffSad * Arrange to try unloading the module after
14202b1203b7Spgoyette * a short delay unless auto-unload is disabled.
142179d9beffSad */
142279d9beffSad mod->mod_autotime = time_second + module_autotime;
1423d91f98a8Spgoyette SET(mod->mod_flags, MODFLG_AUTO_LOADED);
142479d9beffSad module_thread_kick();
142579d9beffSad }
1426d91f98a8Spgoyette SLIST_REMOVE_HEAD(&pend_stack, pe_entry);
142760332beaSpgoyette module_print("module `%s' loaded successfully", mi->mi_name);
1428d5abe492Schs module_callback_load(mod);
1429bbc79e58Sad return 0;
14308a7c44d0Srumble
1431a036e37dSpgoyette fail1:
1432a036e37dSpgoyette (*mi->mi_modcmd)(MODULE_CMD_FINI, NULL);
1433e24002baSpgoyette fail3:
1434e24002baSpgoyette /*
1435e24002baSpgoyette * If there were any registered SYSCTL_SETUP funcs, make sure
1436e24002baSpgoyette * we release the sysctl entries
1437e24002baSpgoyette */
1438e24002baSpgoyette if (mod->mod_sysctllog) {
1439e24002baSpgoyette sysctl_teardown(&mod->mod_sysctllog);
1440e24002baSpgoyette }
1441e24002baSpgoyette /* Also detach any static evcnt's */
1442e24002baSpgoyette module_unload_evcnt(mod);
1443bbc79e58Sad fail:
1444bbc79e58Sad kobj_unload(mod->mod_kobj);
1445d0bd9aa4Sad fail2:
1446a8ed404dSpooka if (filedict != NULL) {
1447a8ed404dSpooka prop_object_release(filedict);
1448a8ed404dSpooka filedict = NULL;
1449a8ed404dSpooka }
14504a743ad4Spgoyette TAILQ_REMOVE(pending, mod, mod_chain);
1451d91f98a8Spgoyette SLIST_REMOVE_HEAD(&pend_stack, pe_entry);
1452d5abe492Schs module_free(mod);
1453bbc79e58Sad return error;
1454bbc79e58Sad }
1455bbc79e58Sad
1456bbc79e58Sad /*
1457bbc79e58Sad * module_do_unload:
1458bbc79e58Sad *
1459bbc79e58Sad * Helper routine: do the dirty work of unloading a module.
1460bbc79e58Sad */
1461bbc79e58Sad static int
module_do_unload(const char * name,bool load_requires_force)1462224f73d8Spgoyette module_do_unload(const char *name, bool load_requires_force)
1463bbc79e58Sad {
14644a743ad4Spgoyette module_t *mod, *prev_active;
1465bbc79e58Sad int error;
1466bbc79e58Sad u_int i;
1467bbc79e58Sad
14684a743ad4Spgoyette KASSERT(kernconfig_is_held());
14692f91d3f4Sjnemeth KASSERT(name != NULL);
1470bbc79e58Sad
147160332beaSpgoyette module_print("unload requested for '%s' (%s)", name,
147260332beaSpgoyette load_requires_force ? "TRUE" : "FALSE");
1473bbc79e58Sad mod = module_lookup(name);
1474bbc79e58Sad if (mod == NULL) {
147578d77f51Schristos module_error("module `%s' not found", name);
1476bbc79e58Sad return ENOENT;
1477bbc79e58Sad }
1478ee7bfacdSpooka if (mod->mod_refcnt != 0) {
147960332beaSpgoyette module_print("module `%s' busy (%d refs)", name,
148054ba967dSpgoyette mod->mod_refcnt);
1481bbc79e58Sad return EBUSY;
1482bbc79e58Sad }
14837086d3d6Spooka
14847086d3d6Spooka /*
14857086d3d6Spooka * Builtin secmodels are there to stay.
14867086d3d6Spooka */
14877086d3d6Spooka if (mod->mod_source == MODULE_SOURCE_KERNEL &&
14887086d3d6Spooka mod->mod_info->mi_class == MODULE_CLASS_SECMODEL) {
148960332beaSpgoyette module_print("cannot unload built-in secmodel module `%s'",
149060332beaSpgoyette name);
14917086d3d6Spooka return EPERM;
14927086d3d6Spooka }
14937086d3d6Spooka
14944a743ad4Spgoyette prev_active = module_active;
14951bb1fee7Sad module_active = mod;
1496d5abe492Schs module_callback_unload(mod);
149797b627ecSpgoyette
1498e24002baSpgoyette /* let the module clean up after itself */
1499e24002baSpgoyette error = (*mod->mod_info->mi_modcmd)(MODULE_CMD_FINI, NULL);
1500e24002baSpgoyette
150197b627ecSpgoyette /*
150297b627ecSpgoyette * If there were any registered SYSCTL_SETUP funcs, make sure
1503e24002baSpgoyette * we release the sysctl entries. Same for static evcnt.
150497b627ecSpgoyette */
1505e24002baSpgoyette if (error == 0) {
150697b627ecSpgoyette if (mod->mod_sysctllog) {
150797b627ecSpgoyette sysctl_teardown(&mod->mod_sysctllog);
150897b627ecSpgoyette }
150969626800Spgoyette module_unload_evcnt(mod);
1510e24002baSpgoyette }
15114a743ad4Spgoyette module_active = prev_active;
1512bbc79e58Sad if (error != 0) {
1513e24002baSpgoyette module_print("could not unload module `%s' error=%d", name,
15146cddbf0cSad error);
1515bbc79e58Sad return error;
1516bbc79e58Sad }
1517bbc79e58Sad module_count--;
1518bbc79e58Sad TAILQ_REMOVE(&module_list, mod, mod_chain);
1519bbc79e58Sad for (i = 0; i < mod->mod_nrequired; i++) {
1520d91f98a8Spgoyette (*mod->mod_required)[i]->mod_refcnt--;
1521bbc79e58Sad }
15222f91d3f4Sjnemeth module_print("unloaded module `%s'", name);
1523bbc79e58Sad if (mod->mod_kobj != NULL) {
1524bbc79e58Sad kobj_unload(mod->mod_kobj);
1525bbc79e58Sad }
1526ee7bfacdSpooka if (mod->mod_source == MODULE_SOURCE_KERNEL) {
1527d91f98a8Spgoyette if (mod->mod_required != NULL) {
1528d91f98a8Spgoyette /*
1529d91f98a8Spgoyette * release "required" resources - will be re-parsed
1530d91f98a8Spgoyette * if the module is re-enabled
1531d91f98a8Spgoyette */
1532d91f98a8Spgoyette kmem_free(mod->mod_required,
1533d91f98a8Spgoyette mod->mod_arequired * sizeof(module_t *));
1534d91f98a8Spgoyette mod->mod_nrequired = 0;
1535d91f98a8Spgoyette mod->mod_arequired = 0;
1536d91f98a8Spgoyette mod->mod_required = NULL;
1537d91f98a8Spgoyette }
1538224f73d8Spgoyette if (load_requires_force)
1539224f73d8Spgoyette module_require_force(mod);
1540ee7bfacdSpooka TAILQ_INSERT_TAIL(&module_builtins, mod, mod_chain);
1541ee7bfacdSpooka module_builtinlist++;
1542ee7bfacdSpooka } else {
1543d5abe492Schs module_free(mod);
1544ee7bfacdSpooka }
154579d9beffSad module_gen++;
1546bbc79e58Sad
1547bbc79e58Sad return 0;
1548bbc79e58Sad }
1549bbc79e58Sad
1550bbc79e58Sad /*
1551bbc79e58Sad * module_prime:
1552bbc79e58Sad *
1553bbc79e58Sad * Push a module loaded by the bootloader onto our internal
1554bbc79e58Sad * list.
1555bbc79e58Sad */
1556bbc79e58Sad int
module_prime(const char * name,void * base,size_t size)1557ec97828fSchristos module_prime(const char *name, void *base, size_t size)
1558bbc79e58Sad {
15591e522d74Spgoyette __link_set_decl(modules, modinfo_t);
15601e522d74Spgoyette modinfo_t *const *mip;
1561bbc79e58Sad module_t *mod;
1562bbc79e58Sad int error;
1563bbc79e58Sad
1564b491c2afSpgoyette /* Check for module name same as a built-in module */
15651e522d74Spgoyette
15661e522d74Spgoyette __link_set_foreach(mip, modules) {
15671e522d74Spgoyette if (*mip == &module_dummy)
15681e522d74Spgoyette continue;
15691e522d74Spgoyette if (strcmp((*mip)->mi_name, name) == 0) {
15701e522d74Spgoyette module_error("module `%s' pushed by boot loader "
15711e522d74Spgoyette "already exists", name);
15721e522d74Spgoyette return EEXIST;
15731e522d74Spgoyette }
15741e522d74Spgoyette }
1575b491c2afSpgoyette
1576b491c2afSpgoyette /* Also eliminate duplicate boolist entries */
1577b491c2afSpgoyette
1578b491c2afSpgoyette TAILQ_FOREACH(mod, &module_bootlist, mod_chain) {
1579b491c2afSpgoyette if (strcmp(mod->mod_info->mi_name, name) == 0) {
1580b491c2afSpgoyette module_error("duplicate bootlist entry for module "
1581b491c2afSpgoyette "`%s'", name);
1582b491c2afSpgoyette return EEXIST;
1583b491c2afSpgoyette }
1584b491c2afSpgoyette }
1585b491c2afSpgoyette
1586b491c2afSpgoyette mod = module_newmodule(MODULE_SOURCE_BOOT);
1587b491c2afSpgoyette if (mod == NULL) {
1588b491c2afSpgoyette return ENOMEM;
1589b491c2afSpgoyette }
1590b491c2afSpgoyette
1591ec97828fSchristos error = kobj_load_mem(&mod->mod_kobj, name, base, size);
15928ef40c77Sad if (error != 0) {
1593d5abe492Schs module_free(mod);
159438cec6f0Schristos module_error("unable to load `%s' pushed by boot loader, "
159538cec6f0Schristos "error %d", name, error);
1596bbc79e58Sad return error;
1597bbc79e58Sad }
15988ef40c77Sad error = module_fetch_info(mod);
15998ef40c77Sad if (error != 0) {
16008ef40c77Sad kobj_unload(mod->mod_kobj);
1601d5abe492Schs module_free(mod);
16021e522d74Spgoyette module_error("unable to fetch_info for `%s' pushed by boot "
16031e522d74Spgoyette "loader, error %d", name, error);
16048ef40c77Sad return error;
16058ef40c77Sad }
16068ef40c77Sad
1607bbc79e58Sad TAILQ_INSERT_TAIL(&module_bootlist, mod, mod_chain);
1608bbc79e58Sad
1609bbc79e58Sad return 0;
1610bbc79e58Sad }
16118ef40c77Sad
16128ef40c77Sad /*
16138ef40c77Sad * module_fetch_into:
16148ef40c77Sad *
16158ef40c77Sad * Fetch modinfo record from a loaded module.
16168ef40c77Sad */
16178ef40c77Sad static int
module_fetch_info(module_t * mod)16188ef40c77Sad module_fetch_info(module_t *mod)
16198ef40c77Sad {
16208ef40c77Sad int error;
16218ef40c77Sad void *addr;
16228ef40c77Sad size_t size;
16238ef40c77Sad
16248ef40c77Sad /*
16258ef40c77Sad * Find module info record and check compatibility.
16268ef40c77Sad */
16278ef40c77Sad error = kobj_find_section(mod->mod_kobj, "link_set_modules",
16288ef40c77Sad &addr, &size);
16298ef40c77Sad if (error != 0) {
163038cec6f0Schristos module_error("`link_set_modules' section not present, "
163138cec6f0Schristos "error %d", error);
16328ef40c77Sad return error;
16338ef40c77Sad }
16348ef40c77Sad if (size != sizeof(modinfo_t **)) {
1635071c66bdSriastradh if (size > sizeof(modinfo_t **) &&
1636071c66bdSriastradh (size % sizeof(modinfo_t **)) == 0) {
1637d91f98a8Spgoyette module_error("`link_set_modules' section wrong size "
1638071c66bdSriastradh "(%zu different MODULE declarations?)",
1639071c66bdSriastradh size / sizeof(modinfo_t **));
1640071c66bdSriastradh } else {
1641071c66bdSriastradh module_error("`link_set_modules' section wrong size "
1642071c66bdSriastradh "(got %zu, wanted %zu)",
1643071c66bdSriastradh size, sizeof(modinfo_t **));
1644071c66bdSriastradh }
1645e9e19f6eStron return ENOEXEC;
16468ef40c77Sad }
16478ef40c77Sad mod->mod_info = *(modinfo_t **)addr;
16488ef40c77Sad
16498ef40c77Sad return 0;
16508ef40c77Sad }
16511bb1fee7Sad
16521bb1fee7Sad /*
16531bb1fee7Sad * module_find_section:
16541bb1fee7Sad *
16551bb1fee7Sad * Allows a module that is being initialized to look up a section
16561bb1fee7Sad * within its ELF object.
16571bb1fee7Sad */
16581bb1fee7Sad int
module_find_section(const char * name,void ** addr,size_t * size)16591bb1fee7Sad module_find_section(const char *name, void **addr, size_t *size)
16601bb1fee7Sad {
16611bb1fee7Sad
16624a743ad4Spgoyette KASSERT(kernconfig_is_held());
16631bb1fee7Sad KASSERT(module_active != NULL);
16641bb1fee7Sad
16651bb1fee7Sad return kobj_find_section(module_active->mod_kobj, name, addr, size);
16661bb1fee7Sad }
166779d9beffSad
166879d9beffSad /*
166979d9beffSad * module_thread:
167079d9beffSad *
167179d9beffSad * Automatically unload modules. We try once to unload autoloaded
167279d9beffSad * modules after module_autotime seconds. If the system is under
16732b1203b7Spgoyette * severe memory pressure, we'll try unloading all modules, else if
16742b1203b7Spgoyette * module_autotime is zero, we don't try to unload, even if the
16752b1203b7Spgoyette * module was previously scheduled for unload.
167679d9beffSad */
167779d9beffSad static void
module_thread(void * cookie)167879d9beffSad module_thread(void *cookie)
167979d9beffSad {
168079d9beffSad module_t *mod, *next;
16812bae5b39Sad modinfo_t *mi;
16822bae5b39Sad int error;
168379d9beffSad
168479d9beffSad for (;;) {
16854a743ad4Spgoyette kernconfig_lock();
168679d9beffSad for (mod = TAILQ_FIRST(&module_list); mod != NULL; mod = next) {
168779d9beffSad next = TAILQ_NEXT(mod, mod_chain);
168886afac92Sjmcneill
168986afac92Sjmcneill /* skip built-in modules */
1690d76b6303Spooka if (mod->mod_source == MODULE_SOURCE_KERNEL)
1691d76b6303Spooka continue;
169286afac92Sjmcneill /* skip modules that weren't auto-loaded */
1693d91f98a8Spgoyette if (!ISSET(mod->mod_flags, MODFLG_AUTO_LOADED))
169486afac92Sjmcneill continue;
169586afac92Sjmcneill
16964b8a875aSad if (uvm_availmem(false) < uvmexp.freemin) {
169779d9beffSad module_thread_ticks = hz;
16982b1203b7Spgoyette } else if (module_autotime == 0 ||
16992b1203b7Spgoyette mod->mod_autotime == 0) {
170079d9beffSad continue;
170179d9beffSad } else if (time_second < mod->mod_autotime) {
170279d9beffSad module_thread_ticks = hz;
170379d9beffSad continue;
17048ff4eb83Sad } else {
170579d9beffSad mod->mod_autotime = 0;
17068ff4eb83Sad }
170786afac92Sjmcneill
17082bae5b39Sad /*
1709c4bc9856Sriastradh * Ask the module if it can be safely unloaded.
1710c4bc9856Sriastradh *
1711c4bc9856Sriastradh * - Modules which have been audited to be OK
1712c4bc9856Sriastradh * with that will return 0.
1713c4bc9856Sriastradh *
1714c4bc9856Sriastradh * - Modules which have not been audited for
1715c4bc9856Sriastradh * safe autounload will return ENOTTY.
1716c4bc9856Sriastradh *
1717c4bc9856Sriastradh * => With kern.module.autounload_unsafe=1,
1718c4bc9856Sriastradh * we treat ENOTTY as acceptance.
1719c4bc9856Sriastradh *
1720c4bc9856Sriastradh * - Some modules would ping-ping in and out
17212bae5b39Sad * because their use is transient but often.
1722c4bc9856Sriastradh * Example: exec_script. Other modules may
1723c4bc9856Sriastradh * still be in use. These modules can
1724c4bc9856Sriastradh * prevent autounload in all cases by
1725c4bc9856Sriastradh * returning EBUSY or some other error code.
17262bae5b39Sad */
17272bae5b39Sad mi = mod->mod_info;
17282bae5b39Sad error = (*mi->mi_modcmd)(MODULE_CMD_AUTOUNLOAD, NULL);
1729c4bc9856Sriastradh if (error == 0 ||
1730c4bc9856Sriastradh (error == ENOTTY && module_autounload_unsafe)) {
1731224f73d8Spgoyette (void)module_do_unload(mi->mi_name, false);
173260332beaSpgoyette } else
173360332beaSpgoyette module_print("module `%s' declined to be "
173460332beaSpgoyette "auto-unloaded error=%d", mi->mi_name,
173560332beaSpgoyette error);
173679d9beffSad }
17374a743ad4Spgoyette kernconfig_unlock();
173879d9beffSad
173979d9beffSad mutex_enter(&module_thread_lock);
174079d9beffSad (void)cv_timedwait(&module_thread_cv, &module_thread_lock,
174179d9beffSad module_thread_ticks);
174279d9beffSad module_thread_ticks = 0;
174379d9beffSad mutex_exit(&module_thread_lock);
174479d9beffSad }
174579d9beffSad }
174679d9beffSad
174779d9beffSad /*
174879d9beffSad * module_thread:
174979d9beffSad *
175079d9beffSad * Kick the module thread into action, perhaps because the
175179d9beffSad * system is low on memory.
175279d9beffSad */
175379d9beffSad void
module_thread_kick(void)175479d9beffSad module_thread_kick(void)
175579d9beffSad {
175679d9beffSad
175779d9beffSad mutex_enter(&module_thread_lock);
175879d9beffSad module_thread_ticks = hz;
175979d9beffSad cv_broadcast(&module_thread_cv);
176079d9beffSad mutex_exit(&module_thread_lock);
176179d9beffSad }
176231afc5b6Sad
176331afc5b6Sad #ifdef DDB
176431afc5b6Sad /*
176531afc5b6Sad * module_whatis:
176631afc5b6Sad *
176731afc5b6Sad * Helper routine for DDB.
176831afc5b6Sad */
176931afc5b6Sad void
module_whatis(uintptr_t addr,void (* pr)(const char *,...))177031afc5b6Sad module_whatis(uintptr_t addr, void (*pr)(const char *, ...))
177131afc5b6Sad {
177231afc5b6Sad module_t *mod;
177331afc5b6Sad size_t msize;
177431afc5b6Sad vaddr_t maddr;
177531afc5b6Sad
177631afc5b6Sad TAILQ_FOREACH(mod, &module_list, mod_chain) {
177727695c89Sad if (mod->mod_kobj == NULL) {
177827695c89Sad continue;
177927695c89Sad }
178061fa5bb9Sdyoung if (kobj_stat(mod->mod_kobj, &maddr, &msize) != 0)
178161fa5bb9Sdyoung continue;
178231afc5b6Sad if (addr < maddr || addr >= maddr + msize) {
178331afc5b6Sad continue;
178431afc5b6Sad }
178531afc5b6Sad (*pr)("%p is %p+%zu, in kernel module `%s'\n",
178631afc5b6Sad (void *)addr, (void *)maddr,
178731afc5b6Sad (size_t)(addr - maddr), mod->mod_info->mi_name);
178831afc5b6Sad }
178931afc5b6Sad }
179031afc5b6Sad
179131afc5b6Sad /*
179231afc5b6Sad * module_print_list:
179331afc5b6Sad *
179431afc5b6Sad * Helper routine for DDB.
179531afc5b6Sad */
179631afc5b6Sad void
module_print_list(void (* pr)(const char *,...))179731afc5b6Sad module_print_list(void (*pr)(const char *, ...))
179831afc5b6Sad {
179931afc5b6Sad const char *src;
180031afc5b6Sad module_t *mod;
180131afc5b6Sad size_t msize;
180231afc5b6Sad vaddr_t maddr;
180331afc5b6Sad
180431afc5b6Sad (*pr)("%16s %16s %8s %8s\n", "NAME", "TEXT/DATA", "SIZE", "SOURCE");
180531afc5b6Sad
180631afc5b6Sad TAILQ_FOREACH(mod, &module_list, mod_chain) {
180731afc5b6Sad switch (mod->mod_source) {
180831afc5b6Sad case MODULE_SOURCE_KERNEL:
180931afc5b6Sad src = "builtin";
181031afc5b6Sad break;
181131afc5b6Sad case MODULE_SOURCE_FILESYS:
181231afc5b6Sad src = "filesys";
181331afc5b6Sad break;
181431afc5b6Sad case MODULE_SOURCE_BOOT:
181531afc5b6Sad src = "boot";
181631afc5b6Sad break;
181731afc5b6Sad default:
181831afc5b6Sad src = "unknown";
181931afc5b6Sad break;
182031afc5b6Sad }
182161fa5bb9Sdyoung if (mod->mod_kobj == NULL) {
182227695c89Sad maddr = 0;
182327695c89Sad msize = 0;
182461fa5bb9Sdyoung } else if (kobj_stat(mod->mod_kobj, &maddr, &msize) != 0)
182561fa5bb9Sdyoung continue;
1826a5a0d685Sad (*pr)("%16s %16lx %8ld %8s\n", mod->mod_info->mi_name,
182731afc5b6Sad (long)maddr, (long)msize, src);
182831afc5b6Sad }
182931afc5b6Sad }
183031afc5b6Sad #endif /* DDB */
18311bdbe18dSjnemeth
183232b67097Sjnemeth static bool
module_merge_dicts(prop_dictionary_t existing_dict,const prop_dictionary_t new_dict)183332b67097Sjnemeth module_merge_dicts(prop_dictionary_t existing_dict,
183432b67097Sjnemeth const prop_dictionary_t new_dict)
183532b67097Sjnemeth {
183632b67097Sjnemeth prop_dictionary_keysym_t props_keysym;
183732b67097Sjnemeth prop_object_iterator_t props_iter;
183832b67097Sjnemeth prop_object_t props_obj;
183932b67097Sjnemeth const char *props_key;
184032b67097Sjnemeth bool error;
184132b67097Sjnemeth
184230d0592bSjnemeth if (new_dict == NULL) { /* nothing to merge */
184330d0592bSjnemeth return true;
184430d0592bSjnemeth }
184530d0592bSjnemeth
184632b67097Sjnemeth error = false;
184732b67097Sjnemeth props_iter = prop_dictionary_iterator(new_dict);
184832b67097Sjnemeth if (props_iter == NULL) {
184932b67097Sjnemeth return false;
185032b67097Sjnemeth }
185132b67097Sjnemeth
185232b67097Sjnemeth while ((props_obj = prop_object_iterator_next(props_iter)) != NULL) {
185332b67097Sjnemeth props_keysym = (prop_dictionary_keysym_t)props_obj;
185498fcc282Sthorpej props_key = prop_dictionary_keysym_value(props_keysym);
185532b67097Sjnemeth props_obj = prop_dictionary_get_keysym(new_dict, props_keysym);
185632b67097Sjnemeth if ((props_obj == NULL) || !prop_dictionary_set(existing_dict,
185732b67097Sjnemeth props_key, props_obj)) {
185832b67097Sjnemeth error = true;
185932b67097Sjnemeth goto out;
186032b67097Sjnemeth }
186132b67097Sjnemeth }
186232b67097Sjnemeth error = false;
186332b67097Sjnemeth
186432b67097Sjnemeth out:
186532b67097Sjnemeth prop_object_iterator_release(props_iter);
186632b67097Sjnemeth
186732b67097Sjnemeth return !error;
186832b67097Sjnemeth }
1869d5abe492Schs
1870d5abe492Schs /*
1871d5abe492Schs * module_specific_key_create:
1872d5abe492Schs *
1873d5abe492Schs * Create a key for subsystem module-specific data.
1874d5abe492Schs */
1875d5abe492Schs specificdata_key_t
module_specific_key_create(specificdata_key_t * keyp,specificdata_dtor_t dtor)1876d5abe492Schs module_specific_key_create(specificdata_key_t *keyp, specificdata_dtor_t dtor)
1877d5abe492Schs {
1878d5abe492Schs
1879d5abe492Schs return specificdata_key_create(module_specificdata_domain, keyp, dtor);
1880d5abe492Schs }
1881d5abe492Schs
1882d5abe492Schs /*
1883d5abe492Schs * module_specific_key_delete:
1884d5abe492Schs *
1885d5abe492Schs * Delete a key for subsystem module-specific data.
1886d5abe492Schs */
1887d5abe492Schs void
module_specific_key_delete(specificdata_key_t key)1888d5abe492Schs module_specific_key_delete(specificdata_key_t key)
1889d5abe492Schs {
1890d5abe492Schs
1891d5abe492Schs return specificdata_key_delete(module_specificdata_domain, key);
1892d5abe492Schs }
1893d5abe492Schs
1894d5abe492Schs /*
1895d5abe492Schs * module_getspecific:
1896d5abe492Schs *
1897d5abe492Schs * Return module-specific data corresponding to the specified key.
1898d5abe492Schs */
1899d5abe492Schs void *
module_getspecific(module_t * mod,specificdata_key_t key)1900d5abe492Schs module_getspecific(module_t *mod, specificdata_key_t key)
1901d5abe492Schs {
1902d5abe492Schs
1903d5abe492Schs return specificdata_getspecific(module_specificdata_domain,
1904d5abe492Schs &mod->mod_sdref, key);
1905d5abe492Schs }
1906d5abe492Schs
1907d5abe492Schs /*
1908d5abe492Schs * module_setspecific:
1909d5abe492Schs *
1910d5abe492Schs * Set module-specific data corresponding to the specified key.
1911d5abe492Schs */
1912d5abe492Schs void
module_setspecific(module_t * mod,specificdata_key_t key,void * data)1913d5abe492Schs module_setspecific(module_t *mod, specificdata_key_t key, void *data)
1914d5abe492Schs {
1915d5abe492Schs
1916d5abe492Schs specificdata_setspecific(module_specificdata_domain,
1917d5abe492Schs &mod->mod_sdref, key, data);
1918d5abe492Schs }
1919d5abe492Schs
1920d5abe492Schs /*
1921d5abe492Schs * module_register_callbacks:
1922d5abe492Schs *
1923d5abe492Schs * Register a new set of callbacks to be called on module load/unload.
1924d5abe492Schs * Call the load callback on each existing module.
1925d5abe492Schs * Return an opaque handle for unregistering these later.
1926d5abe492Schs */
1927d5abe492Schs void *
module_register_callbacks(void (* load)(struct module *),void (* unload)(struct module *))1928d5abe492Schs module_register_callbacks(void (*load)(struct module *),
1929d5abe492Schs void (*unload)(struct module *))
1930d5abe492Schs {
1931d5abe492Schs struct module_callbacks *modcb;
1932d5abe492Schs struct module *mod;
1933d5abe492Schs
1934d5abe492Schs modcb = kmem_alloc(sizeof(*modcb), KM_SLEEP);
1935d5abe492Schs modcb->modcb_load = load;
1936d5abe492Schs modcb->modcb_unload = unload;
1937d5abe492Schs
1938d5abe492Schs kernconfig_lock();
1939d5abe492Schs TAILQ_INSERT_TAIL(&modcblist, modcb, modcb_list);
194001096732Sriastradh TAILQ_FOREACH_REVERSE(mod, &module_list, modlist, mod_chain)
1941d5abe492Schs load(mod);
1942d5abe492Schs kernconfig_unlock();
1943d5abe492Schs
1944d5abe492Schs return modcb;
1945d5abe492Schs }
1946d5abe492Schs
1947d5abe492Schs /*
1948d5abe492Schs * module_unregister_callbacks:
1949d5abe492Schs *
1950d5abe492Schs * Unregister a previously-registered set of module load/unload callbacks.
1951d5abe492Schs * Call the unload callback on each existing module.
1952d5abe492Schs */
1953d5abe492Schs void
module_unregister_callbacks(void * opaque)1954d5abe492Schs module_unregister_callbacks(void *opaque)
1955d5abe492Schs {
1956d5abe492Schs struct module_callbacks *modcb;
1957d5abe492Schs struct module *mod;
1958d5abe492Schs
1959d5abe492Schs modcb = opaque;
1960d5abe492Schs kernconfig_lock();
1961d5abe492Schs TAILQ_FOREACH(mod, &module_list, mod_chain)
1962d5abe492Schs modcb->modcb_unload(mod);
1963d5abe492Schs TAILQ_REMOVE(&modcblist, modcb, modcb_list);
1964d5abe492Schs kernconfig_unlock();
1965d5abe492Schs kmem_free(modcb, sizeof(*modcb));
1966d5abe492Schs }
1967d5abe492Schs
1968d5abe492Schs /*
1969d5abe492Schs * module_callback_load:
1970d5abe492Schs *
1971d5abe492Schs * Helper routine: call all load callbacks on a module being loaded.
1972d5abe492Schs */
1973d5abe492Schs static void
module_callback_load(struct module * mod)1974d5abe492Schs module_callback_load(struct module *mod)
1975d5abe492Schs {
1976d5abe492Schs struct module_callbacks *modcb;
1977d5abe492Schs
1978d5abe492Schs TAILQ_FOREACH(modcb, &modcblist, modcb_list) {
1979d5abe492Schs modcb->modcb_load(mod);
1980d5abe492Schs }
1981d5abe492Schs }
1982d5abe492Schs
1983d5abe492Schs /*
1984d5abe492Schs * module_callback_unload:
1985d5abe492Schs *
1986d5abe492Schs * Helper routine: call all unload callbacks on a module being unloaded.
1987d5abe492Schs */
1988d5abe492Schs static void
module_callback_unload(struct module * mod)1989d5abe492Schs module_callback_unload(struct module *mod)
1990d5abe492Schs {
1991d5abe492Schs struct module_callbacks *modcb;
1992d5abe492Schs
1993d5abe492Schs TAILQ_FOREACH(modcb, &modcblist, modcb_list) {
1994d5abe492Schs modcb->modcb_unload(mod);
1995d5abe492Schs }
1996d5abe492Schs }
1997