kern_module.c revision 176252
134192Sjdp/*- 234192Sjdp * Copyright (c) 1997 Doug Rabson 334192Sjdp * All rights reserved. 434192Sjdp * 534192Sjdp * Redistribution and use in source and binary forms, with or without 634192Sjdp * modification, are permitted provided that the following conditions 734192Sjdp * are met: 834192Sjdp * 1. Redistributions of source code must retain the above copyright 934192Sjdp * notice, this list of conditions and the following disclaimer. 1034192Sjdp * 2. Redistributions in binary form must reproduce the above copyright 1134192Sjdp * notice, this list of conditions and the following disclaimer in the 1234192Sjdp * documentation and/or other materials provided with the distribution. 1334192Sjdp * 1434192Sjdp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1534192Sjdp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1634192Sjdp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1734192Sjdp * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1834192Sjdp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1934192Sjdp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2034192Sjdp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2134192Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2234192Sjdp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2334192Sjdp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2434192Sjdp * SUCH DAMAGE. 2534192Sjdp */ 2634192Sjdp 2734192Sjdp#include "opt_compat.h" 2834192Sjdp 2934192Sjdp#include <sys/cdefs.h> 3034192Sjdp__FBSDID("$FreeBSD: head/sys/kern/kern_module.c 176252 2008-02-13 21:34:06Z jhb $"); 3134192Sjdp 3234192Sjdp#include <sys/param.h> 3334192Sjdp#include <sys/kernel.h> 3434192Sjdp#include <sys/systm.h> 3534192Sjdp#include <sys/eventhandler.h> 3650476Speter#include <sys/malloc.h> 3734192Sjdp#include <sys/sysproto.h> 3834192Sjdp#include <sys/sysent.h> 3934192Sjdp#include <sys/proc.h> 4034192Sjdp#include <sys/lock.h> 4134192Sjdp#include <sys/mutex.h> 4234192Sjdp#include <sys/reboot.h> 4334192Sjdp#include <sys/sx.h> 4434192Sjdp#include <sys/module.h> 4534192Sjdp#include <sys/linker.h> 4634192Sjdp 4734192Sjdpstatic MALLOC_DEFINE(M_MODULE, "module", "module data structures"); 4834192Sjdp 4934192Sjdptypedef TAILQ_HEAD(, module) modulelist_t; 5034192Sjdpstruct module { 5134192Sjdp TAILQ_ENTRY(module) link; /* chain together all modules */ 5269793Sobrien TAILQ_ENTRY(module) flink; /* all modules in a file */ 53110803Skan struct linker_file *file; /* file which contains this module */ 54119255Simp int refs; /* reference count */ 55110803Skan int id; /* unique id number */ 5634192Sjdp char *name; /* module name */ 5734192Sjdp modeventhand_t handler; /* event handler */ 5834192Sjdp void *arg; /* argument for handler */ 5934192Sjdp modspecific_t data; /* module specific data */ 6034192Sjdp}; 6134192Sjdp 6234192Sjdp#define MOD_EVENT(mod, type) (mod)->handler((mod), (type), (mod)->arg) 6334192Sjdp 6434192Sjdpstatic modulelist_t modules; 6534192Sjdpstruct sx modules_sx; 6634192Sjdpstatic int nextid = 1; 6734192Sjdpstatic void module_shutdown(void *, int); 6834192Sjdp 6934192Sjdpstatic int 7034192Sjdpmodevent_nop(module_t mod, int what, void *arg) 7134192Sjdp{ 7234192Sjdp 7334192Sjdp switch(what) { 7434192Sjdp case MOD_LOAD: 7534192Sjdp return (0); 7634192Sjdp case MOD_UNLOAD: 7734192Sjdp return (EBUSY); 7834192Sjdp default: 7934192Sjdp return (EOPNOTSUPP); 8034192Sjdp } 8134192Sjdp} 8234192Sjdp 8334192Sjdpstatic void 8434192Sjdpmodule_init(void *arg) 8534192Sjdp{ 8634192Sjdp 8734192Sjdp sx_init(&modules_sx, "module subsystem sx lock"); 8834192Sjdp TAILQ_INIT(&modules); 8934192Sjdp EVENTHANDLER_REGISTER(shutdown_final, module_shutdown, NULL, 9034192Sjdp SHUTDOWN_PRI_DEFAULT); 9134192Sjdp} 9234192Sjdp 9334192SjdpSYSINIT(module, SI_SUB_KLD, SI_ORDER_FIRST, module_init, 0) 9434192Sjdp 9534192Sjdpstatic void 9634192Sjdpmodule_shutdown(void *arg1, int arg2) 9734192Sjdp{ 9834192Sjdp module_t mod; 9934192Sjdp 10034192Sjdp if (arg2 & RB_NOSYNC) 10134192Sjdp return; 10234192Sjdp mtx_lock(&Giant); 10334192Sjdp MOD_SLOCK; 10434192Sjdp TAILQ_FOREACH(mod, &modules, link) 10534192Sjdp MOD_EVENT(mod, MOD_SHUTDOWN); 10634192Sjdp MOD_SUNLOCK; 10734192Sjdp mtx_unlock(&Giant); 10834192Sjdp} 10934192Sjdp 11034192Sjdpvoid 11134192Sjdpmodule_register_init(const void *arg) 11234192Sjdp{ 11334192Sjdp const moduledata_t *data = (const moduledata_t *)arg; 11434192Sjdp int error; 11534192Sjdp module_t mod; 11634192Sjdp 11734192Sjdp mtx_lock(&Giant); 11834192Sjdp MOD_SLOCK; 11934192Sjdp mod = module_lookupbyname(data->name); 12034192Sjdp if (mod == NULL) 12134192Sjdp panic("module_register_init: module named %s not found\n", 12234192Sjdp data->name); 12334192Sjdp MOD_SUNLOCK; 12434192Sjdp error = MOD_EVENT(mod, MOD_LOAD); 12534192Sjdp if (error) { 12634192Sjdp MOD_EVENT(mod, MOD_UNLOAD); 12734192Sjdp MOD_XLOCK; 12834192Sjdp module_release(mod); 12934192Sjdp MOD_XUNLOCK; 13034192Sjdp printf("module_register_init: MOD_LOAD (%s, %p, %p) error" 13134192Sjdp " %d\n", data->name, (void *)data->evhand, data->priv, 13234192Sjdp error); 13334192Sjdp } 13434192Sjdp mtx_unlock(&Giant); 13534192Sjdp} 13634192Sjdp 13734192Sjdpint 13834192Sjdpmodule_register(const moduledata_t *data, linker_file_t container) 13934192Sjdp{ 14034192Sjdp size_t namelen; 14134192Sjdp module_t newmod; 14234192Sjdp 14334192Sjdp MOD_XLOCK; 14434192Sjdp newmod = module_lookupbyname(data->name); 14534192Sjdp if (newmod != NULL) { 14634192Sjdp MOD_XUNLOCK; 14734192Sjdp printf("module_register: module %s already exists!\n", 14834192Sjdp data->name); 14934192Sjdp return (EEXIST); 15034192Sjdp } 15134192Sjdp namelen = strlen(data->name) + 1; 152110801Skan newmod = malloc(sizeof(struct module) + namelen, M_MODULE, M_WAITOK); 15334192Sjdp if (newmod == NULL) { 15434192Sjdp MOD_XUNLOCK; 15534192Sjdp return (ENOMEM); 15634192Sjdp } 15734192Sjdp newmod->refs = 1; 15834192Sjdp newmod->id = nextid++; 15934192Sjdp newmod->name = (char *)(newmod + 1); 16038816Sdfr strcpy(newmod->name, data->name); 16138816Sdfr newmod->handler = data->evhand ? data->evhand : modevent_nop; 16234192Sjdp newmod->arg = data->priv; 16334192Sjdp bzero(&newmod->data, sizeof(newmod->data)); 16434192Sjdp TAILQ_INSERT_TAIL(&modules, newmod, link); 16534192Sjdp 16634192Sjdp if (container) 16734192Sjdp TAILQ_INSERT_TAIL(&container->modules, newmod, flink); 16834192Sjdp newmod->file = container; 16934192Sjdp MOD_XUNLOCK; 17034192Sjdp return (0); 17134192Sjdp} 17234192Sjdp 17338816Sdfrvoid 17434192Sjdpmodule_reference(module_t mod) 17534192Sjdp{ 17634192Sjdp 17734192Sjdp MOD_XLOCK_ASSERT; 17834192Sjdp 17934192Sjdp MOD_DPF(REFS, ("module_reference: before, refs=%d\n", mod->refs)); 18034192Sjdp mod->refs++; 181114625Sobrien} 18234192Sjdp 18334192Sjdpvoid 18434192Sjdpmodule_release(module_t mod) 18534192Sjdp{ 18634192Sjdp 18734192Sjdp MOD_XLOCK_ASSERT; 18834192Sjdp 18934192Sjdp if (mod->refs <= 0) 19034192Sjdp panic("module_release: bad reference count"); 19134192Sjdp 192114625Sobrien MOD_DPF(REFS, ("module_release: before, refs=%d\n", mod->refs)); 19334192Sjdp 19434192Sjdp mod->refs--; 19534192Sjdp if (mod->refs == 0) { 19634192Sjdp TAILQ_REMOVE(&modules, mod, link); 19734192Sjdp if (mod->file) 19834192Sjdp TAILQ_REMOVE(&mod->file->modules, mod, flink); 19934192Sjdp MOD_XUNLOCK; 20034192Sjdp free(mod, M_MODULE); 20134192Sjdp MOD_XLOCK; 20234192Sjdp } 20334192Sjdp} 20434192Sjdp 20534192Sjdpmodule_t 20634192Sjdpmodule_lookupbyname(const char *name) 20734192Sjdp{ 20834192Sjdp module_t mod; 20934192Sjdp int err; 21034192Sjdp 21134192Sjdp MOD_LOCK_ASSERT; 21234192Sjdp 21334192Sjdp TAILQ_FOREACH(mod, &modules, link) { 21434192Sjdp err = strcmp(mod->name, name); 21534192Sjdp if (err == 0) 21634192Sjdp return (mod); 21734192Sjdp } 21834192Sjdp return (NULL); 21934192Sjdp} 22034192Sjdp 22134192Sjdpmodule_t 22234192Sjdpmodule_lookupbyid(int modid) 22334192Sjdp{ 22434192Sjdp module_t mod; 22534192Sjdp 22634192Sjdp MOD_LOCK_ASSERT; 22734192Sjdp 22834192Sjdp TAILQ_FOREACH(mod, &modules, link) 22934192Sjdp if (mod->id == modid) 23034192Sjdp return(mod); 23134192Sjdp return (NULL); 23234192Sjdp} 23334192Sjdp 23434192Sjdpint 23534192Sjdpmodule_unload(module_t mod, int flags) 23634192Sjdp{ 23734192Sjdp int error; 23834192Sjdp 23934192Sjdp mtx_lock(&Giant); 24034192Sjdp error = MOD_EVENT(mod, MOD_QUIESCE); 24134192Sjdp if (error == EOPNOTSUPP || error == EINVAL) 24234192Sjdp error = 0; 24334192Sjdp if (error == 0 || flags == LINKER_UNLOAD_FORCE) 24434192Sjdp error = MOD_EVENT(mod, MOD_UNLOAD); 24534192Sjdp mtx_unlock(&Giant); 24634192Sjdp return (error); 24734192Sjdp} 24834192Sjdp 24934192Sjdpint 25034192Sjdpmodule_getid(module_t mod) 25134192Sjdp{ 25234192Sjdp 25334192Sjdp MOD_LOCK_ASSERT; 25434192Sjdp return (mod->id); 25534192Sjdp} 25634192Sjdp 25734192Sjdpmodule_t 25834192Sjdpmodule_getfnext(module_t mod) 25934192Sjdp{ 26034192Sjdp 26134192Sjdp MOD_LOCK_ASSERT; 26234192Sjdp return (TAILQ_NEXT(mod, flink)); 26334192Sjdp} 26434192Sjdp 26534192Sjdpvoid 26634192Sjdpmodule_setspecific(module_t mod, modspecific_t *datap) 26734192Sjdp{ 26834192Sjdp 26934192Sjdp MOD_XLOCK_ASSERT; 27034192Sjdp mod->data = *datap; 27134192Sjdp} 27234192Sjdp 27334192Sjdplinker_file_t 27434192Sjdpmodule_file(module_t mod) 27534192Sjdp{ 27634192Sjdp 27734192Sjdp return (mod->file); 27834192Sjdp} 27934192Sjdp 28034192Sjdp/* 28134192Sjdp * Syscalls. 28234192Sjdp */ 28334192Sjdpint 28434192Sjdpmodnext(struct thread *td, struct modnext_args *uap) 28534192Sjdp{ 28634192Sjdp module_t mod; 28734192Sjdp int error = 0; 28834192Sjdp 28934192Sjdp td->td_retval[0] = -1; 29034192Sjdp 29134192Sjdp MOD_SLOCK; 29234192Sjdp if (uap->modid == 0) { 29334192Sjdp mod = TAILQ_FIRST(&modules); 29434192Sjdp if (mod) 29534192Sjdp td->td_retval[0] = mod->id; 29634192Sjdp else 29734192Sjdp error = ENOENT; 29834192Sjdp goto done2; 29934192Sjdp } 30034192Sjdp mod = module_lookupbyid(uap->modid); 30134192Sjdp if (mod == NULL) { 30234192Sjdp error = ENOENT; 30334192Sjdp goto done2; 30434192Sjdp } 30534192Sjdp if (TAILQ_NEXT(mod, link)) 30634192Sjdp td->td_retval[0] = TAILQ_NEXT(mod, link)->id; 30734192Sjdp else 30834192Sjdp td->td_retval[0] = 0; 30934192Sjdpdone2: 31034192Sjdp MOD_SUNLOCK; 31134192Sjdp return (error); 31234192Sjdp} 31334192Sjdp 31434192Sjdpint 31534192Sjdpmodfnext(struct thread *td, struct modfnext_args *uap) 31634192Sjdp{ 31734192Sjdp module_t mod; 31834192Sjdp int error; 31934192Sjdp 32034192Sjdp td->td_retval[0] = -1; 32134192Sjdp 32234192Sjdp MOD_SLOCK; 32334192Sjdp mod = module_lookupbyid(uap->modid); 32434192Sjdp if (mod == NULL) { 32534192Sjdp error = ENOENT; 32634192Sjdp } else { 32734192Sjdp error = 0; 32834192Sjdp if (TAILQ_NEXT(mod, flink)) 32934192Sjdp td->td_retval[0] = TAILQ_NEXT(mod, flink)->id; 33034192Sjdp else 33134192Sjdp td->td_retval[0] = 0; 33234192Sjdp } 33334192Sjdp MOD_SUNLOCK; 33434192Sjdp return (error); 33534192Sjdp} 33634192Sjdp 33734192Sjdpstruct module_stat_v1 { 33834192Sjdp int version; /* set to sizeof(struct module_stat) */ 33934192Sjdp char name[MAXMODNAME]; 34034192Sjdp int refs; 34134192Sjdp int id; 34234192Sjdp}; 34334192Sjdp 34434192Sjdpint 34534192Sjdpmodstat(struct thread *td, struct modstat_args *uap) 34634192Sjdp{ 34734192Sjdp module_t mod; 34834192Sjdp modspecific_t data; 34934192Sjdp int error = 0; 35034192Sjdp int id, namelen, refs, version; 35134192Sjdp struct module_stat *stat; 35234192Sjdp char *name; 35334192Sjdp 35434192Sjdp MOD_SLOCK; 35534192Sjdp mod = module_lookupbyid(uap->modid); 35634192Sjdp if (mod == NULL) { 35734192Sjdp MOD_SUNLOCK; 35834192Sjdp return (ENOENT); 35934192Sjdp } 36034192Sjdp id = mod->id; 36134192Sjdp refs = mod->refs; 36234192Sjdp name = mod->name; 36334192Sjdp data = mod->data; 36434192Sjdp MOD_SUNLOCK; 365114625Sobrien stat = uap->stat; 36634192Sjdp 36734192Sjdp /* 36834192Sjdp * Check the version of the user's structure. 36934192Sjdp */ 37034192Sjdp if ((error = copyin(&stat->version, &version, sizeof(version))) != 0) 37134192Sjdp return (error); 37234192Sjdp if (version != sizeof(struct module_stat_v1) 37334192Sjdp && version != sizeof(struct module_stat)) 37434192Sjdp return (EINVAL); 37534192Sjdp namelen = strlen(mod->name) + 1; 37634192Sjdp if (namelen > MAXMODNAME) 37734192Sjdp namelen = MAXMODNAME; 378114625Sobrien if ((error = copyout(name, &stat->name[0], namelen)) != 0) 37934192Sjdp return (error); 38034192Sjdp 38134192Sjdp if ((error = copyout(&refs, &stat->refs, sizeof(int))) != 0) 38234192Sjdp return (error); 38334192Sjdp if ((error = copyout(&id, &stat->id, sizeof(int))) != 0) 38434192Sjdp return (error); 38534192Sjdp 38634192Sjdp /* 38734192Sjdp * >v1 stat includes module data. 38834192Sjdp */ 38934192Sjdp if (version == sizeof(struct module_stat)) 39034192Sjdp if ((error = copyout(&data, &stat->data, 39134192Sjdp sizeof(data))) != 0) 39234192Sjdp return (error); 39334192Sjdp td->td_retval[0] = 0; 39434192Sjdp return (error); 39534192Sjdp} 39634192Sjdp 39734192Sjdpint 39834192Sjdpmodfind(struct thread *td, struct modfind_args *uap) 39934192Sjdp{ 40034192Sjdp int error = 0; 40134192Sjdp char name[MAXMODNAME]; 40234192Sjdp module_t mod; 40334192Sjdp 40434192Sjdp if ((error = copyinstr(uap->name, name, sizeof name, 0)) != 0) 40534192Sjdp return (error); 40634192Sjdp 40734192Sjdp MOD_SLOCK; 40834192Sjdp mod = module_lookupbyname(name); 40934192Sjdp if (mod == NULL) 41034192Sjdp error = ENOENT; 41134192Sjdp else 41234192Sjdp td->td_retval[0] = module_getid(mod); 41334192Sjdp MOD_SUNLOCK; 41434192Sjdp return (error); 41534192Sjdp} 41634192Sjdp 41734192SjdpMODULE_VERSION(kernel, __FreeBSD_version); 41834192Sjdp 41934192Sjdp#ifdef COMPAT_IA32 42034192Sjdp#include <sys/mount.h> 42134192Sjdp#include <sys/socket.h> 42234192Sjdp#include <compat/freebsd32/freebsd32_util.h> 42334192Sjdp#include <compat/freebsd32/freebsd32.h> 42434192Sjdp#include <compat/freebsd32/freebsd32_proto.h> 42534192Sjdp 42634192Sjdptypedef union modspecific32 { 42734192Sjdp int intval; 42834192Sjdp u_int32_t uintval; 42934192Sjdp int longval; 43034192Sjdp u_int32_t ulongval; 43134192Sjdp} modspecific32_t; 43234192Sjdp 43334192Sjdpstruct module_stat32 { 43434192Sjdp int version; 43534192Sjdp char name[MAXMODNAME]; 43634192Sjdp int refs; 43734192Sjdp int id; 43834192Sjdp modspecific32_t data; 43934192Sjdp}; 44034192Sjdp 44134192Sjdpint 44234192Sjdpfreebsd32_modstat(struct thread *td, struct freebsd32_modstat_args *uap) 44334192Sjdp{ 44434192Sjdp module_t mod; 44534192Sjdp modspecific32_t data32; 44634192Sjdp int error = 0; 44734192Sjdp int id, namelen, refs, version; 44834192Sjdp struct module_stat32 *stat32; 44934192Sjdp char *name; 45034192Sjdp 45134192Sjdp MOD_SLOCK; 45234192Sjdp mod = module_lookupbyid(uap->modid); 45334192Sjdp if (mod == NULL) { 45434192Sjdp MOD_SUNLOCK; 45534192Sjdp return (ENOENT); 45634192Sjdp } 45734192Sjdp 45834192Sjdp id = mod->id; 45934192Sjdp refs = mod->refs; 46069793Sobrien name = mod->name; 46134192Sjdp CP(mod->data, data32, intval); 46269793Sobrien CP(mod->data, data32, uintval); 46334192Sjdp CP(mod->data, data32, longval); 46434192Sjdp CP(mod->data, data32, ulongval); 46534192Sjdp MOD_SUNLOCK; 46634192Sjdp stat32 = uap->stat; 46738816Sdfr 46834192Sjdp if ((error = copyin(&stat32->version, &version, sizeof(version))) != 0) 46934192Sjdp return (error); 47034192Sjdp if (version != sizeof(struct module_stat_v1) 47134192Sjdp && version != sizeof(struct module_stat32)) 47238816Sdfr return (EINVAL); 47334192Sjdp namelen = strlen(mod->name) + 1; 47434192Sjdp if (namelen > MAXMODNAME) 47534192Sjdp namelen = MAXMODNAME; 47634192Sjdp if ((error = copyout(name, &stat32->name[0], namelen)) != 0) 47734192Sjdp return (error); 47834192Sjdp 47934192Sjdp if ((error = copyout(&refs, &stat32->refs, sizeof(int))) != 0) 48034192Sjdp return (error); 48134192Sjdp if ((error = copyout(&id, &stat32->id, sizeof(int))) != 0) 48234192Sjdp return (error); 48334192Sjdp 48434192Sjdp /* 48534192Sjdp * >v1 stat includes module data. 48634192Sjdp */ 48734192Sjdp if (version == sizeof(struct module_stat32)) 488110801Skan if ((error = copyout(&data32, &stat32->data, 489110801Skan sizeof(data32))) != 0) 490110801Skan return (error); 491110801Skan td->td_retval[0] = 0; 492110801Skan return (error); 493110801Skan} 494110801Skan#endif 495110801Skan