1 /*- 2 * Copyright (c) 1997 Doug Rabson 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29 #include <sys/param.h> 30 #include <sys/kernel.h> 31 #include <sys/systm.h> 32 #include <sys/eventhandler.h> 33 #include <sys/malloc.h> 34 #include <sys/sysproto.h> 35 #include <sys/sysent.h> 36 #include <sys/proc.h> 37 #include <sys/lock.h> 38 #include <sys/mutex.h> 39 #include <sys/sx.h> 40 #include <sys/module.h> 41 #include <sys/linker.h> 42 43 static MALLOC_DEFINE(M_MODULE, "module", "module data structures"); 44 45 typedef TAILQ_HEAD(, module) modulelist_t; 46 struct module { 47 TAILQ_ENTRY(module) link; /* chain together all modules */ 48 TAILQ_ENTRY(module) flink; /* all modules in a file */ 49 struct linker_file *file; /* file which contains this module */ 50 int refs; /* reference count */ 51 int id; /* unique id number */ 52 char *name; /* module name */ 53 modeventhand_t handler; /* event handler */ 54 void *arg; /* argument for handler */ 55 modspecific_t data; /* module specific data */ 56 }; 57 58 #define MOD_EVENT(mod, type) (mod)->handler((mod), (type), (mod)->arg) 59 60 static modulelist_t modules; 61 struct sx modules_sx; 62 static int nextid = 1; 63 static void module_shutdown(void *, int); 64 65 static int 66 modevent_nop(module_t mod, int what, void *arg) 67 { 68 return (0); 69 } 70 71 static void 72 module_init(void *arg) 73 { 74 75 sx_init(&modules_sx, "module subsystem sx lock"); 76 TAILQ_INIT(&modules); 77 EVENTHANDLER_REGISTER(shutdown_post_sync, module_shutdown, NULL, 78 SHUTDOWN_PRI_DEFAULT); 79 } 80 81 SYSINIT(module, SI_SUB_KLD, SI_ORDER_FIRST, module_init, 0) 82 83 static void 84 module_shutdown(void *arg1, int arg2) 85 { 86 module_t mod; 87 88 MOD_SLOCK; 89 TAILQ_FOREACH(mod, &modules, link) 90 MOD_EVENT(mod, MOD_SHUTDOWN); 91 MOD_SUNLOCK; 92 } 93 94 void 95 module_register_init(const void *arg) 96 { 97 const moduledata_t *data = (const moduledata_t *)arg; 98 int error; 99 module_t mod; 100 101 MOD_SLOCK; 102 mod = module_lookupbyname(data->name); 103 if (mod == NULL) 104 panic("module_register_init: module named %s not found\n", 105 data->name); 106 MOD_SUNLOCK; 107 error = MOD_EVENT(mod, MOD_LOAD); 108 if (error) { 109 MOD_EVENT(mod, MOD_UNLOAD); 110 MOD_XLOCK; 111 module_release(mod); 112 MOD_XUNLOCK; 113 printf("module_register_init: MOD_LOAD (%s, %p, %p) error" 114 " %d\n", data->name, (void *)data->evhand, data->priv, 115 error); 116 } 117 } 118 119 int 120 module_register(const moduledata_t *data, linker_file_t container) 121 { 122 size_t namelen; 123 module_t newmod; 124 125 MOD_SLOCK; 126 newmod = module_lookupbyname(data->name); 127 if (newmod != NULL) { 128 MOD_SUNLOCK; 129 printf("module_register: module %s already exists!\n", 130 data->name); 131 return (EEXIST); 132 } 133 MOD_SUNLOCK; 134 namelen = strlen(data->name) + 1; 135 newmod = malloc(sizeof(struct module) + namelen, M_MODULE, M_WAITOK); 136 if (newmod == NULL) 137 return (ENOMEM); 138 MOD_XLOCK; 139 newmod->refs = 1; 140 newmod->id = nextid++; 141 newmod->name = (char *)(newmod + 1); 142 strcpy(newmod->name, data->name); 143 newmod->handler = data->evhand ? data->evhand : modevent_nop; 144 newmod->arg = data->priv; 145 bzero(&newmod->data, sizeof(newmod->data)); 146 TAILQ_INSERT_TAIL(&modules, newmod, link); 147 148 if (container) 149 TAILQ_INSERT_TAIL(&container->modules, newmod, flink); 150 newmod->file = container; 151 MOD_XUNLOCK; 152 return (0); 153 } 154 155 void 156 module_reference(module_t mod) 157 { 158 159 MOD_XLOCK_ASSERT; 160 161 MOD_DPF(REFS, ("module_reference: before, refs=%d\n", mod->refs)); 162 mod->refs++; 163 } 164 165 void 166 module_release(module_t mod) 167 { 168 169 MOD_XLOCK_ASSERT; 170 171 if (mod->refs <= 0) 172 panic("module_release: bad reference count"); 173 174 MOD_DPF(REFS, ("module_release: before, refs=%d\n", mod->refs)); 175 176 mod->refs--; 177 if (mod->refs == 0) { 178 TAILQ_REMOVE(&modules, mod, link); 179 if (mod->file) 180 TAILQ_REMOVE(&mod->file->modules, mod, flink); 181 MOD_XUNLOCK; 182 free(mod, M_MODULE); 183 MOD_XLOCK; 184 } 185 } 186 187 module_t 188 module_lookupbyname(const char *name) 189 { 190 module_t mod; 191 int err; 192 193 MOD_LOCK_ASSERT; 194 195 TAILQ_FOREACH(mod, &modules, link) { 196 err = strcmp(mod->name, name); 197 if (err == 0) 198 return (mod); 199 } 200 return (NULL); 201 } 202 203 module_t 204 module_lookupbyid(int modid) 205 { 206 module_t mod; 207 208 MOD_LOCK_ASSERT; 209 210 TAILQ_FOREACH(mod, &modules, link) 211 if (mod->id == modid) 212 return(mod); 213 return (NULL); 214 } 215 216 int 217 module_unload(module_t mod) 218 { 219 220 return (MOD_EVENT(mod, MOD_UNLOAD)); 221 } 222 223 int 224 module_getid(module_t mod) 225 { 226 227 MOD_LOCK_ASSERT; 228 return (mod->id); 229 } 230 231 module_t 232 module_getfnext(module_t mod) 233 { 234 235 MOD_LOCK_ASSERT; 236 return (TAILQ_NEXT(mod, flink)); 237 } 238 239 void 240 module_setspecific(module_t mod, modspecific_t *datap) 241 { 242 243 MOD_XLOCK_ASSERT; 244 mod->data = *datap; 245 } 246 247 /* 248 * Syscalls. 249 */ 250 /* 251 * MPSAFE 252 */ 253 int 254 modnext(struct thread *td, struct modnext_args *uap) 255 { 256 module_t mod; 257 int error = 0; 258 259 mtx_lock(&Giant); 260 261 td->td_retval[0] = -1; 262 MOD_SLOCK; 263 if (SCARG(uap, modid) == 0) { 264 mod = TAILQ_FIRST(&modules); 265 if (mod) 266 td->td_retval[0] = mod->id; 267 else 268 error = ENOENT; 269 goto done2; 270 } 271 mod = module_lookupbyid(SCARG(uap, modid)); 272 if (mod == NULL) { 273 error = ENOENT; 274 goto done2; 275 } 276 if (TAILQ_NEXT(mod, link)) 277 td->td_retval[0] = TAILQ_NEXT(mod, link)->id; 278 else 279 td->td_retval[0] = 0; 280 done2: 281 MOD_SUNLOCK; 282 mtx_unlock(&Giant); 283 return (error); 284 } 285 286 /* 287 * MPSAFE 288 */ 289 int 290 modfnext(struct thread *td, struct modfnext_args *uap) 291 { 292 module_t mod; 293 int error; 294 295 td->td_retval[0] = -1; 296 297 mtx_lock(&Giant); 298 299 MOD_SLOCK; 300 mod = module_lookupbyid(SCARG(uap, modid)); 301 if (mod == NULL) { 302 error = ENOENT; 303 } else { 304 error = 0; 305 if (TAILQ_NEXT(mod, flink)) 306 td->td_retval[0] = TAILQ_NEXT(mod, flink)->id; 307 else 308 td->td_retval[0] = 0; 309 } 310 MOD_SUNLOCK; 311 mtx_unlock(&Giant); 312 return (error); 313 } 314 315 struct module_stat_v1 { 316 int version; /* set to sizeof(struct module_stat) */ 317 char name[MAXMODNAME]; 318 int refs; 319 int id; 320 }; 321 322 /* 323 * MPSAFE 324 */ 325 int 326 modstat(struct thread *td, struct modstat_args *uap) 327 { 328 module_t mod; 329 modspecific_t data; 330 int error = 0; 331 int id, namelen, refs, version; 332 struct module_stat *stat; 333 char *name; 334 335 mtx_lock(&Giant); 336 337 MOD_SLOCK; 338 mod = module_lookupbyid(SCARG(uap, modid)); 339 if (mod == NULL) { 340 MOD_SUNLOCK; 341 error = ENOENT; 342 goto out; 343 } 344 id = mod->id; 345 refs = mod->refs; 346 name = mod->name; 347 data = mod->data; 348 MOD_SUNLOCK; 349 stat = SCARG(uap, stat); 350 351 /* 352 * Check the version of the user's structure. 353 */ 354 if ((error = copyin(&stat->version, &version, sizeof(version))) != 0) 355 goto out; 356 if (version != sizeof(struct module_stat_v1) 357 && version != sizeof(struct module_stat)) { 358 error = EINVAL; 359 goto out; 360 } 361 namelen = strlen(mod->name) + 1; 362 if (namelen > MAXMODNAME) 363 namelen = MAXMODNAME; 364 if ((error = copyout(name, &stat->name[0], namelen)) != 0) 365 goto out; 366 367 if ((error = copyout(&refs, &stat->refs, sizeof(int))) != 0) 368 goto out; 369 if ((error = copyout(&id, &stat->id, sizeof(int))) != 0) 370 goto out; 371 372 /* 373 * >v1 stat includes module data. 374 */ 375 if (version == sizeof(struct module_stat)) { 376 if ((error = copyout(&data, &stat->data, 377 sizeof(data))) != 0) 378 goto out; 379 } 380 td->td_retval[0] = 0; 381 out: 382 mtx_unlock(&Giant); 383 return (error); 384 } 385 386 /* 387 * MPSAFE 388 */ 389 int 390 modfind(struct thread *td, struct modfind_args *uap) 391 { 392 int error = 0; 393 char name[MAXMODNAME]; 394 module_t mod; 395 396 if ((error = copyinstr(SCARG(uap, name), name, sizeof name, 0)) != 0) 397 goto out; 398 399 mtx_lock(&Giant); 400 MOD_SLOCK; 401 mod = module_lookupbyname(name); 402 if (mod == NULL) 403 error = ENOENT; 404 else 405 td->td_retval[0] = module_getid(mod); 406 MOD_SUNLOCK; 407 mtx_unlock(&Giant); 408 out: 409 return (error); 410 } 411