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$"); 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 4925537Sdfrstruct module { 5090963Sarr TAILQ_ENTRY(module) link; /* chain together all modules */ 5190963Sarr TAILQ_ENTRY(module) flink; /* all modules in a file */ 5290963Sarr struct linker_file *file; /* file which contains this module */ 5390963Sarr int refs; /* reference count */ 5490963Sarr int id; /* unique id number */ 5590963Sarr char *name; /* module name */ 5690963Sarr modeventhand_t handler; /* event handler */ 5790963Sarr void *arg; /* argument for handler */ 5890963Sarr modspecific_t data; /* module specific data */ 5925537Sdfr}; 6025537Sdfr 6190963Sarr#define MOD_EVENT(mod, type) (mod)->handler((mod), (type), (mod)->arg) 6225537Sdfr 63215683Sattiliostatic TAILQ_HEAD(modulelist, module) modules; 6492547Sarrstruct sx modules_sx; 6525537Sdfrstatic int nextid = 1; 6690963Sarrstatic void module_shutdown(void *, int); 6725537Sdfr 6852991Speterstatic int 6990963Sarrmodevent_nop(module_t mod, int what, void *arg) 7052991Speter{ 71132167Sphk 72132167Sphk switch(what) { 73132167Sphk case MOD_LOAD: 74132167Sphk return (0); 75132167Sphk case MOD_UNLOAD: 76132167Sphk return (EBUSY); 77132167Sphk default: 78132167Sphk return (EOPNOTSUPP); 79132167Sphk } 8052991Speter} 8152991Speter 8225537Sdfrstatic void 8390963Sarrmodule_init(void *arg) 8425537Sdfr{ 8590963Sarr 8692547Sarr sx_init(&modules_sx, "module subsystem sx lock"); 8790963Sarr TAILQ_INIT(&modules); 88108905Speter EVENTHANDLER_REGISTER(shutdown_final, module_shutdown, NULL, 8990963Sarr SHUTDOWN_PRI_DEFAULT); 9025537Sdfr} 9125537Sdfr 92177253SrwatsonSYSINIT(module, SI_SUB_KLD, SI_ORDER_FIRST, module_init, 0); 9325537Sdfr 9425537Sdfrstatic void 9590963Sarrmodule_shutdown(void *arg1, int arg2) 9625537Sdfr{ 9790963Sarr module_t mod; 9825537Sdfr 99134092Struckman if (arg2 & RB_NOSYNC) 100134092Struckman return; 101159957Sjhb mtx_lock(&Giant); 10292547Sarr MOD_SLOCK; 103215683Sattilio TAILQ_FOREACH_REVERSE(mod, &modules, modulelist, link) 10490963Sarr MOD_EVENT(mod, MOD_SHUTDOWN); 10592547Sarr MOD_SUNLOCK; 106159957Sjhb mtx_unlock(&Giant); 10725537Sdfr} 10825537Sdfr 10925537Sdfrvoid 11043387Sdillonmodule_register_init(const void *arg) 11125537Sdfr{ 11290963Sarr const moduledata_t *data = (const moduledata_t *)arg; 11390963Sarr int error; 11490963Sarr module_t mod; 11525537Sdfr 116159957Sjhb mtx_lock(&Giant); 11792547Sarr MOD_SLOCK; 11890963Sarr mod = module_lookupbyname(data->name); 11990963Sarr if (mod == NULL) 12091067Sarr panic("module_register_init: module named %s not found\n", 12190963Sarr data->name); 12292547Sarr MOD_SUNLOCK; 12390963Sarr error = MOD_EVENT(mod, MOD_LOAD); 12490963Sarr if (error) { 12590963Sarr MOD_EVENT(mod, MOD_UNLOAD); 12692547Sarr MOD_XLOCK; 12790963Sarr module_release(mod); 12892547Sarr MOD_XUNLOCK; 12991261Speter printf("module_register_init: MOD_LOAD (%s, %p, %p) error" 13091261Speter " %d\n", data->name, (void *)data->evhand, data->priv, 13191067Sarr error); 132185642Sjhb } else { 133185642Sjhb MOD_XLOCK; 134185642Sjhb if (mod->file) { 135185642Sjhb /* 136239586Sjhb * Once a module is successfully loaded, move 137185642Sjhb * it to the head of the module list for this 138185642Sjhb * linker file. This resorts the list so that 139185642Sjhb * when the kernel linker iterates over the 140185642Sjhb * modules to unload them, it will unload them 141185642Sjhb * in the reverse order they were loaded. 142185642Sjhb */ 143185642Sjhb TAILQ_REMOVE(&mod->file->modules, mod, flink); 144185642Sjhb TAILQ_INSERT_HEAD(&mod->file->modules, mod, flink); 145185642Sjhb } 146185642Sjhb MOD_XUNLOCK; 14790963Sarr } 148159957Sjhb mtx_unlock(&Giant); 14925537Sdfr} 15025537Sdfr 15125537Sdfrint 15246693Spetermodule_register(const moduledata_t *data, linker_file_t container) 15325537Sdfr{ 15490963Sarr size_t namelen; 15590963Sarr module_t newmod; 15625537Sdfr 157159634Smaxim MOD_XLOCK; 15890963Sarr newmod = module_lookupbyname(data->name); 15990963Sarr if (newmod != NULL) { 160159634Smaxim MOD_XUNLOCK; 16191067Sarr printf("module_register: module %s already exists!\n", 16290963Sarr data->name); 16390980Sarr return (EEXIST); 16490963Sarr } 16590963Sarr namelen = strlen(data->name) + 1; 166111119Simp newmod = malloc(sizeof(struct module) + namelen, M_MODULE, M_WAITOK); 167159634Smaxim if (newmod == NULL) { 168159634Smaxim MOD_XUNLOCK; 16990980Sarr return (ENOMEM); 170159634Smaxim } 17190963Sarr newmod->refs = 1; 17290963Sarr newmod->id = nextid++; 17390963Sarr newmod->name = (char *)(newmod + 1); 17490963Sarr strcpy(newmod->name, data->name); 17590963Sarr newmod->handler = data->evhand ? data->evhand : modevent_nop; 17690963Sarr newmod->arg = data->priv; 17790963Sarr bzero(&newmod->data, sizeof(newmod->data)); 17890963Sarr TAILQ_INSERT_TAIL(&modules, newmod, link); 17925537Sdfr 18090963Sarr if (container) 18190963Sarr TAILQ_INSERT_TAIL(&container->modules, newmod, flink); 18290963Sarr newmod->file = container; 18392547Sarr MOD_XUNLOCK; 18490980Sarr return (0); 18525537Sdfr} 18625537Sdfr 18725537Sdfrvoid 18825537Sdfrmodule_reference(module_t mod) 18925537Sdfr{ 19025537Sdfr 19192547Sarr MOD_XLOCK_ASSERT; 19292547Sarr 19390963Sarr MOD_DPF(REFS, ("module_reference: before, refs=%d\n", mod->refs)); 19490963Sarr mod->refs++; 19525537Sdfr} 19625537Sdfr 19725537Sdfrvoid 19825537Sdfrmodule_release(module_t mod) 19925537Sdfr{ 20025537Sdfr 20192547Sarr MOD_XLOCK_ASSERT; 20292547Sarr 20390963Sarr if (mod->refs <= 0) 20490963Sarr panic("module_release: bad reference count"); 20525537Sdfr 20690963Sarr MOD_DPF(REFS, ("module_release: before, refs=%d\n", mod->refs)); 20792547Sarr 20890963Sarr mod->refs--; 20990963Sarr if (mod->refs == 0) { 21090963Sarr TAILQ_REMOVE(&modules, mod, link); 21190963Sarr if (mod->file) 21290963Sarr TAILQ_REMOVE(&mod->file->modules, mod, flink); 21390963Sarr free(mod, M_MODULE); 21425537Sdfr } 21525537Sdfr} 21625537Sdfr 21725537Sdfrmodule_t 21890963Sarrmodule_lookupbyname(const char *name) 21925537Sdfr{ 22090963Sarr module_t mod; 22190963Sarr int err; 22225537Sdfr 22392547Sarr MOD_LOCK_ASSERT; 22492547Sarr 22590963Sarr TAILQ_FOREACH(mod, &modules, link) { 22690963Sarr err = strcmp(mod->name, name); 22790963Sarr if (err == 0) 22890980Sarr return (mod); 22990963Sarr } 23090980Sarr return (NULL); 23125537Sdfr} 23225537Sdfr 23325537Sdfrmodule_t 23425537Sdfrmodule_lookupbyid(int modid) 23525537Sdfr{ 23692547Sarr module_t mod; 23725537Sdfr 23892547Sarr MOD_LOCK_ASSERT; 23992547Sarr 24092547Sarr TAILQ_FOREACH(mod, &modules, link) 24192547Sarr if (mod->id == modid) 24292547Sarr return(mod); 24392547Sarr return (NULL); 24425537Sdfr} 24525537Sdfr 24625537Sdfrint 247185635Sjhbmodule_quiesce(module_t mod) 24825537Sdfr{ 249132117Sphk int error; 25090963Sarr 251159957Sjhb mtx_lock(&Giant); 252132117Sphk error = MOD_EVENT(mod, MOD_QUIESCE); 253185635Sjhb mtx_unlock(&Giant); 254132199Sphk if (error == EOPNOTSUPP || error == EINVAL) 255132117Sphk error = 0; 256185635Sjhb return (error); 257185635Sjhb} 258185635Sjhb 259185635Sjhbint 260185635Sjhbmodule_unload(module_t mod) 261185635Sjhb{ 262185635Sjhb int error; 263185635Sjhb 264185635Sjhb mtx_lock(&Giant); 265185635Sjhb error = MOD_EVENT(mod, MOD_UNLOAD); 266159957Sjhb mtx_unlock(&Giant); 267159957Sjhb return (error); 26825537Sdfr} 26925537Sdfr 27025537Sdfrint 27125537Sdfrmodule_getid(module_t mod) 27225537Sdfr{ 27390963Sarr 27492547Sarr MOD_LOCK_ASSERT; 27590980Sarr return (mod->id); 27625537Sdfr} 27725537Sdfr 27825537Sdfrmodule_t 27925537Sdfrmodule_getfnext(module_t mod) 28025537Sdfr{ 28190963Sarr 28292547Sarr MOD_LOCK_ASSERT; 28390980Sarr return (TAILQ_NEXT(mod, flink)); 28425537Sdfr} 28525537Sdfr 286185635Sjhbconst char * 287185635Sjhbmodule_getname(module_t mod) 288185635Sjhb{ 289185635Sjhb 290185635Sjhb MOD_LOCK_ASSERT; 291185635Sjhb return (mod->name); 292185635Sjhb} 293185635Sjhb 29442435Sdfrvoid 29542435Sdfrmodule_setspecific(module_t mod, modspecific_t *datap) 29642435Sdfr{ 29790963Sarr 29892547Sarr MOD_XLOCK_ASSERT; 29990963Sarr mod->data = *datap; 30042435Sdfr} 30142435Sdfr 302157818Sjhblinker_file_t 303157818Sjhbmodule_file(module_t mod) 304157818Sjhb{ 305157818Sjhb 306157818Sjhb return (mod->file); 307157818Sjhb} 308157818Sjhb 30925537Sdfr/* 31025537Sdfr * Syscalls. 31125537Sdfr */ 31225537Sdfrint 313225617Skmacysys_modnext(struct thread *td, struct modnext_args *uap) 31425537Sdfr{ 31590963Sarr module_t mod; 31690963Sarr int error = 0; 31725537Sdfr 31898835Sarr td->td_retval[0] = -1; 31982749Sdillon 32092547Sarr MOD_SLOCK; 321107849Salfred if (uap->modid == 0) { 32290963Sarr mod = TAILQ_FIRST(&modules); 32390963Sarr if (mod) 32490963Sarr td->td_retval[0] = mod->id; 32590963Sarr else 32690963Sarr error = ENOENT; 32790963Sarr goto done2; 32890963Sarr } 329107849Salfred mod = module_lookupbyid(uap->modid); 33090963Sarr if (mod == NULL) { 33190963Sarr error = ENOENT; 33290963Sarr goto done2; 33390963Sarr } 33490963Sarr if (TAILQ_NEXT(mod, link)) 33590963Sarr td->td_retval[0] = TAILQ_NEXT(mod, link)->id; 33682749Sdillon else 33790963Sarr td->td_retval[0] = 0; 33882749Sdillondone2: 33992547Sarr MOD_SUNLOCK; 34090963Sarr return (error); 34125537Sdfr} 34225537Sdfr 34325537Sdfrint 344225617Skmacysys_modfnext(struct thread *td, struct modfnext_args *uap) 34525537Sdfr{ 34690963Sarr module_t mod; 34790963Sarr int error; 34825537Sdfr 34990963Sarr td->td_retval[0] = -1; 35025537Sdfr 35192547Sarr MOD_SLOCK; 352107849Salfred mod = module_lookupbyid(uap->modid); 35390963Sarr if (mod == NULL) { 35490963Sarr error = ENOENT; 35590963Sarr } else { 35690963Sarr error = 0; 35790963Sarr if (TAILQ_NEXT(mod, flink)) 35890963Sarr td->td_retval[0] = TAILQ_NEXT(mod, flink)->id; 35990963Sarr else 36090963Sarr td->td_retval[0] = 0; 36190963Sarr } 36292547Sarr MOD_SUNLOCK; 36390963Sarr return (error); 36425537Sdfr} 36525537Sdfr 36642435Sdfrstruct module_stat_v1 { 36791067Sarr int version; /* set to sizeof(struct module_stat) */ 36891067Sarr char name[MAXMODNAME]; 36991067Sarr int refs; 37091067Sarr int id; 37142435Sdfr}; 37242435Sdfr 37325537Sdfrint 374225617Skmacysys_modstat(struct thread *td, struct modstat_args *uap) 37525537Sdfr{ 37690963Sarr module_t mod; 37792547Sarr modspecific_t data; 37890963Sarr int error = 0; 37992547Sarr int id, namelen, refs, version; 38090963Sarr struct module_stat *stat; 38192547Sarr char *name; 38225537Sdfr 38392547Sarr MOD_SLOCK; 384107849Salfred mod = module_lookupbyid(uap->modid); 38590963Sarr if (mod == NULL) { 38692547Sarr MOD_SUNLOCK; 38798835Sarr return (ENOENT); 38890963Sarr } 38992547Sarr id = mod->id; 39092547Sarr refs = mod->refs; 39192547Sarr name = mod->name; 39292547Sarr data = mod->data; 39392547Sarr MOD_SUNLOCK; 394107849Salfred stat = uap->stat; 39525537Sdfr 39690963Sarr /* 39790963Sarr * Check the version of the user's structure. 39890963Sarr */ 39990963Sarr if ((error = copyin(&stat->version, &version, sizeof(version))) != 0) 40098835Sarr return (error); 40190963Sarr if (version != sizeof(struct module_stat_v1) 40298835Sarr && version != sizeof(struct module_stat)) 40398835Sarr return (EINVAL); 40490963Sarr namelen = strlen(mod->name) + 1; 40590963Sarr if (namelen > MAXMODNAME) 40690963Sarr namelen = MAXMODNAME; 40792547Sarr if ((error = copyout(name, &stat->name[0], namelen)) != 0) 40898835Sarr return (error); 40925537Sdfr 41092547Sarr if ((error = copyout(&refs, &stat->refs, sizeof(int))) != 0) 41198835Sarr return (error); 41292547Sarr if ((error = copyout(&id, &stat->id, sizeof(int))) != 0) 41398835Sarr return (error); 41425537Sdfr 41590963Sarr /* 41690963Sarr * >v1 stat includes module data. 41790963Sarr */ 41898835Sarr if (version == sizeof(struct module_stat)) 41992547Sarr if ((error = copyout(&data, &stat->data, 42092547Sarr sizeof(data))) != 0) 42198835Sarr return (error); 42290963Sarr td->td_retval[0] = 0; 42390980Sarr return (error); 42425537Sdfr} 42525537Sdfr 42625537Sdfrint 427225617Skmacysys_modfind(struct thread *td, struct modfind_args *uap) 42825537Sdfr{ 42990963Sarr int error = 0; 43090963Sarr char name[MAXMODNAME]; 43190963Sarr module_t mod; 43225537Sdfr 433107849Salfred if ((error = copyinstr(uap->name, name, sizeof name, 0)) != 0) 43498835Sarr return (error); 43525537Sdfr 43692547Sarr MOD_SLOCK; 43790963Sarr mod = module_lookupbyname(name); 43890963Sarr if (mod == NULL) 43990963Sarr error = ENOENT; 44090963Sarr else 44192547Sarr td->td_retval[0] = module_getid(mod); 44292547Sarr MOD_SUNLOCK; 44390980Sarr return (error); 44425537Sdfr} 445140482Sps 446176252SjhbMODULE_VERSION(kernel, __FreeBSD_version); 447176252Sjhb 448205014Snwhitehorn#ifdef COMPAT_FREEBSD32 449140482Sps#include <sys/mount.h> 450174377Sjhb#include <sys/socket.h> 451140482Sps#include <compat/freebsd32/freebsd32_util.h> 452140482Sps#include <compat/freebsd32/freebsd32.h> 453140482Sps#include <compat/freebsd32/freebsd32_proto.h> 454140482Sps 455140482Spstypedef union modspecific32 { 456140482Sps int intval; 457209390Sed uint32_t uintval; 458140482Sps int longval; 459209390Sed uint32_t ulongval; 460140482Sps} modspecific32_t; 461140482Sps 462140482Spsstruct module_stat32 { 463140482Sps int version; 464140482Sps char name[MAXMODNAME]; 465140482Sps int refs; 466140482Sps int id; 467140482Sps modspecific32_t data; 468140482Sps}; 469140482Sps 470140482Spsint 471140482Spsfreebsd32_modstat(struct thread *td, struct freebsd32_modstat_args *uap) 472140482Sps{ 473140482Sps module_t mod; 474140482Sps modspecific32_t data32; 475140482Sps int error = 0; 476140482Sps int id, namelen, refs, version; 477140482Sps struct module_stat32 *stat32; 478140482Sps char *name; 479140482Sps 480140482Sps MOD_SLOCK; 481140482Sps mod = module_lookupbyid(uap->modid); 482140482Sps if (mod == NULL) { 483140482Sps MOD_SUNLOCK; 484140482Sps return (ENOENT); 485140482Sps } 486140482Sps 487140482Sps id = mod->id; 488140482Sps refs = mod->refs; 489140482Sps name = mod->name; 490142067Sps CP(mod->data, data32, intval); 491142067Sps CP(mod->data, data32, uintval); 492142067Sps CP(mod->data, data32, longval); 493142067Sps CP(mod->data, data32, ulongval); 494140482Sps MOD_SUNLOCK; 495140482Sps stat32 = uap->stat; 496140482Sps 497140482Sps if ((error = copyin(&stat32->version, &version, sizeof(version))) != 0) 498140482Sps return (error); 499140482Sps if (version != sizeof(struct module_stat_v1) 500140482Sps && version != sizeof(struct module_stat32)) 501140482Sps return (EINVAL); 502140482Sps namelen = strlen(mod->name) + 1; 503140482Sps if (namelen > MAXMODNAME) 504140482Sps namelen = MAXMODNAME; 505140482Sps if ((error = copyout(name, &stat32->name[0], namelen)) != 0) 506140482Sps return (error); 507140482Sps 508140482Sps if ((error = copyout(&refs, &stat32->refs, sizeof(int))) != 0) 509140482Sps return (error); 510140482Sps if ((error = copyout(&id, &stat32->id, sizeof(int))) != 0) 511140482Sps return (error); 512140482Sps 513140482Sps /* 514140482Sps * >v1 stat includes module data. 515140482Sps */ 516140482Sps if (version == sizeof(struct module_stat32)) 517140482Sps if ((error = copyout(&data32, &stat32->data, 518140482Sps sizeof(data32))) != 0) 519140482Sps return (error); 520140482Sps td->td_retval[0] = 0; 521140482Sps return (error); 522140482Sps} 523140482Sps#endif 524