kern_module.c revision 159957
125537Sdfr/*- 225537Sdfr * Copyright (c) 1997 Doug Rabson 325537Sdfr * All rights reserved. 425537Sdfr * 525537Sdfr * Redistribution and use in source and binary forms, with or without 625537Sdfr * modification, are permitted provided that the following conditions 725537Sdfr * are met: 825537Sdfr * 1. Redistributions of source code must retain the above copyright 925537Sdfr * notice, this list of conditions and the following disclaimer. 1025537Sdfr * 2. Redistributions in binary form must reproduce the above copyright 1125537Sdfr * notice, this list of conditions and the following disclaimer in the 1225537Sdfr * documentation and/or other materials provided with the distribution. 1325537Sdfr * 1425537Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1525537Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1625537Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1725537Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1825537Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1925537Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2025537Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2125537Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2225537Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2325537Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2425537Sdfr * SUCH DAMAGE. 2525537Sdfr */ 2625537Sdfr 27140482Sps#include "opt_compat.h" 28140482Sps 29116182Sobrien#include <sys/cdefs.h> 30116182Sobrien__FBSDID("$FreeBSD: head/sys/kern/kern_module.c 159957 2006-06-26 18:34:45Z jhb $"); 31116182Sobrien 3225537Sdfr#include <sys/param.h> 3325537Sdfr#include <sys/kernel.h> 3425537Sdfr#include <sys/systm.h> 3550107Smsmith#include <sys/eventhandler.h> 3625537Sdfr#include <sys/malloc.h> 3725537Sdfr#include <sys/sysproto.h> 3825537Sdfr#include <sys/sysent.h> 3930994Sphk#include <sys/proc.h> 4082749Sdillon#include <sys/lock.h> 4182749Sdillon#include <sys/mutex.h> 42134092Struckman#include <sys/reboot.h> 4392547Sarr#include <sys/sx.h> 4492547Sarr#include <sys/module.h> 4592547Sarr#include <sys/linker.h> 4625537Sdfr 4769774Sphkstatic MALLOC_DEFINE(M_MODULE, "module", "module data structures"); 4825537Sdfr 4960938Sjaketypedef TAILQ_HEAD(, module) modulelist_t; 5025537Sdfrstruct module { 5190963Sarr TAILQ_ENTRY(module) link; /* chain together all modules */ 5290963Sarr TAILQ_ENTRY(module) flink; /* all modules in a file */ 5390963Sarr struct linker_file *file; /* file which contains this module */ 5490963Sarr int refs; /* reference count */ 5590963Sarr int id; /* unique id number */ 5690963Sarr char *name; /* module name */ 5790963Sarr modeventhand_t handler; /* event handler */ 5890963Sarr void *arg; /* argument for handler */ 5990963Sarr modspecific_t data; /* module specific data */ 6025537Sdfr}; 6125537Sdfr 6290963Sarr#define MOD_EVENT(mod, type) (mod)->handler((mod), (type), (mod)->arg) 6325537Sdfr 6425537Sdfrstatic modulelist_t modules; 6592547Sarrstruct sx modules_sx; 6625537Sdfrstatic int nextid = 1; 6790963Sarrstatic void module_shutdown(void *, int); 6825537Sdfr 6952991Speterstatic int 7090963Sarrmodevent_nop(module_t mod, int what, void *arg) 7152991Speter{ 72132167Sphk 73132167Sphk switch(what) { 74132167Sphk case MOD_LOAD: 75132167Sphk return (0); 76132167Sphk case MOD_UNLOAD: 77132167Sphk return (EBUSY); 78132167Sphk default: 79132167Sphk return (EOPNOTSUPP); 80132167Sphk } 8152991Speter} 8252991Speter 8325537Sdfrstatic void 8490963Sarrmodule_init(void *arg) 8525537Sdfr{ 8690963Sarr 8792547Sarr sx_init(&modules_sx, "module subsystem sx lock"); 8890963Sarr TAILQ_INIT(&modules); 89108905Speter EVENTHANDLER_REGISTER(shutdown_final, module_shutdown, NULL, 9090963Sarr SHUTDOWN_PRI_DEFAULT); 9125537Sdfr} 9225537Sdfr 9390963SarrSYSINIT(module, SI_SUB_KLD, SI_ORDER_FIRST, module_init, 0) 9425537Sdfr 9525537Sdfrstatic void 9690963Sarrmodule_shutdown(void *arg1, int arg2) 9725537Sdfr{ 9890963Sarr module_t mod; 9925537Sdfr 100134092Struckman if (arg2 & RB_NOSYNC) 101134092Struckman return; 102159957Sjhb mtx_lock(&Giant); 10392547Sarr MOD_SLOCK; 10490963Sarr TAILQ_FOREACH(mod, &modules, link) 10590963Sarr MOD_EVENT(mod, MOD_SHUTDOWN); 10692547Sarr MOD_SUNLOCK; 107159957Sjhb mtx_unlock(&Giant); 10825537Sdfr} 10925537Sdfr 11025537Sdfrvoid 11143387Sdillonmodule_register_init(const void *arg) 11225537Sdfr{ 11390963Sarr const moduledata_t *data = (const moduledata_t *)arg; 11490963Sarr int error; 11590963Sarr module_t mod; 11625537Sdfr 117159957Sjhb mtx_lock(&Giant); 11892547Sarr MOD_SLOCK; 11990963Sarr mod = module_lookupbyname(data->name); 12090963Sarr if (mod == NULL) 12191067Sarr panic("module_register_init: module named %s not found\n", 12290963Sarr data->name); 12392547Sarr MOD_SUNLOCK; 12490963Sarr error = MOD_EVENT(mod, MOD_LOAD); 12590963Sarr if (error) { 12690963Sarr MOD_EVENT(mod, MOD_UNLOAD); 12792547Sarr MOD_XLOCK; 12890963Sarr module_release(mod); 12992547Sarr MOD_XUNLOCK; 13091261Speter printf("module_register_init: MOD_LOAD (%s, %p, %p) error" 13191261Speter " %d\n", data->name, (void *)data->evhand, data->priv, 13291067Sarr error); 13390963Sarr } 134159957Sjhb mtx_unlock(&Giant); 13525537Sdfr} 13625537Sdfr 13725537Sdfrint 13846693Spetermodule_register(const moduledata_t *data, linker_file_t container) 13925537Sdfr{ 14090963Sarr size_t namelen; 14190963Sarr module_t newmod; 14225537Sdfr 143159634Smaxim MOD_XLOCK; 14490963Sarr newmod = module_lookupbyname(data->name); 14590963Sarr if (newmod != NULL) { 146159634Smaxim MOD_XUNLOCK; 14791067Sarr printf("module_register: module %s already exists!\n", 14890963Sarr data->name); 14990980Sarr return (EEXIST); 15090963Sarr } 15190963Sarr namelen = strlen(data->name) + 1; 152111119Simp newmod = malloc(sizeof(struct module) + namelen, M_MODULE, M_WAITOK); 153159634Smaxim if (newmod == NULL) { 154159634Smaxim MOD_XUNLOCK; 15590980Sarr return (ENOMEM); 156159634Smaxim } 15790963Sarr newmod->refs = 1; 15890963Sarr newmod->id = nextid++; 15990963Sarr newmod->name = (char *)(newmod + 1); 16090963Sarr strcpy(newmod->name, data->name); 16190963Sarr newmod->handler = data->evhand ? data->evhand : modevent_nop; 16290963Sarr newmod->arg = data->priv; 16390963Sarr bzero(&newmod->data, sizeof(newmod->data)); 16490963Sarr TAILQ_INSERT_TAIL(&modules, newmod, link); 16525537Sdfr 16690963Sarr if (container) 16790963Sarr TAILQ_INSERT_TAIL(&container->modules, newmod, flink); 16890963Sarr newmod->file = container; 16992547Sarr MOD_XUNLOCK; 17090980Sarr return (0); 17125537Sdfr} 17225537Sdfr 17325537Sdfrvoid 17425537Sdfrmodule_reference(module_t mod) 17525537Sdfr{ 17625537Sdfr 17792547Sarr MOD_XLOCK_ASSERT; 17892547Sarr 17990963Sarr MOD_DPF(REFS, ("module_reference: before, refs=%d\n", mod->refs)); 18090963Sarr mod->refs++; 18125537Sdfr} 18225537Sdfr 18325537Sdfrvoid 18425537Sdfrmodule_release(module_t mod) 18525537Sdfr{ 18625537Sdfr 18792547Sarr MOD_XLOCK_ASSERT; 18892547Sarr 18990963Sarr if (mod->refs <= 0) 19090963Sarr panic("module_release: bad reference count"); 19125537Sdfr 19290963Sarr MOD_DPF(REFS, ("module_release: before, refs=%d\n", mod->refs)); 19392547Sarr 19490963Sarr mod->refs--; 19590963Sarr if (mod->refs == 0) { 19690963Sarr TAILQ_REMOVE(&modules, mod, link); 19790963Sarr if (mod->file) 19890963Sarr TAILQ_REMOVE(&mod->file->modules, mod, flink); 19992547Sarr MOD_XUNLOCK; 20090963Sarr free(mod, M_MODULE); 20192547Sarr MOD_XLOCK; 20225537Sdfr } 20325537Sdfr} 20425537Sdfr 20525537Sdfrmodule_t 20690963Sarrmodule_lookupbyname(const char *name) 20725537Sdfr{ 20890963Sarr module_t mod; 20990963Sarr int err; 21025537Sdfr 21192547Sarr MOD_LOCK_ASSERT; 21292547Sarr 21390963Sarr TAILQ_FOREACH(mod, &modules, link) { 21490963Sarr err = strcmp(mod->name, name); 21590963Sarr if (err == 0) 21690980Sarr return (mod); 21790963Sarr } 21890980Sarr return (NULL); 21925537Sdfr} 22025537Sdfr 22125537Sdfrmodule_t 22225537Sdfrmodule_lookupbyid(int modid) 22325537Sdfr{ 22492547Sarr module_t mod; 22525537Sdfr 22692547Sarr MOD_LOCK_ASSERT; 22792547Sarr 22892547Sarr TAILQ_FOREACH(mod, &modules, link) 22992547Sarr if (mod->id == modid) 23092547Sarr return(mod); 23192547Sarr return (NULL); 23225537Sdfr} 23325537Sdfr 23425537Sdfrint 235132117Sphkmodule_unload(module_t mod, int flags) 23625537Sdfr{ 237132117Sphk int error; 23890963Sarr 239159957Sjhb mtx_lock(&Giant); 240132117Sphk error = MOD_EVENT(mod, MOD_QUIESCE); 241132199Sphk if (error == EOPNOTSUPP || error == EINVAL) 242132117Sphk error = 0; 243159957Sjhb if (error == 0 || flags == LINKER_UNLOAD_FORCE) 244159957Sjhb error = MOD_EVENT(mod, MOD_UNLOAD); 245159957Sjhb mtx_unlock(&Giant); 246159957Sjhb return (error); 24725537Sdfr} 24825537Sdfr 24925537Sdfrint 25025537Sdfrmodule_getid(module_t mod) 25125537Sdfr{ 25290963Sarr 25392547Sarr MOD_LOCK_ASSERT; 25490980Sarr return (mod->id); 25525537Sdfr} 25625537Sdfr 25725537Sdfrmodule_t 25825537Sdfrmodule_getfnext(module_t mod) 25925537Sdfr{ 26090963Sarr 26192547Sarr MOD_LOCK_ASSERT; 26290980Sarr return (TAILQ_NEXT(mod, flink)); 26325537Sdfr} 26425537Sdfr 26542435Sdfrvoid 26642435Sdfrmodule_setspecific(module_t mod, modspecific_t *datap) 26742435Sdfr{ 26890963Sarr 26992547Sarr MOD_XLOCK_ASSERT; 27090963Sarr mod->data = *datap; 27142435Sdfr} 27242435Sdfr 273157818Sjhblinker_file_t 274157818Sjhbmodule_file(module_t mod) 275157818Sjhb{ 276157818Sjhb 277157818Sjhb return (mod->file); 278157818Sjhb} 279157818Sjhb 28025537Sdfr/* 28125537Sdfr * Syscalls. 28225537Sdfr */ 28382749Sdillon/* 28482749Sdillon * MPSAFE 28582749Sdillon */ 28625537Sdfrint 28783366Sjulianmodnext(struct thread *td, struct modnext_args *uap) 28825537Sdfr{ 28990963Sarr module_t mod; 29090963Sarr int error = 0; 29125537Sdfr 29298835Sarr td->td_retval[0] = -1; 29382749Sdillon 29492547Sarr MOD_SLOCK; 295107849Salfred if (uap->modid == 0) { 29690963Sarr mod = TAILQ_FIRST(&modules); 29790963Sarr if (mod) 29890963Sarr td->td_retval[0] = mod->id; 29990963Sarr else 30090963Sarr error = ENOENT; 30190963Sarr goto done2; 30290963Sarr } 303107849Salfred mod = module_lookupbyid(uap->modid); 30490963Sarr if (mod == NULL) { 30590963Sarr error = ENOENT; 30690963Sarr goto done2; 30790963Sarr } 30890963Sarr if (TAILQ_NEXT(mod, link)) 30990963Sarr td->td_retval[0] = TAILQ_NEXT(mod, link)->id; 31082749Sdillon else 31190963Sarr td->td_retval[0] = 0; 31282749Sdillondone2: 31392547Sarr MOD_SUNLOCK; 31490963Sarr return (error); 31525537Sdfr} 31625537Sdfr 31782749Sdillon/* 31882749Sdillon * MPSAFE 31982749Sdillon */ 32025537Sdfrint 32183366Sjulianmodfnext(struct thread *td, struct modfnext_args *uap) 32225537Sdfr{ 32390963Sarr module_t mod; 32490963Sarr int error; 32525537Sdfr 32690963Sarr td->td_retval[0] = -1; 32725537Sdfr 32892547Sarr MOD_SLOCK; 329107849Salfred mod = module_lookupbyid(uap->modid); 33090963Sarr if (mod == NULL) { 33190963Sarr error = ENOENT; 33290963Sarr } else { 33390963Sarr error = 0; 33490963Sarr if (TAILQ_NEXT(mod, flink)) 33590963Sarr td->td_retval[0] = TAILQ_NEXT(mod, flink)->id; 33690963Sarr else 33790963Sarr td->td_retval[0] = 0; 33890963Sarr } 33992547Sarr MOD_SUNLOCK; 34090963Sarr return (error); 34125537Sdfr} 34225537Sdfr 34342435Sdfrstruct module_stat_v1 { 34491067Sarr int version; /* set to sizeof(struct module_stat) */ 34591067Sarr char name[MAXMODNAME]; 34691067Sarr int refs; 34791067Sarr int id; 34842435Sdfr}; 34942435Sdfr 35082749Sdillon/* 35182749Sdillon * MPSAFE 35282749Sdillon */ 35325537Sdfrint 35483366Sjulianmodstat(struct thread *td, struct modstat_args *uap) 35525537Sdfr{ 35690963Sarr module_t mod; 35792547Sarr modspecific_t data; 35890963Sarr int error = 0; 35992547Sarr int id, namelen, refs, version; 36090963Sarr struct module_stat *stat; 36192547Sarr char *name; 36225537Sdfr 36392547Sarr MOD_SLOCK; 364107849Salfred mod = module_lookupbyid(uap->modid); 36590963Sarr if (mod == NULL) { 36692547Sarr MOD_SUNLOCK; 36798835Sarr return (ENOENT); 36890963Sarr } 36992547Sarr id = mod->id; 37092547Sarr refs = mod->refs; 37192547Sarr name = mod->name; 37292547Sarr data = mod->data; 37392547Sarr MOD_SUNLOCK; 374107849Salfred stat = uap->stat; 37525537Sdfr 37690963Sarr /* 37790963Sarr * Check the version of the user's structure. 37890963Sarr */ 37990963Sarr if ((error = copyin(&stat->version, &version, sizeof(version))) != 0) 38098835Sarr return (error); 38190963Sarr if (version != sizeof(struct module_stat_v1) 38298835Sarr && version != sizeof(struct module_stat)) 38398835Sarr return (EINVAL); 38490963Sarr namelen = strlen(mod->name) + 1; 38590963Sarr if (namelen > MAXMODNAME) 38690963Sarr namelen = MAXMODNAME; 38792547Sarr if ((error = copyout(name, &stat->name[0], namelen)) != 0) 38898835Sarr return (error); 38925537Sdfr 39092547Sarr if ((error = copyout(&refs, &stat->refs, sizeof(int))) != 0) 39198835Sarr return (error); 39292547Sarr if ((error = copyout(&id, &stat->id, sizeof(int))) != 0) 39398835Sarr return (error); 39425537Sdfr 39590963Sarr /* 39690963Sarr * >v1 stat includes module data. 39790963Sarr */ 39898835Sarr if (version == sizeof(struct module_stat)) 39992547Sarr if ((error = copyout(&data, &stat->data, 40092547Sarr sizeof(data))) != 0) 40198835Sarr return (error); 40290963Sarr td->td_retval[0] = 0; 40390980Sarr return (error); 40425537Sdfr} 40525537Sdfr 40682749Sdillon/* 40782749Sdillon * MPSAFE 40882749Sdillon */ 40925537Sdfrint 41083366Sjulianmodfind(struct thread *td, struct modfind_args *uap) 41125537Sdfr{ 41290963Sarr int error = 0; 41390963Sarr char name[MAXMODNAME]; 41490963Sarr module_t mod; 41525537Sdfr 416107849Salfred if ((error = copyinstr(uap->name, name, sizeof name, 0)) != 0) 41798835Sarr return (error); 41825537Sdfr 41992547Sarr MOD_SLOCK; 42090963Sarr mod = module_lookupbyname(name); 42190963Sarr if (mod == NULL) 42290963Sarr error = ENOENT; 42390963Sarr else 42492547Sarr td->td_retval[0] = module_getid(mod); 42592547Sarr MOD_SUNLOCK; 42690980Sarr return (error); 42725537Sdfr} 428140482Sps 429140482Sps#ifdef COMPAT_IA32 430140482Sps#include <sys/mount.h> 431140482Sps#include <compat/freebsd32/freebsd32_util.h> 432140482Sps#include <compat/freebsd32/freebsd32.h> 433140482Sps#include <compat/freebsd32/freebsd32_proto.h> 434140482Sps 435140482Spstypedef union modspecific32 { 436140482Sps int intval; 437140482Sps u_int32_t uintval; 438140482Sps int longval; 439140482Sps u_int32_t ulongval; 440140482Sps} modspecific32_t; 441140482Sps 442140482Spsstruct module_stat32 { 443140482Sps int version; 444140482Sps char name[MAXMODNAME]; 445140482Sps int refs; 446140482Sps int id; 447140482Sps modspecific32_t data; 448140482Sps}; 449140482Sps 450140482Sps/* 451140482Sps * MPSAFE 452140482Sps */ 453140482Spsint 454140482Spsfreebsd32_modstat(struct thread *td, struct freebsd32_modstat_args *uap) 455140482Sps{ 456140482Sps module_t mod; 457140482Sps modspecific32_t data32; 458140482Sps int error = 0; 459140482Sps int id, namelen, refs, version; 460140482Sps struct module_stat32 *stat32; 461140482Sps char *name; 462140482Sps 463140482Sps MOD_SLOCK; 464140482Sps mod = module_lookupbyid(uap->modid); 465140482Sps if (mod == NULL) { 466140482Sps MOD_SUNLOCK; 467140482Sps return (ENOENT); 468140482Sps } 469140482Sps 470140482Sps id = mod->id; 471140482Sps refs = mod->refs; 472140482Sps name = mod->name; 473142067Sps CP(mod->data, data32, intval); 474142067Sps CP(mod->data, data32, uintval); 475142067Sps CP(mod->data, data32, longval); 476142067Sps CP(mod->data, data32, ulongval); 477140482Sps MOD_SUNLOCK; 478140482Sps stat32 = uap->stat; 479140482Sps 480140482Sps if ((error = copyin(&stat32->version, &version, sizeof(version))) != 0) 481140482Sps return (error); 482140482Sps if (version != sizeof(struct module_stat_v1) 483140482Sps && version != sizeof(struct module_stat32)) 484140482Sps return (EINVAL); 485140482Sps namelen = strlen(mod->name) + 1; 486140482Sps if (namelen > MAXMODNAME) 487140482Sps namelen = MAXMODNAME; 488140482Sps if ((error = copyout(name, &stat32->name[0], namelen)) != 0) 489140482Sps return (error); 490140482Sps 491140482Sps if ((error = copyout(&refs, &stat32->refs, sizeof(int))) != 0) 492140482Sps return (error); 493140482Sps if ((error = copyout(&id, &stat32->id, sizeof(int))) != 0) 494140482Sps return (error); 495140482Sps 496140482Sps /* 497140482Sps * >v1 stat includes module data. 498140482Sps */ 499140482Sps if (version == sizeof(struct module_stat32)) 500140482Sps if ((error = copyout(&data32, &stat32->data, 501140482Sps sizeof(data32))) != 0) 502140482Sps return (error); 503140482Sps td->td_retval[0] = 0; 504140482Sps return (error); 505140482Sps} 506140482Sps#endif 507