| @@ -1,1611 +1,1611 @@ | | | @@ -1,1611 +1,1611 @@ |
1 | /* $NetBSD: kern_module.c,v 1.130.2.2 2018/03/11 07:25:59 pgoyette Exp $ */ | | 1 | /* $NetBSD: kern_module.c,v 1.130.2.3 2018/03/11 08:32:21 pgoyette Exp $ */ |
2 | | | 2 | |
3 | /*- | | 3 | /*- |
4 | * Copyright (c) 2008 The NetBSD Foundation, Inc. | | 4 | * Copyright (c) 2008 The NetBSD Foundation, Inc. |
5 | * All rights reserved. | | 5 | * All rights reserved. |
6 | * | | 6 | * |
7 | * This code is derived from software developed for The NetBSD Foundation | | 7 | * This code is derived from software developed for The NetBSD Foundation |
8 | * by Andrew Doran. | | 8 | * by Andrew Doran. |
9 | * | | 9 | * |
10 | * Redistribution and use in source and binary forms, with or without | | 10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions | | 11 | * modification, are permitted provided that the following conditions |
12 | * are met: | | 12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright | | 13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. | | 14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright | | 15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the | | 16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. | | 17 | * documentation and/or other materials provided with the distribution. |
18 | * | | 18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS | | 19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED | | 20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | | 21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS | | 22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | | 23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | | 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | | 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | | 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | | 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | | 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. | | 29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ | | 30 | */ |
31 | | | 31 | |
32 | /* | | 32 | /* |
33 | * Kernel module support. | | 33 | * Kernel module support. |
34 | */ | | 34 | */ |
35 | | | 35 | |
36 | #include <sys/cdefs.h> | | 36 | #include <sys/cdefs.h> |
37 | __KERNEL_RCSID(0, "$NetBSD: kern_module.c,v 1.130.2.2 2018/03/11 07:25:59 pgoyette Exp $"); | | 37 | __KERNEL_RCSID(0, "$NetBSD: kern_module.c,v 1.130.2.3 2018/03/11 08:32:21 pgoyette Exp $"); |
38 | | | 38 | |
39 | #define _MODULE_INTERNAL | | 39 | #define _MODULE_INTERNAL |
40 | | | 40 | |
41 | #ifdef _KERNEL_OPT | | 41 | #ifdef _KERNEL_OPT |
42 | #include "opt_ddb.h" | | 42 | #include "opt_ddb.h" |
43 | #include "opt_modular.h" | | 43 | #include "opt_modular.h" |
44 | #endif | | 44 | #endif |
45 | | | 45 | |
46 | #include <sys/param.h> | | 46 | #include <sys/param.h> |
47 | #include <sys/systm.h> | | 47 | #include <sys/systm.h> |
48 | #include <sys/kernel.h> | | 48 | #include <sys/kernel.h> |
49 | #include <sys/proc.h> | | 49 | #include <sys/proc.h> |
50 | #include <sys/kauth.h> | | 50 | #include <sys/kauth.h> |
51 | #include <sys/kobj.h> | | 51 | #include <sys/kobj.h> |
52 | #include <sys/kmem.h> | | 52 | #include <sys/kmem.h> |
53 | #include <sys/module.h> | | 53 | #include <sys/module.h> |
54 | #include <sys/kthread.h> | | 54 | #include <sys/kthread.h> |
55 | #include <sys/sysctl.h> | | 55 | #include <sys/sysctl.h> |
56 | #include <sys/lock.h> | | 56 | #include <sys/lock.h> |
57 | | | 57 | |
58 | #include <uvm/uvm_extern.h> | | 58 | #include <uvm/uvm_extern.h> |
59 | | | 59 | |
60 | struct vm_map *module_map; | | 60 | struct vm_map *module_map; |
61 | const char *module_machine; | | 61 | const char *module_machine; |
62 | char module_base[MODULE_BASE_SIZE]; | | 62 | char module_base[MODULE_BASE_SIZE]; |
63 | | | 63 | |
64 | struct modlist module_list = TAILQ_HEAD_INITIALIZER(module_list); | | 64 | struct modlist module_list = TAILQ_HEAD_INITIALIZER(module_list); |
65 | struct modlist module_builtins = TAILQ_HEAD_INITIALIZER(module_builtins); | | 65 | struct modlist module_builtins = TAILQ_HEAD_INITIALIZER(module_builtins); |
66 | static struct modlist module_bootlist = TAILQ_HEAD_INITIALIZER(module_bootlist); | | 66 | static struct modlist module_bootlist = TAILQ_HEAD_INITIALIZER(module_bootlist); |
67 | | | 67 | |
68 | static module_t *module_active; | | 68 | static module_t *module_active; |
69 | bool module_verbose_on; | | 69 | bool module_verbose_on; |
70 | #ifdef MODULAR_DEFAULT_AUTOLOAD | | 70 | #ifdef MODULAR_DEFAULT_AUTOLOAD |
71 | bool module_autoload_on = true; | | 71 | bool module_autoload_on = true; |
72 | #else | | 72 | #else |
73 | bool module_autoload_on = false; | | 73 | bool module_autoload_on = false; |
74 | #endif | | 74 | #endif |
75 | u_int module_count; | | 75 | u_int module_count; |
76 | u_int module_builtinlist; | | 76 | u_int module_builtinlist; |
77 | u_int module_autotime = 10; | | 77 | u_int module_autotime = 10; |
78 | u_int module_gen = 1; | | 78 | u_int module_gen = 1; |
79 | static kcondvar_t module_thread_cv; | | 79 | static kcondvar_t module_thread_cv; |
80 | static kmutex_t module_thread_lock; | | 80 | static kmutex_t module_thread_lock; |
81 | static int module_thread_ticks; | | 81 | static int module_thread_ticks; |
82 | int (*module_load_vfs_vec)(const char *, int, bool, module_t *, | | 82 | int (*module_load_vfs_vec)(const char *, int, bool, module_t *, |
83 | prop_dictionary_t *) = (void *)eopnotsupp; | | 83 | prop_dictionary_t *) = (void *)eopnotsupp; |
84 | | | 84 | |
85 | static kauth_listener_t module_listener; | | 85 | static kauth_listener_t module_listener; |
86 | | | 86 | |
87 | /* Ensure that the kernel's link set isn't empty. */ | | 87 | /* Ensure that the kernel's link set isn't empty. */ |
88 | static modinfo_t module_dummy; | | 88 | static modinfo_t module_dummy; |
89 | __link_set_add_rodata(modules, module_dummy); | | 89 | __link_set_add_rodata(modules, module_dummy); |
90 | | | 90 | |
91 | static module_t *module_newmodule(modsrc_t); | | 91 | static module_t *module_newmodule(modsrc_t); |
92 | static void module_require_force(module_t *); | | 92 | static void module_require_force(module_t *); |
93 | static int module_do_load(const char *, bool, int, prop_dictionary_t, | | 93 | static int module_do_load(const char *, bool, int, prop_dictionary_t, |
94 | module_t **, modclass_t modclass, bool); | | 94 | module_t **, modclass_t modclass, bool); |
95 | static int module_do_unload(const char *, bool); | | 95 | static int module_do_unload(const char *, bool); |
96 | static int module_do_builtin(const module_t *, const char *, module_t **, | | 96 | static int module_do_builtin(const module_t *, const char *, module_t **, |
97 | prop_dictionary_t); | | 97 | prop_dictionary_t); |
98 | static int module_fetch_info(module_t *); | | 98 | static int module_fetch_info(module_t *); |
99 | static void module_thread(void *); | | 99 | static void module_thread(void *); |
100 | | | 100 | |
101 | static module_t *module_lookup(const char *); | | 101 | static module_t *module_lookup(const char *); |
102 | int module_alias_lookup(const char *, module_t *); | | 102 | int module_alias_lookup(const char *, module_t *); |
103 | static void module_enqueue(module_t *); | | 103 | static void module_enqueue(module_t *); |
104 | | | 104 | |
105 | static bool module_merge_dicts(prop_dictionary_t, const prop_dictionary_t); | | 105 | static bool module_merge_dicts(prop_dictionary_t, const prop_dictionary_t); |
106 | | | 106 | |
107 | static void sysctl_module_setup(void); | | 107 | static void sysctl_module_setup(void); |
108 | static int sysctl_module_autotime(SYSCTLFN_PROTO); | | 108 | static int sysctl_module_autotime(SYSCTLFN_PROTO); |
109 | | | 109 | |
110 | #define MODULE_CLASS_MATCH(mi, modclass) \ | | 110 | #define MODULE_CLASS_MATCH(mi, modclass) \ |
111 | ((modclass) == MODULE_CLASS_ANY || (modclass) == (mi)->mi_class) | | 111 | ((modclass) == MODULE_CLASS_ANY || (modclass) == (mi)->mi_class) |
112 | | | 112 | |
113 | static void | | 113 | static void |
114 | module_incompat(const modinfo_t *mi, int modclass) | | 114 | module_incompat(const modinfo_t *mi, int modclass) |
115 | { | | 115 | { |
116 | module_error("incompatible module class for `%s' (%d != %d)", | | 116 | module_error("incompatible module class for `%s' (%d != %d)", |
117 | mi->mi_name, modclass, mi->mi_class); | | 117 | mi->mi_name, modclass, mi->mi_class); |
118 | } | | 118 | } |
119 | | | 119 | |
120 | /* | | 120 | /* |
121 | * module_error: | | 121 | * module_error: |
122 | * | | 122 | * |
123 | * Utility function: log an error. | | 123 | * Utility function: log an error. |
124 | */ | | 124 | */ |
125 | void | | 125 | void |
126 | module_error(const char *fmt, ...) | | 126 | module_error(const char *fmt, ...) |
127 | { | | 127 | { |
128 | va_list ap; | | 128 | va_list ap; |
129 | | | 129 | |
130 | va_start(ap, fmt); | | 130 | va_start(ap, fmt); |
131 | printf("WARNING: module error: "); | | 131 | printf("WARNING: module error: "); |
132 | vprintf(fmt, ap); | | 132 | vprintf(fmt, ap); |
133 | printf("\n"); | | 133 | printf("\n"); |
134 | va_end(ap); | | 134 | va_end(ap); |
135 | } | | 135 | } |
136 | | | 136 | |
137 | /* | | 137 | /* |
138 | * module_print: | | 138 | * module_print: |
139 | * | | 139 | * |
140 | * Utility function: log verbose output. | | 140 | * Utility function: log verbose output. |
141 | */ | | 141 | */ |
142 | void | | 142 | void |
143 | module_print(const char *fmt, ...) | | 143 | module_print(const char *fmt, ...) |
144 | { | | 144 | { |
145 | va_list ap; | | 145 | va_list ap; |
146 | | | 146 | |
147 | if (module_verbose_on) { | | 147 | if (module_verbose_on) { |
148 | va_start(ap, fmt); | | 148 | va_start(ap, fmt); |
149 | printf("DEBUG: module: "); | | 149 | printf("DEBUG: module: "); |
150 | vprintf(fmt, ap); | | 150 | vprintf(fmt, ap); |
151 | printf("\n"); | | 151 | printf("\n"); |
152 | va_end(ap); | | 152 | va_end(ap); |
153 | } | | 153 | } |
154 | } | | 154 | } |
155 | | | 155 | |
156 | static int | | 156 | static int |
157 | module_listener_cb(kauth_cred_t cred, kauth_action_t action, void *cookie, | | 157 | module_listener_cb(kauth_cred_t cred, kauth_action_t action, void *cookie, |
158 | void *arg0, void *arg1, void *arg2, void *arg3) | | 158 | void *arg0, void *arg1, void *arg2, void *arg3) |
159 | { | | 159 | { |
160 | int result; | | 160 | int result; |
161 | | | 161 | |
162 | result = KAUTH_RESULT_DEFER; | | 162 | result = KAUTH_RESULT_DEFER; |
163 | | | 163 | |
164 | if (action != KAUTH_SYSTEM_MODULE) | | 164 | if (action != KAUTH_SYSTEM_MODULE) |
165 | return result; | | 165 | return result; |
166 | | | 166 | |
167 | if ((uintptr_t)arg2 != 0) /* autoload */ | | 167 | if ((uintptr_t)arg2 != 0) /* autoload */ |
168 | result = KAUTH_RESULT_ALLOW; | | 168 | result = KAUTH_RESULT_ALLOW; |
169 | | | 169 | |
170 | return result; | | 170 | return result; |
171 | } | | 171 | } |
172 | | | 172 | |
173 | /* | | 173 | /* |
174 | * Allocate a new module_t | | 174 | * Allocate a new module_t |
175 | */ | | 175 | */ |
176 | static module_t * | | 176 | static module_t * |
177 | module_newmodule(modsrc_t source) | | 177 | module_newmodule(modsrc_t source) |
178 | { | | 178 | { |
179 | module_t *mod; | | 179 | module_t *mod; |
180 | | | 180 | |
181 | mod = kmem_zalloc(sizeof(*mod), KM_SLEEP); | | 181 | mod = kmem_zalloc(sizeof(*mod), KM_SLEEP); |
182 | mod->mod_source = source; | | 182 | mod->mod_source = source; |
183 | mod->mod_info = NULL; | | 183 | mod->mod_info = NULL; |
184 | mod->mod_flags = 0; | | 184 | mod->mod_flags = 0; |
185 | return mod; | | 185 | return mod; |
186 | } | | 186 | } |
187 | | | 187 | |
188 | /* | | 188 | /* |
189 | * Require the -f (force) flag to load a module | | 189 | * Require the -f (force) flag to load a module |
190 | */ | | 190 | */ |
191 | static void | | 191 | static void |
192 | module_require_force(struct module *mod) | | 192 | module_require_force(struct module *mod) |
193 | { | | 193 | { |
194 | mod->mod_flags |= MODFLG_MUST_FORCE; | | 194 | mod->mod_flags |= MODFLG_MUST_FORCE; |
195 | } | | 195 | } |
196 | | | 196 | |
197 | /* | | 197 | /* |
198 | * Add modules to the builtin list. This can done at boottime or | | 198 | * Add modules to the builtin list. This can done at boottime or |
199 | * at runtime if the module is linked into the kernel with an | | 199 | * at runtime if the module is linked into the kernel with an |
200 | * external linker. All or none of the input will be handled. | | 200 | * external linker. All or none of the input will be handled. |
201 | * Optionally, the modules can be initialized. If they are not | | 201 | * Optionally, the modules can be initialized. If they are not |
202 | * initialized, module_init_class() or module_load() can be used | | 202 | * initialized, module_init_class() or module_load() can be used |
203 | * later, but these are not guaranteed to give atomic results. | | 203 | * later, but these are not guaranteed to give atomic results. |
204 | */ | | 204 | */ |
205 | int | | 205 | int |
206 | module_builtin_add(modinfo_t *const *mip, size_t nmodinfo, bool init) | | 206 | module_builtin_add(modinfo_t *const *mip, size_t nmodinfo, bool init) |
207 | { | | 207 | { |
208 | struct module **modp = NULL, *mod_iter; | | 208 | struct module **modp = NULL, *mod_iter; |
209 | int rv = 0, i, mipskip; | | 209 | int rv = 0, i, mipskip; |
210 | | | 210 | |
211 | if (init) { | | 211 | if (init) { |
212 | rv = kauth_authorize_system(kauth_cred_get(), | | 212 | rv = kauth_authorize_system(kauth_cred_get(), |
213 | KAUTH_SYSTEM_MODULE, 0, (void *)(uintptr_t)MODCTL_LOAD, | | 213 | KAUTH_SYSTEM_MODULE, 0, (void *)(uintptr_t)MODCTL_LOAD, |
214 | (void *)(uintptr_t)1, NULL); | | 214 | (void *)(uintptr_t)1, NULL); |
215 | if (rv) { | | 215 | if (rv) { |
216 | return rv; | | 216 | return rv; |
217 | } | | 217 | } |
218 | } | | 218 | } |
219 | | | 219 | |
220 | for (i = 0, mipskip = 0; i < nmodinfo; i++) { | | 220 | for (i = 0, mipskip = 0; i < nmodinfo; i++) { |
221 | if (mip[i] == &module_dummy) { | | 221 | if (mip[i] == &module_dummy) { |
222 | KASSERT(nmodinfo > 0); | | 222 | KASSERT(nmodinfo > 0); |
223 | nmodinfo--; | | 223 | nmodinfo--; |
224 | } | | 224 | } |
225 | } | | 225 | } |
226 | if (nmodinfo == 0) | | 226 | if (nmodinfo == 0) |
227 | return 0; | | 227 | return 0; |
228 | | | 228 | |
229 | modp = kmem_zalloc(sizeof(*modp) * nmodinfo, KM_SLEEP); | | 229 | modp = kmem_zalloc(sizeof(*modp) * nmodinfo, KM_SLEEP); |
230 | for (i = 0, mipskip = 0; i < nmodinfo; i++) { | | 230 | for (i = 0, mipskip = 0; i < nmodinfo; i++) { |
231 | if (mip[i+mipskip] == &module_dummy) { | | 231 | if (mip[i+mipskip] == &module_dummy) { |
232 | mipskip++; | | 232 | mipskip++; |
233 | continue; | | 233 | continue; |
234 | } | | 234 | } |
235 | modp[i] = module_newmodule(MODULE_SOURCE_KERNEL); | | 235 | modp[i] = module_newmodule(MODULE_SOURCE_KERNEL); |
236 | modp[i]->mod_info = mip[i+mipskip]; | | 236 | modp[i]->mod_info = mip[i+mipskip]; |
237 | } | | 237 | } |
238 | kernconfig_lock(); | | 238 | kernconfig_lock(); |
239 | | | 239 | |
240 | /* do this in three stages for error recovery and atomicity */ | | 240 | /* do this in three stages for error recovery and atomicity */ |
241 | | | 241 | |
242 | /* first check for presence */ | | 242 | /* first check for presence */ |
243 | for (i = 0; i < nmodinfo; i++) { | | 243 | for (i = 0; i < nmodinfo; i++) { |
244 | TAILQ_FOREACH(mod_iter, &module_builtins, mod_chain) { | | 244 | TAILQ_FOREACH(mod_iter, &module_builtins, mod_chain) { |
245 | if (strcmp(mod_iter->mod_info->mi_name, | | 245 | if (strcmp(mod_iter->mod_info->mi_name, |
246 | modp[i]->mod_info->mi_name) == 0) | | 246 | modp[i]->mod_info->mi_name) == 0) |
247 | break; | | 247 | break; |
248 | } | | 248 | } |
249 | if (mod_iter) { | | 249 | if (mod_iter) { |
250 | rv = EEXIST; | | 250 | rv = EEXIST; |
251 | goto out; | | 251 | goto out; |
252 | } | | 252 | } |
253 | | | 253 | |
254 | if (module_lookup(modp[i]->mod_info->mi_name) != NULL) { | | 254 | if (module_lookup(modp[i]->mod_info->mi_name) != NULL) { |
255 | rv = EEXIST; | | 255 | rv = EEXIST; |
256 | goto out; | | 256 | goto out; |
257 | } | | 257 | } |
258 | } | | 258 | } |
259 | | | 259 | |
260 | /* then add to list */ | | 260 | /* then add to list */ |
261 | for (i = 0; i < nmodinfo; i++) { | | 261 | for (i = 0; i < nmodinfo; i++) { |
262 | TAILQ_INSERT_TAIL(&module_builtins, modp[i], mod_chain); | | 262 | TAILQ_INSERT_TAIL(&module_builtins, modp[i], mod_chain); |
263 | module_builtinlist++; | | 263 | module_builtinlist++; |
264 | } | | 264 | } |
265 | | | 265 | |
266 | /* finally, init (if required) */ | | 266 | /* finally, init (if required) */ |
267 | if (init) { | | 267 | if (init) { |
268 | for (i = 0; i < nmodinfo; i++) { | | 268 | for (i = 0; i < nmodinfo; i++) { |
269 | rv = module_do_builtin(modp[i], | | 269 | rv = module_do_builtin(modp[i], |
270 | modp[i]->mod_info->mi_name, NULL, NULL); | | 270 | modp[i]->mod_info->mi_name, NULL, NULL); |
271 | /* throw in the towel, recovery hard & not worth it */ | | 271 | /* throw in the towel, recovery hard & not worth it */ |
272 | if (rv) | | 272 | if (rv) |
273 | panic("%s: builtin module \"%s\" init failed:" | | 273 | panic("%s: builtin module \"%s\" init failed:" |
274 | " %d", __func__, | | 274 | " %d", __func__, |
275 | modp[i]->mod_info->mi_name, rv); | | 275 | modp[i]->mod_info->mi_name, rv); |
276 | } | | 276 | } |
277 | } | | 277 | } |
278 | | | 278 | |
279 | out: | | 279 | out: |
280 | kernconfig_unlock(); | | 280 | kernconfig_unlock(); |
281 | if (rv != 0) { | | 281 | if (rv != 0) { |
282 | for (i = 0; i < nmodinfo; i++) { | | 282 | for (i = 0; i < nmodinfo; i++) { |
283 | if (modp[i]) | | 283 | if (modp[i]) |
284 | kmem_free(modp[i], sizeof(*modp[i])); | | 284 | kmem_free(modp[i], sizeof(*modp[i])); |
285 | } | | 285 | } |
286 | } | | 286 | } |
287 | kmem_free(modp, sizeof(*modp) * nmodinfo); | | 287 | kmem_free(modp, sizeof(*modp) * nmodinfo); |
288 | return rv; | | 288 | return rv; |
289 | } | | 289 | } |
290 | | | 290 | |
291 | /* | | 291 | /* |
292 | * Optionally fini and remove builtin module from the kernel. | | 292 | * Optionally fini and remove builtin module from the kernel. |
293 | * Note: the module will now be unreachable except via mi && builtin_add. | | 293 | * Note: the module will now be unreachable except via mi && builtin_add. |
294 | */ | | 294 | */ |
295 | int | | 295 | int |
296 | module_builtin_remove(modinfo_t *mi, bool fini) | | 296 | module_builtin_remove(modinfo_t *mi, bool fini) |
297 | { | | 297 | { |
298 | struct module *mod; | | 298 | struct module *mod; |
299 | int rv = 0; | | 299 | int rv = 0; |
300 | | | 300 | |
301 | if (fini) { | | 301 | if (fini) { |
302 | rv = kauth_authorize_system(kauth_cred_get(), | | 302 | rv = kauth_authorize_system(kauth_cred_get(), |
303 | KAUTH_SYSTEM_MODULE, 0, (void *)(uintptr_t)MODCTL_UNLOAD, | | 303 | KAUTH_SYSTEM_MODULE, 0, (void *)(uintptr_t)MODCTL_UNLOAD, |
304 | NULL, NULL); | | 304 | NULL, NULL); |
305 | if (rv) | | 305 | if (rv) |
306 | return rv; | | 306 | return rv; |
307 | | | 307 | |
308 | kernconfig_lock(); | | 308 | kernconfig_lock(); |
309 | rv = module_do_unload(mi->mi_name, true); | | 309 | rv = module_do_unload(mi->mi_name, true); |
310 | if (rv) { | | 310 | if (rv) { |
311 | goto out; | | 311 | goto out; |
312 | } | | 312 | } |
313 | } else { | | 313 | } else { |
314 | kernconfig_lock(); | | 314 | kernconfig_lock(); |
315 | } | | 315 | } |
316 | TAILQ_FOREACH(mod, &module_builtins, mod_chain) { | | 316 | TAILQ_FOREACH(mod, &module_builtins, mod_chain) { |
317 | if (strcmp(mod->mod_info->mi_name, mi->mi_name) == 0) | | 317 | if (strcmp(mod->mod_info->mi_name, mi->mi_name) == 0) |
318 | break; | | 318 | break; |
319 | } | | 319 | } |
320 | if (mod) { | | 320 | if (mod) { |
321 | TAILQ_REMOVE(&module_builtins, mod, mod_chain); | | 321 | TAILQ_REMOVE(&module_builtins, mod, mod_chain); |
322 | module_builtinlist--; | | 322 | module_builtinlist--; |
323 | } else { | | 323 | } else { |
324 | KASSERT(fini == false); | | 324 | KASSERT(fini == false); |
325 | rv = ENOENT; | | 325 | rv = ENOENT; |
326 | } | | 326 | } |
327 | | | 327 | |
328 | out: | | 328 | out: |
329 | kernconfig_unlock(); | | 329 | kernconfig_unlock(); |
330 | return rv; | | 330 | return rv; |
331 | } | | 331 | } |
332 | | | 332 | |
333 | /* | | 333 | /* |
334 | * module_init: | | 334 | * module_init: |
335 | * | | 335 | * |
336 | * Initialize the module subsystem. | | 336 | * Initialize the module subsystem. |
337 | */ | | 337 | */ |
338 | void | | 338 | void |
339 | module_init(void) | | 339 | module_init(void) |
340 | { | | 340 | { |
341 | __link_set_decl(modules, modinfo_t); | | 341 | __link_set_decl(modules, modinfo_t); |
342 | extern struct vm_map *module_map; | | 342 | extern struct vm_map *module_map; |
343 | modinfo_t *const *mip; | | 343 | modinfo_t *const *mip; |
344 | int rv; | | 344 | int rv; |
345 | | | 345 | |
346 | if (module_map == NULL) { | | 346 | if (module_map == NULL) { |
347 | module_map = kernel_map; | | 347 | module_map = kernel_map; |
348 | } | | 348 | } |
349 | cv_init(&module_thread_cv, "mod_unld"); | | 349 | cv_init(&module_thread_cv, "mod_unld"); |
350 | mutex_init(&module_thread_lock, MUTEX_DEFAULT, IPL_NONE); | | 350 | mutex_init(&module_thread_lock, MUTEX_DEFAULT, IPL_NONE); |
351 | | | 351 | |
352 | #ifdef MODULAR /* XXX */ | | 352 | #ifdef MODULAR /* XXX */ |
353 | module_init_md(); | | 353 | module_init_md(); |
354 | #endif | | 354 | #endif |
355 | | | 355 | |
356 | if (!module_machine) | | 356 | if (!module_machine) |
357 | module_machine = machine; | | 357 | module_machine = machine; |
358 | #if __NetBSD_Version__ / 1000000 % 100 == 99 /* -current */ | | 358 | #if __NetBSD_Version__ / 1000000 % 100 == 99 /* -current */ |
359 | snprintf(module_base, sizeof(module_base), "/stand/%s/%s/modules", | | 359 | snprintf(module_base, sizeof(module_base), "/stand/%s/%s/modules", |
360 | module_machine, osrelease); | | 360 | module_machine, osrelease); |
361 | #else /* release */ | | 361 | #else /* release */ |
362 | snprintf(module_base, sizeof(module_base), "/stand/%s/%d.%d/modules", | | 362 | snprintf(module_base, sizeof(module_base), "/stand/%s/%d.%d/modules", |
363 | module_machine, __NetBSD_Version__ / 100000000, | | 363 | module_machine, __NetBSD_Version__ / 100000000, |
364 | __NetBSD_Version__ / 1000000 % 100); | | 364 | __NetBSD_Version__ / 1000000 % 100); |
365 | #endif | | 365 | #endif |
366 | | | 366 | |
367 | module_listener = kauth_listen_scope(KAUTH_SCOPE_SYSTEM, | | 367 | module_listener = kauth_listen_scope(KAUTH_SCOPE_SYSTEM, |
368 | module_listener_cb, NULL); | | 368 | module_listener_cb, NULL); |
369 | | | 369 | |
370 | __link_set_foreach(mip, modules) { | | 370 | __link_set_foreach(mip, modules) { |
371 | if ((rv = module_builtin_add(mip, 1, false)) != 0) | | 371 | if ((rv = module_builtin_add(mip, 1, false)) != 0) |
372 | module_error("builtin %s failed: %d\n", | | 372 | module_error("builtin %s failed: %d\n", |
373 | (*mip)->mi_name, rv); | | 373 | (*mip)->mi_name, rv); |
374 | } | | 374 | } |
375 | | | 375 | |
376 | sysctl_module_setup(); | | 376 | sysctl_module_setup(); |
377 | } | | 377 | } |
378 | | | 378 | |
379 | /* | | 379 | /* |
380 | * module_start_unload_thread: | | 380 | * module_start_unload_thread: |
381 | * | | 381 | * |
382 | * Start the auto unload kthread. | | 382 | * Start the auto unload kthread. |
383 | */ | | 383 | */ |
384 | void | | 384 | void |
385 | module_start_unload_thread(void) | | 385 | module_start_unload_thread(void) |
386 | { | | 386 | { |
387 | int error; | | 387 | int error; |
388 | | | 388 | |
389 | error = kthread_create(PRI_VM, KTHREAD_MPSAFE, NULL, module_thread, | | 389 | error = kthread_create(PRI_VM, KTHREAD_MPSAFE, NULL, module_thread, |
390 | NULL, NULL, "modunload"); | | 390 | NULL, NULL, "modunload"); |
391 | if (error != 0) | | 391 | if (error != 0) |
392 | panic("%s: %d", __func__, error); | | 392 | panic("%s: %d", __func__, error); |
393 | } | | 393 | } |
394 | | | 394 | |
395 | /* | | 395 | /* |
396 | * module_builtin_require_force | | 396 | * module_builtin_require_force |
397 | * | | 397 | * |
398 | * Require MODCTL_MUST_FORCE to load any built-in modules that have | | 398 | * Require MODCTL_MUST_FORCE to load any built-in modules that have |
399 | * not yet been initialized | | 399 | * not yet been initialized |
400 | */ | | 400 | */ |
401 | void | | 401 | void |
402 | module_builtin_require_force(void) | | 402 | module_builtin_require_force(void) |
403 | { | | 403 | { |
404 | module_t *mod; | | 404 | module_t *mod; |
405 | | | 405 | |
406 | kernconfig_lock(); | | 406 | kernconfig_lock(); |
407 | TAILQ_FOREACH(mod, &module_builtins, mod_chain) { | | 407 | TAILQ_FOREACH(mod, &module_builtins, mod_chain) { |
408 | module_require_force(mod); | | 408 | module_require_force(mod); |
409 | } | | 409 | } |
410 | kernconfig_unlock(); | | 410 | kernconfig_unlock(); |
411 | } | | 411 | } |
412 | | | 412 | |
413 | static struct sysctllog *module_sysctllog; | | 413 | static struct sysctllog *module_sysctllog; |
414 | | | 414 | |
415 | static int | | 415 | static int |
416 | sysctl_module_autotime(SYSCTLFN_ARGS) | | 416 | sysctl_module_autotime(SYSCTLFN_ARGS) |
417 | { | | 417 | { |
418 | struct sysctlnode node; | | 418 | struct sysctlnode node; |
419 | int t, error; | | 419 | int t, error; |
420 | | | 420 | |
421 | t = *(int *)rnode->sysctl_data; | | 421 | t = *(int *)rnode->sysctl_data; |
422 | | | 422 | |
423 | node = *rnode; | | 423 | node = *rnode; |
424 | node.sysctl_data = &t; | | 424 | node.sysctl_data = &t; |
425 | error = sysctl_lookup(SYSCTLFN_CALL(&node)); | | 425 | error = sysctl_lookup(SYSCTLFN_CALL(&node)); |
426 | if (error || newp == NULL) | | 426 | if (error || newp == NULL) |
427 | return (error); | | 427 | return (error); |
428 | | | 428 | |
429 | if (t < 0) | | 429 | if (t < 0) |
430 | return (EINVAL); | | 430 | return (EINVAL); |
431 | | | 431 | |
432 | *(int *)rnode->sysctl_data = t; | | 432 | *(int *)rnode->sysctl_data = t; |
433 | return (0); | | 433 | return (0); |
434 | } | | 434 | } |
435 | | | 435 | |
436 | static void | | 436 | static void |
437 | sysctl_module_setup(void) | | 437 | sysctl_module_setup(void) |
438 | { | | 438 | { |
439 | const struct sysctlnode *node = NULL; | | 439 | const struct sysctlnode *node = NULL; |
440 | | | 440 | |
441 | sysctl_createv(&module_sysctllog, 0, NULL, &node, | | 441 | sysctl_createv(&module_sysctllog, 0, NULL, &node, |
442 | CTLFLAG_PERMANENT, | | 442 | CTLFLAG_PERMANENT, |
443 | CTLTYPE_NODE, "module", | | 443 | CTLTYPE_NODE, "module", |
444 | SYSCTL_DESCR("Module options"), | | 444 | SYSCTL_DESCR("Module options"), |
445 | NULL, 0, NULL, 0, | | 445 | NULL, 0, NULL, 0, |
446 | CTL_KERN, CTL_CREATE, CTL_EOL); | | 446 | CTL_KERN, CTL_CREATE, CTL_EOL); |
447 | | | 447 | |
448 | if (node == NULL) | | 448 | if (node == NULL) |
449 | return; | | 449 | return; |
450 | | | 450 | |
451 | sysctl_createv(&module_sysctllog, 0, &node, NULL, | | 451 | sysctl_createv(&module_sysctllog, 0, &node, NULL, |
452 | CTLFLAG_PERMANENT | CTLFLAG_READWRITE, | | 452 | CTLFLAG_PERMANENT | CTLFLAG_READWRITE, |
453 | CTLTYPE_BOOL, "autoload", | | 453 | CTLTYPE_BOOL, "autoload", |
454 | SYSCTL_DESCR("Enable automatic load of modules"), | | 454 | SYSCTL_DESCR("Enable automatic load of modules"), |
455 | NULL, 0, &module_autoload_on, 0, | | 455 | NULL, 0, &module_autoload_on, 0, |
456 | CTL_CREATE, CTL_EOL); | | 456 | CTL_CREATE, CTL_EOL); |
457 | sysctl_createv(&module_sysctllog, 0, &node, NULL, | | 457 | sysctl_createv(&module_sysctllog, 0, &node, NULL, |
458 | CTLFLAG_PERMANENT | CTLFLAG_READWRITE, | | 458 | CTLFLAG_PERMANENT | CTLFLAG_READWRITE, |
459 | CTLTYPE_BOOL, "verbose", | | 459 | CTLTYPE_BOOL, "verbose", |
460 | SYSCTL_DESCR("Enable verbose output"), | | 460 | SYSCTL_DESCR("Enable verbose output"), |
461 | NULL, 0, &module_verbose_on, 0, | | 461 | NULL, 0, &module_verbose_on, 0, |
462 | CTL_CREATE, CTL_EOL); | | 462 | CTL_CREATE, CTL_EOL); |
463 | sysctl_createv(&module_sysctllog, 0, &node, NULL, | | 463 | sysctl_createv(&module_sysctllog, 0, &node, NULL, |
464 | CTLFLAG_PERMANENT | CTLFLAG_READONLY, | | 464 | CTLFLAG_PERMANENT | CTLFLAG_READONLY, |
465 | CTLTYPE_STRING, "path", | | 465 | CTLTYPE_STRING, "path", |
466 | SYSCTL_DESCR("Default module load path"), | | 466 | SYSCTL_DESCR("Default module load path"), |
467 | NULL, 0, module_base, 0, | | 467 | NULL, 0, module_base, 0, |
468 | CTL_CREATE, CTL_EOL); | | 468 | CTL_CREATE, CTL_EOL); |
469 | sysctl_createv(&module_sysctllog, 0, &node, NULL, | | 469 | sysctl_createv(&module_sysctllog, 0, &node, NULL, |
470 | CTLFLAG_PERMANENT | CTLFLAG_READWRITE, | | 470 | CTLFLAG_PERMANENT | CTLFLAG_READWRITE, |
471 | CTLTYPE_INT, "autotime", | | 471 | CTLTYPE_INT, "autotime", |
472 | SYSCTL_DESCR("Auto-unload delay"), | | 472 | SYSCTL_DESCR("Auto-unload delay"), |
473 | sysctl_module_autotime, 0, &module_autotime, 0, | | 473 | sysctl_module_autotime, 0, &module_autotime, 0, |
474 | CTL_CREATE, CTL_EOL); | | 474 | CTL_CREATE, CTL_EOL); |
475 | } | | 475 | } |
476 | | | 476 | |
477 | /* | | 477 | /* |
478 | * module_init_class: | | 478 | * module_init_class: |
479 | * | | 479 | * |
480 | * Initialize all built-in and pre-loaded modules of the | | 480 | * Initialize all built-in and pre-loaded modules of the |
481 | * specified class. | | 481 | * specified class. |
482 | */ | | 482 | */ |
483 | void | | 483 | void |
484 | module_init_class(modclass_t modclass) | | 484 | module_init_class(modclass_t modclass) |
485 | { | | 485 | { |
486 | TAILQ_HEAD(, module) bi_fail = TAILQ_HEAD_INITIALIZER(bi_fail); | | 486 | TAILQ_HEAD(, module) bi_fail = TAILQ_HEAD_INITIALIZER(bi_fail); |
487 | module_t *mod; | | 487 | module_t *mod; |
488 | modinfo_t *mi; | | 488 | modinfo_t *mi; |
489 | | | 489 | |
490 | kernconfig_lock(); | | 490 | kernconfig_lock(); |
491 | /* | | 491 | /* |
492 | * Builtins first. These will not depend on pre-loaded modules | | 492 | * Builtins first. These will not depend on pre-loaded modules |
493 | * (because the kernel would not link). | | 493 | * (because the kernel would not link). |
494 | */ | | 494 | */ |
495 | do { | | 495 | do { |
496 | TAILQ_FOREACH(mod, &module_builtins, mod_chain) { | | 496 | TAILQ_FOREACH(mod, &module_builtins, mod_chain) { |
497 | mi = mod->mod_info; | | 497 | mi = mod->mod_info; |
498 | if (!MODULE_CLASS_MATCH(mi, modclass)) | | 498 | if (!MODULE_CLASS_MATCH(mi, modclass)) |
499 | continue; | | 499 | continue; |
500 | /* | | 500 | /* |
501 | * If initializing a builtin module fails, don't try | | 501 | * If initializing a builtin module fails, don't try |
502 | * to load it again. But keep it around and queue it | | 502 | * to load it again. But keep it around and queue it |
503 | * on the builtins list after we're done with module | | 503 | * on the builtins list after we're done with module |
504 | * init. Don't set it to MODFLG_MUST_FORCE in case a | | 504 | * init. Don't set it to MODFLG_MUST_FORCE in case a |
505 | * future attempt to initialize can be successful. | | 505 | * future attempt to initialize can be successful. |
506 | * (If the module has previously been set to | | 506 | * (If the module has previously been set to |
507 | * MODFLG_MUST_FORCE, don't try to override that!) | | 507 | * MODFLG_MUST_FORCE, don't try to override that!) |
508 | */ | | 508 | */ |
509 | if ((mod->mod_flags & MODFLG_MUST_FORCE) || | | 509 | if ((mod->mod_flags & MODFLG_MUST_FORCE) || |
510 | module_do_builtin(mod, mi->mi_name, NULL, | | 510 | module_do_builtin(mod, mi->mi_name, NULL, |
511 | NULL) != 0) { | | 511 | NULL) != 0) { |
512 | TAILQ_REMOVE(&module_builtins, mod, mod_chain); | | 512 | TAILQ_REMOVE(&module_builtins, mod, mod_chain); |
513 | TAILQ_INSERT_TAIL(&bi_fail, mod, mod_chain); | | 513 | TAILQ_INSERT_TAIL(&bi_fail, mod, mod_chain); |
514 | } | | 514 | } |
515 | break; | | 515 | break; |
516 | } | | 516 | } |
517 | } while (mod != NULL); | | 517 | } while (mod != NULL); |
518 | | | 518 | |
519 | /* | | 519 | /* |
520 | * Now preloaded modules. These will be pulled off the | | 520 | * Now preloaded modules. These will be pulled off the |
521 | * list as we call module_do_load(); | | 521 | * list as we call module_do_load(); |
522 | */ | | 522 | */ |
523 | do { | | 523 | do { |
524 | TAILQ_FOREACH(mod, &module_bootlist, mod_chain) { | | 524 | TAILQ_FOREACH(mod, &module_bootlist, mod_chain) { |
525 | mi = mod->mod_info; | | 525 | mi = mod->mod_info; |
526 | if (!MODULE_CLASS_MATCH(mi, modclass)) | | 526 | if (!MODULE_CLASS_MATCH(mi, modclass)) |
527 | continue; | | 527 | continue; |
528 | module_do_load(mi->mi_name, false, 0, NULL, NULL, | | 528 | module_do_load(mi->mi_name, false, 0, NULL, NULL, |
529 | modclass, false); | | 529 | modclass, false); |
530 | break; | | 530 | break; |
531 | } | | 531 | } |
532 | } while (mod != NULL); | | 532 | } while (mod != NULL); |
533 | | | 533 | |
534 | /* return failed builtin modules to builtin list */ | | 534 | /* return failed builtin modules to builtin list */ |
535 | while ((mod = TAILQ_FIRST(&bi_fail)) != NULL) { | | 535 | while ((mod = TAILQ_FIRST(&bi_fail)) != NULL) { |
536 | TAILQ_REMOVE(&bi_fail, mod, mod_chain); | | 536 | TAILQ_REMOVE(&bi_fail, mod, mod_chain); |
537 | TAILQ_INSERT_TAIL(&module_builtins, mod, mod_chain); | | 537 | TAILQ_INSERT_TAIL(&module_builtins, mod, mod_chain); |
538 | } | | 538 | } |
539 | | | 539 | |
540 | kernconfig_unlock(); | | 540 | kernconfig_unlock(); |
541 | } | | 541 | } |
542 | | | 542 | |
543 | /* | | 543 | /* |
544 | * module_compatible: | | 544 | * module_compatible: |
545 | * | | 545 | * |
546 | * Return true if the two supplied kernel versions are said to | | 546 | * Return true if the two supplied kernel versions are said to |
547 | * have the same binary interface for kernel code. The entire | | 547 | * have the same binary interface for kernel code. The entire |
548 | * version is signficant for the development tree (-current), | | 548 | * version is signficant for the development tree (-current), |
549 | * major and minor versions are significant for official | | 549 | * major and minor versions are significant for official |
550 | * releases of the system. | | 550 | * releases of the system. |
551 | */ | | 551 | */ |
552 | bool | | 552 | bool |
553 | module_compatible(int v1, int v2) | | 553 | module_compatible(int v1, int v2) |
554 | { | | 554 | { |
555 | | | 555 | |
556 | #if __NetBSD_Version__ / 1000000 % 100 == 99 /* -current */ | | 556 | #if __NetBSD_Version__ / 1000000 % 100 == 99 /* -current */ |
557 | return v1 == v2; | | 557 | return v1 == v2; |
558 | #else /* release */ | | 558 | #else /* release */ |
559 | return abs(v1 - v2) < 10000; | | 559 | return abs(v1 - v2) < 10000; |
560 | #endif | | 560 | #endif |
561 | } | | 561 | } |
562 | | | 562 | |
563 | /* | | 563 | /* |
564 | * module_load: | | 564 | * module_load: |
565 | * | | 565 | * |
566 | * Load a single module from the file system. | | 566 | * Load a single module from the file system. |
567 | */ | | 567 | */ |
568 | int | | 568 | int |
569 | module_load(const char *filename, int flags, prop_dictionary_t props, | | 569 | module_load(const char *filename, int flags, prop_dictionary_t props, |
570 | modclass_t modclass) | | 570 | modclass_t modclass) |
571 | { | | 571 | { |
572 | module_t *mod; | | 572 | module_t *mod; |
573 | int error; | | 573 | int error; |
574 | | | 574 | |
575 | /* Test if we already have the module loaded before | | 575 | /* Test if we already have the module loaded before |
576 | * authorizing so we have the opportunity to return EEXIST. */ | | 576 | * authorizing so we have the opportunity to return EEXIST. */ |
577 | kernconfig_lock(); | | 577 | kernconfig_lock(); |
578 | mod = module_lookup(filename); | | 578 | mod = module_lookup(filename); |
579 | if (mod != NULL) { | | 579 | if (mod != NULL) { |
580 | module_print("%s module `%s' already loaded", | | 580 | module_print("%s module `%s' already loaded", |
581 | "requested", filename); | | 581 | "requested", filename); |
582 | error = EEXIST; | | 582 | error = EEXIST; |
583 | goto out; | | 583 | goto out; |
584 | } | | 584 | } |
585 | | | 585 | |
586 | /* Authorize. */ | | 586 | /* Authorize. */ |
587 | error = kauth_authorize_system(kauth_cred_get(), KAUTH_SYSTEM_MODULE, | | 587 | error = kauth_authorize_system(kauth_cred_get(), KAUTH_SYSTEM_MODULE, |
588 | 0, (void *)(uintptr_t)MODCTL_LOAD, NULL, NULL); | | 588 | 0, (void *)(uintptr_t)MODCTL_LOAD, NULL, NULL); |
589 | if (error != 0) | | 589 | if (error != 0) |
590 | goto out; | | 590 | goto out; |
591 | | | 591 | |
592 | error = module_do_load(filename, false, flags, props, NULL, modclass, | | 592 | error = module_do_load(filename, false, flags, props, NULL, modclass, |
593 | false); | | 593 | false); |
594 | | | 594 | |
595 | out: | | 595 | out: |
596 | kernconfig_unlock(); | | 596 | kernconfig_unlock(); |
597 | return error; | | 597 | return error; |
598 | } | | 598 | } |
599 | | | 599 | |
600 | /* | | 600 | /* |
601 | * module_autoload: | | 601 | * module_autoload: |
602 | * | | 602 | * |
603 | * Load a single module from the file system, system initiated. | | 603 | * Load a single module from the file system, system initiated. |
604 | */ | | 604 | */ |
605 | int | | 605 | int |
606 | module_autoload(const char *filename, modclass_t modclass) | | 606 | module_autoload(const char *filename, modclass_t modclass) |
607 | { | | 607 | { |
608 | int error; | | 608 | int error; |
609 | | | 609 | |
610 | kernconfig_lock(); | | 610 | kernconfig_lock(); |
611 | | | 611 | |
612 | /* Nothing if the user has disabled it. */ | | 612 | /* Nothing if the user has disabled it. */ |
613 | if (!module_autoload_on) { | | 613 | if (!module_autoload_on) { |
614 | kernconfig_unlock(); | | 614 | kernconfig_unlock(); |
615 | return EPERM; | | 615 | return EPERM; |
616 | } | | 616 | } |
617 | | | 617 | |
618 | /* Disallow path separators and magic symlinks. */ | | 618 | /* Disallow path separators and magic symlinks. */ |
619 | if (strchr(filename, '/') != NULL || strchr(filename, '@') != NULL || | | 619 | if (strchr(filename, '/') != NULL || strchr(filename, '@') != NULL || |
620 | strchr(filename, '.') != NULL) { | | 620 | strchr(filename, '.') != NULL) { |
621 | kernconfig_unlock(); | | 621 | kernconfig_unlock(); |
622 | return EPERM; | | 622 | return EPERM; |
623 | } | | 623 | } |
624 | | | 624 | |
625 | /* Authorize. */ | | 625 | /* Authorize. */ |
626 | error = kauth_authorize_system(kauth_cred_get(), KAUTH_SYSTEM_MODULE, | | 626 | error = kauth_authorize_system(kauth_cred_get(), KAUTH_SYSTEM_MODULE, |
627 | 0, (void *)(uintptr_t)MODCTL_LOAD, (void *)(uintptr_t)1, NULL); | | 627 | 0, (void *)(uintptr_t)MODCTL_LOAD, (void *)(uintptr_t)1, NULL); |
628 | | | 628 | |
629 | if (error == 0) | | 629 | if (error == 0) |
630 | error = module_do_load(filename, false, 0, NULL, NULL, modclass, | | 630 | error = module_do_load(filename, false, 0, NULL, NULL, modclass, |
631 | true); | | 631 | true); |
632 | | | 632 | |
633 | kernconfig_unlock(); | | 633 | kernconfig_unlock(); |
634 | return error; | | 634 | return error; |
635 | } | | 635 | } |
636 | | | 636 | |
637 | /* | | 637 | /* |
638 | * module_unload: | | 638 | * module_unload: |
639 | * | | 639 | * |
640 | * Find and unload a module by name. | | 640 | * Find and unload a module by name. |
641 | */ | | 641 | */ |
642 | int | | 642 | int |
643 | module_unload(const char *name) | | 643 | module_unload(const char *name) |
644 | { | | 644 | { |
645 | int error; | | 645 | int error; |
646 | | | 646 | |
647 | /* Authorize. */ | | 647 | /* Authorize. */ |
648 | error = kauth_authorize_system(kauth_cred_get(), KAUTH_SYSTEM_MODULE, | | 648 | error = kauth_authorize_system(kauth_cred_get(), KAUTH_SYSTEM_MODULE, |
649 | 0, (void *)(uintptr_t)MODCTL_UNLOAD, NULL, NULL); | | 649 | 0, (void *)(uintptr_t)MODCTL_UNLOAD, NULL, NULL); |
650 | if (error != 0) { | | 650 | if (error != 0) { |
651 | return error; | | 651 | return error; |
652 | } | | 652 | } |
653 | | | 653 | |
654 | kernconfig_lock(); | | 654 | kernconfig_lock(); |
655 | error = module_do_unload(name, true); | | 655 | error = module_do_unload(name, true); |
656 | kernconfig_unlock(); | | 656 | kernconfig_unlock(); |
657 | | | 657 | |
658 | return error; | | 658 | return error; |
659 | } | | 659 | } |
660 | | | 660 | |
661 | /* | | 661 | /* |
662 | * module_alias_lookup | | 662 | * module_alias_lookup |
663 | * | | 663 | * |
664 | * locate a name within a module's alias list | | 664 | * locate a name within a module's alias list |
665 | */ | | 665 | */ |
666 | int | | 666 | int |
667 | module_alias_lookup(const char *name, module_t *mod) | | 667 | module_alias_lookup(const char *name, module_t *mod) |
668 | { | | 668 | { |
669 | const char * const aliasp[]; | | 669 | const char * const *aliasp; |
670 | | | 670 | |
671 | aliasp = *mod->mod_info->mi_aliases; | | 671 | aliasp = *mod->mod_info->mi_aliases; |
672 | if (aliasp == NULL) | | 672 | if (aliasp == NULL) |
673 | return 0; | | 673 | return 0; |
674 | while (*aliasp) | | 674 | while (*aliasp) |
675 | if (strcmp(*aliasp++, name) == 0) | | 675 | if (strcmp(*aliasp++, name) == 0) |
676 | return 1; | | 676 | return 1; |
677 | return 0; | | 677 | return 0; |
678 | } | | 678 | } |
679 | | | 679 | |
680 | /* | | 680 | /* |
681 | * module_lookup: | | 681 | * module_lookup: |
682 | * | | 682 | * |
683 | * Look up a module by name. | | 683 | * Look up a module by name. |
684 | */ | | 684 | */ |
685 | module_t * | | 685 | module_t * |
686 | module_lookup(const char *name) | | 686 | module_lookup(const char *name) |
687 | { | | 687 | { |
688 | module_t *mod; | | 688 | module_t *mod; |
689 | | | 689 | |
690 | KASSERT(kernconfig_is_held()); | | 690 | KASSERT(kernconfig_is_held()); |
691 | | | 691 | |
692 | TAILQ_FOREACH(mod, &module_list, mod_chain) { | | 692 | TAILQ_FOREACH(mod, &module_list, mod_chain) { |
693 | if (strcmp(mod->mod_info->mi_name, name) == 0) { | | 693 | if (strcmp(mod->mod_info->mi_name, name) == 0) { |
694 | break; | | 694 | break; |
695 | if (module_alias_lookup(name, mod)) | | 695 | if (module_alias_lookup(name, mod)) |
696 | break; | | 696 | break; |
697 | } | | 697 | } |
698 | } | | 698 | } |
699 | | | 699 | |
700 | return mod; | | 700 | return mod; |
701 | } | | 701 | } |
702 | | | 702 | |
703 | /* | | 703 | /* |
704 | * module_hold: | | 704 | * module_hold: |
705 | * | | 705 | * |
706 | * Add a single reference to a module. It's the caller's | | 706 | * Add a single reference to a module. It's the caller's |
707 | * responsibility to ensure that the reference is dropped | | 707 | * responsibility to ensure that the reference is dropped |
708 | * later. | | 708 | * later. |
709 | */ | | 709 | */ |
710 | int | | 710 | int |
711 | module_hold(const char *name) | | 711 | module_hold(const char *name) |
712 | { | | 712 | { |
713 | module_t *mod; | | 713 | module_t *mod; |
714 | | | 714 | |
715 | kernconfig_lock(); | | 715 | kernconfig_lock(); |
716 | mod = module_lookup(name); | | 716 | mod = module_lookup(name); |
717 | if (mod == NULL) { | | 717 | if (mod == NULL) { |
718 | kernconfig_unlock(); | | 718 | kernconfig_unlock(); |
719 | return ENOENT; | | 719 | return ENOENT; |
720 | } | | 720 | } |
721 | mod->mod_refcnt++; | | 721 | mod->mod_refcnt++; |
722 | kernconfig_unlock(); | | 722 | kernconfig_unlock(); |
723 | | | 723 | |
724 | return 0; | | 724 | return 0; |
725 | } | | 725 | } |
726 | | | 726 | |
727 | /* | | 727 | /* |
728 | * module_rele: | | 728 | * module_rele: |
729 | * | | 729 | * |
730 | * Release a reference acquired with module_hold(). | | 730 | * Release a reference acquired with module_hold(). |
731 | */ | | 731 | */ |
732 | void | | 732 | void |
733 | module_rele(const char *name) | | 733 | module_rele(const char *name) |
734 | { | | 734 | { |
735 | module_t *mod; | | 735 | module_t *mod; |
736 | | | 736 | |
737 | kernconfig_lock(); | | 737 | kernconfig_lock(); |
738 | mod = module_lookup(name); | | 738 | mod = module_lookup(name); |
739 | if (mod == NULL) { | | 739 | if (mod == NULL) { |
740 | kernconfig_unlock(); | | 740 | kernconfig_unlock(); |
741 | panic("%s: gone", __func__); | | 741 | panic("%s: gone", __func__); |
742 | } | | 742 | } |
743 | mod->mod_refcnt--; | | 743 | mod->mod_refcnt--; |
744 | kernconfig_unlock(); | | 744 | kernconfig_unlock(); |
745 | } | | 745 | } |
746 | | | 746 | |
747 | /* | | 747 | /* |
748 | * module_enqueue: | | 748 | * module_enqueue: |
749 | * | | 749 | * |
750 | * Put a module onto the global list and update counters. | | 750 | * Put a module onto the global list and update counters. |
751 | */ | | 751 | */ |
752 | void | | 752 | void |
753 | module_enqueue(module_t *mod) | | 753 | module_enqueue(module_t *mod) |
754 | { | | 754 | { |
755 | int i; | | 755 | int i; |
756 | | | 756 | |
757 | KASSERT(kernconfig_is_held()); | | 757 | KASSERT(kernconfig_is_held()); |
758 | | | 758 | |
759 | /* | | 759 | /* |
760 | * Put new entry at the head of the queue so autounload can unload | | 760 | * Put new entry at the head of the queue so autounload can unload |
761 | * requisite modules with only one pass through the queue. | | 761 | * requisite modules with only one pass through the queue. |
762 | */ | | 762 | */ |
763 | TAILQ_INSERT_HEAD(&module_list, mod, mod_chain); | | 763 | TAILQ_INSERT_HEAD(&module_list, mod, mod_chain); |
764 | if (mod->mod_nrequired) { | | 764 | if (mod->mod_nrequired) { |
765 | | | 765 | |
766 | /* Add references to the requisite modules. */ | | 766 | /* Add references to the requisite modules. */ |
767 | for (i = 0; i < mod->mod_nrequired; i++) { | | 767 | for (i = 0; i < mod->mod_nrequired; i++) { |
768 | KASSERT(mod->mod_required[i] != NULL); | | 768 | KASSERT(mod->mod_required[i] != NULL); |
769 | mod->mod_required[i]->mod_refcnt++; | | 769 | mod->mod_required[i]->mod_refcnt++; |
770 | } | | 770 | } |
771 | } | | 771 | } |
772 | module_count++; | | 772 | module_count++; |
773 | module_gen++; | | 773 | module_gen++; |
774 | } | | 774 | } |
775 | | | 775 | |
776 | /* | | 776 | /* |
777 | * module_do_builtin: | | 777 | * module_do_builtin: |
778 | * | | 778 | * |
779 | * Initialize a module from the list of modules that are | | 779 | * Initialize a module from the list of modules that are |
780 | * already linked into the kernel. | | 780 | * already linked into the kernel. |
781 | */ | | 781 | */ |
782 | static int | | 782 | static int |
783 | module_do_builtin(const module_t *pmod, const char *name, module_t **modp, | | 783 | module_do_builtin(const module_t *pmod, const char *name, module_t **modp, |
784 | prop_dictionary_t props) | | 784 | prop_dictionary_t props) |
785 | { | | 785 | { |
786 | const char *p, *s; | | 786 | const char *p, *s; |
787 | const char * const aliasp[]; | | 787 | const char * const *aliasp; |
788 | char buf[MAXMODNAME]; | | 788 | char buf[MAXMODNAME]; |
789 | modinfo_t *mi = NULL; | | 789 | modinfo_t *mi = NULL; |
790 | module_t *mod, *mod2, *mod_loaded, *prev_active; | | 790 | module_t *mod, *mod2, *mod_loaded, *prev_active; |
791 | size_t len; | | 791 | size_t len; |
792 | int error; | | 792 | int error; |
793 | | | 793 | |
794 | KASSERT(kernconfig_is_held()); | | 794 | KASSERT(kernconfig_is_held()); |
795 | | | 795 | |
796 | /* | | 796 | /* |
797 | * Search the list to see if we have a module by this name. | | 797 | * Search the list to see if we have a module by this name. |
798 | */ | | 798 | */ |
799 | TAILQ_FOREACH(mod, &module_builtins, mod_chain) { | | 799 | TAILQ_FOREACH(mod, &module_builtins, mod_chain) { |
800 | if (strcmp(mod->mod_info->mi_name, name) == 0) { | | 800 | if (strcmp(mod->mod_info->mi_name, name) == 0) { |
801 | mi = mod->mod_info; | | 801 | mi = mod->mod_info; |
802 | break; | | 802 | break; |
803 | } | | 803 | } |
804 | } | | 804 | } |
805 | | | 805 | |
806 | /* | | 806 | /* |
807 | * Check to see if already loaded. This might happen if we | | 807 | * Check to see if already loaded. This might happen if we |
808 | * were already loaded as a dependency. | | 808 | * were already loaded as a dependency. |
809 | */ | | 809 | */ |
810 | if ((mod_loaded = module_lookup(name)) != NULL) { | | 810 | if ((mod_loaded = module_lookup(name)) != NULL) { |
811 | KASSERT(mod == NULL); | | 811 | KASSERT(mod == NULL); |
812 | if (modp) | | 812 | if (modp) |
813 | *modp = mod_loaded; | | 813 | *modp = mod_loaded; |
814 | return 0; | | 814 | return 0; |
815 | } | | 815 | } |
816 | | | 816 | |
817 | /* Note! This is from TAILQ, not immediate above */ | | 817 | /* Note! This is from TAILQ, not immediate above */ |
818 | if (mi == NULL) { | | 818 | if (mi == NULL) { |
819 | /* | | 819 | /* |
820 | * XXX: We'd like to panic here, but currently in some | | 820 | * XXX: We'd like to panic here, but currently in some |
821 | * cases (such as nfsserver + nfs), the dependee can be | | 821 | * cases (such as nfsserver + nfs), the dependee can be |
822 | * succesfully linked without the dependencies. | | 822 | * succesfully linked without the dependencies. |
823 | */ | | 823 | */ |
824 | module_error("%s: can't find builtin dependency `%s'", | | 824 | module_error("%s: can't find builtin dependency `%s'", |
825 | pmod->mod_info->mi_name, name); | | 825 | pmod->mod_info->mi_name, name); |
826 | return ENOENT; | | 826 | return ENOENT; |
827 | } | | 827 | } |
828 | | | 828 | |
829 | /* | | 829 | /* |
830 | * Initialize pre-requisites. | | 830 | * Initialize pre-requisites. |
831 | */ | | 831 | */ |
832 | if (mi->mi_required != NULL) { | | 832 | if (mi->mi_required != NULL) { |
833 | for (s = mi->mi_required; *s != '\0'; s = p) { | | 833 | for (s = mi->mi_required; *s != '\0'; s = p) { |
834 | if (*s == ',') | | 834 | if (*s == ',') |
835 | s++; | | 835 | s++; |
836 | p = s; | | 836 | p = s; |
837 | while (*p != '\0' && *p != ',') | | 837 | while (*p != '\0' && *p != ',') |
838 | p++; | | 838 | p++; |
839 | len = min(p - s + 1, sizeof(buf)); | | 839 | len = min(p - s + 1, sizeof(buf)); |
840 | strlcpy(buf, s, len); | | 840 | strlcpy(buf, s, len); |
841 | if (buf[0] == '\0') | | 841 | if (buf[0] == '\0') |
842 | break; | | 842 | break; |
843 | if (mod->mod_nrequired == MAXMODDEPS - 1) { | | 843 | if (mod->mod_nrequired == MAXMODDEPS - 1) { |
844 | module_error("%s: too many required modules " | | 844 | module_error("%s: too many required modules " |
845 | "%d >= %d", pmod->mod_info->mi_name, | | 845 | "%d >= %d", pmod->mod_info->mi_name, |
846 | mod->mod_nrequired, MAXMODDEPS - 1); | | 846 | mod->mod_nrequired, MAXMODDEPS - 1); |
847 | return EINVAL; | | 847 | return EINVAL; |
848 | } | | 848 | } |
849 | error = module_do_builtin(mod, buf, &mod2, NULL); | | 849 | error = module_do_builtin(mod, buf, &mod2, NULL); |
850 | if (error != 0) { | | 850 | if (error != 0) { |
851 | return error; | | 851 | return error; |
852 | } | | 852 | } |
853 | mod->mod_required[mod->mod_nrequired++] = mod2; | | 853 | mod->mod_required[mod->mod_nrequired++] = mod2; |
854 | } | | 854 | } |
855 | } | | 855 | } |
856 | | | 856 | |
857 | /* | | 857 | /* |
858 | * Retrieve that none of the module's aliases already exist | | 858 | * Retrieve that none of the module's aliases already exist |
859 | */ | | 859 | */ |
860 | | | 860 | |
861 | if ((aliasp = *mod->mod_info->mi_aliases) != NULL) { | | 861 | if ((aliasp = *mod->mod_info->mi_aliases) != NULL) { |
862 | while (*aliasp) | | 862 | while (*aliasp) |
863 | if (module_lookup(*aliasp++) != NULL) | | 863 | if (module_lookup(*aliasp++) != NULL) |
864 | return EEXIST; | | 864 | return EEXIST; |
865 | } | | 865 | } |
866 | /* | | 866 | /* |
867 | * Try to initialize the module. | | 867 | * Try to initialize the module. |
868 | */ | | 868 | */ |
869 | prev_active = module_active; | | 869 | prev_active = module_active; |
870 | module_active = mod; | | 870 | module_active = mod; |
871 | error = (*mi->mi_modcmd)(MODULE_CMD_INIT, props); | | 871 | error = (*mi->mi_modcmd)(MODULE_CMD_INIT, props); |
872 | module_active = prev_active; | | 872 | module_active = prev_active; |
873 | if (error != 0) { | | 873 | if (error != 0) { |
874 | module_error("builtin module `%s' " | | 874 | module_error("builtin module `%s' " |
875 | "failed to init, error %d", mi->mi_name, error); | | 875 | "failed to init, error %d", mi->mi_name, error); |
876 | return error; | | 876 | return error; |
877 | } | | 877 | } |
878 | | | 878 | |
879 | /* load always succeeds after this point */ | | 879 | /* load always succeeds after this point */ |
880 | | | 880 | |
881 | TAILQ_REMOVE(&module_builtins, mod, mod_chain); | | 881 | TAILQ_REMOVE(&module_builtins, mod, mod_chain); |
882 | module_builtinlist--; | | 882 | module_builtinlist--; |
883 | if (modp != NULL) { | | 883 | if (modp != NULL) { |
884 | *modp = mod; | | 884 | *modp = mod; |
885 | } | | 885 | } |
886 | module_enqueue(mod); | | 886 | module_enqueue(mod); |
887 | return 0; | | 887 | return 0; |
888 | } | | 888 | } |
889 | | | 889 | |
890 | /* | | 890 | /* |
891 | * module_do_load: | | 891 | * module_do_load: |
892 | * | | 892 | * |
893 | * Helper routine: load a module from the file system, or one | | 893 | * Helper routine: load a module from the file system, or one |
894 | * pushed by the boot loader. | | 894 | * pushed by the boot loader. |
895 | */ | | 895 | */ |
896 | static int | | 896 | static int |
897 | module_do_load(const char *name, bool isdep, int flags, | | 897 | module_do_load(const char *name, bool isdep, int flags, |
898 | prop_dictionary_t props, module_t **modp, modclass_t modclass, | | 898 | prop_dictionary_t props, module_t **modp, modclass_t modclass, |
899 | bool autoload) | | 899 | bool autoload) |
900 | { | | 900 | { |
901 | #define MODULE_MAX_DEPTH 6 | | 901 | #define MODULE_MAX_DEPTH 6 |
902 | | | 902 | |
903 | TAILQ_HEAD(pending_t, module); | | 903 | TAILQ_HEAD(pending_t, module); |
904 | static int depth = 0; | | 904 | static int depth = 0; |
905 | static struct pending_t *pending_lists[MODULE_MAX_DEPTH]; | | 905 | static struct pending_t *pending_lists[MODULE_MAX_DEPTH]; |
906 | struct pending_t *pending; | | 906 | struct pending_t *pending; |
907 | struct pending_t new_pending = TAILQ_HEAD_INITIALIZER(new_pending); | | 907 | struct pending_t new_pending = TAILQ_HEAD_INITIALIZER(new_pending); |
908 | modinfo_t *mi; | | 908 | modinfo_t *mi; |
909 | module_t *mod, *mod2, *prev_active; | | 909 | module_t *mod, *mod2, *prev_active; |
910 | prop_dictionary_t filedict; | | 910 | prop_dictionary_t filedict; |
911 | char buf[MAXMODNAME]; | | 911 | char buf[MAXMODNAME]; |
912 | const char *s, *p; | | 912 | const char *s, *p; |
913 | const char * const aliasp[]; | | 913 | const char * const *aliasp; |
914 | int error; | | 914 | int error; |
915 | size_t len; | | 915 | size_t len; |
916 | | | 916 | |
917 | KASSERT(kernconfig_is_held()); | | 917 | KASSERT(kernconfig_is_held()); |
918 | | | 918 | |
919 | filedict = NULL; | | 919 | filedict = NULL; |
920 | error = 0; | | 920 | error = 0; |
921 | | | 921 | |
922 | /* | | 922 | /* |
923 | * Avoid recursing too far. | | 923 | * Avoid recursing too far. |
924 | */ | | 924 | */ |
925 | if (++depth > MODULE_MAX_DEPTH) { | | 925 | if (++depth > MODULE_MAX_DEPTH) { |
926 | module_error("recursion too deep for `%s' %d > %d", name, | | 926 | module_error("recursion too deep for `%s' %d > %d", name, |
927 | depth, MODULE_MAX_DEPTH); | | 927 | depth, MODULE_MAX_DEPTH); |
928 | depth--; | | 928 | depth--; |
929 | return EMLINK; | | 929 | return EMLINK; |
930 | } | | 930 | } |
931 | | | 931 | |
932 | /* | | 932 | /* |
933 | * Set up the pending list for this depth. If this is a | | 933 | * Set up the pending list for this depth. If this is a |
934 | * recursive entry, then use same list as for outer call, | | 934 | * recursive entry, then use same list as for outer call, |
935 | * else use the locally allocated list. In either case, | | 935 | * else use the locally allocated list. In either case, |
936 | * remember which one we're using. | | 936 | * remember which one we're using. |
937 | */ | | 937 | */ |
938 | if (isdep) { | | 938 | if (isdep) { |
939 | KASSERT(depth > 1); | | 939 | KASSERT(depth > 1); |
940 | pending = pending_lists[depth - 2]; | | 940 | pending = pending_lists[depth - 2]; |
941 | } else | | 941 | } else |
942 | pending = &new_pending; | | 942 | pending = &new_pending; |
943 | pending_lists[depth - 1] = pending; | | 943 | pending_lists[depth - 1] = pending; |
944 | | | 944 | |
945 | /* | | 945 | /* |
946 | * Search the list of disabled builtins first. | | 946 | * Search the list of disabled builtins first. |
947 | */ | | 947 | */ |
948 | TAILQ_FOREACH(mod, &module_builtins, mod_chain) { | | 948 | TAILQ_FOREACH(mod, &module_builtins, mod_chain) { |
949 | if (strcmp(mod->mod_info->mi_name, name) == 0) { | | 949 | if (strcmp(mod->mod_info->mi_name, name) == 0) { |
950 | break; | | 950 | break; |
951 | } | | 951 | } |
952 | } | | 952 | } |
953 | if (mod) { | | 953 | if (mod) { |
954 | if ((mod->mod_flags & MODFLG_MUST_FORCE) && | | 954 | if ((mod->mod_flags & MODFLG_MUST_FORCE) && |
955 | (flags & MODCTL_LOAD_FORCE) == 0) { | | 955 | (flags & MODCTL_LOAD_FORCE) == 0) { |
956 | if (!autoload) { | | 956 | if (!autoload) { |
957 | module_error("use -f to reinstate " | | 957 | module_error("use -f to reinstate " |
958 | "builtin module `%s'", name); | | 958 | "builtin module `%s'", name); |
959 | } | | 959 | } |
960 | depth--; | | 960 | depth--; |
961 | return EPERM; | | 961 | return EPERM; |
962 | } else { | | 962 | } else { |
963 | error = module_do_builtin(mod, name, modp, props); | | 963 | error = module_do_builtin(mod, name, modp, props); |
964 | depth--; | | 964 | depth--; |
965 | return error; | | 965 | return error; |
966 | } | | 966 | } |
967 | } | | 967 | } |
968 | | | 968 | |
969 | /* | | 969 | /* |
970 | * Load the module and link. Before going to the file system, | | 970 | * Load the module and link. Before going to the file system, |
971 | * scan the list of modules loaded by the boot loader. | | 971 | * scan the list of modules loaded by the boot loader. |
972 | */ | | 972 | */ |
973 | TAILQ_FOREACH(mod, &module_bootlist, mod_chain) { | | 973 | TAILQ_FOREACH(mod, &module_bootlist, mod_chain) { |
974 | if (strcmp(mod->mod_info->mi_name, name) == 0) { | | 974 | if (strcmp(mod->mod_info->mi_name, name) == 0) { |
975 | TAILQ_REMOVE(&module_bootlist, mod, mod_chain); | | 975 | TAILQ_REMOVE(&module_bootlist, mod, mod_chain); |
976 | break; | | 976 | break; |
977 | } | | 977 | } |
978 | } | | 978 | } |
979 | if (mod != NULL) { | | 979 | if (mod != NULL) { |
980 | TAILQ_INSERT_TAIL(pending, mod, mod_chain); | | 980 | TAILQ_INSERT_TAIL(pending, mod, mod_chain); |
981 | } else { | | 981 | } else { |
982 | /* | | 982 | /* |
983 | * Check to see if module is already present. | | 983 | * Check to see if module is already present. |
984 | */ | | 984 | */ |
985 | mod = module_lookup(name); | | 985 | mod = module_lookup(name); |
986 | if (mod != NULL) { | | 986 | if (mod != NULL) { |
987 | if (modp != NULL) { | | 987 | if (modp != NULL) { |
988 | *modp = mod; | | 988 | *modp = mod; |
989 | } | | 989 | } |
990 | module_print("%s module `%s' already loaded", | | 990 | module_print("%s module `%s' already loaded", |
991 | isdep ? "dependent" : "requested", name); | | 991 | isdep ? "dependent" : "requested", name); |
992 | depth--; | | 992 | depth--; |
993 | return EEXIST; | | 993 | return EEXIST; |
994 | } | | 994 | } |
995 | | | 995 | |
996 | mod = module_newmodule(MODULE_SOURCE_FILESYS); | | 996 | mod = module_newmodule(MODULE_SOURCE_FILESYS); |
997 | if (mod == NULL) { | | 997 | if (mod == NULL) { |
998 | module_error("out of memory for `%s'", name); | | 998 | module_error("out of memory for `%s'", name); |
999 | depth--; | | 999 | depth--; |
1000 | return ENOMEM; | | 1000 | return ENOMEM; |
1001 | } | | 1001 | } |
1002 | | | 1002 | |
1003 | error = module_load_vfs_vec(name, flags, autoload, mod, | | 1003 | error = module_load_vfs_vec(name, flags, autoload, mod, |
1004 | &filedict); | | 1004 | &filedict); |
1005 | if (error != 0) { | | 1005 | if (error != 0) { |
1006 | #ifdef DEBUG | | 1006 | #ifdef DEBUG |
1007 | /* | | 1007 | /* |
1008 | * The exec class of modules contains a list of | | 1008 | * The exec class of modules contains a list of |
1009 | * modules that is the union of all the modules | | 1009 | * modules that is the union of all the modules |
1010 | * available for each architecture, so we don't | | 1010 | * available for each architecture, so we don't |
1011 | * print an error if they are missing. | | 1011 | * print an error if they are missing. |
1012 | */ | | 1012 | */ |
1013 | if ((modclass != MODULE_CLASS_EXEC || error != ENOENT) | | 1013 | if ((modclass != MODULE_CLASS_EXEC || error != ENOENT) |
1014 | && root_device != NULL) | | 1014 | && root_device != NULL) |
1015 | module_error("vfs load failed for `%s', " | | 1015 | module_error("vfs load failed for `%s', " |
1016 | "error %d", name, error); | | 1016 | "error %d", name, error); |
1017 | #endif | | 1017 | #endif |
1018 | kmem_free(mod, sizeof(*mod)); | | 1018 | kmem_free(mod, sizeof(*mod)); |
1019 | depth--; | | 1019 | depth--; |
1020 | return error; | | 1020 | return error; |
1021 | } | | 1021 | } |
1022 | TAILQ_INSERT_TAIL(pending, mod, mod_chain); | | 1022 | TAILQ_INSERT_TAIL(pending, mod, mod_chain); |
1023 | | | 1023 | |
1024 | error = module_fetch_info(mod); | | 1024 | error = module_fetch_info(mod); |
1025 | if (error != 0) { | | 1025 | if (error != 0) { |
1026 | module_error("cannot fetch info for `%s', error %d", | | 1026 | module_error("cannot fetch info for `%s', error %d", |
1027 | name, error); | | 1027 | name, error); |
1028 | goto fail; | | 1028 | goto fail; |
1029 | } | | 1029 | } |
1030 | } | | 1030 | } |
1031 | | | 1031 | |
1032 | /* | | 1032 | /* |
1033 | * Check compatibility. | | 1033 | * Check compatibility. |
1034 | */ | | 1034 | */ |
1035 | mi = mod->mod_info; | | 1035 | mi = mod->mod_info; |
1036 | if (strlen(mi->mi_name) >= MAXMODNAME) { | | 1036 | if (strlen(mi->mi_name) >= MAXMODNAME) { |
1037 | error = EINVAL; | | 1037 | error = EINVAL; |
1038 | module_error("module name `%s' longer than %d", mi->mi_name, | | 1038 | module_error("module name `%s' longer than %d", mi->mi_name, |
1039 | MAXMODNAME); | | 1039 | MAXMODNAME); |
1040 | goto fail; | | 1040 | goto fail; |
1041 | } | | 1041 | } |
1042 | if (!module_compatible(mi->mi_version, __NetBSD_Version__)) { | | 1042 | if (!module_compatible(mi->mi_version, __NetBSD_Version__)) { |
1043 | module_error("module `%s' built for `%d', system `%d'", | | 1043 | module_error("module `%s' built for `%d', system `%d'", |
1044 | mi->mi_name, mi->mi_version, __NetBSD_Version__); | | 1044 | mi->mi_name, mi->mi_version, __NetBSD_Version__); |
1045 | if ((flags & MODCTL_LOAD_FORCE) != 0) { | | 1045 | if ((flags & MODCTL_LOAD_FORCE) != 0) { |
1046 | module_error("forced load, system may be unstable"); | | 1046 | module_error("forced load, system may be unstable"); |
1047 | } else { | | 1047 | } else { |
1048 | error = EPROGMISMATCH; | | 1048 | error = EPROGMISMATCH; |
1049 | goto fail; | | 1049 | goto fail; |
1050 | } | | 1050 | } |
1051 | } | | 1051 | } |
1052 | | | 1052 | |
1053 | /* | | 1053 | /* |
1054 | * If a specific kind of module was requested, ensure that we have | | 1054 | * If a specific kind of module was requested, ensure that we have |
1055 | * a match. | | 1055 | * a match. |
1056 | */ | | 1056 | */ |
1057 | if (!MODULE_CLASS_MATCH(mi, modclass)) { | | 1057 | if (!MODULE_CLASS_MATCH(mi, modclass)) { |
1058 | module_incompat(mi, modclass); | | 1058 | module_incompat(mi, modclass); |
1059 | error = ENOENT; | | 1059 | error = ENOENT; |
1060 | goto fail; | | 1060 | goto fail; |
1061 | } | | 1061 | } |
1062 | | | 1062 | |
1063 | /* | | 1063 | /* |
1064 | * If loading a dependency, `name' is a plain module name. | | 1064 | * If loading a dependency, `name' is a plain module name. |
1065 | * The name must match. | | 1065 | * The name must match. |
1066 | */ | | 1066 | */ |
1067 | if (isdep && strcmp(mi->mi_name, name) != 0) { | | 1067 | if (isdep && strcmp(mi->mi_name, name) != 0) { |
1068 | module_error("dependency name mismatch (`%s' != `%s')", | | 1068 | module_error("dependency name mismatch (`%s' != `%s')", |
1069 | name, mi->mi_name); | | 1069 | name, mi->mi_name); |
1070 | error = ENOENT; | | 1070 | error = ENOENT; |
1071 | goto fail; | | 1071 | goto fail; |
1072 | } | | 1072 | } |
1073 | | | 1073 | |
1074 | /* | | 1074 | /* |
1075 | * If we loaded a module from the filesystem, check the actual | | 1075 | * If we loaded a module from the filesystem, check the actual |
1076 | * module name (from the modinfo_t) to ensure another module | | 1076 | * module name (from the modinfo_t) to ensure another module |
1077 | * with the same name doesn't already exist. (There's no | | 1077 | * with the same name doesn't already exist. (There's no |
1078 | * guarantee the filename will match the module name, and the | | 1078 | * guarantee the filename will match the module name, and the |
1079 | * dup-symbols check may not be sufficient.) | | 1079 | * dup-symbols check may not be sufficient.) |
1080 | */ | | 1080 | */ |
1081 | if (mod->mod_source == MODULE_SOURCE_FILESYS) { | | 1081 | if (mod->mod_source == MODULE_SOURCE_FILESYS) { |
1082 | mod2 = module_lookup(mod->mod_info->mi_name); | | 1082 | mod2 = module_lookup(mod->mod_info->mi_name); |
1083 | if ( mod2 && mod2 != mod) { | | 1083 | if ( mod2 && mod2 != mod) { |
1084 | module_error("module with name `%s' already loaded", | | 1084 | module_error("module with name `%s' already loaded", |
1085 | mod2->mod_info->mi_name); | | 1085 | mod2->mod_info->mi_name); |
1086 | error = EEXIST; | | 1086 | error = EEXIST; |
1087 | goto fail; | | 1087 | goto fail; |
1088 | } | | 1088 | } |
1089 | } | | 1089 | } |
1090 | | | 1090 | |
1091 | /* | | 1091 | /* |
1092 | * Block circular dependencies. | | 1092 | * Block circular dependencies. |
1093 | */ | | 1093 | */ |
1094 | TAILQ_FOREACH(mod2, pending, mod_chain) { | | 1094 | TAILQ_FOREACH(mod2, pending, mod_chain) { |
1095 | if (mod == mod2) { | | 1095 | if (mod == mod2) { |
1096 | continue; | | 1096 | continue; |
1097 | } | | 1097 | } |
1098 | if (strcmp(mod2->mod_info->mi_name, mi->mi_name) == 0) { | | 1098 | if (strcmp(mod2->mod_info->mi_name, mi->mi_name) == 0) { |
1099 | error = EDEADLK; | | 1099 | error = EDEADLK; |
1100 | module_error("circular dependency detected for `%s'", | | 1100 | module_error("circular dependency detected for `%s'", |
1101 | mi->mi_name); | | 1101 | mi->mi_name); |
1102 | goto fail; | | 1102 | goto fail; |
1103 | } | | 1103 | } |
1104 | } | | 1104 | } |
1105 | | | 1105 | |
1106 | /* | | 1106 | /* |
1107 | * Now try to load any requisite modules. | | 1107 | * Now try to load any requisite modules. |
1108 | */ | | 1108 | */ |
1109 | if (mi->mi_required != NULL) { | | 1109 | if (mi->mi_required != NULL) { |
1110 | for (s = mi->mi_required; *s != '\0'; s = p) { | | 1110 | for (s = mi->mi_required; *s != '\0'; s = p) { |
1111 | if (*s == ',') | | 1111 | if (*s == ',') |
1112 | s++; | | 1112 | s++; |
1113 | p = s; | | 1113 | p = s; |
1114 | while (*p != '\0' && *p != ',') | | 1114 | while (*p != '\0' && *p != ',') |
1115 | p++; | | 1115 | p++; |
1116 | len = p - s + 1; | | 1116 | len = p - s + 1; |
1117 | if (len >= MAXMODNAME) { | | 1117 | if (len >= MAXMODNAME) { |
1118 | error = EINVAL; | | 1118 | error = EINVAL; |
1119 | module_error("required module name `%s' " | | 1119 | module_error("required module name `%s' " |
1120 | "longer than %d", mi->mi_required, | | 1120 | "longer than %d", mi->mi_required, |
1121 | MAXMODNAME); | | 1121 | MAXMODNAME); |
1122 | goto fail; | | 1122 | goto fail; |
1123 | } | | 1123 | } |
1124 | strlcpy(buf, s, len); | | 1124 | strlcpy(buf, s, len); |
1125 | if (buf[0] == '\0') | | 1125 | if (buf[0] == '\0') |
1126 | break; | | 1126 | break; |
1127 | if (mod->mod_nrequired == MAXMODDEPS - 1) { | | 1127 | if (mod->mod_nrequired == MAXMODDEPS - 1) { |
1128 | error = EINVAL; | | 1128 | error = EINVAL; |
1129 | module_error("too many required modules " | | 1129 | module_error("too many required modules " |
1130 | "%d >= %d", mod->mod_nrequired, | | 1130 | "%d >= %d", mod->mod_nrequired, |
1131 | MAXMODDEPS - 1); | | 1131 | MAXMODDEPS - 1); |
1132 | goto fail; | | 1132 | goto fail; |
1133 | } | | 1133 | } |
1134 | if (strcmp(buf, mi->mi_name) == 0) { | | 1134 | if (strcmp(buf, mi->mi_name) == 0) { |
1135 | error = EDEADLK; | | 1135 | error = EDEADLK; |
1136 | module_error("self-dependency detected for " | | 1136 | module_error("self-dependency detected for " |
1137 | "`%s'", mi->mi_name); | | 1137 | "`%s'", mi->mi_name); |
1138 | goto fail; | | 1138 | goto fail; |
1139 | } | | 1139 | } |
1140 | error = module_do_load(buf, true, flags, NULL, | | 1140 | error = module_do_load(buf, true, flags, NULL, |
1141 | &mod2, MODULE_CLASS_ANY, true); | | 1141 | &mod2, MODULE_CLASS_ANY, true); |
1142 | if (error != 0 && error != EEXIST) { | | 1142 | if (error != 0 && error != EEXIST) { |
1143 | module_error("recursive load failed for `%s' " | | 1143 | module_error("recursive load failed for `%s' " |
1144 | "(`%s' required), error %d", mi->mi_name, | | 1144 | "(`%s' required), error %d", mi->mi_name, |
1145 | buf, error); | | 1145 | buf, error); |
1146 | goto fail; | | 1146 | goto fail; |
1147 | } | | 1147 | } |
1148 | mod->mod_required[mod->mod_nrequired++] = mod2; | | 1148 | mod->mod_required[mod->mod_nrequired++] = mod2; |
1149 | } | | 1149 | } |
1150 | } | | 1150 | } |
1151 | | | 1151 | |
1152 | /* | | 1152 | /* |
1153 | * We loaded all needed modules successfully: perform global | | 1153 | * We loaded all needed modules successfully: perform global |
1154 | * relocations and initialize. | | 1154 | * relocations and initialize. |
1155 | */ | | 1155 | */ |
1156 | error = kobj_affix(mod->mod_kobj, mi->mi_name); | | 1156 | error = kobj_affix(mod->mod_kobj, mi->mi_name); |
1157 | if (error != 0) { | | 1157 | if (error != 0) { |
1158 | /* Cannot touch 'mi' as the module is now gone. */ | | 1158 | /* Cannot touch 'mi' as the module is now gone. */ |
1159 | module_error("unable to affix module `%s', error %d", name, | | 1159 | module_error("unable to affix module `%s', error %d", name, |
1160 | error); | | 1160 | error); |
1161 | goto fail2; | | 1161 | goto fail2; |
1162 | } | | 1162 | } |
1163 | | | 1163 | |
1164 | if (filedict) { | | 1164 | if (filedict) { |
1165 | if (!module_merge_dicts(filedict, props)) { | | 1165 | if (!module_merge_dicts(filedict, props)) { |
1166 | module_error("module properties failed for %s", name); | | 1166 | module_error("module properties failed for %s", name); |
1167 | error = EINVAL; | | 1167 | error = EINVAL; |
1168 | goto fail; | | 1168 | goto fail; |
1169 | } | | 1169 | } |
1170 | } | | 1170 | } |
1171 | /* | | 1171 | /* |
1172 | * One last check for duplicate module name/alias | | 1172 | * One last check for duplicate module name/alias |
1173 | */ | | 1173 | */ |
1174 | if ((aliasp = *mod->mod_info->mi_aliases) != NULL) | | 1174 | if ((aliasp = *mod->mod_info->mi_aliases) != NULL) |
1175 | while (*aliasp != NULL) | | 1175 | while (*aliasp != NULL) |
1176 | if (module_lookup(*aliasp) != NULL) { | | 1176 | if (module_lookup(*aliasp) != NULL) { |
1177 | module_error("Module `%s' alias `%s' already " | | 1177 | module_error("Module `%s' alias `%s' already " |
1178 | "exists", mod->mod_info->mi_name, *aliasp); | | 1178 | "exists", mod->mod_info->mi_name, *aliasp); |
1179 | goto fail; | | 1179 | goto fail; |
1180 | } | | 1180 | } |
1181 | | | 1181 | |
1182 | prev_active = module_active; | | 1182 | prev_active = module_active; |
1183 | module_active = mod; | | 1183 | module_active = mod; |
1184 | error = (*mi->mi_modcmd)(MODULE_CMD_INIT, filedict ? filedict : props); | | 1184 | error = (*mi->mi_modcmd)(MODULE_CMD_INIT, filedict ? filedict : props); |
1185 | module_active = prev_active; | | 1185 | module_active = prev_active; |
1186 | if (filedict) { | | 1186 | if (filedict) { |
1187 | prop_object_release(filedict); | | 1187 | prop_object_release(filedict); |
1188 | filedict = NULL; | | 1188 | filedict = NULL; |
1189 | } | | 1189 | } |
1190 | if (error != 0) { | | 1190 | if (error != 0) { |
1191 | module_error("modcmd function failed for `%s', error %d", | | 1191 | module_error("modcmd function failed for `%s', error %d", |
1192 | mi->mi_name, error); | | 1192 | mi->mi_name, error); |
1193 | goto fail; | | 1193 | goto fail; |
1194 | } | | 1194 | } |
1195 | | | 1195 | |
1196 | /* | | 1196 | /* |
1197 | * If a recursive load already added a module with the same | | 1197 | * If a recursive load already added a module with the same |
1198 | * name, abort. | | 1198 | * name, abort. |
1199 | */ | | 1199 | */ |
1200 | mod2 = module_lookup(mi->mi_name); | | 1200 | mod2 = module_lookup(mi->mi_name); |
1201 | if (mod2 && mod2 != mod) { | | 1201 | if (mod2 && mod2 != mod) { |
1202 | module_error("recursive load causes duplicate module `%s'", | | 1202 | module_error("recursive load causes duplicate module `%s'", |
1203 | mi->mi_name); | | 1203 | mi->mi_name); |
1204 | error = EEXIST; | | 1204 | error = EEXIST; |
1205 | goto fail1; | | 1205 | goto fail1; |
1206 | } | | 1206 | } |
1207 | | | 1207 | |
1208 | /* | | 1208 | /* |
1209 | * Good, the module loaded successfully. Put it onto the | | 1209 | * Good, the module loaded successfully. Put it onto the |
1210 | * list and add references to its requisite modules. | | 1210 | * list and add references to its requisite modules. |
1211 | */ | | 1211 | */ |
1212 | TAILQ_REMOVE(pending, mod, mod_chain); | | 1212 | TAILQ_REMOVE(pending, mod, mod_chain); |
1213 | module_enqueue(mod); | | 1213 | module_enqueue(mod); |
1214 | if (modp != NULL) { | | 1214 | if (modp != NULL) { |
1215 | *modp = mod; | | 1215 | *modp = mod; |
1216 | } | | 1216 | } |
1217 | if (autoload && module_autotime > 0) { | | 1217 | if (autoload && module_autotime > 0) { |
1218 | /* | | 1218 | /* |
1219 | * Arrange to try unloading the module after | | 1219 | * Arrange to try unloading the module after |
1220 | * a short delay unless auto-unload is disabled. | | 1220 | * a short delay unless auto-unload is disabled. |
1221 | */ | | 1221 | */ |
1222 | mod->mod_autotime = time_second + module_autotime; | | 1222 | mod->mod_autotime = time_second + module_autotime; |
1223 | mod->mod_flags |= MODFLG_AUTO_LOADED; | | 1223 | mod->mod_flags |= MODFLG_AUTO_LOADED; |
1224 | module_thread_kick(); | | 1224 | module_thread_kick(); |
1225 | } | | 1225 | } |
1226 | depth--; | | 1226 | depth--; |
1227 | module_print("module `%s' loaded successfully", mi->mi_name); | | 1227 | module_print("module `%s' loaded successfully", mi->mi_name); |
1228 | return 0; | | 1228 | return 0; |
1229 | | | 1229 | |
1230 | fail1: | | 1230 | fail1: |
1231 | (*mi->mi_modcmd)(MODULE_CMD_FINI, NULL); | | 1231 | (*mi->mi_modcmd)(MODULE_CMD_FINI, NULL); |
1232 | fail: | | 1232 | fail: |
1233 | kobj_unload(mod->mod_kobj); | | 1233 | kobj_unload(mod->mod_kobj); |
1234 | fail2: | | 1234 | fail2: |
1235 | if (filedict != NULL) { | | 1235 | if (filedict != NULL) { |
1236 | prop_object_release(filedict); | | 1236 | prop_object_release(filedict); |
1237 | filedict = NULL; | | 1237 | filedict = NULL; |
1238 | } | | 1238 | } |
1239 | TAILQ_REMOVE(pending, mod, mod_chain); | | 1239 | TAILQ_REMOVE(pending, mod, mod_chain); |
1240 | kmem_free(mod, sizeof(*mod)); | | 1240 | kmem_free(mod, sizeof(*mod)); |
1241 | depth--; | | 1241 | depth--; |
1242 | return error; | | 1242 | return error; |
1243 | } | | 1243 | } |
1244 | | | 1244 | |
1245 | /* | | 1245 | /* |
1246 | * module_do_unload: | | 1246 | * module_do_unload: |
1247 | * | | 1247 | * |
1248 | * Helper routine: do the dirty work of unloading a module. | | 1248 | * Helper routine: do the dirty work of unloading a module. |
1249 | */ | | 1249 | */ |
1250 | static int | | 1250 | static int |
1251 | module_do_unload(const char *name, bool load_requires_force) | | 1251 | module_do_unload(const char *name, bool load_requires_force) |
1252 | { | | 1252 | { |
1253 | module_t *mod, *prev_active; | | 1253 | module_t *mod, *prev_active; |
1254 | int error; | | 1254 | int error; |
1255 | u_int i; | | 1255 | u_int i; |
1256 | | | 1256 | |
1257 | KASSERT(kernconfig_is_held()); | | 1257 | KASSERT(kernconfig_is_held()); |
1258 | KASSERT(name != NULL); | | 1258 | KASSERT(name != NULL); |
1259 | | | 1259 | |
1260 | module_print("unload requested for '%s' (%s)", name, | | 1260 | module_print("unload requested for '%s' (%s)", name, |
1261 | load_requires_force ? "TRUE" : "FALSE"); | | 1261 | load_requires_force ? "TRUE" : "FALSE"); |
1262 | mod = module_lookup(name); | | 1262 | mod = module_lookup(name); |
1263 | if (mod == NULL) { | | 1263 | if (mod == NULL) { |
1264 | module_error("module `%s' not found", name); | | 1264 | module_error("module `%s' not found", name); |
1265 | return ENOENT; | | 1265 | return ENOENT; |
1266 | } | | 1266 | } |
1267 | if (mod->mod_refcnt != 0) { | | 1267 | if (mod->mod_refcnt != 0) { |
1268 | module_print("module `%s' busy (%d refs)", name, | | 1268 | module_print("module `%s' busy (%d refs)", name, |
1269 | mod->mod_refcnt); | | 1269 | mod->mod_refcnt); |
1270 | return EBUSY; | | 1270 | return EBUSY; |
1271 | } | | 1271 | } |
1272 | | | 1272 | |
1273 | /* | | 1273 | /* |
1274 | * Builtin secmodels are there to stay. | | 1274 | * Builtin secmodels are there to stay. |
1275 | */ | | 1275 | */ |
1276 | if (mod->mod_source == MODULE_SOURCE_KERNEL && | | 1276 | if (mod->mod_source == MODULE_SOURCE_KERNEL && |
1277 | mod->mod_info->mi_class == MODULE_CLASS_SECMODEL) { | | 1277 | mod->mod_info->mi_class == MODULE_CLASS_SECMODEL) { |
1278 | module_print("cannot unload built-in secmodel module `%s'", | | 1278 | module_print("cannot unload built-in secmodel module `%s'", |
1279 | name); | | 1279 | name); |
1280 | return EPERM; | | 1280 | return EPERM; |
1281 | } | | 1281 | } |
1282 | | | 1282 | |
1283 | prev_active = module_active; | | 1283 | prev_active = module_active; |
1284 | module_active = mod; | | 1284 | module_active = mod; |
1285 | error = (*mod->mod_info->mi_modcmd)(MODULE_CMD_FINI, NULL); | | 1285 | error = (*mod->mod_info->mi_modcmd)(MODULE_CMD_FINI, NULL); |
1286 | module_active = prev_active; | | 1286 | module_active = prev_active; |
1287 | if (error != 0) { | | 1287 | if (error != 0) { |
1288 | module_print("cannot unload module `%s' error=%d", name, | | 1288 | module_print("cannot unload module `%s' error=%d", name, |
1289 | error); | | 1289 | error); |
1290 | return error; | | 1290 | return error; |
1291 | } | | 1291 | } |
1292 | module_count--; | | 1292 | module_count--; |
1293 | TAILQ_REMOVE(&module_list, mod, mod_chain); | | 1293 | TAILQ_REMOVE(&module_list, mod, mod_chain); |
1294 | for (i = 0; i < mod->mod_nrequired; i++) { | | 1294 | for (i = 0; i < mod->mod_nrequired; i++) { |
1295 | mod->mod_required[i]->mod_refcnt--; | | 1295 | mod->mod_required[i]->mod_refcnt--; |
1296 | } | | 1296 | } |
1297 | module_print("unloaded module `%s'", name); | | 1297 | module_print("unloaded module `%s'", name); |
1298 | if (mod->mod_kobj != NULL) { | | 1298 | if (mod->mod_kobj != NULL) { |
1299 | kobj_unload(mod->mod_kobj); | | 1299 | kobj_unload(mod->mod_kobj); |
1300 | } | | 1300 | } |
1301 | if (mod->mod_source == MODULE_SOURCE_KERNEL) { | | 1301 | if (mod->mod_source == MODULE_SOURCE_KERNEL) { |
1302 | mod->mod_nrequired = 0; /* will be re-parsed */ | | 1302 | mod->mod_nrequired = 0; /* will be re-parsed */ |
1303 | if (load_requires_force) | | 1303 | if (load_requires_force) |
1304 | module_require_force(mod); | | 1304 | module_require_force(mod); |
1305 | TAILQ_INSERT_TAIL(&module_builtins, mod, mod_chain); | | 1305 | TAILQ_INSERT_TAIL(&module_builtins, mod, mod_chain); |
1306 | module_builtinlist++; | | 1306 | module_builtinlist++; |
1307 | } else { | | 1307 | } else { |
1308 | kmem_free(mod, sizeof(*mod)); | | 1308 | kmem_free(mod, sizeof(*mod)); |
1309 | } | | 1309 | } |
1310 | module_gen++; | | 1310 | module_gen++; |
1311 | | | 1311 | |
1312 | return 0; | | 1312 | return 0; |
1313 | } | | 1313 | } |
1314 | | | 1314 | |
1315 | /* | | 1315 | /* |
1316 | * module_prime: | | 1316 | * module_prime: |
1317 | * | | 1317 | * |
1318 | * Push a module loaded by the bootloader onto our internal | | 1318 | * Push a module loaded by the bootloader onto our internal |
1319 | * list. | | 1319 | * list. |
1320 | */ | | 1320 | */ |
1321 | int | | 1321 | int |
1322 | module_prime(const char *name, void *base, size_t size) | | 1322 | module_prime(const char *name, void *base, size_t size) |
1323 | { | | 1323 | { |
1324 | __link_set_decl(modules, modinfo_t); | | 1324 | __link_set_decl(modules, modinfo_t); |
1325 | modinfo_t *const *mip; | | 1325 | modinfo_t *const *mip; |
1326 | module_t *mod; | | 1326 | module_t *mod; |
1327 | int error; | | 1327 | int error; |
1328 | | | 1328 | |
1329 | /* Check for module name same as a built-in module */ | | 1329 | /* Check for module name same as a built-in module */ |
1330 | | | 1330 | |
1331 | __link_set_foreach(mip, modules) { | | 1331 | __link_set_foreach(mip, modules) { |
1332 | if (*mip == &module_dummy) | | 1332 | if (*mip == &module_dummy) |
1333 | continue; | | 1333 | continue; |
1334 | if (strcmp((*mip)->mi_name, name) == 0) { | | 1334 | if (strcmp((*mip)->mi_name, name) == 0) { |
1335 | module_error("module `%s' pushed by boot loader " | | 1335 | module_error("module `%s' pushed by boot loader " |
1336 | "already exists", name); | | 1336 | "already exists", name); |
1337 | return EEXIST; | | 1337 | return EEXIST; |
1338 | } | | 1338 | } |
1339 | } | | 1339 | } |
1340 | | | 1340 | |
1341 | /* Also eliminate duplicate boolist entries */ | | 1341 | /* Also eliminate duplicate boolist entries */ |
1342 | | | 1342 | |
1343 | TAILQ_FOREACH(mod, &module_bootlist, mod_chain) { | | 1343 | TAILQ_FOREACH(mod, &module_bootlist, mod_chain) { |
1344 | if (strcmp(mod->mod_info->mi_name, name) == 0) { | | 1344 | if (strcmp(mod->mod_info->mi_name, name) == 0) { |
1345 | module_error("duplicate bootlist entry for module " | | 1345 | module_error("duplicate bootlist entry for module " |
1346 | "`%s'", name); | | 1346 | "`%s'", name); |
1347 | return EEXIST; | | 1347 | return EEXIST; |
1348 | } | | 1348 | } |
1349 | } | | 1349 | } |
1350 | | | 1350 | |
1351 | mod = module_newmodule(MODULE_SOURCE_BOOT); | | 1351 | mod = module_newmodule(MODULE_SOURCE_BOOT); |
1352 | if (mod == NULL) { | | 1352 | if (mod == NULL) { |
1353 | return ENOMEM; | | 1353 | return ENOMEM; |
1354 | } | | 1354 | } |
1355 | | | 1355 | |
1356 | error = kobj_load_mem(&mod->mod_kobj, name, base, size); | | 1356 | error = kobj_load_mem(&mod->mod_kobj, name, base, size); |
1357 | if (error != 0) { | | 1357 | if (error != 0) { |
1358 | kmem_free(mod, sizeof(*mod)); | | 1358 | kmem_free(mod, sizeof(*mod)); |
1359 | module_error("unable to load `%s' pushed by boot loader, " | | 1359 | module_error("unable to load `%s' pushed by boot loader, " |
1360 | "error %d", name, error); | | 1360 | "error %d", name, error); |
1361 | return error; | | 1361 | return error; |
1362 | } | | 1362 | } |
1363 | error = module_fetch_info(mod); | | 1363 | error = module_fetch_info(mod); |
1364 | if (error != 0) { | | 1364 | if (error != 0) { |
1365 | kobj_unload(mod->mod_kobj); | | 1365 | kobj_unload(mod->mod_kobj); |
1366 | kmem_free(mod, sizeof(*mod)); | | 1366 | kmem_free(mod, sizeof(*mod)); |
1367 | module_error("unable to fetch_info for `%s' pushed by boot " | | 1367 | module_error("unable to fetch_info for `%s' pushed by boot " |
1368 | "loader, error %d", name, error); | | 1368 | "loader, error %d", name, error); |
1369 | return error; | | 1369 | return error; |
1370 | } | | 1370 | } |
1371 | | | 1371 | |
1372 | TAILQ_INSERT_TAIL(&module_bootlist, mod, mod_chain); | | 1372 | TAILQ_INSERT_TAIL(&module_bootlist, mod, mod_chain); |
1373 | | | 1373 | |
1374 | return 0; | | 1374 | return 0; |
1375 | } | | 1375 | } |
1376 | | | 1376 | |
1377 | /* | | 1377 | /* |
1378 | * module_fetch_into: | | 1378 | * module_fetch_into: |
1379 | * | | 1379 | * |
1380 | * Fetch modinfo record from a loaded module. | | 1380 | * Fetch modinfo record from a loaded module. |
1381 | */ | | 1381 | */ |
1382 | static int | | 1382 | static int |
1383 | module_fetch_info(module_t *mod) | | 1383 | module_fetch_info(module_t *mod) |
1384 | { | | 1384 | { |
1385 | int error; | | 1385 | int error; |
1386 | void *addr; | | 1386 | void *addr; |
1387 | size_t size; | | 1387 | size_t size; |
1388 | | | 1388 | |
1389 | /* | | 1389 | /* |
1390 | * Find module info record and check compatibility. | | 1390 | * Find module info record and check compatibility. |
1391 | */ | | 1391 | */ |
1392 | error = kobj_find_section(mod->mod_kobj, "link_set_modules", | | 1392 | error = kobj_find_section(mod->mod_kobj, "link_set_modules", |
1393 | &addr, &size); | | 1393 | &addr, &size); |
1394 | if (error != 0) { | | 1394 | if (error != 0) { |
1395 | module_error("`link_set_modules' section not present, " | | 1395 | module_error("`link_set_modules' section not present, " |
1396 | "error %d", error); | | 1396 | "error %d", error); |
1397 | return error; | | 1397 | return error; |
1398 | } | | 1398 | } |
1399 | if (size != sizeof(modinfo_t **)) { | | 1399 | if (size != sizeof(modinfo_t **)) { |
1400 | module_error("`link_set_modules' section wrong size %zu != %zu", | | 1400 | module_error("`link_set_modules' section wrong size %zu != %zu", |
1401 | size, sizeof(modinfo_t **)); | | 1401 | size, sizeof(modinfo_t **)); |
1402 | return ENOEXEC; | | 1402 | return ENOEXEC; |
1403 | } | | 1403 | } |
1404 | mod->mod_info = *(modinfo_t **)addr; | | 1404 | mod->mod_info = *(modinfo_t **)addr; |
1405 | | | 1405 | |
1406 | return 0; | | 1406 | return 0; |
1407 | } | | 1407 | } |
1408 | | | 1408 | |
1409 | /* | | 1409 | /* |
1410 | * module_find_section: | | 1410 | * module_find_section: |
1411 | * | | 1411 | * |
1412 | * Allows a module that is being initialized to look up a section | | 1412 | * Allows a module that is being initialized to look up a section |
1413 | * within its ELF object. | | 1413 | * within its ELF object. |
1414 | */ | | 1414 | */ |
1415 | int | | 1415 | int |
1416 | module_find_section(const char *name, void **addr, size_t *size) | | 1416 | module_find_section(const char *name, void **addr, size_t *size) |
1417 | { | | 1417 | { |
1418 | | | 1418 | |
1419 | KASSERT(kernconfig_is_held()); | | 1419 | KASSERT(kernconfig_is_held()); |
1420 | KASSERT(module_active != NULL); | | 1420 | KASSERT(module_active != NULL); |
1421 | | | 1421 | |
1422 | return kobj_find_section(module_active->mod_kobj, name, addr, size); | | 1422 | return kobj_find_section(module_active->mod_kobj, name, addr, size); |
1423 | } | | 1423 | } |
1424 | | | 1424 | |
1425 | /* | | 1425 | /* |
1426 | * module_thread: | | 1426 | * module_thread: |
1427 | * | | 1427 | * |
1428 | * Automatically unload modules. We try once to unload autoloaded | | 1428 | * Automatically unload modules. We try once to unload autoloaded |
1429 | * modules after module_autotime seconds. If the system is under | | 1429 | * modules after module_autotime seconds. If the system is under |
1430 | * severe memory pressure, we'll try unloading all modules, else if | | 1430 | * severe memory pressure, we'll try unloading all modules, else if |
1431 | * module_autotime is zero, we don't try to unload, even if the | | 1431 | * module_autotime is zero, we don't try to unload, even if the |
1432 | * module was previously scheduled for unload. | | 1432 | * module was previously scheduled for unload. |
1433 | */ | | 1433 | */ |
1434 | static void | | 1434 | static void |
1435 | module_thread(void *cookie) | | 1435 | module_thread(void *cookie) |
1436 | { | | 1436 | { |
1437 | module_t *mod, *next; | | 1437 | module_t *mod, *next; |
1438 | modinfo_t *mi; | | 1438 | modinfo_t *mi; |
1439 | int error; | | 1439 | int error; |
1440 | | | 1440 | |
1441 | for (;;) { | | 1441 | for (;;) { |
1442 | kernconfig_lock(); | | 1442 | kernconfig_lock(); |
1443 | for (mod = TAILQ_FIRST(&module_list); mod != NULL; mod = next) { | | 1443 | for (mod = TAILQ_FIRST(&module_list); mod != NULL; mod = next) { |
1444 | next = TAILQ_NEXT(mod, mod_chain); | | 1444 | next = TAILQ_NEXT(mod, mod_chain); |
1445 | | | 1445 | |
1446 | /* skip built-in modules */ | | 1446 | /* skip built-in modules */ |
1447 | if (mod->mod_source == MODULE_SOURCE_KERNEL) | | 1447 | if (mod->mod_source == MODULE_SOURCE_KERNEL) |
1448 | continue; | | 1448 | continue; |
1449 | /* skip modules that weren't auto-loaded */ | | 1449 | /* skip modules that weren't auto-loaded */ |
1450 | if ((mod->mod_flags & MODFLG_AUTO_LOADED) == 0) | | 1450 | if ((mod->mod_flags & MODFLG_AUTO_LOADED) == 0) |
1451 | continue; | | 1451 | continue; |
1452 | | | 1452 | |
1453 | if (uvmexp.free < uvmexp.freemin) { | | 1453 | if (uvmexp.free < uvmexp.freemin) { |
1454 | module_thread_ticks = hz; | | 1454 | module_thread_ticks = hz; |
1455 | } else if (module_autotime == 0 || | | 1455 | } else if (module_autotime == 0 || |
1456 | mod->mod_autotime == 0) { | | 1456 | mod->mod_autotime == 0) { |
1457 | continue; | | 1457 | continue; |
1458 | } else if (time_second < mod->mod_autotime) { | | 1458 | } else if (time_second < mod->mod_autotime) { |
1459 | module_thread_ticks = hz; | | 1459 | module_thread_ticks = hz; |
1460 | continue; | | 1460 | continue; |
1461 | } else { | | 1461 | } else { |
1462 | mod->mod_autotime = 0; | | 1462 | mod->mod_autotime = 0; |
1463 | } | | 1463 | } |
1464 | | | 1464 | |
1465 | /* | | 1465 | /* |
1466 | * If this module wants to avoid autounload then | | 1466 | * If this module wants to avoid autounload then |
1467 | * skip it. Some modules can ping-pong in and out | | 1467 | * skip it. Some modules can ping-pong in and out |
1468 | * because their use is transient but often. | | 1468 | * because their use is transient but often. |
1469 | * Example: exec_script. | | 1469 | * Example: exec_script. |
1470 | */ | | 1470 | */ |
1471 | mi = mod->mod_info; | | 1471 | mi = mod->mod_info; |
1472 | error = (*mi->mi_modcmd)(MODULE_CMD_AUTOUNLOAD, NULL); | | 1472 | error = (*mi->mi_modcmd)(MODULE_CMD_AUTOUNLOAD, NULL); |
1473 | if (error == 0 || error == ENOTTY) { | | 1473 | if (error == 0 || error == ENOTTY) { |
1474 | (void)module_do_unload(mi->mi_name, false); | | 1474 | (void)module_do_unload(mi->mi_name, false); |
1475 | } else | | 1475 | } else |
1476 | module_print("module `%s' declined to be " | | 1476 | module_print("module `%s' declined to be " |
1477 | "auto-unloaded error=%d", mi->mi_name, | | 1477 | "auto-unloaded error=%d", mi->mi_name, |
1478 | error); | | 1478 | error); |
1479 | } | | 1479 | } |
1480 | kernconfig_unlock(); | | 1480 | kernconfig_unlock(); |
1481 | | | 1481 | |
1482 | mutex_enter(&module_thread_lock); | | 1482 | mutex_enter(&module_thread_lock); |
1483 | (void)cv_timedwait(&module_thread_cv, &module_thread_lock, | | 1483 | (void)cv_timedwait(&module_thread_cv, &module_thread_lock, |
1484 | module_thread_ticks); | | 1484 | module_thread_ticks); |
1485 | module_thread_ticks = 0; | | 1485 | module_thread_ticks = 0; |
1486 | mutex_exit(&module_thread_lock); | | 1486 | mutex_exit(&module_thread_lock); |
1487 | } | | 1487 | } |
1488 | } | | 1488 | } |
1489 | | | 1489 | |
1490 | /* | | 1490 | /* |
1491 | * module_thread: | | 1491 | * module_thread: |
1492 | * | | 1492 | * |
1493 | * Kick the module thread into action, perhaps because the | | 1493 | * Kick the module thread into action, perhaps because the |
1494 | * system is low on memory. | | 1494 | * system is low on memory. |
1495 | */ | | 1495 | */ |
1496 | void | | 1496 | void |
1497 | module_thread_kick(void) | | 1497 | module_thread_kick(void) |
1498 | { | | 1498 | { |
1499 | | | 1499 | |
1500 | mutex_enter(&module_thread_lock); | | 1500 | mutex_enter(&module_thread_lock); |
1501 | module_thread_ticks = hz; | | 1501 | module_thread_ticks = hz; |
1502 | cv_broadcast(&module_thread_cv); | | 1502 | cv_broadcast(&module_thread_cv); |
1503 | mutex_exit(&module_thread_lock); | | 1503 | mutex_exit(&module_thread_lock); |
1504 | } | | 1504 | } |
1505 | | | 1505 | |
1506 | #ifdef DDB | | 1506 | #ifdef DDB |
1507 | /* | | 1507 | /* |
1508 | * module_whatis: | | 1508 | * module_whatis: |
1509 | * | | 1509 | * |
1510 | * Helper routine for DDB. | | 1510 | * Helper routine for DDB. |
1511 | */ | | 1511 | */ |
1512 | void | | 1512 | void |
1513 | module_whatis(uintptr_t addr, void (*pr)(const char *, ...)) | | 1513 | module_whatis(uintptr_t addr, void (*pr)(const char *, ...)) |
1514 | { | | 1514 | { |
1515 | module_t *mod; | | 1515 | module_t *mod; |
1516 | size_t msize; | | 1516 | size_t msize; |
1517 | vaddr_t maddr; | | 1517 | vaddr_t maddr; |
1518 | | | 1518 | |
1519 | TAILQ_FOREACH(mod, &module_list, mod_chain) { | | 1519 | TAILQ_FOREACH(mod, &module_list, mod_chain) { |
1520 | if (mod->mod_kobj == NULL) { | | 1520 | if (mod->mod_kobj == NULL) { |
1521 | continue; | | 1521 | continue; |
1522 | } | | 1522 | } |
1523 | if (kobj_stat(mod->mod_kobj, &maddr, &msize) != 0) | | 1523 | if (kobj_stat(mod->mod_kobj, &maddr, &msize) != 0) |
1524 | continue; | | 1524 | continue; |
1525 | if (addr < maddr || addr >= maddr + msize) { | | 1525 | if (addr < maddr || addr >= maddr + msize) { |
1526 | continue; | | 1526 | continue; |
1527 | } | | 1527 | } |
1528 | (*pr)("%p is %p+%zu, in kernel module `%s'\n", | | 1528 | (*pr)("%p is %p+%zu, in kernel module `%s'\n", |
1529 | (void *)addr, (void *)maddr, | | 1529 | (void *)addr, (void *)maddr, |
1530 | (size_t)(addr - maddr), mod->mod_info->mi_name); | | 1530 | (size_t)(addr - maddr), mod->mod_info->mi_name); |
1531 | } | | 1531 | } |
1532 | } | | 1532 | } |
1533 | | | 1533 | |
1534 | /* | | 1534 | /* |
1535 | * module_print_list: | | 1535 | * module_print_list: |
1536 | * | | 1536 | * |
1537 | * Helper routine for DDB. | | 1537 | * Helper routine for DDB. |
1538 | */ | | 1538 | */ |
1539 | void | | 1539 | void |
1540 | module_print_list(void (*pr)(const char *, ...)) | | 1540 | module_print_list(void (*pr)(const char *, ...)) |
1541 | { | | 1541 | { |
1542 | const char *src; | | 1542 | const char *src; |
1543 | module_t *mod; | | 1543 | module_t *mod; |
1544 | size_t msize; | | 1544 | size_t msize; |
1545 | vaddr_t maddr; | | 1545 | vaddr_t maddr; |
1546 | | | 1546 | |
1547 | (*pr)("%16s %16s %8s %8s\n", "NAME", "TEXT/DATA", "SIZE", "SOURCE"); | | 1547 | (*pr)("%16s %16s %8s %8s\n", "NAME", "TEXT/DATA", "SIZE", "SOURCE"); |
1548 | | | 1548 | |
1549 | TAILQ_FOREACH(mod, &module_list, mod_chain) { | | 1549 | TAILQ_FOREACH(mod, &module_list, mod_chain) { |
1550 | switch (mod->mod_source) { | | 1550 | switch (mod->mod_source) { |
1551 | case MODULE_SOURCE_KERNEL: | | 1551 | case MODULE_SOURCE_KERNEL: |
1552 | src = "builtin"; | | 1552 | src = "builtin"; |
1553 | break; | | 1553 | break; |
1554 | case MODULE_SOURCE_FILESYS: | | 1554 | case MODULE_SOURCE_FILESYS: |
1555 | src = "filesys"; | | 1555 | src = "filesys"; |
1556 | break; | | 1556 | break; |
1557 | case MODULE_SOURCE_BOOT: | | 1557 | case MODULE_SOURCE_BOOT: |
1558 | src = "boot"; | | 1558 | src = "boot"; |
1559 | break; | | 1559 | break; |
1560 | default: | | 1560 | default: |
1561 | src = "unknown"; | | 1561 | src = "unknown"; |
1562 | break; | | 1562 | break; |
1563 | } | | 1563 | } |
1564 | if (mod->mod_kobj == NULL) { | | 1564 | if (mod->mod_kobj == NULL) { |
1565 | maddr = 0; | | 1565 | maddr = 0; |
1566 | msize = 0; | | 1566 | msize = 0; |
1567 | } else if (kobj_stat(mod->mod_kobj, &maddr, &msize) != 0) | | 1567 | } else if (kobj_stat(mod->mod_kobj, &maddr, &msize) != 0) |
1568 | continue; | | 1568 | continue; |
1569 | (*pr)("%16s %16lx %8ld %8s\n", mod->mod_info->mi_name, | | 1569 | (*pr)("%16s %16lx %8ld %8s\n", mod->mod_info->mi_name, |
1570 | (long)maddr, (long)msize, src); | | 1570 | (long)maddr, (long)msize, src); |
1571 | } | | 1571 | } |
1572 | } | | 1572 | } |
1573 | #endif /* DDB */ | | 1573 | #endif /* DDB */ |
1574 | | | 1574 | |
1575 | static bool | | 1575 | static bool |
1576 | module_merge_dicts(prop_dictionary_t existing_dict, | | 1576 | module_merge_dicts(prop_dictionary_t existing_dict, |
1577 | const prop_dictionary_t new_dict) | | 1577 | const prop_dictionary_t new_dict) |
1578 | { | | 1578 | { |
1579 | prop_dictionary_keysym_t props_keysym; | | 1579 | prop_dictionary_keysym_t props_keysym; |
1580 | prop_object_iterator_t props_iter; | | 1580 | prop_object_iterator_t props_iter; |
1581 | prop_object_t props_obj; | | 1581 | prop_object_t props_obj; |
1582 | const char *props_key; | | 1582 | const char *props_key; |
1583 | bool error; | | 1583 | bool error; |
1584 | | | 1584 | |
1585 | if (new_dict == NULL) { /* nothing to merge */ | | 1585 | if (new_dict == NULL) { /* nothing to merge */ |
1586 | return true; | | 1586 | return true; |
1587 | } | | 1587 | } |
1588 | | | 1588 | |
1589 | error = false; | | 1589 | error = false; |
1590 | props_iter = prop_dictionary_iterator(new_dict); | | 1590 | props_iter = prop_dictionary_iterator(new_dict); |
1591 | if (props_iter == NULL) { | | 1591 | if (props_iter == NULL) { |
1592 | return false; | | 1592 | return false; |
1593 | } | | 1593 | } |
1594 | | | 1594 | |
1595 | while ((props_obj = prop_object_iterator_next(props_iter)) != NULL) { | | 1595 | while ((props_obj = prop_object_iterator_next(props_iter)) != NULL) { |
1596 | props_keysym = (prop_dictionary_keysym_t)props_obj; | | 1596 | props_keysym = (prop_dictionary_keysym_t)props_obj; |
1597 | props_key = prop_dictionary_keysym_cstring_nocopy(props_keysym); | | 1597 | props_key = prop_dictionary_keysym_cstring_nocopy(props_keysym); |
1598 | props_obj = prop_dictionary_get_keysym(new_dict, props_keysym); | | 1598 | props_obj = prop_dictionary_get_keysym(new_dict, props_keysym); |
1599 | if ((props_obj == NULL) || !prop_dictionary_set(existing_dict, | | 1599 | if ((props_obj == NULL) || !prop_dictionary_set(existing_dict, |
1600 | props_key, props_obj)) { | | 1600 | props_key, props_obj)) { |
1601 | error = true; | | 1601 | error = true; |
1602 | goto out; | | 1602 | goto out; |
1603 | } | | 1603 | } |
1604 | } | | 1604 | } |
1605 | error = false; | | 1605 | error = false; |
1606 | | | 1606 | |
1607 | out: | | 1607 | out: |
1608 | prop_object_iterator_release(props_iter); | | 1608 | prop_object_iterator_release(props_iter); |
1609 | | | 1609 | |
1610 | return !error; | | 1610 | return !error; |
1611 | } | | 1611 | } |